前提・実現したいこと
初めての質問です。不手際があれば申し訳ありません。
C#初心者でわからないことがあり質問させていただきます。
C#プログラムで現在作成しているwindows formから
別で作成したwindows formのlistViewのデータを取得したく、
Win APIのsendmessage使用して作成しています。
listviewのハンドルを取って、行削除などのsendmessageはうまく動作するので
ハンドルの取得自体は問題なくいけていると思うのですが、
テキストを取得するsendmessageがどうしても上手くいきません。
sendmessageを投げると、listviewのあるアプリ側が特にエラーも発生せずに落ちてしまいます。
構造体をセットするのでメモリ関係だとは思うのですが、C++を扱ったことがなくよくわかっていない状態です。
よろしくお願いいたします。
発生している問題・エラーメッセージ
エラーメッセージは出ず、取得したいアプリ側が落ちてしまいます
該当のソースコード
C#
public partial class Form1:Form { [DllImport("user32.dll",CharSet = CharSet.Auto,SetLastError = true)] public static extern IntPtr FindWindow (string lpClassName,string lpWindowName); [DllImport("user32.dll",CharSet = CharSet.Auto,SetLastError = true)] public static extern IntPtr SendMessage(IntPtr hWnd,UInt32 Msg,int wParam,StringBuilder Iparam); [DllImport("user32.dll",CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd,UInt32 Msg,int wParam,StringBuilder Iparam); [DllImport("user32.dll",CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd,UInt32 Msg,int wParam,byte[] Iparam); public delegate bool EnumWindows(IntPtr hWnd, IntPtr lparam); public const int LVM_FIRST = 0x1000; public Form1() { IntitializeComporment(); } private void button1_Click(object sender, EventArgs e) { IntPtr hWnd = IntPtr.Zero; hWnd = FindWindw(null,"Form1");//ListViewのあるアプリのwindowハンドルを取得 if(hWnd != Int.Zero) { bool ret = NativeMethods.EnumChildWindows(hWnd , EnumChildWindowsProc,new IntPtr(1));//現在のハンドルの子ウインドウを取得 } } public bool EnumChildWindows(IntPtr hWnd ,IntPtr lParam) { tagLVITEMA tL = new tagLVITEMA();//構造体のnew int size = Marshal.SixeOf(tL); IntPtr ptrtL = Marshal.AllocCoTaskMem(size);//アンマネージメモリからメモリを割り当て byte[] bytetL = new byte [size]; Marshal.StructureToPtr(tL,ptrTl,false);//マネージオブジェクトからアンマネージメモリにデータをマーシャリング Marshal.Copy(ptrtL,bytetl,0,size);//アンマネージデータをマネージのbyte[]にコピーする var count = NatuveMethods.GetWindowTextLength(hwnd); var sb = new StringBuilder (count + 1); StringBuilder classname = new StringBuilder(256); var name = classname.ToString(); if(name == "WindosForm10.SysListView32.app.-----")//listView IntPtr count2 = SendMessage(hWnd,LVM_FIRST + 4 ,0,0);//アイテム数取得→行数を取得できる var SendMessage(hWnd ,LVM_FIRST + 45,0,bytetL);//LVM_GETITEMTEXT →ここで相手側ソフトが落ちる } return true; } class NativeMethods { [return:MarshalAs (UnmanagedType.Bool)] public delegate bool EnumWindowsProc(IntPtr hWnd,IntPtr lParam); [DllImport(user32.dll),SetLastError = true)] [return:MarshalAs (UnmanagedType.Bool)] public static extern bool EnumWindows(EnumWindowProc lpEnumFunc,IntPtr lPram); [DllImport(user32.dll),SetLastError = true)] [return:MarshalAs (UnmanagedType.Bool)] public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowProc lpEnumFunc,IntPtr lPram); [DllImport(user32.dll),SetLastError = true)] public static extern Int32 GetWindowTextLength(IntPtr hWnd); } [StructLayout(LayoutKind.Sequential)] public struct tagLVITEMA //構造体 { public uint mask; public in iItem; public int iSubItem; public uint state; public uint stateMask; public IntPtr pszText; public int cchTextMax; public int iImage; public IntPtr lParam; public int iIndent; public iGroupId; public uint cColumns; public IntPtr puColumns; public IntPtr piColFmt; public int iGroup; }
###9/16修正後
ソフトが落ちることはなくなりましたが、textの中身は””状態で取得することができませんでした。。
構造体はすべてintに変更しています
〇主コード抜粋
//listViewのプロセスIDを取得する int dwProcessId; GetWindowThreadProcessId(hWnd, out dwProcessId); // 取得したプロセスIDをオープンする IntPtr hProcess; hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, (uint)dwProcessId); //必要なリストビューアイテムのテキストを格納するために、文字列にバッファを割り当て var textBufferPtr = VirtualAllocEx(hProcess, IntPtr.Zero,MAX_LVMSTRING,AllocationType.Commit, MemoryProtection.ReadWrite); //listViewの行数 int itemId = 2; //listViewの列数 int subItemId = 2; //構造体のnew tagLVITEMA lvItem = new tagLVITEMA(); lvItem.mask = LVIF_TEXT; lvItem.cchTextMax = (int)MAX_LVMSTRING; lvItem.iItem = itemId; lvItem.iSubItem = subItemId; //リモートプロセスでLVITEM構造にメモリを割り当て int lvItemSize = Marshal.SizeOf(lvItem); var lvItemBufferPtr = VirtualAllocEx(hProcess, IntPtr.Zero,(uint)lvItemSize,AllocationType.Commit,MemoryProtection.ReadWrite); // LVITEM構造体を挿入するには、ポインターからポインターへのコピーを実行するWriteProcessMemoryAPIを使用する必要があります。したがって、マネージLVITEM構造をアンマネージLVITEMポインターに変換する必要があります //最初にアンマネージメモリの一部を割り当てます。 var lvItemLocalPtr = Marshal.AllocHGlobal(lvItemSize); //管理対象オブジェクトを管理対象外メモリにコピーします Marshal.StructureToPtr(lvItem, lvItemLocalPtr, false); int NumberOfBytesRead = 0; //リモートプロセスのメモリに書き込みます WriteProcessMemory(hProcess, lvItemBufferPtr,lvItemLocalPtr,(uint)lvItemSize,out NumberOfBytesRead) ; //リストビューに、必要なテキストを入力するように指示します SendMessage(hWnd, LVM_GETITEMTEXT, itemId, lvItemBufferPtr); //テキストを読む。 CLRはポインタとバイト配列の間でマーシャリングする方法を知っているため、 //AllocHGlobalの代わりに、取得したテキストを格納するためにマネージバイト配列を割り当てます。 var localTextBuffer = new byte[MAX_GETITEMTEXT_LENGTH]; IntPtr ptrNumberOfBytesRead = IntPtr.Zero; ReadProcessMemory(hProcess,textBufferPtr,localTextBuffer,(int)MAX_LVMSTRING, out ptrNumberOfBytesRead); //バイト配列を文字列に変換します。リモートプロセスがUnicodeを使用すると仮定します var text = Encoding.Unicode.GetString(localTextBuffer); //末尾のゼロは自動的にクリアされません text = text.Substring(0, text.IndexOf('\0')); //最後に、割り当てたすべてのメモリを解放し、開いたプロセスハンドルを閉じます VirtualFreeEx(hProcess, textBufferPtr, 0, AllocationType.Release); VirtualFreeEx(hProcess, lvItemBufferPtr, 0, AllocationType.Release); Marshal.FreeHGlobal(lvItemLocalPtr);
試したこと
SendMessageの第4引数をIntPtr型や構造体そのものを入れてみたりしましたが、どれも同じく相手側が落ちてしまうという結果になりました。
sendmessageに構造体のポインタをセットする方法と、
出来たら返してきたものをどのように文字列に戻したらよいかを教えていただきたいです。
補足情報(FW/ツールのバージョンなど)
C#
visual studio 2015
windows10
回答2件
あなたの回答
tips
プレビュー