Windowsのフックのはまりどころ備忘録

# WindowsのHookが不安定になる原因

※勉強中。対策までには至っていないのであしからず。

1. <b>ローカルHookになっている(DLLにしていない)</b>
   スレッドの管理の問題で、[DLLにする必要があるらしい。](http://www.kab-studio.biz/Programing/Codian/DLL_Hook_SClass/06.html)
[ここもよさそう](http://dsas.blog.klab.org/archives/50829204.html)
なお、`WH_KEYBOARD_LL`はDLLにしなくてよいらしい。だたしメッセージループを持つことが必要。
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644985(v%3Dvs.85)#remarks
http://blog.mikage.to/mika/2005/07/windows_3805.html

2. <b>32bit 64bitの片方しかhookしていない</b>
   [公式](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexa#remarks)による。よく読んでいないが32bitと64bitは別扱いのようです。両方Hookしろということっぽい？

ここらへんがよさそう ttp://uisteven.blog.fc2.com/blog-entry-3.html
<b>左記のサイトの中のリンクがウイルスサイトくさい。</b>

3. <b>GC(ガベージコレクト)されてしまっている(GCがある言語特有)</b>
   Hookのハンドルと、Hookに登録したProcはGCされないような工夫が必要。
   C#では、とりあえずメンバ宣言しておく。そのインスタンスも何かから参照されている状態にする必要があるかと。
   (Hookのハンドルのほうは、UnHookするのに使うのでうっかりGCされることはないと思うが、Procはやりがち。)

4. <b>`SendKeys`, `SendInput`, `SendMessage`, `PostMessage`などと併用している</b>
   メッセージキューに積みすぎたり、送ったメッセージを再度Hookして無限ループしたり。とりあえず`SendKeys`は[タイミングが安定しないと公式が言っているよう](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.sendkeys?view=netframework-4.8)なのでやめたほうがよさげ。

5. <b>`SetWindowsHookEx` に失敗している</b>
   対応としてはまず `GetLastError` (C#/VB.NET : `Marshal.GetLastWin32Error`)のエラーコードを調べる。
   参考サイト(C#/VB.NETでのGetLastErrorの呼び出し): https://www.atmarkit.co.jp/fdotnet/dotnettips/740win32errcode/win32errcode.html
   参考サイト(エラーコード一覧): http://ir9.jp/prog/ayu/win32err.htm <br>
   エラーコードが 0x7E (`ERROR_MOD_NOT_FOUND`)であれば、おそらく下記に該当している。
   自身のプロセスから自身のHookを登録するときはhMod は `NULL` (`IntPtr.Zero`) にする必要があるらしい(？)
   参考: https://stackoverflow.com/questions/2774741/module-not-found

6. <b>他言語から使うときにミスっている</b>
C#にて、Win32のドキュメント上のハンドル類が値`NULL`となる場合は`IntPtr.Zero`に置き換えるケースが多いが、うっかり `null` にしてしまっているなど。コードによってはコンパイルが通るので気づきにくい。

7. <b>コールバック関数を実行するスレッドをブロックしてしまっている</b>（未検証）
`Form`の初期化とかでフックすると、UIスレッドと同じスレッドでフックのコールバック処理が実行される。UIのイベント処理内で重い処理やユーザ操作待ちなどをすると、フックのコールバックが待たされてハマるかも。
スレッドIDの調べ方：`Console.WriteLine("ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString());`

# キーボードのフックについて

1. <b>コールバック関数の処理時間が長すぎる(タイムアウトしている)</b>
[公式によるとWM_KEYBOARD_LLはタイムアウト時間が設定されており](https://docs.microsoft.com/ja-jp/previous-versions/windows/desktop/legacy/ms644985(v=vs.85)#remarks)、超過すると勝手にHookが外されるらしい。外されたことを検出する有効な手段はないらしい。ほかのHookはどうなのか知らない。。
同ページの説明によるとタイムアウト時間はレジストリ設定(`LowLevelHooksTimeout`)によるらしいが、自分の環境では該当のレジストリキーが見つけられなかった。<br>
デフォルトは300msっぽい。10回超えるとダメらしい。あと、原理的に回避不可能なのではないか？：
参考サイト: https://nazochu.blogspot.com/2011/08/windows7.html
https://blogs.msdn.microsoft.com/alejacma/2010/10/14/global-hooks-getting-lost-on-windows-7/

2. <b>「Fn」キーを取ろうとしている</b>
    「Fn」キーはOSが受け付けないらしい。

3. <b>「Alt」キーを拾いそこねている</b>
   `WM_SYSKEYDOWN`を見ていない。

4. <b>`WM_KEYDOWN`と`WM_KEYUP`をペアで処置できていない</b>
     ・・・というか原理的に無理かもしれないが。

# マウスのフック(`WH_MOUSE_LL`)について

1. <b>座標のスケーリングが異なる</b><br>
   lParamで得られるマウス座標は高解像度対応版の座標らしい。[ここ参照](https://qiita.com/kob58im/items/9dabf54444b01519f5e7)<br>
   [LowLevelMouseProc callback function](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644986(v%3Dvs.85))
⇒ [MSLLHOOKSTRUCT](https://docs.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-msllhookstruct?redirectedfrom=MSDN)<br>
> The x- and y-coordinates of the cursor, in [per-monitor-aware](https://docs.microsoft.com/ja-jp/windows/win32/api/shellscalingapi/ne-shellscalingapi-process_dpi_awareness) screen coordinates.