java调用钩子jna / jni失败

时间:2018-04-19 09:11:27

标签: java c++ java-native-interface hook jna

我需要全局禁用鼠标和键盘输入,但使用纯java无法解决问题。

我选择了jni / jna调用c ++函数。当我通过jna调用c ++函数时,java程序没有任何效果,没有任何错误或异常。

我已经测试了.dll文件,在c ++中它无法正常运行。

这是我的java代码:

public class HookTest {
    public interface  Hook extends Library{
        Hook INSTANCE = (Hook) Native.loadLibrary("lib/Hook",Hook.class);
        public void FuncEndHook();
        public void FuncHookDevice();
    }

    public static void main(String[] args) {
        Hook.INSTANCE.FuncHookDevice();
    }
}

我的c ++代码:

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include "stdio.h"

HHOOK g_HookHwnd = NULL;
HHOOK g_hMouse = NULL;

// 钩子子程
extern "C" _declspec(dllexport) LRESULT CALLBACK MyHookFun(int nCode, WPARAM wParam, LPARAM lParam)
{
    printf("in hook key function\n");
    // 这个Structure包含了键盘的信息
    /*typedef struct {
    DWORD vkCode;
    DWORD scanCode;
    DWORD flags;
    DWORD time;
    ULONG_PTR dwExtraInfo;
    } KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;*/
    // 我们只需要那个vkCode
    PKBDLLHOOKSTRUCT pVirKey = (PKBDLLHOOKSTRUCT)lParam;

    // MSDN说了,nCode < 0的时候别处理
    if (nCode >= 0)
    {
        // 按键消息
        switch (wParam)
        {
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
        case WM_KEYUP:
        case WM_SYSKEYUP:
            switch (pVirKey->vkCode)
            {
            case VK_LWIN:
            case VK_RWIN:
                return 1;  // 吃掉消息
                break;
            }
            return 1;
            break;
        }
    }

    return CallNextHookEx(g_HookHwnd, nCode, wParam, lParam);
}

extern "C" _declspec(dllexport) LRESULT CALLBACK  MyHookMouse(int nCode, WPARAM wParam, LPARAM lParam)
{
    printf("in hook mouse function\n");
    return 1;
}


HMODULE g_Module;
extern "C" _declspec(dllexport)  BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
    {

        break;
    }
    }
    return TRUE;
}


extern "C" _declspec(dllexport) void FuncHookDevice()
{
    if (!g_HookHwnd)
    {
        printf("start hook\n");
        g_HookHwnd = SetWindowsHookEx(WH_KEYBOARD_LL, MyHookFun, g_Module, 0);
        //g_hMouse = SetWindowsHookEx(WH_MOUSE_LL, MyHookMouse, g_Module, 0);  //暂时禁用鼠标钩子
    }
}

extern "C" _declspec(dllexport) void FuncEndHook()
{
    printf("end hook\n");
    UnhookWindowsHookEx(g_HookHwnd);
    //UnhookWindowsHookEx(g_hMouse);   //暂时禁用鼠标钩子
    g_HookHwnd = NULL;
}

运行结果:

result

程序执行了c ++,但没有禁用键盘。

感谢您的阅读和帮助。

1 个答案:

答案 0 :(得分:0)

查看LowLevelKeyboardProc回调函数的备注部分:

  

在安装它的线程的上下文中调用此钩子。通过向安装了挂钩的线程发送消息来进行调用。因此,安装钩子的线程必须有一个消息循环。

您已为事件创建了一个钩子,但没有消息循环处理这些事件。所有使用事件的Windows应用程序(例如使用本机Windows样式的图形桌面应用程序)都有一个消息循环,调用GetMessage来获取事件并采取相应的行动。

您可以手动调用WinAPI函数来处理消息循环(GetMessageDispatchMessage等),但我很高兴地说 JNA确实有帮助

jna-platform包中(您需要将其添加到Maven / Gradle /任何依赖项中),有一个名为User32Util.MessageLoopThread的类。

以下示例适用于我:

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32Util.MessageLoopThread;

public class HookTest {

    public interface Hook extends Library {
        Hook INSTANCE = (Hook) Native.loadLibrary("lib/Hook", Hook.class);
        public void FuncEndHook();
        public void FuncHookDevice();
    }

    public static void main(String[] args) {
        MessageLoopThread thread = new MessageLoopThread();
        thread.start();
        thread.runOnThread(() -> {
            Hook.INSTANCE.FuncHookDevice();
            return null;
        });
    }
}

注意我在一个新线程上显式调用FuncHookDevice,该线程与消息循环的线程相同,这是备注所要求的。