簡體   English   中英

為什么TVM_GETITEM消息在comctl32.ocx或mscomctl.ocx樹視圖上失敗?

[英]Why does the TVM_GETITEM message fail on comctl32.ocx or mscomctl.ocx tree views?

我寫了一個函數,它可以生成樹視圖項的文本,即使樹視圖在遠程進程中也是如此。 該函數在遠程進程中分配兩個內存塊,填充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時獲得的普通樹視圖非常WC_TREEVIEW 但是,我注意到它不適用於MS Common Controls v5(comctl32.ocx)或MS Common Controls v6(mscomctl.ocx)提供的較新樹。 在這些情況下,返回的文本始終為空(緩沖區全為零)。 我還注意到SendMessage調用返回零(因此錯誤處理由上面的// handle error注釋表示)。 我不清楚這是否真的表示錯誤,在任何情況下緩沖區都填充了全零。

所有其他樹視圖消息(如TVM_GETITEMRECT)似乎都運行良好。

有誰知道那是為什么? 我嘗試使用UNICODE標志(我注意到TVM_GETITEM要么定義為TVM_GETITEMA還是TVM_GETITEMW ),但這似乎沒有幫助。

如果使用UNICODE定義編譯,但代碼不能按預期工作,但遠程進程不是(或反過來)。 您應首先在treeView句柄上調用IsWindowUnicode ,以檢查遠程端是否需要Unicode消息。

這是必需的,因為在這種情況下,SendMessage執行的標准雙向編組是不夠的:根據遠程端是否是Unicode窗口,您必須發送兩個完全不同的窗口消息。 如果是Unicode,請將SendMessageW與TVM_GETITEMW一起使用。 如果是ANSI,請將SendMessageA與TVM_GETITEMA一起使用。

這適用於所有常用控件,但不適用於基本控件集(使用窗口消息<1024)。

我也相信如果代碼被編譯成64位二進制文​​件,代碼就會中斷,但遠程進程是32位(或者反之亦然)。 這是因為代碼將其本地(例如:64位)TVITEM復制到遠程進程中,然后期望遠程進程在處理TVM_GETITEM(A | W)消息時按預期讀取它。 但是,結構的大小可能不同(由於指針大小不同)。

好吧,讓我們再試一次。

較新的TreeViews期望TVITEMEX而不是TVITEM ,並且由於沒有通常的cbSize字段,控件無法分辨它接收的版本並假設TVITEMEX 也許有一個問題,樹視圖無法訪問TVITEMEX最后成員,因為沒有分配內存。 嘗試使用TVITEMEX或為TVITEM分配比實際需要更多的內存。

還要考慮一下

返回的文本不一定存儲在應用程序傳遞的原始緩沖區中。 pszText可能會指向新緩沖區中的文本而不是將其放在舊緩沖區中。

因此,您可能需要從不同的進程內存中讀取。

並且,緩沖區歸零,因為VirtualAllocEx重置內存。

作為最后也可能無用的手段,請嘗試使用MEM_RESERVE|MEM_COMMIT而不僅僅是MEM_COMMIT

使用Spy ++查看樹視圖是否使用NM_CUSTOMDRAW通知標志處理WM_NOTIFY消息。 如果確實如此,那么,運氣不好。 實際數據以某種方式存儲在內部,你幾乎沒有機會把它拉出來。

這同樣適用於以前版本的Windows BTW。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM