I wrote a function which can yield the text of a tree view item, even if the tree view is in a remote process. The function allocates two chunks of memory in the remote process, populates a TVITEM structure (which is the copied into the remote process), sends a TVM_GETITEM message and finally reads the contents of the second remote memory chunk back into a local buffer. This is the code:
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;
}
This code works very nicely with the plain tree views which you get when passing the WC_TREEVIEW
class name to CreateWindow
. However, I noticed that it does not work with the newer trees as provided by MS Common Controls v5 (comctl32.ocx) or MS Common Controls v6 (mscomctl.ocx). In those cases, the returned text is always empty (the buffer is all zeroes). I also noticed that the SendMessage call returns zero (hence the error handling denoted by the // handle error
comments above kicks in). It's unclear to me whether this really indicates an error, in any case the buffer is filled with all zeroes.
All other tree view messages (like TVM_GETITEMRECT) seem to work perfectly well.
Does anybody know why that is? I tried playing around with the UNICODE flag (I noticed that TVM_GETITEM
is either defined to TVM_GETITEMA
or TVM_GETITEMW
) but that didn't seem to help.