2010-02-11 30 views
2

我寫了一個函數,可以生成樹視圖項的文本,即使樹視圖位於遠程進程中。該函數在遠程進程中分配兩塊內存,填充TVITEM結構(將其複製到遠程進程中),發送TVM_GETITEM消息,最後將第二個遠程內存塊的內容讀回本地緩衝區。這是代碼:爲什麼TVM_GETITEM消息在comctl32.ocx或mscomctl.ocx樹視圖上失敗?

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),但這似乎沒有幫助。

+0

SendMessage返回0後,請嘗試GetLastError。它重新啓動了什麼?MSDN:Microsoft Windows Vista和更高版本。當一個消息被UIPI阻塞時,最後一個錯誤(使用GetLastError檢索)被設置爲5(拒絕訪問)。 – Igor 2010-02-17 16:05:29

+0

@Igor:好主意,但沒有運氣:調用GetLastError函數()後的SendMessage()產生錯誤代碼0可能的錯誤代碼是在遠程進程設置,因爲同時發放TVM_GETITEM出現了錯誤? – 2010-02-17 16:20:49

回答

3

好吧,讓我們再來一次。

較新的TreeView,預計TVITEMEX代替TVITEM,並且因爲沒有平常cbSize場,控制不能夠告訴它接收並假定TVITEMEX哪個版本。也許有一個問題,因爲沒有分配內存,treeview無法訪問TVITEMEX的最後一個成員。嘗試使用TVITEMEX或爲實際需要分配TVITEM多一點的內存。

同時認爲,

返回文本不一定 被存儲在由應用程序傳遞的原始緩衝區 。它是 可能pszText將指向 文本在一個新的緩衝區中,而不是將其放置在舊緩衝區中的 。

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

而且,由於VirtualAllocEx重置內存,緩衝區歸零。

作爲最後也是無用的度假村,請嘗試使用MEM_RESERVE|MEM_COMMIT而不是MEM_COMMIT

+0

感謝您的建議!我試過TVITEMEX,但沒有效果。最後,事實證明,我不得不明確檢查遠程端是否是Unicode窗口,因爲ANSI和Unicode過程之間的默認SendMessage雙向轉換不適用。詳情請參閱我的回答。 – 2010-02-18 09:38:21

1

使用Spy ++查看樹視圖是否處理帶有NM_CUSTOMDRAW通知標誌的WM_NOTIFY消息。如果確實如此,那麼運氣不好。實際的數據以某種方式存儲在內部,你很少有機會將其取出。

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

+0

好提示!但是,Visual Studio 2008附帶的Spy ++似乎沒有顯示任何記事本的窗口消息。它適用於例如Firefox,但對於記事本,我什麼也沒看到。我仔細檢查了確實啓用了日誌記錄並激活了所有消息類型。可惜: -/ – 2010-02-11 12:00:58

+0

然後檢查UAC設置。應該允許Spy ++運行提升。剛剛檢查,所有的記事本消息都在那裏。 – GSerg 2010-02-11 12:25:08

+0

不幸的是,似乎沒有涉及WM_NOTIFY。 – 2010-02-17 15:24:11

5

如果使用定義的UNICODE編譯代碼,則代碼無法按預期工作,但遠程過程不是(或其他方式)。您應該在treeView句柄上首先調用IsWindowUnicode以檢查遠程端是否期望Unicode消息。

這是必需的,因爲在這種情況下,SendMessage所做的標準雙向編組不夠用:根據遠程端是否是Unicode窗口,您必須發送兩個完全不同的窗口消息。如果是Unicode,請使用帶有TVM_GETITEMW的SendMessageW。如果是ANSI,則使用帶有TVM_GETITEMA的SendMessageA。

這適用於所有的公共控件,但不與基本組控制器(其使用窗口消息< 1024)。

我也相信,如果它編譯成二進制64位的代碼將打破,但遠程過程是32位(或者反過來)。這是因爲代碼將本地(例如:64位)TVITEM複製到遠程進程中,然後期望遠程進程在處理TVM_GETITEM(A | W)消息時按預期方式讀取它。但是,結構的大小可能不同(由於指針大小不同)。

+0

奇怪的是,當我將TVM_GETITEMA發送到Windows 7的treeview(這是unicode的,但其他方式不好,這是真的)時,它在這裏工作得很好。指針問題是一個非常好的點。 – GSerg 2010-02-18 10:39:33

相關問題