简体   繁体   English

如何在jni中创建CBTHook,然后在回调函数中调用Java函数?

[英]How to create a CBTHook in jni and then call a java function in the callback function?

Update (23/02/13): Tested the Hook separatedly, it works. 更新(13/02/23):单独测试了Hook,它可以工作。 The problem was that you can't use printf in a .dll, unless you associate a console with it. 问题是,除非您将控制台与其关联,否则您不能在.dll中使用printf

I instead replaced every printf with fprintf and wrote the output in a logfile. 我改为将每个printf替换为fprintf ,并将输出写入日志文件。

Update (22/02/13): Initialized the variable clazz in a correct way. 更新(13/02/22):以正确的方式初始化了变量clazz

I want to call a method in my java application, if any desktop window is created/activated/destroyed . 如果要created/activated/destroyed任何桌面窗口,我想在Java应用程序中调用一个方法。

I wrote a simple Java class with two native functions: 我编写了一个简单的Java类,其中包含两个本机函数:

public class Hook {

public Hook(){}

public void setstatus(){
    System.out.println("status set");
}

public native void starthook();
public native void stophook();
}

And in CI wrote the following: 并在CI中写道:

#include "Hook.h"
#include <windows.h>
#include <jni.h>
#include <stdio.h>

#pragma data_seg("Shared")
HHOOK   g_hHook  = NULL;
FILE *pFile=NULL;
//do mid, clazz and jvm have to be shared ?
//jmethodID mid=NULL;
//jclass clazz=NULL;
//static JavaVM *jvm = NULL;

#pragma data_seg()

HINSTANCE   g_hInstance = NULL;

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved ){
    switch (ul_reason_for_call){
    case DLL_PROCESS_ATTACH:
        g_hInstance  = (HINSTANCE) hModule;
        break;

    case DLL_THREAD_ATTACH:  break;
    case DLL_THREAD_DETACH:  break;
    case DLL_PROCESS_DETACH: break;
    }

    return TRUE;
}

LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam){
    if (nCode < 0)
        return CallNextHookEx(g_hHook, nCode, wParam, lParam);       
    /*
    JNIEnv *env;
    int res =(*jvm)->AttachCurrentThread(jvm,(void **)&env, NULL);
        if(res<0){
            fprintf(pFile,"AttachCurrentThread failed\n");
            return 0;
        }
    */

    HWND hWnd = (HWND)wParam;
    if (!hWnd){
        fprintf(pFile,"hWnd==NULL\n");
        //(*jvm)->DetachCurrentThread(jvm);
        return 0;
    }
    if (nCode == HCBT_ACTIVATE){            
        // is printed
        fprintf(pFile,"activated\n");
        // isn't called
        //(*env)->CallVoidMethod(env, clazz, mid);
        // Nothing is logged
        //if((*env)->ExceptionOccurred(env))
        //      (*env)->ExceptionDescribe(env);
    }
    else if (nCode == HCBT_DESTROYWND){
        fprintf(pFile,"destroyed\n");
    }
    else if (nCode == HCBT_CREATEWND){
        fprintf(pFile,"created\n");
    }
    //(*jvm)->DetachCurrentThread(jvm);
    return 0;
}

JNIEXPORT void JNICALL Java_Hook_starthook(JNIEnv *e, jobject o){
/*
if(jvm==NULL)
    if(((*e)->GetJavaVM(e,&jvm))<0){
        printf("GetJavaVM failed\n");
        return;
        }
    jclass localRef = (*e)->GetObjectClass(e, o);
        clazz = (*e)->NewGlobalRef(e,localRef);
    if(clazz==NULL){
        printf("GetObjectClass failed\n");
        return;
    }
    mid = (*e)->GetMethodID(e, clazz, "setstatus", "()V");
    if(mid==0){
        printf("GetMethodID failed\n");
        return;
    }
*/
    pFile = fopen("C:/workspace/CBTHook/log.txt","a");
    g_hHook      = SetWindowsHookEx(WH_CBT, (HOOKPROC) CBTProc, g_hInstance, 0);
    printf("Hook started\n");
}

JNIEXPORT void JNICALL Java_Hook_stophook(JNIEnv *e, jobject o){
    if (g_hHook){
        UnhookWindowsHookEx(g_hHook);
        g_hHook = NULL;
        //(*e)->DeleteGlobalRef(e,clazz);
    }
    if(pFile)
        fclose(pFile);
    }
    printf("Hook stopped\n");
}

The CBTHook works, but I can't call the setstatus function from within the callback function. CBTHook可以工作,但是我不能从callback函数中调用setstatus函数。

(*env)->CallVoidMethod(env, clazz, mid);

doesn't do anything and no Exception is thrown. 什么都不做,也不会抛出异常。

What do I have to do ? 我需要做什么 ?

Furthermore do I have to share the global variables: mid , clazz and jvm ? 此外,我是否必须共享全局变量: midclazzjvm Every variable in the 中的每个变量

#pragma data_seg("SHARED")

segment is "shared", which means that they are not unique to a specific process. 段是“共享的”,这意味着它们不是特定过程唯一的。

And what would be the correct way to attach/detach the other threads, if this is necessary? 如果需要,连接/分离其他线程的正确方法是什么?

Given the lack of clear explanation about what " don't receive messages " exactly means, i will speculate that your CBTProc is being called , and you just don't get the JVM callback. 鉴于对“ 不接收消息 ”的确切含义缺乏清晰的解释,我推测您的CBTProc 正在被调用 ,而您只是没有得到JVM回调。 If CBTProc is not called, then it's not a JNI problem at all . 如果未调用CBTProc ,则根本不是JNI问题

You did it almost right. 您做得差不多。 jmethodID is reusable between threads and between JNI calls, so it can be cached as you did. jmethodID可在线程之间和JNI调用之间重用,因此可以像您一样进行缓存。 The way you get JavaVM* in starthook() and cache it is also correct. starthook()获取JavaVM*并将其缓存的方式也是正确的。 You are only wrong about jclass clazz . 您对jclass clazz只是错了。 You try to cache it in starthook() and then try to use it not only in different call, but most probably even in different thread. 您尝试将其缓存在starthook() ,然后不仅尝试在不同的调用中使用它,而且甚至可能在不同的线程中使用它。 What you get from GetObjectClass is a local reference which means that it's validity is not ensured after the current JNI call exits back to JVM. GetObjectClass得到的是一个本地引用 ,这意味着在当前JNI调用返回到JVM之后不能确保其有效性。 It might work, but you can't rely on it. 它可能有效,但是您不能依靠它。 And it definitely won't work when used from different thread. 当从其他线程使用时,它绝对不起作用。 You must make a global reference : 您必须进行全局引用

jclass localRef = (*e)->GetObjectClass(e,o);
clazz = (*e)->NewGlobalRef(e,localRef);

Then you can use clazz in the way you already have in CBTProc commented out. 然后,您可以按照已在CBTProc注释掉的方式使用clazz The only work left is in stophook : 剩下的唯一工作是在stophook

(*e)->DeleteGlobalRef(e,clazz);

So that it doesn't leak. 这样它才不会泄漏。

Bottom note : i don't understand what you mean by " sharing the global variables ". 底部注释 :我不理解“ 共享全局变量 ”的含义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM