¿Falló SetWindowsHookEx en .NET 4.0 en una máquina de 32 bits con "módulo no encontrado"?

Encontré preguntas similares en esta página, pero parece que no puedo entender cómo interpretar las respuestas o averiguar si realmente son duplicados.

Aquí están los posibles duplicados que he encontrado, con comentarios:

SetWindowsHookEx devuelve 0 al compilar para .NET 4.0 framework en máquinas de 32 bits

Parece que no devuelve 0 en el mío, pero noté que el identificador informado cuando se bloquea (.NET 4.0 en 32 bits) es muy diferente del identificador informado cuando se ejecuta (.NET 3.5 en 32 bits), como crash handle = 523727, y Working handle = 172738378.

Llamar a SetWindowsHookEx dentro del depurador VS2008 siempre devuelve NULL

Puedo reprobar mi problema cuando ejecuto fuera de Visual Studio

Módulo no encontrado

Esto parece más prometedor, excepto que los comentarios a la respuesta eliminada mencionan que debería usar LoadLibrary y GetProcAddress para cargar user32.dll en .NET 4.0 ya que algo cambió sobre la carga de ensamblados. Sin embargo, estoy bastante seguro de que es mi propio módulo el que no puede encontrar, pero no sé si esto aplica.

Los comentarios en cuestión sobre la respuesta eliminada a esa última, de Hans Passant, dicen:

¿Estás usando .NET 4.0? Su CLR cambió la forma en que se cargan los ensamblados, ya no hay una llamada LoadLibrary, no habrá un controlador de módulo para ellos. Usar GetEntryAssembly () en su lugar sería otra solución. - Hans Passant 5 de mayo a las 19:43

Entonces, ¿cuál es la palabra aquí? ¿Estás usando .NET 4.0? ¿Intentó usar LoadLibrary ("user32.dll") para obtener un identificador de DLL utilizable? - Hans Passant 6 de mayo a las 15:43

Estoy bastante seguro de que no necesito hacer esto, pero obviamente no estoy 100% seguro. La pregunta que me queda si necesito cambiar esto es por qué funciona en un sistema operativo de 64 bits, cuando se compila paraAny CPU, pero no funciona en 32 bits, en ninguna configuración.

Si de hecho algo ha cambiado con respecto a la carga de ensamblados .NET, para que no obtenga un identificador adecuado para la biblioteca de clases, tengo las siguientes preguntas:

¿Hay alguna forma de engañarlo para que haga lo que quiero, sin tener que degradar a .NET 3.5 o cambiar la biblioteca de ganchos a no administrada?¿Por qué funciona cuando se ejecuta en SO de 64 bits, pero no en 32 bits?

Antecedentes

He creado un programa, en .NET 4.0, que usa SetWindowsHookEx con el tipo de gancho WH_KEYBOARD_LL para capturar las pulsaciones de teclas. Esto funciona bien en mi Windows 7 de 64 bits, pero se bloquea con un "módulo no encontrado" cuando el gancho del teclado está instalado en Windows 7 de 32 bits.

Esto es lo que he intentado:

Compila para x86, ejecuta en un sistema operativo de 64 bits, se bloquea con "módulo no encontrado"Compila para x86, ejecuta en SO de 32 bits, se bloqueaCompila para cualquier CPU, ejecuta en SO de 64 bits, funciona bienCompilar para cualquier CPU, ejecutar en SO de 32 bits, se bloqueaCambie a .NET 3.5 y repita los cuatro casos anteriores, todos funcionan

Prefiero no cambiar mi código a .NET 3.5, ya que estoy usando algunas de mis bibliotecas de clases para facilitar el trabajo, y el último código solo está en .NET 4.0.

Usted puededescargue un archivo .ZIP con todo como un proyecto de Visual Studio 2010 si lo desea, o puede pegar en los siguientes dos archivos.

Para recrear si quieres seguir esa ruta:

Cree un nuevo proyecto de consola, .NET 4.0Agregue otro proyecto de biblioteca de clases, también .NET 4.0Agregue una referencia al proyecto de biblioteca de clases desde el proyecto de programa de consolaPegue el contenido de Program.cs a continuación en el archivo Program.cs que tiene en el proyecto de consolaPegue el contenido de Hook.cs a continuación en un archivo en el proyecto de biblioteca de clases. Puede pegarlo en el archivo predeterminado de Class1.cs o agregar otro archivo. Túno puedo poner esto en el proyecto de la consola

Luego compila y ejecuta, prueba varias configuraciones.

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);
    }
}

Respuestas a la pregunta(3)

Su respuesta a la pregunta