14

このページで同様の質問を見つけましたが、回答を解釈する方法や、それらが本当に重複しているかどうかを判断する方法がわかりません。

コメント付きで、私が見つけた可能性のある重複は次のとおりです。

Hans Passant による、その最後の回答に対する削除された回答に関する問題のコメントは次のとおりです。

.NET 4.0 を使用していますか? その CLR は、アセンブリのロード方法を変更し、LoadLibrary 呼び出しがなくなり、それらのモジュール ハンドルがなくなりました。代わりに GetEntryAssembly() を使用すると、別の修正になります。– Hans Passant 5 月 5 日 19:43

それで、ここの言葉は何ですか?.NET 4.0 を使用していますか? LoadLibrary("user32.dll") を使用して、使用可能な DLL ハンドルを取得しようとしましたか? – Hans Passant 5 月 6 日 15:43

これを行う必要はないと確信していますが、明らかに100%確実ではありません。これを変更する必要がある場合に私が残した問題は、.NET 用にコンパイルすると 64 ビット OS で動作するAny CPUのに、どの構成でも 32 ビットでは動作しない理由です。

.NET アセンブリの読み込みに関して実際に何かが変更されたため、クラス ライブラリの適切なハンドルを取得できない場合、次の質問があります。

  • .NET 3.5 にダウングレードしたり、フック ライブラリを管理対象外に変更したりすることなく、これをだまして自分のやりたいことを実行させる方法はありますか?
  • 64 ビット OS では動作するのに、32 ビットでは動作しないのはなぜですか?

バックグラウンド

キーの押下をキャプチャする WH_KEYBOARD_LL フック タイプで SetWindowsHookEx を使用するプログラムを .NET 4.0 で作成しました。これは私の 64 ビット Windows 7 では問題なく動作しますが、キーボード フックが 32 ビット Windows 7 にインストールされていると、「モジュールが見つかりません」というメッセージが表示されてクラッシュします。

これが私が試したことです:

  • x86 用にコンパイルし、64 ビット OS で実行すると、「モジュールが見つかりません」でクラッシュする
  • x86 用にコンパイル、32 ビット OS で実行、クラッシュ
  • 任意の CPU 用にコンパイル、64 ビット OS で実行、問題なく動作
  • 任意の CPU 用にコンパイル、32 ビット OS で実行、クラッシュ
  • .NET 3.5 に切り替えて、上記の 4 つのケースを繰り返すと、すべて機能します

私はコードを .NET 3.5 に切り替えたくありません。作業を容易にするためにいくつかのクラス ライブラリを使用しており、最新のコードは .NET 4.0 しかないからです。

必要に応じて、すべてを含む .ZIP ファイルを Visual Studio 2010 プロジェクトとしてダウンロードするか、次の 2 つのファイルに貼り付けることができます。

そのルートをたどりたい場合に再作成するには:

  1. 新しいコンソール プロジェクト、.NET 4.0 を作成します。
  2. 別のクラス ライブラリ プロジェクトを追加します。これも .NET 4.0 です。
  3. コンソール プログラム プロジェクトからクラス ライブラリ プロジェクトへの参照を追加します。
  4. 以下の Program.cs コンテンツを、コンソール プロジェクトにある Program.cs ファイルに貼り付けます。
  5. 以下の Hook.cs コンテンツを class-library プロジェクトのファイルに貼り付けます。これを Class1.cs の既定のファイルに貼り付けるか、別のファイルを追加できます。これをコンソールプロジェクトに入れることはできません

次に、ビルドして実行し、さまざまな構成をテストします。

Program.cs

using System;
using HookLib;

namespace HookTest
{
    class Program
    {
        static void Main()
        {
            var hook = new Hook();

            Console.Out.WriteLine("hooking");
            hook.Enable();
            Console.Out.WriteLine("hooked");

            Console.Out.WriteLine("unhooking");
            hook.Disable();
            Console.Out.WriteLine("unhooked");
        }
    }
}

Hook.cs

using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;

namespace HookLib
{
    public class Hook
    {
        private IntPtr _Handle;
        private HookProcDelegate _Hook;

        public void Enable()
        {
            Module module = Assembly.GetExecutingAssembly().GetModules()[0];
            if (module != null)
                Console.Out.WriteLine("found module");
            IntPtr moduleHandle = Marshal.GetHINSTANCE(module);
            if (moduleHandle != IntPtr.Zero)
                Console.Out.WriteLine("got module handle: " +
                    moduleHandle.ToString());
            _Hook = HookProc;
            _Handle = SetWindowsHookEx(WH_KEYBOARD_LL, _Hook, moduleHandle, 0);
            if (_Handle == IntPtr.Zero)
                throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        public void Disable()
        {
            bool ok = UnhookWindowsHookEx(_Handle);
            _Handle = IntPtr.Zero;
            if (!ok)
                throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        private delegate int HookProcDelegate(
            int code, IntPtr wParam, IntPtr lParam);

        private int HookProc(int code, IntPtr wParam, IntPtr lParam)
        {
            return CallNextHookEx(_Handle, code, wParam, lParam);
        }

        private const int WH_KEYBOARD_LL = 13;

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(
            int hookType, HookProcDelegate lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern int CallNextHookEx(
            IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    }
}
4

3 に答える 3

17

うん、何が起こっているのか理解していると思う。SetWindowsHookEx() は有効なモジュール ハンドルを必要とし、それを検証しますが、低レベルのフックを設定するときに実際には使用しません。有効なハンドルが必要なだけです。特定のハンドルは関係ありません。LoadLibrary("user32.dll") を呼び出すことは、ハンドルを取得するための良い方法です。DLL は、そのメソッドを P/Invoke するため、常にロードされます。また、CLR ブートストラップ (mscoree.dll) によって常に読み込まれます。わざわざ FreeLibrary() を呼び出さないでください。違いはありません。

それ以降のバージョンの Windows では、このチェックは実行されなくなりました。それがいつ始まったのか正確にはわかりませんが、Windows 7 SP1 あたりのどこかだと思います。おそらく役立つことを意図していますが、「顧客のマシンではなく、自分のマシンで動作する」という失敗のシナリオを呼び起こします。

于 2010-09-10T11:37:25.613 に答える
2

このコードが機能する.Net 4.0では、呼び出しを置き換える必要がありました。

SetWindowsHookEx(WH_KEYBOARD_LL, _Hook, moduleHandle, 0);

と:

SetWindowsHookEx(WH_KEYBOARD_LL, _Hook, IntPtr.Zero, 0);

これにより問題が修正されました。これは、呼び出しが同じモジュールから行われたときに機能します。

私はここからこれを手に入れまし

于 2012-03-06T11:55:27.470 に答える
2

.net 2 と 4 の両方で機能する私のソリューションを次に示します。hInstance は ProcessModule.BaseAddress です。

public static class ModuleHelper
    {
        public static ProcessModule GetCurrentModule()
        {
            // need instance handle to module to create a system-wide hook
            Module[] list = System.Reflection.Assembly.GetExecutingAssembly().GetModules();
            System.Diagnostics.Debug.Assert(list != null && list.Length > 0);

            var currentProcess = Process.GetCurrentProcess();
            var modules = currentProcess.Modules;
            ProcessModule mod = null;
            foreach (ProcessModule m in modules)
                            //for .net 2 we will find module here
                if (m.ModuleName == list[0].Name)
                {
                    mod = m;
                    break;
                }

                    //for .net 4 take current module
            if (mod == null)
                mod = Process.GetCurrentProcess().MainModule;

            return mod;
        }
    }
于 2010-11-22T19:56:45.533 に答える