如何正确循环/获取文本/选择SysTreeView32窗口项

时间:2016-01-10 07:59:54

标签: windows user-interface visual-c++

我花了几个小时在微软的开发中心倾注;但是,我似乎无法弄清楚如何做以下两件事:

  1. 在“导航器”子窗口的“EA交易”部分循环查看每个程序的名称(例如下面屏幕截图中的“MACD Sample”)

  2. 选择并双击该程序(例如'MACD Sample')。

  3. Winspector(Left) | Application(Right)

    我的主要问题似乎是我不知道如何正确使用HTREEITEM来访问信息。我注意到有一个函数ListView_GetItemText,但我一直无法找到TreeView_GetItemText或等效函数。

    非常感谢任何帮助。

    以下是我的计划的主要功能:

    int _tmain(int argc, _TCHAR* argv[])
    {
        wcout << TEXT("Enumerating Windows...") << endl;
        HWND handle = NULL;
    
        //--- Success: gets application handle
        bool success1 = getHandle(L"MetaTrader", L"20", handle);
    
        cout << "Success1: " << success1 << endl;
        cout << "Result1: " << handle << endl;
    
        //--- Success: gets navigator window
        bool success2 = getChildHandle(handle, L"", L"Navigator", handle);
    
        cout << "Success2: " << success2 << endl;
        cout << "Result2: " << handle << endl;
    
        //--- Success: gets "SysTreeView32" handle
        handle = FindWindowEx(handle, 0, L"SysTreeView32", L"");
    
        cout << "Result3: " << handle << endl;
    
        //--- Success: get "SysTreeView32" root nod
        HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT);
        cout << "root: " << root << endl;
    
        return 0;
    }
    

    运行代码的结果似乎正常工作

    完整性的完整代码:

    // MT4Terminal-test.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    
    #pragma once
    
    #include "targetver.h"
    
    
    #include <iostream>
    #include <map>
    #include <string>
    
    namespace std {
    #if defined _UNICODE || defined UNICODE
        typedef wstring tstring;
    #else
        typedef string tstring;
    #endif
    }
    
    #include <stdio.h>
    #include <tchar.h>
    #include <Windows.h>
    #include <psapi.h>
    
    #include <Windows.h>
    #include <Commctrl.h>
    #include <windows.system.h>
    
    using namespace std;
    
    HWND glb_handle;
    tstring glb_searchWindowTitle;
    tstring glb_seachClassName;
    
    BOOL CALLBACK enumWindowsChildProc(
        __in  HWND hWnd,
        __in  LPARAM lParam
        ) {
    
    
        return TRUE;
    }
    
    BOOL CALLBACK enumWindowsProc(
        __in  HWND hWnd,
        __in  LPARAM lParam
        ) {
    
        int length = ::GetWindowTextLength(hWnd);
        if (0 == length) return TRUE;
    
        TCHAR* bufferA;
        bufferA = new TCHAR[length + 1];
        memset(bufferA, 0, (length + 1) * sizeof(TCHAR));
    
        TCHAR* bufferB;
        bufferB = new TCHAR[100];
        memset(bufferB, 0, 100 * sizeof(TCHAR));
    
        GetWindowText(hWnd, bufferA, length + 1);
        GetClassName(hWnd, bufferB, 100);
        tstring windowTitle = tstring(bufferA);
        tstring className = tstring(bufferB);
        delete bufferA;
        delete bufferB;
    
        if (windowTitle.find(glb_searchWindowTitle) < string::npos &&
            className.find(glb_seachClassName) < string::npos)
                glb_handle = hWnd;
    
        wcout.clear();
    
        return TRUE;
    }
    
    bool getHandle(wstring searchClassName, wstring searchWindowTitle, HWND &handle)
    {
        handle = NULL;
        glb_handle = NULL;
        glb_searchWindowTitle = searchWindowTitle;
        glb_seachClassName = searchClassName;
        BOOL enumeratingWindowsSucceeded = EnumWindows(enumWindowsProc, NULL);
    
        if (enumeratingWindowsSucceeded)
        {
            if (glb_handle != NULL)
            {
                handle = glb_handle;
                return true;
            }
        }
    
        glb_handle = NULL;
        glb_searchWindowTitle = L"";
        glb_seachClassName = L"";
        return false;
    }
    
    bool getChildHandle(HWND parent_handle, wstring searchClassName, wstring searchWindowTitle, HWND &handle)
    {
        handle = NULL;
        glb_handle = NULL;
        glb_searchWindowTitle = searchWindowTitle;
        glb_seachClassName = searchClassName;
        BOOL enumeratingWindowsSucceeded = EnumChildWindows(parent_handle, enumWindowsProc, NULL);
    
        if (enumeratingWindowsSucceeded)
        {
            if (glb_handle != NULL)
            {
                handle = glb_handle;
                return true;
            }
        }
    
        glb_handle = NULL;
        glb_searchWindowTitle = L"";
        glb_seachClassName = L"";
        return false;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        wcout << TEXT("Enumerating Windows...") << endl;
        HWND handle = NULL;
    
        //--- Success: gets application handle
        bool success1 = getHandle(L"MetaTrader", L"20", handle);
    
        cout << "Success1: " << success1 << endl;
        cout << "Result1: " << handle << endl;
    
        //--- Success: gets navigator window
        bool success2 = getChildHandle(handle, L"", L"Navigator", handle);
    
        cout << "Success2: " << success2 << endl;
        cout << "Result2: " << handle << endl;
    
        //--- Success: gets "SysTreeView32" handle
        handle = FindWindowEx(handle, 0, L"SysTreeView32", L"");
    
        cout << "Result3: " << handle << endl;
    
        //--- Success: get "SysTreeView32" root nod
        HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT);
        cout << "root: " << root << endl;
    
        return 0;
    }
    

    选择SysTreeView32项目

    (为了澄清,当我说选择SysTreeView32项时,我指的是在树节点上模拟双击操作 - 类似于双击桌面上的图标以打开程序的方式)< / p>

    在查看文档后,我确信:

    1. 不存在显式消息,该消息将模拟使用树视图项的句柄双击树上的节点

    2. 可能的解决方法是发送TVM_GETITEMRECT消息以获取树节点的坐标,然后使用SendInput()发送单击

    3. 上述两个陈述是否正确?

      在实施Barmak Shemirani的代码之后,我尝试使用与Barmak Shemirani修复相同的方法实现上面的#2。具体来说,我尝试使用VirtualAllocEx()在其他Application程序的内存中分配一个Rect结构,使用指向矩形的指针调用程序中的TreeView_GetItemRect宏,并使用ReadProcessMemory()读取结果。

      但是,当我调用TreeView_GetItemRect()时,我的程序崩溃,同时将指针传递给其他Apps内存中的Rect。很可能是因为TreeView_GetItemRect()试图将Rect坐标写入无效的内存地址。这让我意识到我并不真正理解宏在做什么:

      1. 因此,检查来源,我发现:

        #define HELLO
        #define TV_FIRST                0x1100      // TreeView messages
        
        #define TVM_GETITEMRECT         (TV_FIRST + 4)
        #define TreeView_GetItemRect(hwnd, hitem, prc, code) \
        (*(HTREEITEM *)(prc) = (hitem), (BOOL)SNDMSG((hwnd), TVM_GETITEMRECT, (WPARAM)(code), (LPARAM)(RECT *)(prc)))
        
      2. 我主要了解除SNDMSG功能之前的部分以外的所有内容:

        (*(HTREEITEM *)(prc) = (hitem),
        

        以上陈述究竟是什么意思?这是否是我传递给HTREEITEM指针的矩形指针,这会导致程序崩溃?

        Screenshot of console freezing

        新代码

        int _tmain(int argc, _TCHAR* argv[])
        {
            wcout << TEXT("Enumerating Windows...") << endl;
            HWND handle = NULL;
        
            //--- Success: gets application handle
            bool success1 = getHandle(L"MetaTrader", L"20", handle);
        
            //--- Success: gets navigator window
            bool success2 = getChildHandle(handle, L"", L"Navigator", handle);
        
            //--- Success: gets "SysTreeView32" handle
            handle = FindWindowEx(handle, 0, L"SysTreeView32", L"");
        
            //--- Success: get "SysTreeView32" root nod
            HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT);
        
            unsigned long pid;
        
            GetWindowThreadProcessId(handle, &pid);
        
            HANDLE process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE |
            PROCESS_QUERY_INFORMATION, FALSE, pid);
        
            TVITEM item, *_item;
        
            wchar_t buf[CHAR_BUF_LEN];
            wchar_t *_buf;
            memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        
            _item = (TVITEM*)VirtualAllocEx(process, NULL, sizeof(TVITEM), MEM_COMMIT, PAGE_READWRITE);
            _buf = (wchar_t*)VirtualAllocEx(process, NULL, CHAR_BUF_LEN, MEM_COMMIT, PAGE_READWRITE);
        
            item.cchTextMax = CHAR_BUF_LEN;
            item.pszText = _buf;
            item.mask = TVIF_TEXT;
        
            //--- find Experts Advisors branch in tree
            HTREEITEM node = TreeView_GetNextItem(handle, root, TVGN_CHILD);
            node = TreeView_GetNextItem(handle, node, TVGN_NEXT);
            node = TreeView_GetNextItem(handle, node, TVGN_NEXT);
        
            RECT rect, *_rect;
        
            _rect = (RECT*)VirtualAllocEx(process, NULL, sizeof(RECT), MEM_COMMIT, PAGE_READWRITE);
        
            rect = { 0 };
        
            WriteProcessMemory(process, _rect, &rect, sizeof(RECT), NULL);
        
            //--- step into Expert Advisors
            node = TreeView_GetNextItem(handle, node, TVGN_CHILD);
        
            //--- target program to open
            wchar_t ea_name[] = L"MACD Sample";
        
            while (node != NULL)
            {
                ZeroMemory(buf, CHAR_BUF_LEN);
        
                item.hItem = node;
        
                //Binds item and _item
                WriteProcessMemory(process, _item, &item, sizeof(TVITEM), NULL);
        
                TreeView_GetItem(handle, _item);
        
                //Read buffer back to this program's process memory
                ReadProcessMemory(process, _buf, buf, CHAR_BUF_LEN, NULL);
        
                //Print program name
                wcout << buf << endl;
        
                if (wcscmp(ea_name, buf) == 0)
                {
                    cout << "Found target program: " << ea_name << endl;
                    cout << "get rectangle coordinates: " << TreeView_GetItemRect(handle, node, _rect, TRUE) << endl;
                }
        
                node = TreeView_GetNextItem(handle, node, TVGN_NEXT);
            }
        
            VirtualFreeEx(process, _item, 0, MEM_RELEASE);
            VirtualFreeEx(process, _buf, 0, MEM_RELEASE);
            VirtualFreeEx(process, _rect, 0, MEM_RELEASE);
        
            return 0;
        }
        

2 个答案:

答案 0 :(得分:2)

这是您通常用于读取TreeView项目文本的方法:

wchar_t buf[100];
memset(buf, 0, sizeof(buf));
TVITEM item = { 0 };
item.hItem = hitem;
item.cchTextMax = 100;
item.pszText = buf;
item.mask = TVIF_TEXT;
TreeView_GetItem(hwnd, &item);
程序中

这不起作用TreeView_GetItem是基于SendMessage的宏,它通过LPARAM参数复制数据。但是不同流程之间不允许这种交换。

你可能花费数小时,甚至几天,试图破解它 (See this example)

或者您可能想要研究并查看目标计划是否支持UI Automation

编辑,这是获取HTREEITEM文字的示例。除非:

,否则这不会奏效
  • 调用者和目标程序都是32位,或者都是64位
  • 调用者和目标程序都是unicode

如果目标程序是ANSI,则将此功能更改为ANSI。

HTREEITEM hitem = TreeView_GetSelection(hwndTree);
if (!hitem)
    debug << "!hitem\n";

const int buflen = 512;

DWORD pid;
GetWindowThreadProcessId(hwndTree, &pid);
HANDLE process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE 
    | PROCESS_QUERY_INFORMATION, FALSE, pid);
TVITEMEX* ptv = (TVITEMEX*)VirtualAllocEx(process, NULL, sizeof(TVITEMEX), 
    MEM_COMMIT, PAGE_READWRITE);
wchar_t* pbuf = (wchar_t*)VirtualAllocEx(process, NULL, buflen, 
    MEM_COMMIT, PAGE_READWRITE);

TVITEMEX tv = { 0 };
tv.hItem = hitem;
tv.cchTextMax = buflen / 2;
tv.pszText = pbuf;
tv.mask = TVIF_TEXT | TVIF_HANDLE;

WriteProcessMemory(process, ptv, &tv, sizeof(TVITEMEX), NULL);

if (SendMessageW(hwndTree, TVM_GETITEM, 0, (LPARAM)(TVITEMEX*)(ptv)))
{
    wchar_t buf[buflen / 2];
    ReadProcessMemory(process, pbuf, buf, buflen, 0);
    debug << "Result:" << buf << "\n";
}
else
    debug << "!SendMessageW\n";

VirtualFreeEx(process, ptv, 0, MEM_RELEASE);
VirtualFreeEx(process, pbuf, 0, MEM_RELEASE);
CloseHandle(process); //*** I forgot this line before

答案 1 :(得分:0)

投票最多的答案已经解决了您的问题,但我想在声明中添加一些评论:

(*(HTREEITEM *)(prc) = (hitem),

TVM_GETITEMRECT解释说:

  

发送此消息时,lParam参数包含要为其获取矩形的项目的句柄。

在宏TreeView_GetItemRect中,prc将被_rect替换,而TreeView_GetItemRect(handle, node, _rect, TRUE) 是在其他进程中分配的。因此程序崩溃了。 根据您的情况,您可以替换代码:

RECT rect, *_rect;

_rect = (RECT*)VirtualAllocEx(process, NULL, sizeof(RECT), MEM_COMMIT, PAGE_READWRITE);

*(HTREEITEM*)&rect = node;

WriteProcessMemory(process, _rect, &rect, sizeof(RECT), NULL);
SendMessage(handle, TVM_GETITEMRECT, true, (LPARAM)_rect);

作者:

customer._id