はじめに
Windowsで他アプリと何かしらやりとりしたいときは、大抵ウィンドウハンドルが必要になる。
いちいち調べなおすのは面倒なので、使ったことのあるAPIとかメソッドをまとめてみた。
Windows APIの使用に際して
C# では、ウィンドウハンドルの型はIntPtr
となる。IntPtr
のサイズは32bit/64bit環境に依存する1ので、型をint
(32bit)とかと間違えないようにすること。
APIについては、とりあえず[DllImport("user32.dll", CharSet = CharSet.Auto)]
つけておけば動くはず。(テキトウ・・)
1. ウィンドウハンドルを取得する
No | 概要 | APIまたはメソッド (Microsoftへリンク) | 使用例へリンク |
---|---|---|---|
1-1 | スクリーン座標から取得 | (API) WindowFromPoint | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
1-2 | 全体から検索・列挙 | (API) FindWindowEx | https://qiita.com/kob58im/items/d5828e6cf5416d776549 |
1-3 | 親を取得 | (API) GetAncestor | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
1-4 | トップレベルウィンドウを列挙しつつ処理する | (API) EnumWindows | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
1-5 | 子ウィンドウを列挙しつつ処理する★ | (API) EnumChildWindows | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
1-A | コントロールから取得 | Control.Handle | https://qiita.com/kob58im/items/4bd061df83212a723be7 |
★・・・ 子のウィンドウハンドルはWin32APIでは取得できないケースがあるようなので注意。
2. ハンドルを使って何かする
No | 概要 | APIまたはメソッド (Microsoftへリンク) | 使用例へリンク |
---|---|---|---|
2-1 | クラス名を得る | (API) GetClassName | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
2-2 | タイトルを得る | (API) GetWindowText | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
2-3 | プロセスIDを得る | (API) GetWindowThreadProcessId | https://qiita.com/kob58im/items/d5828e6cf5416d776549 |
2-4 | 座標やサイズなどの情報を得る | (API) GetWindowInfo | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
2-5 | メッセージを送る | (API) SendMessage | https://qiita.com/kob58im/items/4bd061df83212a723be7 |
2-A | UI Automationを使う | AutomationElement.FromHandle | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
抜粋
※抜粋しているので、そのままでは動きません。使用例の記事を参照ください。
※この記事でのDllImport
の属性指定や、メソッド宣言のパラメータ指定は、あくまで例であり、これを推奨するものではありません。
1-1. WindowFromPoint
// 定義部staticclassNativeMethods{[StructLayout(LayoutKind.Sequential)]publicstructPOINT{publicintx;publicinty;}[DllImport("user32.dll",SetLastError=true)]publicstaticexternIntPtrWindowFromPoint(POINTpoint);}// 使用部voidhoge(){varp=newPOINT();NativeMethods.GetCursorPos(outp);IntPtrhWnd=NativeMethods.WindowFromPoint(p);}
1-2. FindWindowEx
// 定義部staticclassNativeMethods{[DllImport("user32.dll",SetLastError=true,CharSet=CharSet.Auto)]publicstaticexternIntPtrFindWindowEx(IntPtrparentWnd,IntPtrpreviousWnd,stringclassName,stringwindowText);}// 使用部voidhoge(){IntPtrhWnd=IntPtr.Zero;while(IntPtr.Zero!=(hWnd=NativeMethods.FindWindowEx(IntPtr.Zero,hWnd,クラス名文字列,ウィンドウ名文字列))){// クラス名・ウィンドウ名に一致したウィンドウに対する処理をここに書く}}
1-3. GetAncestor
// 定義部staticclassNativeMethods{[DllImport("user32.dll",SetLastError=true)]publicstaticexternIntPtrGetAncestor(IntPtrhWnd,uintgaFlags);publicconstuintGA_PARENT=1;publicconstuintGA_ROOT=2;publicconstuintGA_ROOTOWNER=3;}// 使用部voidhoge(IntPtrhWnd){IntPtrhWndRoot=IntPtr.Zero;hWndRoot=NativeMethods.GetAncestor(hWnd,NativeMethods.GA_ROOT);}
1-4. EnumWindows
// 定義部staticclassNativeMethods{publicdelegateboolEnumWindowsDelegate(IntPtrhWnd,IntPtrlparam);[DllImport("user32.dll")][return:MarshalAs(UnmanagedType.Bool)]publicexternstaticboolEnumWindows(EnumWindowsDelegatelpEnumFunc,IntPtrlparam);}// 使用部boolEnumWindowCallBack(IntPtrhWnd,IntPtrlparam){// hWndを使った処理をここに書く// trueを返すことで、すべてのウィンドウを列挙するreturntrue;}voidhoge(){NativeMethods.EnumWindows(EnumWindowCallBack,IntPtr.Zero);}
FindWindowEx
のほうが使いやすいかも
1-5. EnumChildWindows
// 定義部staticclassNativeMethods{publicdelegateboolEnumWindowsDelegate(IntPtrhWnd,IntPtrlparam);[DllImport("user32.dll")][return:MarshalAs(UnmanagedType.Bool)]publicstaticexternboolEnumChildWindows(IntPtrhandle,EnumWindowsDelegateenumProc,IntPtrlParam);}// 使用部boolEnumChildWindowCallBack(IntPtrhWnd,IntPtrlparam){// 処理...// hWndの子ウィンドウを探すNativeMethods.EnumChildWindows(hWnd,EnumChildWindowCallBack,IntPtr.Zero);returntrue;// すべての兄弟ウィンドウを列挙する}voidhoge(){NativeMethods.EnumChildWindows(ルートとなるウィンドウのハンドル,EnumChildWindowCallBack,IntPtr.Zero);}
FindWindowEx
のほうが使いやすいかも
★子のウィンドウハンドルはWin32APIでは取得できないケースがあるようなので注意。
2-1. GetClassName
// 定義部staticclassNativeMethods{[DllImport("user32.dll",CharSet=CharSet.Auto,SetLastError=true)]publicstaticexternintGetClassName(IntPtrhWnd,StringBuilderlpClassName,intnMaxCount);}// 使用部publicstaticstringMyGetClassName(IntPtrhWnd,outintretCode){StringBuildercsb=newStringBuilder(MaxTextLength);retCode=NativeMethods.GetClassName(hWnd,csb,csb.Capacity);if(retCode>0){returncsb.ToString();}else{returnstring.Empty;}}
2-2. GetWindowText
// 定義部staticclassNativeMethods{[DllImport("user32.dll",CharSet=CharSet.Auto,SetLastError=true)]publicstaticexternintGetWindowTextLength(IntPtrhWnd);[DllImport("user32.dll",CharSet=CharSet.Auto,SetLastError=true)]publicstaticexternintGetWindowText(IntPtrhWnd,StringBuilderlpString,intnMaxCount);}// 使用部publicstaticstringMyGetWindowText(IntPtrhWnd,outintretCode){//ウィンドウのタイトルを取得するStringBuildertsb=newStringBuilder(MaxTextLength);retCode=NativeMethods.GetWindowText(hWnd,tsb,tsb.Capacity);if(retCode>0){// 成功すると長さが返るreturntsb.ToString();}else{// 0の場合はエラーの可能性があるreturnstring.Empty;}}
2-3. GetWindowThreadProcessId
// 定義部staticclassNativeMethods{[DllImport("user32.dll",SetLastError=true)]publicstaticexternintGetWindowThreadProcessId(IntPtrhWnd,outintlpdwProcessId);}// 使用部voidhoge(){intpid;NativeMethods.GetWindowThreadProcessId(hWnd,outpid);Processp=Process.GetProcessById(pid);}
2-4. GetWindowInfo
// 定義部staticclassNativeMethods{[StructLayout(LayoutKind.Sequential)]publicstructWINDOWINFO{publicintcbSize;publicRECTrcWindow;publicRECTrcClient;publicintdwStyle;publicintdwExStyle;publicintdwWindowStatus;publicuintcxWindowBorders;publicuintcyWindowBorders;publicshortatomWindowType;publicshortwCreatorVersion;}[StructLayout(LayoutKind.Sequential)]publicstructRECT{publicintleft;publicinttop;publicintright;publicintbottom;}[DllImport("user32.dll",SetLastError=true)]publicstaticexternintGetWindowInfo(IntPtrhwnd,refWINDOWINFOpwi);}// 使用部publicstaticWINDOWINFOMyGetWindowInfo(IntPtrhWnd,outintretCode){varwi=newWINDOWINFO();wi.cbSize=Marshal.SizeOf(wi);retCode=NativeMethods.GetWindowInfo(hWnd,refwi);returnwi;}
2-5. SendMessage
本質と関係ないとこで解放処理が漏れてます。そのうち見直します。(というか例を変えたい・・)
// 定義部staticclassNativeMethods{[DllImport("user32")]publicexternstaticIntPtrSendMessage(IntPtrhWnd,intMsg,IntPtrwParam,IntPtrlParam);}// 使用部staticIntPtrSetTabStop(TextBoxBaset,inttabSize){int[]tabarray=newint[]{tabSize*4};intwparam=tabarray.Length;IntPtrparray=Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int))*tabarray.Length);Marshal.Copy(tabarray,0,parray,tabarray.Length);IntPtrret=SendMessage(t.Handle,EM_SETTABSTOPS,newIntPtr(wparam),parray);// 解放処理が漏れている。 Marshal.FreeCoTaskMem とかで解放してください。returnret;}
脱線編 - WinAPIまわりのメモリ確保と解放について
メモリの解放は完璧にしようとすると奥が深そう(参考サイト参照)であるが、趣味の範疇ならMarshal.AllocCoTaskMem
, Marshal.FreeCoTaskMem
と確保;try{処理;}finally{解放;}
で十分かと。
参考サイト
正確にはコンパイル時のオプション指定による。 ↩