【问题标题】:Get Treeview(SysTreeView32) items text using win32 api使用 win32 api 获取 Treeview(SysTreeView32) 项目文本
【发布时间】:2018-05-02 04:02:36
【问题描述】:

我正在编写一个应用程序来自动化我工作中的一些重复性任务。 我希望做的一项任务是能够自动化从 Windows 10 中的“RecoveryDrive.exe”创建恢复驱动器的过程。所有过程都已完成,但在一个步骤中,人类需要选择驱动器在 SysTreeView32 控件中。

我试图找到如何获取当前选定的treeNodeItem的文本。

我有控件的句柄,但是当我尝试阅读它时,使用网上找到的代码示例,recoveryDrive 应用程序崩溃了。

我怀疑这与 64 位/32 位与我正在使用的 api 方法不匹配以及 ASCI 和 Unicode 编码不匹配有关......我还认为我需要在目标应用程序句柄或内存中使用 LocalAlloc

here is the pasteBin of the code in the present state.

它也有我基于我的代码的 3 页。使用 sendMessage 时,GetTreeItemText 函数中的应用程序崩溃。

我找到了一些关于如何在 C++ 中执行此操作的示例,但我不太了解。

 public static string GetTreeItemText(IntPtr treeViewHwnd, IntPtr hItem)
            {
                int ret;
                TVITEM tvi = new TVITEM();
                IntPtr pszText = LocalAlloc(0x40, MY_MAXLVITEMTEXT);

                tvi.mask = TVIF_TEXT;
                tvi.hItem = hItem;
                tvi.cchTextMax = MY_MAXLVITEMTEXT;
                tvi.pszText = pszText;

                ret = SendMessageTVI(treeViewHwnd, TVM_GETITEM, 0, ref tvi);
                string buffer = Marshal.PtrToStringUni((IntPtr)tvi.pszText,
                MY_MAXLVITEMTEXT);

                //char[] arr = buffer.ToCharArray(); //<== use this array to look at the bytes in debug mode

                LocalFree(pszText);
                return buffer;
            }

【问题讨论】:

  • 为什么不使用 UIAutomation?
  • 你正在另一个进程中访问内存,你必须用VirtualAllocEx分配内存。调用者和目标之间的 32 位/64 位匹配也很重要。 ANSI/Unicode 匹配无关紧要(我认为只要源应用程序是 Unicode 就可以了)在 C# 中,与这种方法相比,使用 UI 自动化应该更容易。
  • 我认为只有核心消息会被自动编组,所以在控件上使用跨进程 SendMessage 是行不通的。
  • @bunglehead 它确实有效,您只需要手动编组内存缓冲区

标签: c# winapi sendmessage win32gui


【解决方案1】:

TVM_GETITEM 消息的LPARAM 是指向TVITEM 结构的指针。问题是,该结构必须在拥有 TreeView 控件的同一进程中分配。所以,在跨进程边界发送TVM_GETITEM时,必须使用VirtualAllocEx()在目标进程的地址空间分配TVITEM及其pszText缓冲区,然后使用WriteProcessMemory()/ReadProcessMemory()进行写入/读取该结构的数据。

尝试这样的事情(您可以在PInvoke.net 找到使用的 Win32 API 函数的声明):

public static string GetTreeItemText(IntPtr treeViewHwnd, IntPtr hItem)
{
    string itemText;

    uint pid;
    GetWindowThreadProcessId(treeViewHwnd, out pid);

    IntPtr process = OpenProcess(ProcessAccessFlags.VirtualMemoryOperation | ProcessAccessFlags.VirtualMemoryRead | ProcessAccessFlags.VirtualMemoryWrite | ProcessAccessFlags.QueryInformation, false, pid);
    if (process == IntPtr.Zero)
        throw new Exception("Could not open handle to owning process of TreeView", new Win32Exception());

    try
    {
        uint tviSize = Marshal.SizeOf(typeof(TVITEM));

        uint textSize = MY_MAXLVITEMTEXT;
        bool isUnicode = IsWindowUnicode(treeViewHwnd);
        if (isUnicode)
            textSize *= 2;

        IntPtr tviPtr = VirtualAllocEx(process, IntPtr.Zero, tviSize + textSize, AllocationType.Commit, MemoryProtection.ReadWrite);
        if (tviPtr == IntPtr.Zero)
            throw new Exception("Could not allocate memory in owning process of TreeView", new Win32Exception());

        try
        {
            IntPtr textPtr = IntPtr.Add(tviPtr, tviSize);

            TVITEM tvi = new TVITEM();
            tvi.mask = TVIF_TEXT;
            tvi.hItem = hItem;
            tvi.cchTextMax = MY_MAXLVITEMTEXT;
            tvi.pszText = textPtr;

            IntPtr ptr = Marshal.AllocHGlobal(tviSize);
            try
            {
                Marshal.StructureToPtr(tvi, ptr, false);
                if (!WriteProcessMemory(process, tviPtr, ptr, tviSize, IntPtr.Zero))
                    throw new Exception("Could not write to memory in owning process of TreeView", new Win32Exception());
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }

            if (SendMessage(treeViewHwnd, isUnicode ? TVM_GETITEMW : TVM_GETITEMA, 0, tviPtr) != 1)
                throw new Exception("Could not get item data from TreeView");

            ptr = Marshal.AllocHGlobal(textSize);
            try
            {
                int bytesRead;
                if (!ReadProcessMemory(process, textPtr, ptr, textSize, out bytesRead))
                    throw new Exception("Could not read from memory in owning process of TreeView", new Win32Exception());

                if (isUnicode)
                    itemText = Marshal.PtrToStringUni(ptr, bytesRead / 2);
                else
                    itemText = Marshal.PtrToStringAnsi(ptr, bytesRead);
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
        }
        finally
        {
            VirtualFreeEx(process, tviPtr, 0, FreeType.Release);
        }
    }
    finally
    {
        CloseHandle(process);
    }

    //char[] arr = itemText.ToCharArray(); //<== use this array to look at the bytes in debug mode

    return itemText;
}

【讨论】:

  • 为代码加油!看起来它会起作用。我没有为您的 WriteProcessMemory 和 ReadProcessMemory 找到正确的声明。 Marshal.SizeOf(TVITEM) 抛出错误:'TVITEM' 是一种类型,在给定的上下文中无效。如果您能提供帮助,我将能够对其进行测试:)
  • @Benoit 声明可在PInvoke.net 获得。 Marshal 有一个 SizeOf() overload,它将 System.Type 作为输入。我只是忘了将typeof() 申请到TVITEM。我已经更新了代码。
  • 坦克,我只有 WriteProcessMemory 问题。如果您检查 pinvoke 上的定义,您会看到 lpBuffer 需要一个字节 []。 (pinvoke.net/default.aspx/kernel32/WriteProcessMemory.html)我可以把它改成 IntPtr 吗?
  • @Benoit 是的,您可以将其更改为IntPtr。为TCITEM on PInvoke.net 显示了一个类似的编组示例,它使用IntPtr 而不是byte[]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
  • 2016-04-14
  • 2015-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多