はじめに
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 |
- | 現在アクティブ(≒フォーカスを持っている)ウィンドウを取得 | (API) GetForegroundWindow | |
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-6 | フォーカスを移す | (API) SetForegroundWindow | 【外部サイトリンク】外部アプリケーションのウィンドウをアクティブにする - dobon.net |
2-7 | DCを取得する | (API) GetWindowDC | 【外部サイトリンク】画面をキャプチャする - dobon.net |
2-A | UI Automationを使う | AutomationElement.FromHandle | https://qiita.com/kob58im/items/3587d8e595e655e9391d |
抜粋
※抜粋しているので、そのままでは動きません。使用例の記事を参照ください。
※この記事でのDllImport
の属性指定や、メソッド宣言のパラメータ指定は、あくまで例であり、これを推奨するものではありません。
1-1. WindowFromPoint
// 定義部
static class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct POINT {
public int x;
public int y;
}
[DllImport("user32.dll",SetLastError = true)]
public static extern IntPtr WindowFromPoint(POINT point);
}
// 使用部
void hoge()
{
var p = new POINT();
NativeMethods.GetCursorPos(out p);
IntPtr hWnd = NativeMethods.WindowFromPoint( p );
}
1-2. FindWindowEx
// 定義部
static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parentWnd, IntPtr previousWnd, string className, string windowText);
}
// 使用部
void hoge()
{
IntPtr hWnd = IntPtr.Zero;
while ( IntPtr.Zero != (hWnd = NativeMethods.FindWindowEx(IntPtr.Zero, hWnd, クラス名文字列, ウィンドウ名文字列))) {
// クラス名・ウィンドウ名に一致したウィンドウに対する処理をここに書く
}
}
1-3. GetAncestor
// 定義部
static class NativeMethods
{
[DllImport("user32.dll",SetLastError = true)]
public static extern IntPtr GetAncestor(IntPtr hWnd, uint gaFlags);
public const uint GA_PARENT = 1;
public const uint GA_ROOT = 2;
public const uint GA_ROOTOWNER = 3;
}
// 使用部
void hoge(IntPtr hWnd)
{
IntPtr hWndRoot = IntPtr.Zero;
hWndRoot = NativeMethods.GetAncestor(hWnd, NativeMethods.GA_ROOT);
}
1-4. EnumWindows
// 定義部
static class NativeMethods
{
public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lparam);
}
// 使用部
bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam)
{
// hWndを使った処理をここに書く
// trueを返すことで、すべてのウィンドウを列挙する
return true;
}
void hoge()
{
NativeMethods.EnumWindows(EnumWindowCallBack, IntPtr.Zero);
}
FindWindowEx
のほうが使いやすいかも
1-5. EnumChildWindows
// 定義部
static class NativeMethods
{
public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr handle, EnumWindowsDelegate enumProc, IntPtr lParam);
}
// 使用部
bool EnumChildWindowCallBack(IntPtr hWnd, IntPtr lparam)
{
// 処理...
// hWndの子ウィンドウを探す
NativeMethods.EnumChildWindows(hWnd, EnumChildWindowCallBack, IntPtr.Zero);
return true; // すべての兄弟ウィンドウを列挙する
}
void hoge()
{
NativeMethods.EnumChildWindows(ルートとなるウィンドウのハンドル, EnumChildWindowCallBack, IntPtr.Zero);
}
FindWindowEx
のほうが使いやすいかも
★子のウィンドウハンドルはWin32APIでは取得できないケースがあるようなので注意。
2-1. GetClassName
// 定義部
static class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
}
// 使用部
public static string MyGetClassName(IntPtr hWnd, out int retCode)
{
StringBuilder csb = new StringBuilder(MaxTextLength);
retCode = NativeMethods.GetClassName(hWnd, csb, csb.Capacity);
if ( retCode > 0 ) {
return csb.ToString();
}
else {
return string.Empty;
}
}
2-2. GetWindowText
// 定義部
static class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
}
// 使用部
public static string MyGetWindowText(IntPtr hWnd, out int retCode)
{
//ウィンドウのタイトルを取得する
StringBuilder tsb = new StringBuilder(MaxTextLength);
retCode = NativeMethods.GetWindowText(hWnd, tsb, tsb.Capacity);
if ( retCode > 0 ) { // 成功すると長さが返る
return tsb.ToString();
}
else { // 0の場合はエラーの可能性がある
return string.Empty;
}
}
2-3. GetWindowThreadProcessId
// 定義部
static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
}
// 使用部
void hoge()
{
int pid;
NativeMethods.GetWindowThreadProcessId(hWnd, out pid);
Process p = Process.GetProcessById(pid);
}
2-4. GetWindowInfo
// 定義部
static class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWINFO
{
public int cbSize;
public RECT rcWindow;
public RECT rcClient;
public int dwStyle;
public int dwExStyle;
public int dwWindowStatus;
public uint cxWindowBorders;
public uint cyWindowBorders;
public short atomWindowType;
public short wCreatorVersion;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll",SetLastError = true)]
public static extern int GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
}
// 使用部
public static WINDOWINFO MyGetWindowInfo(IntPtr hWnd, out int retCode)
{
var wi = new WINDOWINFO();
wi.cbSize = Marshal.SizeOf(wi);
retCode = NativeMethods.GetWindowInfo(hWnd, ref wi);
return wi;
}
2-5. SendMessage
本質と関係ないとこで解放処理が漏れてます。そのうち見直します。(というか例を変えたい・・)
// 定義部
static class NativeMethods
{
[DllImport("user32")]
public extern static IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
}
// 使用部
static IntPtr SetTabStop(TextBoxBase t, int tabSize)
{
int[] tabarray = new int[] { tabSize*4 };
int wparam = tabarray.Length;
IntPtr parray = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * tabarray.Length);
Marshal.Copy(tabarray, 0, parray, tabarray.Length);
IntPtr ret = SendMessage(t.Handle, EM_SETTABSTOPS, new IntPtr(wparam), parray);
// 解放処理が漏れている。 Marshal.FreeCoTaskMem とかで解放してください。
return ret;
}
脱線編 - WinAPIまわりのメモリ確保と解放について
メモリの解放は完璧にしようとすると奥が深そう(参考サイト参照)であるが、趣味の範疇ならMarshal.AllocCoTaskMem
, Marshal.FreeCoTaskMem
と確保;try{処理;}finally{解放;}
で十分かと。
参考サイト
-
正確にはコンパイル時のオプション指定による。 ↩