2

我编写了一个函数,它可以产生树视图项的文本,即使树视图位于远程进程中。该函数在远程进程中分配两个内存块,填充 TVITEM 结构(复制到远程进程中),发送 TVM_GETITEM 消息,最后将第二个远程内存块的内容读回本地缓冲区。这是代码:

std::string getTreeViewItemText( HWND treeView, HTREEITEM item )
{
    DWORD pid;
    ::GetWindowThreadProcessId( treeView, &pid );

    HANDLE proc = ::OpenProcess( PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, pid );
    if ( !proc )
        // handle error

    TVITEM tvi;
    ZeroMemory( &tvi, sizeof(tvi) );

    LPVOID tvi_ = ::VirtualAllocEx( proc, NULL, sizeof(tvi), MEM_COMMIT, PAGE_READWRITE);
    if ( !tvi_ )
        // handle error

    TCHAR buffer[100] = { 'X' };

    LPVOID txt_ = ::VirtualAllocEx( proc, NULL, sizeof(buffer), MEM_COMMIT, PAGE_READWRITE );
    if ( !txt_ )
        // handle error

    tvi.mask = TVIF_TEXT | TVIF_HANDLE;
    tvi.pszText =  (LPTSTR)txt_;
    tvi.cchTextMax = sizeof(buffer) / sizeof(buffer[0] );
    tvi.hItem = item;

    if ( !::WriteProcessMemory( proc, tvi_, &tvi, sizeof(tvi), NULL ) )
        // handle error

    if ( !::SendMessage( treeView, TVM_GETITEM, 0, (LPARAM)tvi_ ) )
        // handle error

    if ( !::ReadProcessMemory( proc, (LPCVOID)txt_, buffer, sizeof( buffer ), NULL ) )
        // handle error

    ::VirtualFreeEx( proc, tvi_, 0, MEM_RELEASE );

    ::VirtualFreeEx( proc, txt_, 0, MEM_RELEASE );

    ::CloseHandle( proc );

    return buffer;
}

此代码与您在将WC_TREEVIEW类名传递给CreateWindow. 但是,我注意到它不适用于 MS Common Controls v5 (comctl32.ocx) 或 MS Common Controls v6 (mscomctl.ocx) 提供的较新的树。在这些情况下,返回的文本始终为空(缓冲区全为零)。我还注意到 SendMessage 调用返回零(因此// handle error上面的注释表示的错误处理开始起作用)。我不清楚这是否真的表示错误,无论如何缓冲区都充满了零。

所有其他树视图消息(如 TVM_GETITEMRECT)似乎都运行良好。

有人知道这是为什么吗?我尝试使用 UNICODE 标志(我注意到它TVM_GETITEM被定义为TVM_GETITEMATVM_GETITEMW),但这似乎没有帮助。

4

3 回答 3

5

如果使用定义的 UNICODE 编译代码,则代码不会按预期工作,但远程进程不是(或相反)。您应该首先在句柄上调用IsWindowUnicodetreeView以检查远程端是否需要 Unicode 消息。

这是必需的,因为在这种情况下,SendMessage 所做的标准双向编组是不够的:您必须根据远程端是否是 Unicode 窗口来发送两个完全不同的窗口消息。如果是 Unicode,请将 SendMessageW 与 TVM_GETITEMW 一起使用。如果是 ANSI,请将 SendMessageA 与 TVM_GETITEMA 一起使用。

这适用于所有常用控件,但不适用于基本控件集(使用 < 1024 的窗口消息)。

我还相信,如果将代码编译成 64 位二进制文​​件,代码会中断,但远程进程是 32 位的(或相反)。这是因为代码将其本地(例如:64 位)TVITEM 复制到远程进程中,然后期望远程进程在处理 TVM_GETITEM(A|W) 消息时按预期读取它。但是,结构的大小可能不同(由于指针大小不同)。

于 2010-02-18T09:37:12.713 回答
3

好吧,让我们再试一次。

较新的 TreeViews 期望TVITEMEX而不是TVITEM,并且由于没有通常的cbSize字段,因此控件无法判断它接收和假定的版本TVITEMEXTVITEMEX由于没有分配内存,因此树视图可能存在无法访问最后一个成员的问题。尝试使用或分配比实际需要TVITEMEX更多的内存。TVITEM

还要考虑到

返回的文本不一定会存储在应用程序传递的原始缓冲区中。pszText 可能会指向新缓冲区中的文本,而不是将其放置在旧缓冲区中。

因此,您可能需要从不同的进程内存中读取。

并且,由于 VirtualAllocEx 重置了内存,缓冲区被清零。

作为最后一个可能无用的手段,尝试使用MEM_RESERVE|MEM_COMMIT而不是仅仅MEM_COMMIT.

于 2010-02-18T01:09:37.067 回答
1

使用 Spy++ 查看树视图是否处理带有 NM_CUSTOMDRAW 通知标志的 WM_NOTIFY 消息。如果是这样,那么,倒霉。实际数据以某种方式存储在内部,您几乎没有机会将其取出。

这同样适用于以前版本的 Windows BTW。

于 2010-02-11T11:46:48.537 回答