Win32 API を 手なずけよう ! 名古屋 KOUJI MATSUI
自己紹介 けきょ ロードバイク乗り Microsoft MVP for Visual Studio and Development Technology 認定スクラムマスター・スクラムプロダクトオーナー Center CLR オーガナイザー
今日のお題.NET から Win32 API を使って 、.NET だけでは難しいことをしよう
巷では UWP が流行ってますが … デスクトップアプリケーションが避けられない場面は あるはずです !! ◦ 最近はやりの IoT (Out of box unit と通信する ) ◦Microsoft Kinect や Intel RealSense カメラ ◦USB 機器 ( libusb ) ◦OpenGL / DirectX ◦ サードパーティ製ライブラリ群 … そして Win32 API
Win32 API となかよくする と言いながら 、 C++ でがりがり書くのは色々つらい 。 C# から 、 どうやって Win32 API にアクセスするか ? これが出来れば 、 いろんなことに応用できる !!
デスクトップを駆けるプロ生ちゃん ウインドウの上を走ってくよ~
WPF だけではどうにもならない … デスクトップ上のどこにウインドウがあるのか ? 表示されているウインドウが全て列挙出来れば 、 ウインドウ の上の縁の座標が分かる 。 あとは計算で何とかなりそう ? ② ウインドウの上辺が分かるので ③ 上に乗るような感じで 横に移動させる ① ウインドウの座標が分かれば
落ちた時の着地点 落ちた時の着地点は 、 現在位置 → 次の位置 ( 等加速度運動 ) で 決まる線分と 、 ウインドウ上辺が交差するところ 。 ① 現在の座標 ② 次の座標 ③ ウインドウの上辺と交差する座標 → 全てのウインドウを検査する
考え方はお k?
ウインドウを調べる デスクトップ上のウインドウを全て列挙するには 、 「 EnumWindows 」 API を使います 。 又は、 EnumWindows でググる
C# から Win32 API は直接呼べません EnumWindows を呼べば 、 すべてのウインドウの位置が分かる 。 でも C# のコードで ”EnumWindows” と書いても 、 もちろん呼び 出せない 。 こういう時には 、「 P/Invoke 」 という機能を使います 。
P/Invoke するには Win32 API に対応する P/Invoke の定義を C# で書きます 。 EnumWindows API の定義 ( C 言語による) EnumWindows API の定義 ( P/Invoke C# )
P/Invoke の定義 API がどの DLL に定義されているか? ( DllImport 属性) API 名はほとんどの場合 C 言語の API 名と同じ 引数と戻り値の並びは同じだけど、 型は要注意 ぴよぴよ
どうやって書けば ? そんな P/Invoke 初心者のために 「 pinvoke.net 」 コミュニティベースで、 Win32 API の P/Invoke 定義を蓄積
pinvoke.net 例 :「 EnumWindows dllimport 」 とかでググると 、 大抵トップ に出てくる 。 コミュニティベースの定義なので 、 定義の質はまちまち 。 複数載ってたりする ( 俺はこんな定義は我慢ならない 、 と 思ったのかも ) ので 、 良さげなやつを選択する 。 この定義をベースに 、 細かく修正するという手法はアリ 。 主に型定義によるばらつきが多い感ある あと、過剰な属性か足りないか …
EnumWindows P/Invoke の詳細 コールバック関数はデリゲート として定義できる SetLastError でエラーコードを返す API の場合は、これを追加する
EnumWindows を使う EnumWindowProc デリゲートに相当する実装。 列挙されたウインドウハンドルをリストに蓄積します。 API が失敗すると false を返すので、その場合は GetHRForLastWin32Error でエラーコードを取得して、 対応する例外を ThrowExceptionForHR でスローします。
ウインドウの矩形を得るには ウインドウハンドルからウインドウ の矩形座標を得るには、 GetWindowRect API を使います。 RECT 構造体が必要 ( LPRECT は RECT へのポインタ)
ウインドウの矩形を得るには RECT 構造体の P/Invoke 表現です。 これも pinvoke.net で探せます。 ウインドウハンドルからウインドウ の矩形座標を得るには、 GetWindowRect API を使います。
GetWindowRect を使う 呼び出すだけ。エラー処理は EnumWindows と同じ。
その他必要な API ウインドウが可視状態かどうか → IsWindowVisible ◦ 見えていないウインドウは除外します 。 デスクトップ全体の矩形を取得する → SystemParametersInfo ◦ 画面外まで移動したかどうかを判定するのに使います 。 ウインドウの矩形を再設定する → SetWindowPos ◦ プロ生ちゃんアイコンの移動に使用します 。 ( WPF データバインディングでの移動に不備があるため )
WPF ウインドウからハンドルを得る WPF のウインドウクラスからウインドウハンドルを取得すれば 、 WPF ウインドウに対して 、 Win32 API を適用できます 。 WindowInteropHelper クラスを使って、 Window クラスのハンドルを操作 内部で SetWindowPos API を呼び出す
総仕上げ デスクトップ上のウインドウ群の矩形座標が手に入ったので 上辺群を抽出 歩行中と落下中をステートマシンで管理 落下中は交点座標を計算 → 結果が得られれば着地 ! 上辺の左端を超えたら落下 画面外に出たら最初の座標にリセット アイコン ( 透過 PNG ) で透過ウインドウを作って表示 歩行時はアイコンを切り替えてアニメーション タイマーで定期的にステートマシンを実行
デモ
もっと面白くするために アニメーションパターンを増やしたいね ( レベル低 ) ◦ せっかく色々イメージがあるので … 反対にも移動したいね ( レベル低 ) ◦ 上辺の端まで来たら気まぐれで反転するとか ちゃんと終われるようにしたい ( レベル低 ) ◦ タスクトレイに常駐 ! ウインドウ移動に追従したいね ( レベル中 ) ◦ ウインドウを移動したら 、 上辺に乗っかったまま移動とか ウインドウの Z オーダーを認識したい ( レベル高 ) ◦ 裏に回っているウインドウにも着地してしまうよ ◦ 手前ウインドウにさえぎられて移動できない場合は反転とか
ご清聴ありがとうございました ! GitHub: Pronama.InteropDemo スライドはブログに上げます 第五回 Center CLR 年末会やります ( ) 名古屋市 中生涯学習センター