DelegateがGCによって破棄されてしまう

Category: visual studio cs_ja

Question

コーベル on Tue, 03 Dec 2019 08:53:15


お世話になっております。

表題の通り、調査してみると【メンバ変数として宣言すると破棄されない】とあったので
宣言しましたが、それでも同じエラーが発生します。

// 宣言
private delegate IntPtr HOOKPROC(int nCode, IntPtr wParam, IntPtr lParam);
private HOOKPROC proc;

// SetHook
IntPtr hmodule = WindowsAPI.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
this.proc = this.MyHookProc;
hHook = SetWindowsHookEx((int)WindowsAPI.HookType.WH_MOUSE_LL, proc, hmodule, IntPtr.Zero);

何が問題なのでしょうか、ご教示ください。

Replies

佐祐理 on Tue, 03 Dec 2019 09:19:40


エラーが起きるのであれば、エラー内容を質問文に明記すべきです。

とは言え、それ以前の問題として、SetWindowsHookExにはWH_MOUSE_LLはGlobal onlyとされています。その上で、Installing and Releasing Hook Proceduresには

You must place a global hook procedure in a DLL separate from the application installing the hook procedure. The installing application must have the handle to the DLL module before it can install the hook procedure. To retrieve a handle to the DLL module, call the LoadLibrary function with the name of the DLL. After you have obtained the handle, you can call the GetProcAddress function to retrieve a pointer to the hook procedure. Finally, use SetWindowsHookEx to install the hook procedure address in the appropriate hook chain.

と説明されています。簡単に言うと、ネイティブDLLで関数をエクスポートしておく必要があります。

この制約から、C#で記述することは不可能です。

コーベル on Tue, 03 Dec 2019 09:28:00


失礼しました。

エラー内容は以下です。

マネージド デバッグ アシスタント 'CallbackOnCollectedDelegate' : 'コールバックが、
型 'HOOKPROC::Invoke' のガベージ コレクションされたデリゲートで行われました。
これは、アプリケーションのクラッシュ、破損、およびデータの損失を発生させる可能性があります。
デリゲートをアンマネージ コードに渡すとき、デリゲートは 2 度と呼び出されないことが
確実になるまでマネージ アプリケーションによって維持されなければなりません。

また、C#で記述できるかどうかというお話では、調べた結果
【C++】で可能な事(メッセージの差し替え等)は出来ないがメッセージを取得するくらいなら
可能である。という認識です。
なので、今回の目的としては要件を満たしていると判断しました。


KOZ6.0 on Tue, 03 Dec 2019 09:38:51


ここを紹介しておきます。

「C#にてマウスとキーボードを操りし者」
https://qiita.com/exliko/items/3135e4413a6da067b35d

何が問題なのでしょうか、ご教示ください。

提示されたコードだけではわかりません。

フックを解除する前に proc が消えているのでは?

コーベル on Tue, 03 Dec 2019 09:47:33


フックを解除する前に proc が消えているのでは?
仰るとおりです、
HOOKPROC で宣言されているのは proc だけなのでそれが破棄されてエラーが出てます。
そしてそれを破棄させないため、メンバとして宣言しているのですが、破棄されます。

KOZ6.0 on Tue, 03 Dec 2019 11:31:11


# 紹介した URL の MouseHook.cs をコピペすれば簡単に実装できるんですけど・・・

「proc が消えている」というのは、proc という変数そのものが GC に回収されちゃっているのでは?ということです。

あと、提示されたコードだけではわからないとも書いてます。

続けるなら再現できる最低限のコードを提示してください。

Azulean on Tue, 03 Dec 2019 12:46:36


フックを解除する前に proc が消えているのでは?
仰るとおりです、
HOOKPROC で宣言されているのは proc だけなのでそれが破棄されてエラーが出てます。
そしてそれを破棄させないため、メンバとして宣言しているのですが、破棄されます。
認識が間違っています。
「メンバーとして持つ」ではなく、「その変数が参照されない状態にしないこと」が必要です。
おそらく、proc があるクラスのオブジェクト(コード片で this となっているもの)が、誰からも参照していない状態、すなわち GC で回収される状態になっているのでは?

Azulean on Tue, 03 Dec 2019 12:55:17


簡単に言うと、ネイティブDLLで関数をエクスポートしておく必要があります。

この制約から、C#で記述することは不可能です。

Low-level hook は、他の global hook のように相手先プロセスにインジェクションしないので、そういった制約はない認識です。

一例:LowLevelMouseProc callback function
 However, the WH_MOUSE_LL hook is not injected into another process. Instead, the context switches back to the process that installed the hook and it is called in its original context. Then the context switches back to the application that generated the event.

旧 MSDN Blog 例

Haruka6002 on Fri, 06 Dec 2019 07:29:01


コーベルさん、こんにちは。フォーラムオペレーターのHarukaです。
MSDNフォーラムにご投稿くださいましてありがとうございます。

ご質問いただいた件ですが、その後いかがでしょうか。
皆様から寄せられた投稿はお役に立ちましたか。

参考になった投稿には [回答としてマーク] をお願い致します。

設定いただくことで、
他のユーザーもお役に立つ回答を見つけやすくなります。

お手数ですが、ご協力の程どうかよろしくお願いいたします。