comctl32.ocxまたはmscomctl.ocxツリービューでtvm_getitemメッセージが失敗するのはなぜですか?
質問
ツリービューがリモートプロセスであっても、ツリービューアイテムのテキストを生成できる関数を書きました。この関数は、リモートプロセスで2つのメモリのチャンクを割り当て、TVITEM構造(リモートプロセスにコピーされたもの)に入力し、TVM_GetItemメッセージを送信し、最後に2番目のリモートメモリチャンクの内容をローカルバッファに戻します。これはコードです:
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_GETITEMA
また TVM_GETITEMW
)しかし、それは助けにはならなかったようです。
解決
わかりました、別のショットを与えましょう。
新しいツリービューが期待しています TVITEMEX
それ以外の TVITEM
, 、そして普通がないので cbSize
フィールド、コントロールは受信し、想定するバージョンを判断できません TVITEMEX
. 。 TreeViewが最後のメンバーにアクセスできないことに問題があるかもしれません TVITEMEX
メモリが割り当てられていないためです。使用してみてください TVITEMEX
または、もう少しメモリを割り当てます TVITEM
実際に必要なものよりも。
それを考慮してください
返されたテキストは、アプリケーションによって渡された元のバッファに必ずしも保存されるわけではありません。 PSZTextは、古いバッファに配置するのではなく、新しいバッファーでテキストを指す可能性があります。
したがって、別のプロセスメモリから読む必要がある場合があります。
また、VirtualAllocexがメモリをリセットするため、バッファーはゼロになります。
そして、最後でおそらく役に立たないリゾートとして、使用してみてください MEM_RESERVE|MEM_COMMIT
ただの代わりに MEM_COMMIT
.
他のヒント
Unicodeが定義されている場合にコンパイルされている場合、コードは予想どおりに機能しませんが、リモートプロセスはそうではありません(またはその逆の方法)。電話する必要があります iSwindowunicode 最初に treeView
リモート側がUnicodeメッセージを期待するかどうかを確認するために処理します。
この場合、SendMessageが行う標準の双方向マーシャリングでは十分ではないため、これが必要です。リモート側がユニコードウィンドウであるかどうかに応じて、2つのまったく異なるウィンドウメッセージを送信する必要があります。 Unicodeの場合は、tvm_getitemwでsendmessagewを使用してください。 ANSIの場合は、tvm_getitemaを使用してsendmessageaを使用してください。
これは、すべての共通コントロールに適用されますが、基本的なコントロールセットには適用されません(ウィンドウメッセージ<1024を使用します)。
また、64ビットのバイナリにコンパイルされている場合、コードは破損すると考えていますが、リモートプロセスは32ビット(またはその逆)です。これは、コードがローカル(たとえば:64bit)TVITEMをリモートプロセスにコピーし、TVM_GETITEM(a | w)メッセージを扱っている間にリモートプロセスが予想どおりにトレアードすることを期待するためです。ただし、構造のサイズは異なる場合があります(ポインターサイズが異なるため)。
Spy ++を使用して、treeviewがnm_customdraw通知フラグを使用してwm_notifyメッセージを処理するかどうかを確認します。もしそうなら、運が悪い。実際のデータはどういうわけか内部に保存されており、それを引き出す機会がほとんどありません。
これは、以前のバージョンのWindows BTWに等しく適用されます。