質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.44%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Q&A

解決済

2回答

5498閲覧

C#においてWin APIを使用して別アプリのlistViewから情報(文字列)を取得したい

shinyama

総合スコア1

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

0グッド

1クリップ

投稿2021/09/15 14:00

編集2021/09/16 13:27

前提・実現したいこと

初めての質問です。不手際があれば申し訳ありません。
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

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bego

2021/09/16 01:27

Microsoft Store から "WinDbg Preview" をダウンロードし、そのデバッガを「落ちるアプリ側」にアタッチさせておけば、落ちる直前にデバッガにブレークインするはずなので、そのタイミングでのコールスタックやパラメータ等をチェックすれば、原因を特定できるはずです。 ちなみに、アプリケーション クラッシュの原因特定は、推測を基に調査すると余計ややこしくなります。 デバッガを使用し、実際に起きていることをしっかり確認することが、問題解決への近道です。 (デバッガに不慣れだと最初は時間がかかるかもしれませんが、慣れてくると格段に問題解決スキルが上がります。)
shinyama

2021/09/16 13:17

アドバイスありがとうございます! 初めて聞くワードが多く戸惑っていますが、少しずつ使用していきたいと思います。デバッガも使用したことないので明日はその辺りを確認してみようと思います。ありがとうございました。
Bego

2021/09/17 01:54

WinDbg を任意のプロセスにアタッチさせトレースすれば、自分が作ったプログラムでなくても、つまりどんなプログラムでも解析することが可能です。 (いわゆる "リバース エンジニアリング" ってやつです。) 例えば、そのプログラムがなんの API をどんなパラーメータで呼び出しているのかも、確認することができます。
shinyama

2021/09/17 16:03

ありがとうございます! 本日確認してみたのですが、まだ使い方がいまいち理解できていない状態ですので、引き続き調査と実行していきたいと思います! ありがとうございます!
guest

回答2

0

ベストアンサー

他プロセスのリストビューから読み書きを行う場合に解決しなければならない問題点が2つあります。
ひとつは cx20 さんが提示された StackOveflow のスレッドにあるように VirtualAllocEx で読み書きできるメモリを確保する必要があるという点。
もうひとつは、LVITEM 構造体の大きさが 32bit と 64bit で異なる点です。

C# では

C#

1[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 2public struct LVITEM 3{ 4 public int mask; 5 public int iItem; 6 public int iSubItem; 7 public int state; 8 public int stateMask; 9 public IntPtr pszText; 10 public int cchTextMax; 11 public int iImage; 12 public IntPtr lParam; 13 public int iIndent; 14 public int iGroupId; 15 public int cColumns; 16 public IntPtr puColumns; 17 public IntPtr piColFmt; 18 public int iGroup; 19}

のように書けますが、実際に確保されるメモリは下の図のようになります。(1マス4バイト)

イメージ説明

ですので、相手側のプロセスが 32bit/64bit のどちらで動作しているかを調べ、以下のように構造体を定義して、切り替える必要があります。

C#

1// 32bit用 2[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 3public struct LVITEM32 4{ 5 public int mask; 6 public int iItem; 7 public int iSubItem; 8 public int state; 9 public int stateMask; 10 public int pszText; 11 public int cchTextMax; 12 public int iImage; 13 public int lParam; 14 public int iIndent; 15 public int iGroupId; 16 public int cColumns; 17 public int puColumns; 18 public int piColFmt; 19 public int iGroup; 20} 21// 64bit用 22[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 23public struct LVITEM64 24{ 25 public int mask; 26 public int iItem; 27 public int iSubItem; 28 public int state; 29 public int stateMask; 30 public int alignment1; 31 public long pszText; 32 public int cchTextMax; 33 public int iImage; 34 public int alignment2; 35 public long lParam; 36 public int iIndent; 37 public int iGroupId; 38 public int cColumns; 39 public int alignment3; 40 public long puColumns; 41 public long piColFmt; 42 public int iGroup; 43 public int alignment4; 44}

ただこの場合、64bit プロセスから 32bit プロセスの ListView は読めますが、32bit プロセスから 64bit プロセスの ListView は読めない可能性があります。

読み出したいプロセスとビット数を合わせれば悩む必要はありません。

投稿2021/09/16 08:29

編集2021/09/16 08:33
KOZ6.0

総合スコア2661

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shinyama

2021/09/16 13:14

ご回答ありがとうございます! bit数の違いでそのような違いがあるのですね。勉強になりました。 調べてみると操作する側は64bitでlistViewがある側は32bitでした! そこで32bit用の構造体と、cx20さんのコードを参考に作成し 直したところ、ソフトが落ちることは回避できました。 ですが、テキストの中身は””状態で取得はできませんでした。
KOZ6.0

2021/09/16 17:10

pszText に、テキストを書き込むためのメモリアドレスをセットしていないようです。 相手側のプロセスが書き込むので、VirtualAllocEx で確保したメモリアドレスを割り当てなければなりません。
shinyama

2021/09/17 16:01

言われた通り、pxzTextにメモリアドレスをセットしたら、 listViewの値をとることができました! とれた瞬間感動しちゃいました、、、 本当にありがとうございました!
guest

0

listviewのあるアプリ側が特にエラーも発生せずに落ちてしまいます。

他プロセスの ListView にアクセスするのは少し面倒なようです。
C++ での例ですが Win32 API で他プロセスのメモリにアクセスする際の問題点などが記載されているので一読されることをお勧めします。

■ ListView to CSVを作ってみよう!!
https://www.inasoft.org/lv2csv/lvcsvcom/chapter1.html

以下は StackOveflow の QA になります。

■ Get ListView items from other windows
https://stackoverflow.com/questions/4857602/get-listview-items-from-other-windows

投稿2021/09/15 14:47

cx20

総合スコア4633

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shinyama

2021/09/15 21:42

早速のご返答ありがとうございます! C++がまだあまり理解できていなので、まずお礼だけ述べさせていただきます。 いただいた資料を確認し、わからない箇所があればまた質問させていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.44%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問