このページで同様の質問を見つけましたが、回答を解釈する方法や、それらが本当に重複しているかどうかを判断する方法がわかりません。
コメント付きで、私が見つけた可能性のある重複は次のとおりです。
32 ビット マシンで .NET 4.0 フレームワーク用にコンパイルすると、SetWindowsHookEx は 0 を返します。
私の場合は 0 を返さないようですが、クラッシュしたときに報告されるハンドル (32 ビットの .NET 4.0) が、実行時に報告されるハンドル (32 ビットの .NET 3.5) とは大きく異なることに気付きました。クラッシュ ハンドル = 523727、ワーキング ハンドル = 172738378 など。
VS2008 デバッガー内で SetWindowsHookEx を呼び出すと、常に NULL が返される
Visual Studio の外部で実行しているときに問題を再現できます
-
アセンブリのロードに関する何かが変更されたため、LoadLibrary と GetProcAddress を使用して .NET 4.0 で user32.dll をロードする必要があることが削除された回答へのコメントに記載されていることを除いて、これは最も有望に思えます。ただし、それが見つからないのは私自身のモジュールであると確信していますが、これが当てはまるかどうかはわかりません。
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 つのファイルに貼り付けることができます。
そのルートをたどりたい場合に再作成するには:
- 新しいコンソール プロジェクト、.NET 4.0 を作成します。
- 別のクラス ライブラリ プロジェクトを追加します。これも .NET 4.0 です。
- コンソール プログラム プロジェクトからクラス ライブラリ プロジェクトへの参照を追加します。
- 以下の Program.cs コンテンツを、コンソール プロジェクトにある Program.cs ファイルに貼り付けます。
- 以下の 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);
}
}