- 題名: C#のWindowsAPIでツリーのテキストが取得できません
- 日時: 2012/01/25 13:33:56
- ID: 29665
- この記事の返信元:
- (なし)
- この記事への返信:
- [29667] Re[1]: C#のWindowsAPIでツリーのテキストが取得できません2012/01/25 14:55:42
- ツリーを表示
Hongliang様 魔界の仮面弁士様
さるあふろです。ご回答いただきどうもありがとうございました。
うわわわわ・・・ずいぶんヘタレなコードだったのですね。お恥ずかしい限りです。
Int64は単に大きいほうがいいかな。とか、あちこちのサイトでコピペしてきたものでよく意味がわかっておりませんでした。
ご丁寧に解説いただきどうもありがとうございます。なるほどと納得です。
エラーについてはSendMessageの戻り値が0になってしまう事で、SendMessageがうまくいかなかったという意味でした。
(うまくいくと0以外の戻り値が返るということでしたのでこりゃエラーかと思い込んでしまいました)
分かりにくい説明をしてしまい申し訳ありませんでした。
お陰さまでSendMessageの戻り値が0以外で戻ってくるコードに修正できました。
ご指摘の通り 最初のSendMessageの宣言部分の wParam,lParamの引数を IntPtr型に修正して、
コード内のSendMessageの第4引数にポインタそのものを指定することでうまくいきました。
<修正1>
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam);
<修正2>
pRet = SendMessage(treewindow, TVM_GETITEM, 0, ref pSysShared);
↓
pRet = SendMessage(treewindow, TVM_GETITEM, IntPtr.Zero, pSysShared);
しかしながら、今度は共有メモリから情報取得して表示するところで 文字化けした変な文字が帰ってきてしまいます。
何度も見直してみたのですが、どうしても解決しません・・・もしよろしければご意見を頂けますと至極幸いに存じます。
using System;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace VaultPanelControl
{
public class VaultPNCTL
{
public struct TVITEM
{
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public uint lParam;
public int iIntegral;
}
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint dwFreeType);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize, ref uint lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize, ref uint lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
static extern void MoveMemory(IntPtr dst, IntPtr src, int size);
private static uint PROCESS_VM_OPERATION = 0x00000008;
private static uint PROCESS_VM_READ = 0x00000010;
private static uint PROCESS_VM_WRITE = 0x00000020;
private static uint MEM_RESERVE = 0x2000;
private static uint MEM_COMMIT = 0x1000;
private static uint MEM_RELEASE = 0x8000;
private static uint PAGE_READWRITE = 0x0040;
//TreeViewのメッセージコード
public static int TVM_EXPAND= 0x1102; //アイテムを開く・閉じる
public static int TVM_GETITEM= 0x110C; //アイテムの属性を取得
public static int TVM_GETNEXTITEM= 0x110A; //指定されたアイテムを取得
//TVM_GETNEXTITEM に指定する wParam = flag
public static int TVGN_ROOT= 0x0000; //ツリービューのルート(最も上の階層)のアイテムを取得します。
public static int TVGN_CARET= 0x0009; //現在選択されているアイテムを取得します。
//TVM_EXPANDに指定するwParam = flag
public static int TVE_EXPAND= 0x1102; //子アイテムのツリーを開きます
//TVITEMEXのmask
public static uint TVIF_TEXT = 0x0001; //pszText, cchTextMax
static void Main(string[] args)
{
uint pid = 0xED8; // 相手アプリケーションのプロセスID(直接指定)
IntPtr treewindow = (IntPtr)0x20E92; // ツリーウィンドウのハンドル(直接指定)
IntPtr hwindow = (IntPtr)0xA0620; //親ウィンドウハンドル直接指定
uint result = GetWindowThreadProcessId(hwindow, out pid);
Process p = Process.GetProcessById((int)pid);
//カレントのハンドル
IntPtr lItemWnd = SendMessage(treewindow, TVM_GETNEXTITEM, (IntPtr)TVGN_CARET, IntPtr.Zero);
//ツリーを開く
SendMessage(treewindow, TVM_EXPAND, (IntPtr)TVE_EXPAND, lItemWnd);
//相手プロセスのオープン
IntPtr pProcessH = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, pid);
if (pProcessH.Equals(IntPtr.Zero))
{
MessageBox.Show("相手側のプロセスが取得できませんでした");
return;
}
//pszText設定用の文字列領域確保
int MAX_STR = 260;
//TVITEM構造体を宣言 初期化
TVITEM tvitem = new TVITEM();
int tvitemSize = Marshal.SizeOf(tvitem);
int dwSize = tvitemSize + MAX_STR;
//自プロセスの共有メモリ確保
IntPtr pLocalShared = Marshal.AllocHGlobal(dwSize);
//Inventor(相手)プロセス内に共有メモリの確保
IntPtr pSysShared = VirtualAllocEx(pProcessH, IntPtr.Zero, (uint)dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pSysShared.Equals(IntPtr.Zero))
{
Marshal.FreeHGlobal(pLocalShared);
CloseHandle(pProcessH);
return;
}
IntPtr pszTextInt =new IntPtr(pSysShared.ToInt32() + (int)tvitemSize);
//構造体の初期化
tvitem.mask = TVIF_TEXT;
tvitem.hItem = lItemWnd;
tvitem.pszText = pszTextInt;
tvitem.cchTextMax = MAX_STR;
Marshal.StructureToPtr(tvitem, pLocalShared, false);
//相手プロセス内にTVITEM構造体をコピー
uint retWrite = 0;
bool bRet = WriteProcessMemory(pProcessH, pSysShared, pLocalShared, dwSize, ref retWrite);
//SendMessage発行
IntPtr pRet;
pRet = SendMessage(treewindow, TVM_GETITEM, IntPtr.Zero, pSysShared);
//共有メモリから情報取得
uint retRead = 0;
bRet = ReadProcessMemory(pProcessH, pSysShared, pLocalShared, dwSize, ref retRead);
IntPtr refBuf = (IntPtr)(pLocalShared.ToInt32() + tvitemSize);
MessageBox.Show(Marshal.PtrToStringUni(refBuf));
string a = Marshal.PtrToStringUni(refBuf);
//Inventor(相手)プロセス内の共有メモリを解放する
VirtualFreeEx(pProcessH, pSysShared, (uint)dwSize, MEM_RELEASE);
//自プロセス内共有メモリを解放する
Marshal.FreeHGlobal(pLocalShared);
//Inventor(相手)プロセスのクローズ
CloseHandle(pProcessH);
}}}
■No29746に返信(さるあふろさんの記事)
> Int64は単に大きいほうがいいかな。とか、
32bit か 64bit かは、IntPtr.Size で判断できます。
最近は 64bit OS も増えてきたので、それぞれの違いを考慮しましょう。
> public uint lParam;
SendMessage の lParam と同様に、TVITEM.lParam も
IntPtr にしておいた方が良いかと。
> [DllImport("User32.dll", EntryPoint = "SendMessage")]
文字列を扱う API を使うときには、Charset 指定を行うことをお奨めします。
同様に、TVITEM にも StructLayout 属性を明示しましょう。
> IntPtr pszTextInt =new IntPtr(pSysShared.ToInt32() + (int)tvitemSize);
TVITEM の後ろに文字列領域を確保すべきではありません。
この構造体の定義は環境によって異なるため、拡張メンバーの位置に
文字列領域を被せると、その値が破壊される可能性があるためです。
なお手元の SDK では、TVITEM の構造は
UINT mask;
HTREEITEM hItem;
UINT state;
UINT stateMask;
LPWSTR pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
LPARAM lParam;
int iIntegral; // (_WIN32_IE >= 0x0400)
UINT uStateEx; // (_WIN32_IE >= 0x0600)
HWND hwnd; // (_WIN32_IE >= 0x0600)
int iExpandedImage; // (_WIN32_IE >= 0x0600)
int iReserved; // (NTDDI_VERSION >= NTDDI_WIN7)
のようになっていました。
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace VaultPanelControl
{
public class VaultPNCTL
{
[StructLayout(LayoutKind.Sequential)]
public struct TVITEM
{
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public IntPtr lParam;
public int iIntegral;
public uint uStateEx;
public IntPtr hwnd;
public int iExpandedImage;
public int iReserved;
}
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr FindWindow(string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hWnd, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint dwFreeType);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize, ref uint lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize, ref uint lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
static extern void MoveMemory(IntPtr dst, IntPtr src, int size);
private static uint PROCESS_VM_OPERATION = 0x00000008;
private static uint PROCESS_VM_READ = 0x00000010;
private static uint PROCESS_VM_WRITE = 0x00000020;
private static uint MEM_RESERVE = 0x2000;
private static uint MEM_COMMIT = 0x1000;
private static uint MEM_RELEASE = 0x8000;
private static uint PAGE_READWRITE = 0x0040;
//TreeViewのメッセージコード
public static int TVM_EXPAND = 0x1102; //アイテムを開く・閉じる
public static int TVM_GETITEM = 0x110C; //アイテムの属性を取得
public static int TVM_GETNEXTITEM = 0x110A; //指定されたアイテムを取得
//TVM_GETNEXTITEM に指定する wParam = flag
public static int TVGN_ROOT = 0x0000; //ツリービューのルート(最も上の階層)のアイテムを取得します。
public static int TVGN_NEXT = 0x0001; //指定されたアイテムの同じグループ内の次のアイテムを取得します。
public static int TVGN_CARET = 0x0009; //現在選択されているアイテムを取得します。
//TVM_EXPANDに指定するwParam = flag
public static int TVE_EXPAND = 0x1102; //子アイテムのツリーを開きます
//TVITEMEXのmask
public static uint TVIF_TEXT = 0x0001; //pszText, cchTextMax
static void Main(string[] args)
{
uint pid = 0x14A8; // 相手アプリケーションのプロセスID(直接指定)
IntPtr treewindow = (IntPtr)0x70F10; // ツリーウィンドウのハンドル(直接指定)
IntPtr hwindow = (IntPtr)0x106D8; //親ウィンドウハンドル直接指定
uint result = GetWindowThreadProcessId(hwindow, out pid);
Process p = Process.GetProcessById((int)pid);
//カレントのハンドル
IntPtr lItemWnd = SendMessage(treewindow, TVM_GETNEXTITEM, (IntPtr)TVGN_CARET, IntPtr.Zero);
//ツリーを開く
SendMessage(treewindow, TVM_EXPAND, (IntPtr)TVE_EXPAND, lItemWnd);
//ノードの名称を取得******************************************
//相手プロセスのオープン
IntPtr pProcessH = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, pid);
if (pProcessH.Equals(IntPtr.Zero))
{
return;
}
try
{
//pszText設定用の文字列領域確保
int MAX_STR = 260;
//TVITEM構造体を宣言 初期化
TVITEM tvitem = new TVITEM();
int tvitemSize = Marshal.SizeOf(tvitem);
int dwSize = tvitemSize + MAX_STR;
//自プロセスの共有メモリ確保
//TVITEM格納分
IntPtr pLocalShared = Marshal.AllocHGlobal(tvitemSize);
try
{
//タイトルバッファ格納分
IntPtr pLocalShared_Buf = Marshal.AllocCoTaskMem(MAX_STR);
try
{
//相手側プロセスの共有メモリ確保
//TVITEM格納分
IntPtr pSysShared = VirtualAllocEx(pProcessH, IntPtr.Zero, (uint)tvitemSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pSysShared.Equals(IntPtr.Zero))
{
return;
}
try
{
//タイトルバッファ格納分
IntPtr pSysShared_Buf = VirtualAllocEx(pProcessH, IntPtr.Zero, (uint)MAX_STR, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pSysShared_Buf.Equals(IntPtr.Zero))
{
return;
}
try
{
//構造体の初期化
tvitem.mask = TVIF_TEXT;
tvitem.hItem = lItemWnd;
tvitem.pszText = pSysShared_Buf;
tvitem.cchTextMax = MAX_STR;
//構造体のポインタを確保したアドレスに移動
Marshal.StructureToPtr(tvitem, pLocalShared, false);
//相手プロセス内にTVITEM構造体をコピー
uint retWrite = 0;
bool bRet = false;
bRet = WriteProcessMemory(pProcessH, pSysShared, pLocalShared, tvitemSize, ref retWrite);
if (bRet == false)
{
return;
}
//SendMessage発行
IntPtr pRet;
pRet = SendMessage(treewindow, TVM_GETITEM, IntPtr.Zero, pSysShared);
if (pRet == IntPtr.Zero)
{
return;
}
//共有メモリから情報取得
uint retRead = 0;
bRet = ReadProcessMemory(pProcessH, pSysShared_Buf, pLocalShared_Buf, MAX_STR, ref retRead);
if (bRet == false)
{
return;
}
MessageBox.Show(Marshal.PtrToStringAnsi(pLocalShared_Buf));
}
finally
{
//相手プロセス内の共有メモリを解放する
VirtualFreeEx(pProcessH, pSysShared_Buf, (uint)dwSize, MEM_RELEASE);
}
}
finally
{
//相手プロセス内の共有メモリを解放する
VirtualFreeEx(pProcessH, pSysShared, (uint)dwSize, MEM_RELEASE);
}
}
finally
{
//自プロセス内共有メモリを解放する
Marshal.FreeHGlobal(pLocalShared_Buf);
}
}
finally
{
//自プロセス内共有メモリを解放する
Marshal.FreeHGlobal(pLocalShared);
}
}
finally
{
//相手プロセスのクローズ
CloseHandle(pProcessH);
}
}
}
}
分類:[.NET]
C#(VisualStadio2008)で、WindowsAPIを使用してツリーのタイトルを取得しようとしています。
ツリーウィンドウのウィンドウハンドルやnodeは取得できているようで、ツリーの展開まではできます。しかし、そこからSendMessageのTVITEMGETを使用してタイトルを取得しようとすると取得してきた変数にはスペースのみが入っており、取得できておりません。
同様の処理をするコードをC++,VB.Net(VS2008)で書いても見ましたが同じ現象です。
以下のコードなのですが、不具合点等ご指摘いただけますと幸いです。
宜しくお願いいたします。
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace VaultPanelControl2
{
public class VaultPNCTL2
{
public struct TVITEM
{
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public uint lParam;
public int iIntegral;
}
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern Int32 SendMessage(Int32 hWnd, Int32 Msg, Int32 wParam, Int32 lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern Int32 SendMessage(Int32 hWnd, Int32 Msg, Int32 wParam, ref TVITEM lParam);
//TreeViewのメッセージコード
public static int TVM_EXPAND = 0x1102; //アイテムを開く・閉じる
public static int TVM_GETNEXTITEM = 0x110A; //指定されたアイテムを取得
public static int TVM_GETITEM = 0x110C; //アイテムの属性を取得
//TVM_EXPANDに指定するwParam = flag
public static int TVE_EXPAND = 0x1102; //子アイテムのツリーを開きます
//TVM_GETNEXTITEM に指定する wParam = flag
public static int TVGN_ROOT = 0x0000; //ツリービューのルート(最も上の階層)のアイテムを取得します。
//TVITEMEXのmask
public static uint TVIF_TEXT = 0x0001; //pszText, cchTextMax
static void Main(string[] args)
{
//TreeViewのウィンドウハンドル(spy++で見て引数として渡しています)を引数で受取り16進数をLong型に変換
int treewindow = Convert.ToInt32(args[0], 16);
//ノードのタイトルを取得して入れるための変数の宣言
string nodeTitle = Microsoft.VisualBasic.Strings.Space(256);
//トップノードの取得
int TopNode = 0;
TopNode = SendMessage(treewindow, TVM_GETNEXTITEM, TVGN_ROOT, 0);
//ツリーを開く(ここまではうまくいっています)
SendMessage(treewindow, TVM_EXPAND, TVE_EXPAND, TopNode);
//ノードの名称を取得
TVITEM tvitem = new TVITEM();
tvitem.mask = TVIF_TEXT;
tvitem.hItem = (IntPtr)TopNode;
tvitem.pszText = nodeTitle;
tvitem.cchTextMax = nodeTitle.Length;
int ret = SendMessage(treewindow, TVM_GETITEM, 0, ref tvitem);
//↓スペースが20個出力されて終わります。
MessageBox.Show(tvitem.pszText);
}
}
}