他アプリのツリーのノードタイトルを得るには?


matu  2012-02-01 09:19:07  No: 73172  IP: 192.*.*.*

他のアプリケーションのツリーのノードのタイトルを取得したいのですが、SendMessageでエラーになります。同様の処理をするVB6で記述したコードではうまくいきます。WindowsAPIを多用たいのでできればC++で開発したいと考えております。
仮想メモリでやり取りを行っているのですが、アドレスの指定の仕方等がおかしいでしょうか。ご意見をお聞かせいただけますと幸いです。

  //treewindowおよびpidはspy++で指定ウィンドウのハンドルとプロセスIDを見てプログラム内で直接指定しています。

  //選択されたツリーNodeを取得
  HTREEITEM SelNode;
  SelNode = (HTREEITEM)SendMessage(treewindow, TVM_GETNEXTITEM, TVGN_CARET, 0);

  //プロセスを開く
  HANDLE hProcess;

  hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ |PROCESS_VM_WRITE,false, (DWORD)pid);
  if(hProcess == (HANDLE)NULL){
    return 0;
  }

  //バッファの下準備
  TCHAR szBuffer[MAXTEXTLEN];
  //TVITEM構造体の宣言
  TVITEM item;

  long dwSize = sizeof(item) + MAXTEXTLEN;

  //相手プロセスにメモリを確保
  LPVOID pSysShared;
  pSysShared  = VirtualAllocEx(hProcess,NULL,dwSize,MEM_RESERVE | MEM_COMMIT,PAGE_READWRITE);

  //自プロセスに結果をコピーするバッファを作成
  LPVOID pLocalShared;
  pLocalShared = VirtualAlloc(0, dwSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  
  //TVITEM構造体の初期化
  item.hItem = SelNode;
  item.mask = TVIF_TEXT;
  item.pszText =((LPTSTR)&pLocalShared)+sizeof(item);
  item.cchTextMax = MAXTEXTLEN;
  
  //自プロセス内の仮想メモリにitemを移動
  MoveMemory(pLocalShared,&item,sizeof(item));

  //仮想メモリにitemを書き込む
  DWORD dwNumberOfBytesWrite;
  WriteProcessMemory(hProcess,pSysShared,&pLocalShared,dwSize,&dwNumberOfBytesWrite);

  //メッセージを送信(返り値は失敗の0となってしまいます)
  long ret;
  ret = SendMessage(treewindow, TVM_GETITEM, 0, (LPARAM)pSysShared);
  
  //仮想メモリからitemの内容を読み込む
  DWORD dwNumberOfBytesRead;
  ReadProcessMemory(hProcess,&pSysShared,&pLocalShared,MAXTEXTLEN,&dwNumberOfBytesRead);

  //自プロセス内の仮想メモリでitem分移動して、それ以降のテキスト分(取得したノードのタイトル名)をszBufferに代入する
  MoveMemory(szBuffer,(&pLocalShared + sizeof(item)),MAXTEXTLEN);


  VirtualFree(pLocalShared,0,MEM_RELEASE);                // ローカルバッファを解放する
  VirtualFreeEx(hProcess,pSysShared,0,MEM_RELEASE);            // 「共有メモリ」を解放する
  CloseHandle(hProcess);                          // プロセスハンドルを閉じる

編集 削除
gak  2012-02-01 18:31:30  No: 73173  IP: 192.*.*.*

> VB6で記述したコードではうまくいきます。
正しく VB6 と C 違いに因る事で旨く動いていないと思われる。
実際に動作確認はしていないが、以下提示コードを見て気になった事。

まず TCHAR を正しく考慮できていない。TCHAR は開発プロジェクトの文字セットの違いによって下記の差が出る。

  ・文字セットが Unicode
    ・TCHAR = WCHAR
    ・sizeof(TCHAR) == (VC++標準では)2Byte

  ・文字セットが Unicode 以外
    ・TCHAR = char
    ・sizeof(TCHAR) == (VC++標準では)1Byte

> long dwSize = sizeof(item) + MAXTEXTLEN;
MAXTEXTLEN は”byteサイズ”では無く”文字数”を意味している。よって↓が正しい

  long dwSize = sizeof(item) + sizeof(TCHAR) * MAXTEXTLEN;

> item.pszText =((LPTSTR)&pLocalShared)+sizeof(item);
sizeof(TCHAR) == 2Byte の場合、↑は「item.pszText = (char*)&pLocalShared + sizeof(TCHAR) * sizeof(item)」となり sizeof(item) 一個分多く加算してしまう。

また「&pLocalShared」だと”[ポインタ変数 pLocalShared]が確保されている場所”になる。「&」不要かと。
というか、自プロセス内メモリを指す「pLocalShared」では無く、対象プロセスの「pSysShared」でないとダメなんじゃなかろうか。
この辺りがエラーの直接の原因のように思われる。

  item.pszText = LPTSTR((char*)pSysShared + sizeof(item));

> ReadProcessMemory(hProcess,&pSysShared,&pLocalShared,MAXTEXTLEN,&dwNumberOfBytesRead);
> MoveMemory(szBuffer,(&pLocalShared + sizeof(item)),MAXTEXTLEN);
  ReadProcessMemory(hProcess, pSysShared, pLocalShared, dwSize, &dwNumberOfBytesRead);
  MoveMemory(szBuffer, (char*)pLocalShared + sizeof(item), sizeof(TCHAR) * MAXTEXTLEN);


> LPVOID pLocalShared;
最後に補足。今は pLocalShared を介して pSysShared とやり取りしているようだけど、多分コレ要らない。

  WriteProcessMemory(hProcess, pSysShared, &item, dwSize, &dwNumberOfBytesWrite);

て感じに直接「item」等を指定してやってもいけるんじゃないかな。

編集 削除
さるあふろ  2012-02-06 11:39:42  No: 73174  IP: 192.*.*.*

ご丁寧にどうもありがとうございます!!
初歩的なところばかりで間違えているようで、誠に恐縮です。
アドバイス頂きどうもありがとうございました。やってみてうまくいったらまたこちらに書き込ませていただきたいと思います。

編集 削除