4

TVM_GETITEMウィンドウ メッセージを使用して、ツリー項目のテキスト (プレーンなCommon Controls Tree Viewに含まれている) を読み取る C++ コードがあります。メッセージを受け取るツリー ビューは別のプロセスにあるため、ウィンドウ メッセージへの引数の 1 つが指す構造体に共有メモリを少し使用しています。リモート プロセスは私の制御下にないため、この作業を行う必要があります (Spy++ に似たアプリケーションを作成しています)。

これは原理的にはうまく機能しますが、ターゲット プロセスが大幅に異なる場合は失敗します。

  1. ターゲット プロセスのコードが UNICODE を定義してビルドされたが、自分のコードはそうでなかった場合、2 つのプロセスは、TVITEM 構造体の文字列メンバーの構造について異なる考えを持つことになります。私はすでにIsWindowUnicode呼び出しを使用してこれを解決し、明示的にまたはのいずれTVM_GETITEMAかを送信しますTVM_GETITEMW(必要に応じて結果を再コーディングします)。

  2. 呼び出しプロセスが 32 ビット モードでビルドされ、ターゲット プロセスが 64 ビット (またはその逆) である場合、ポインターのサイズが異なるため、 TVITEM 構造体のレイアウト (およびサイズ) は異なります。

私は現在、2番目の問題を解決する良い方法を見つけようとしています。この特定の使用例 (ツリー項目テキストの取得) は単なる例です。コードが送信する他のウィンドウ メッセージにも同じ問題が存在します。現在、次の 2 つの方法を検討しています。

  1. コードを 2 回ビルドしてから、ターゲット プロセスの動作に応じて 32 ビットまたは 64 ビット コードを実行します。これには、ビルドおよびパッケージング システムにいくつかの変更が必要であり、アーキテクチャ固有のコードを専用のプロセスに分割する必要があります (現在は DLL 内にあります)。それが完了すると、うまく機能するはずです。
  2. 実行時にターゲット プロセスのイメージ フォーマットを検出し、32 ビットまたは 64 ビット幅のポインターを明示的に使用するTVITEM 構造体の代わりにカスタム構造体を使用します。これには、リモート プロセスのアーキテクチャを検出するためのコードを記述し (リモート プロセスでGetModuleFileNameを呼び出してから、 Image Help Libraryを使用して PE ヘッダーを分析することでこれを実行できることを願っています)、2 つの構造体 (1 つは 32 ビット ポインターを持ち、もう 1 つは を含む) をハードコーディングする必要があります。 64ビット)。さらに、共有メモリ アドレスが 32 ビット アドレス空間にあることを確認する必要があります (32 ビット モードでコンパイルされている場合でも、自分のコードが常にアクセスできるようにするため)。

他の誰かが同様の問題を解決する必要がありましたか? もっと簡単な解決策はありますか?

4

3 に答える 3

1

実行時にリモート プロセスが 32 ビットか 64 ビットかを確認し、メッセージを送信する前に正しい構造を共有メモリに書き込むことになりました。

たとえば、TVM_GETITEMメッセージの発信者と受信者の間で 32 ビット <-> 64 ビットが混在している場合でも、メッセージを使用する方法は次のとおりです。

/* This template is basically a copy of the TVITEM struct except that
 * all fields which return a pointer have a variable type. This allows
 * creating different types for different pointer sizes.
 */
template <typename AddrType>
struct TVITEM_3264 {
  UINT      mask;
  AddrType  hItem;
  UINT      state;
  UINT      stateMask;
  AddrType  pszText;
  int       cchTextMax;
  int       iImage;
  int       iSelectedImage;
  int       cChildren;
  AddrType  lParam;
};
typedef TVITEM_3264<UINT32> TVITEM32;
typedef TVITEM_3264<UINT64> TVITEM64;

// .... later, I can then use the above template like this:
LPARAM _itemInfo;
DWORD pid;
::GetWindowThreadProcessId( treeViewWindow, &pid );
if ( is64BitProcess( pid ) ) {
    TVITEM64 itemInfo;
    ZeroMemory( &itemInfo, sizeof( itemInfo ) );

    itemInfo.mask = TVIF_HANDLE | TVIF_TEXT;
    itemInfo.hItem = (UINT64)m_item;
    itemInfo.pszText = (UINT64)(LPTSTR)sharedMem->getSharedMemory( sizeof(itemInfo) );
    itemInfo.cchTextMax = MaxTextLength;
    _itemInfo = (LPARAM)sharedMem->write( &itemInfo, sizeof(itemInfo) );
} else {
    TVITEM32 itemInfo;
    ZeroMemory( &itemInfo, sizeof( itemInfo ) );

    itemInfo.mask = TVIF_HANDLE | TVIF_TEXT;
    itemInfo.hItem = (UINT32)m_item;
    itemInfo.pszText = (UINT32)(LPTSTR)sharedMem->getSharedMemory( sizeof(itemInfo) );
    itemInfo.cchTextMax = MaxTextLength;
    _itemInfo = (LPARAM)sharedMem->write( &itemInfo, sizeof(itemInfo) );
}

このsharedMem->getSharedMemory関数は、共有メモリ領域へのポインターを取得するための小さなヘルパー関数です。オプションの関数引数は、オフセット値を指定します。重要なのは、共有メモリ領域が常に 32 ビット アドレス空間にあることです (32 ビットのリモート プロセスでもアクセスできるようにするため)。

于 2011-01-05T10:32:13.960 に答える
0

私はこの特定のメッセージに精通していませんが、Windows の TVM_GETITEM メッセージがプロセス間で正しく機能するはずである場合、Windows は呼び出し元のアドレス空間に TVITEM 構造体を入力し、必要な変換を処理する必要があります。指定する必要はありません。共有メモリ。もしそうでなければ、あなたがここで見ている問題は、不快なゆがみなしに簡単に解決できるとは思えません.

共有メモリのビットは私を混乱させます。一般に、両方のプロセスに共有メモリ セグメントを明示的に認識させる必要があり、DLL インジェクションなどについては触れていません。呼び出し先は、アドレス空間内の共有メモリ セクションをどの程度正確に認識し、どのように使用していますか? 本当にこの API に必要ですか?

于 2010-12-20T09:20:49.230 に答える
0

私見には設計上の問題があります。なぜあなたがこのようにしているのかわかりません。おそらく、すべての部分を完全に制御することはできません. しかし、基本的な MVC の観点では、モデルに値を要求するのではなく、ビューから値を覗いています。

于 2010-12-20T08:55:51.063 に答える