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

  • 質問

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

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

    // 宣言
    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);

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

    2019年12月3日 8:53

すべての返信

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

    とは言え、それ以前の問題として、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#で記述することは不可能です。

    2019年12月3日 9:19
  • 失礼しました。

    エラー内容は以下です。

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

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


    • 編集済み コーベル 2019年12月3日 9:28 誤字修正
    2019年12月3日 9:28
  • ここを紹介しておきます。

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

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

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

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

    2019年12月3日 9:38
  • フックを解除する前に proc が消えているのでは?
    仰るとおりです、
    HOOKPROC で宣言されているのは proc だけなのでそれが破棄されてエラーが出てます。
    そしてそれを破棄させないため、メンバとして宣言しているのですが、破棄されます。
    2019年12月3日 9:47
  • # 紹介した URL の MouseHook.cs をコピペすれば簡単に実装できるんですけど・・・

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

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

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

    2019年12月3日 11:31
  • フックを解除する前に proc が消えているのでは?
    仰るとおりです、
    HOOKPROC で宣言されているのは proc だけなのでそれが破棄されてエラーが出てます。
    そしてそれを破棄させないため、メンバとして宣言しているのですが、破棄されます。
    認識が間違っています。
    「メンバーとして持つ」ではなく、「その変数が参照されない状態にしないこと」が必要です。
    おそらく、proc があるクラスのオブジェクト(コード片で this となっているもの)が、誰からも参照していない状態、すなわち GC で回収される状態になっているのでは?
    2019年12月3日 12:46
    モデレータ
  • 簡単に言うと、ネイティブ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 例

    2019年12月3日 12:55
    モデレータ
  • コーベルさん、こんにちは。フォーラムオペレーターのHarukaです。
    MSDNフォーラムにご投稿くださいましてありがとうございます。

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

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

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

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

    MSDN/ TechNet Community Support Haruka
    ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、 ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~

    2019年12月6日 7:29
    モデレータ