2

私のアプリケーションは、ユーザーがWindowsで特定のキーを押すたびに、何らかのアクションを実行する必要があります。

SetWindowsHookExオプションで呼び出すWH_KEYBOARD_LLことは、これを達成するための標準的な方法のようです。しかし、私の場合、何かが明らかに間違っていて、コールバックが実行されません。

デバッグコンソールアプリケーションの主な方法:

static void Main(string[] args)
{
    IntPtr moduleHandle = GetCurrentModuleHandle();
    IntPtr hookHandle = IntPtr.Zero;

    try
    {
        User32.HookProc hook = (nCode, wParam, lParam) =>
        {
            // code is never called :-(
            if (nCode >= 0)
            {
                Console.WriteLine("{0}, {1}", wParam.ToInt32(), lParam.ToInt32());
            }
            return User32.CallNextHookEx(hookHandle, nCode, wParam, lParam);
        };

        hookHandle = User32.SetWindowsHookEx(User32.WH_KEYBOARD_LL, hook, moduleHandle, 0);

        Console.ReadLine(); // 
    }
    finally
    {
        if (hoodHandle != IntPtr.Zero)
        {
            var unhooked = User32.UnhookWindowsHookEx(hookHandle);
            Console.WriteLine(unhooked); // true
            hookHandle = IntPtr.Zero;                   
        }
    }
}

GetCurrentModuleHandleメソッド:

private static IntPtr GetCurrentModuleHandle()
{
    using (var currentProcess = Process.GetCurrentProcess())
    using (var mainModule = currentProcess.MainModule)
    {
        var moduleName = mainModule.ModuleName;
        return Kernel32.GetModuleHandle(moduleName);
    }           
}

user32.dllおよびからのインポートkernel32.dll

public static class User32
{
    public const int WH_KEYBOARD_LL = 13;

    public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool UnhookWindowsHookEx(IntPtr hhk);

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

public static class Kernel32
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr GetModuleHandle(string lpModuleName);
}

私の問題が何か分かりますか?

4

3 に答える 3

4

SetwindowHook APIは、C#コンソールアプリケーションから呼び出すことはできません。Windows DLLで呼び出す必要があります(.Net DLLでは機能しません)。

この問題を回避する方法は確かにあり、私はずっと前に構築したアプリケーションの1つを使用しました。でも今は覚えていません。あなたが十分に長く検索するかどうかを知ることができるかもしれません。

于 2010-10-21T17:02:21.357 に答える
3

Main(string[] args) と Console.ReadLine() が示唆するように、これはコンソールアプリですか?

もしそうなら、これがあなたの問題の原因かもしれません

于 2010-10-21T18:21:41.793 に答える
2

このように Win32 API を使用する場合、自分でエラーをチェックすることが非常に重要です。代わりにそれを実行して例外をスローする使いやすい .NET ラッパーはもうありません。これはいくつかの場所で行う必要がありますが、ここではその 1 つを示します。

    hookHandle = User32.SetWindowsHookEx(User32.WH_KEYBOARD_LL, hook, moduleHandle, 0);
    if (hookHandle == IntPtr.Zero) throw new Win32Exception();

本当の問題は、mainModule.ModuleName の使用です。フルパスではなく、ファイルの名前のみを指定する場合。GetModuleHandle() は失敗し、IntPtr.Zero を返します。上記のように、これをテストした場合、問題はすぐに見つかります。

このコードを .NET 4.0 で実行すると、追加のエラー モードがあります。CLR は、マネージ コードのネイティブ モジュールを偽装しなくなりました。SetWindowsHookEx() には有効な DLL ハンドルが必要ですが、これは低レベルのフックであるため、実際には使用しません。これを取得する最善の方法は、user32.dll を要求することです。これは常にマネージ プログラムにロードされます。

    IntPtr moduleHandle = LoadLibrary("user32.dll");
    if (moduleHandle == IntPtr.Zero) throw new Win32Exception();

リリースする必要はありません。

于 2010-10-21T17:21:50.470 に答える