本文介绍了如何正确循环/获取文本/选择 SysTreeView32 窗口项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've spent a couple of hours pouring through Microsoft's Dev Center; however, I can't seem to figure out how to do the following two things:

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

  1. Cycle through and view the names of each program under the 'Expert Advisors' section of the 'Navigator' sub window (for example 'MACD Sample' in screenshot below)

选择并双击程序(例如MACD Sample").

select and double click the program (e.g. 'MACD Sample').

Winspector(左) |申请(右)

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

My main problem seems to be that I don't know how to properly use HTREEITEM to access the information. I noticed there is a function ListView_GetItemText, but I've been unable to find a TreeView_GetItemText or equivalent function.

任何帮助将不胜感激.

以下是我的程序的主要功能:

Below is the main function of my program:

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;
}

运行代码的结果似乎可以正常运行

The result of running the code seems to be working properly

完整代码:

// 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 项时,我指的是在树节点上模拟双击操作——类似于双击桌面上的图标以打开程序)

(For clarification, when I say selecting a SysTreeView32 item, I'm referring to simulating a double-click operation on a tree node -- similar to how one can double click an icon on their Desktop to open a program)

看了文档后,我确信:

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

  1. There doesn't exist an explicit message that will simulate double-clicking a node on a tree using the handle to the tree-view item

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

A possible work around would be to send the TVM_GETITEMRECT message to get the coordinates of the tree node, and then use SendInput() to send a click

以上两种说法是否正确?

Are the above two statements correct?

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

After implementing Barmak Shemirani's code, I tried to implement #2 above using the same methodology as in Barmak Shemirani's fix. Specifically, I attempted to allocate a Rect struct in the other Application program's memory with VirtualAllocEx(), call the TreeView_GetItemRect macro in my program with a pointer to the rectangle, and read the results with ReadProcessMemory().

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

However, my program crashes when I call TreeView_GetItemRect(), while passing the pointer to the Rect in the other Apps memory. Most likely, because TreeView_GetItemRect() is trying to write the Rect coordinates to an invalid memory address. This caused me to realize that I don't really understand what the macro is doing:

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

  1. Hence, checking out the source, I found:

#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)))

除了 SNDMSG 函数之前的部分,我基本上都理解了:

I mostly understand everything except for the part before the SNDMSG function:

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

上面的说法到底是什么意思?这是将我传递给 HTREEITEM 指针的矩形指针强制转换为某种方式导致程序崩溃吗?

What exactly does the above statement mean? Is this casting the rectangle pointer that I pass to a HTREEITEM pointer, which is somehow causing the program to crash?

控制台冻结截图

新代码

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;
}

推荐答案

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

This is the method you would normally use to read a TreeView item's text:

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 参数复制数据.但是这种交换是不允许在不同进程之间进行的.

This will not work in your program. TreeView_GetItem is a macro based on SendMessage, it copies data through LPARAM parameter. But this exchange is not allowed between different processes.

您可能会花费数小时甚至数天的时间来尝试破解它(参见此示例)

You could spend hours, possibly days, trying to hack it(See this example)

或者您可能想研究一下目标程序是否支持 用户界面自动化

Or you may want to research and see if the target program supports UI Automation

编辑,这里是获取HTREEITEM文本的示例.这将不起作用,除非:

Edit, here is example to get HTREEITEM text. This won't work unless:

  • 调用者和目标程序都是 32 位的,或者都是 64 位的
  • 调用者和目标程序都是unicode

如果目标程序是 ANSI,则将此函数更改为 ANSI.

If target program is ANSI then change this function to 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

这篇关于如何正确循环/获取文本/选择 SysTreeView32 窗口项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-04 21:25