Dot-Net-3.5

SetWindowsHookEx 在 32 位機器上的 .NET 4.0 中以“找不到模組”失敗?

  • May 21, 2013

我在此頁面上發現了類似的問題,但我似乎無法弄清楚如何解釋答案或弄清楚它們是否真的重複。

以下是我發現的可能重複項,並附有評論:

它似乎沒有返回 0,但我注意到它崩潰時報告的句柄(32 位上的 .NET 4.0)與它執行時報告的句柄(32 位上的 .NET 3.5)有很大不同,比如崩潰句柄 = 523727,工作句柄 = 172738378。

在 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% 確定。如果我需要更改它,我留下的問題是為什麼它在編譯為 64 位作業系統時可以在 64 位作業系統上執行Any CPU,但在任何配置中都不能在 32 位作業系統上執行。

如果在載入 .NET 程序集方面確實發生了一些變化,以至於我無法正確處理類庫,那麼我有以下問題:

  • 有什麼辦法可以欺騙它做我想做的事,而不必降級到 .NET 3.5 或將鉤子庫更改為非託管?
  • 為什麼在 64 位作業系統上執行時它可以工作,但在 32 位作業系統上卻不行?

背景

我在 .NET 4.0 中建構了一個程序,它使用帶有 WH_KEYBOARD_LL 鉤子類型的 SetWindowsHookEx 來擷取按鍵。這在我的 64 位 Windows 7 上執行良好,但在 32 位 Windows 7 上安裝鍵盤掛鉤時會因“找不到模組”而崩潰。

這是我嘗試過的:

  • 為 x86 編譯,在 64 位作業系統上執行,因“找不到模組”而崩潰
  • 為 x86 編譯,在 32 位作業系統上執行,崩潰
  • 為任何 CPU 編譯,在 64 位作業系統上執行,執行良好
  • 為任何 CPU 編譯,在 32 位作業系統上執行,崩潰
  • 切換到.NET 3.5,重複以上四種情況,都行

我寧願不將我的程式碼切換到 .NET 3.5,因為我正在使用我的一些類庫來簡化工作,而最新的程式碼僅在 .NET 4.0 中。

如果需要,您可以下載包含所有內容的 .ZIP 文件作為 Visual Studio 2010 項目,也可以粘貼以下兩個文件。

如果你想沿著這條路線重新創建:

  1. 創建一個新的控制台項目,.NET 4.0
  2. 添加另一個類庫項目,也是 .NET 4.0
  3. 從控制台程序項目中添加對類庫項目的引用
  4. 將下面的 Program.cs 內容粘貼到控制台項目中的 Program.cs 文件中
  5. 將下面的 Hook.cs 內容粘貼到類庫項目中的文件中。您可以將其粘貼到 Class1.cs 預設文件中,或添加另一個文件。你不能把它放到控制台項目中

然後編譯執行,測試各種配置。

程序.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");
       }
   }
}

鉤子.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);
   }
}

是的,我想你明白髮生了什麼。SetWindowsHookEx() 需要一個有效的模組句柄,並對其進行驗證,但在設置低級掛鉤時它實際上並沒有使用它。您只需要一個有效的句柄,哪個特定的句柄都沒有關係。呼叫 LoadLibrary(“user32.dll”) 是獲取句柄的好方法,因為您 P/Invoke 它的方法,DLL 將始終被載入。它總是由 CLR 引導程序 (mscoree.dll) 載入。不要打擾呼叫 FreeLibrary(),它沒有區別。

更高版本的 Windows 不再執行此檢查。不完全確定什麼時候開始的,我想是在 Windows 7 SP1 附近的某個地方。可能意味著有幫助,但會呼叫“在我的機器上工作,而不是客戶的”失敗場景。

引用自:https://stackoverflow.com/questions/3671673