2010-08-25 11 views
6

Estoy tratando de establecer un gancho de teclado de Windows de bajo nivel para capturar tres teclas presionadas incluso si la aplicación no está enfocada. Para ello voy a llamar SetWindowsHookEx comoSetWindowsHookEx devuelve 0 al compilar para .NET Framework 4.0 en máquinas de 32 bits

// Create an instance of HookProc. 
KeyboardHookProcedure = new HookProc(KeyboardHookProc); 
//install hook 
hKeyboardHook = SetWindowsHookEx(
    WH_KEYBOARD_LL, 
    KeyboardHookProcedure, 
    Marshal.GetHINSTANCE(
     Assembly.GetExecutingAssembly().GetModules()[0]), 
    0); 
//If SetWindowsHookEx fails. 
if (hKeyboardHook == 0) 
{ 
    //Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set. 
    int errorCode = Marshal.GetLastWin32Error(); 
    //do cleanup 
    Stop(false, true, false); 
    //Initializes and throws a new instance of the Win32Exception class with the specified error. 
    throw new Win32Exception(errorCode); 
} 

Esto solía trabajar en las máquinas de 32 y 64 bits utilizando el .NET Framework 3.5, pero después de actualizar a .NET Framework 4.0 dejado de trabajar en máquinas de 32 bits.

¿Alguien sabe cómo resolver esto para que yo pueda usar el Framework 4.0 y hacer que esto funcione tanto en máquinas de 32 bits como de 64 bits?

+1

En .NET 4.0, SetWindowsHookEx devuelve 0 para mí con un error de procedimiento de enlace no es válido. – tofutim

Respuesta

21

importar el archivo DLL como esto:

[DllImport("kernel32.dll")] 
    public static extern IntPtr GetModuleHandle(string name); 

a continuación, utilizar

GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName) 

para reemplazar

Marshal.GetHINSTANCE(
    Assembly.GetExecutingAssembly().GetModules()[0] 
+1

Tenía el mismo problema y la solución era el cambio de código sugerido, usando GetModuleHandle (...) para obtener el identificador. – user2928227

+0

Gracias. Esto funcionó perfectamente para mí. No estaba recibiendo el módulo principal. Obtenía el módulo actual y devolvía 0. –

+0

El mismo problema aquí.Escribir un administrador de ventanas rápido para poder presionar una tecla, arrastrar un rectángulo en la pantalla y hacer que la ventana encaje. Desenterré un viejo módulo de "GlobalHooks" que escribí hace años para .NET 2.0, y descubrí que SetWindowsHookEx estaba fallando con cero. Esto resolvió el problema. También me hice notar que necesito pasar por todas las teclas llamando a GetAsyncKeyState, en lugar de hacer una sola llamada a GetKeyboardState, porque falla con problemas de subprocesamiento, como que no obtendrá los estados clave de modificación correctamente cuando otras aplicaciones están en primer plano. GetAsyncKeyState debe ser utilizado. – Triynko

1

Resuelto al orientar cada plataforma por separado. VS configurado para compilar tanto una versión de Win32 como una de Win64 y desplegar en las máquinas x86 y x64 su correspondiente binario.

El Win32 o x86 se ejecuta en las máquinas de 32 bits y de 64 bits.

2

De la documentación para SetWindowsHookEx

HMOD [en]
HINSTANCE
un identificador para el DLL que contiene el procedimiento de enlace a la que apunta el parámetro lpfn. El parámetro hMod debe establecerse en NULL si el parámetro dwThreadId especifica un subproceso creado por el proceso actual y si el procedimiento de enlace está dentro del código asociado con el proceso actual.

Por lo que debe estar pasando en IntPtr.Zero NULL

//install hook 
    hKeyboardHook = SetWindowsHookEx(
    WH_KEYBOARD_LL, 
    KeyboardHookProcedure, 
    IntPtr.Zero, 
    0); 
0

Hans Passant:

Cualquier asa del módulo va a hacer, ya que en realidad no se acostumbre a ganchos de bajo nivel, sin DLL tiene que ser inyectado para hacerlos funcionar. Se requiere cierta atención en en la selección de .NET 4 ya que su CLR ya no contiene , maneja el módulo falso para ensamblajes administrados puros. Uno bueno para usar es el que obtienes de pinvoking LoadLibrary ("user32.dll") ya que es siempre cargado. No tiene que llamar a FreeLibrary().

que necesitará esta declaración para llamar a LoadLibrary:

[DllImport("kernel32", SetLastError=true, CharSet = CharSet.Auto)] 
private static extern IntPtr LoadLibrary(string fileName); 
Cuestiones relacionadas