Programmatically (in Java) keep OS X from going asleep - java

OS X Lion still goes to sleep, even if programmatic mouse "wiggles" are being periodically issued from a Java Robot (this is a standard approach to keeping machines from going to sleep and is documented elsewhere in stackoverflow, and it worked for me prior to OS X Lion).
Can anyone suggest an alternative approach, which does not require permission escalation, of keeping the machine awake from a Java application?
((The fallback solution is obviously to change the Energy Saving preferences so that the machine never sleeps, but we'd like to be smarter than that because requiring the machine to stay awake is the exception rather than the rule)).

It turns out that it is no longer possible to do this with pure-Java hacks, and one must resort to implementing a JNI I/O listener, the code of which would contain something like
http://developer.apple.com/library/mac/#qa/qa1340/_index.html
An alternative, if you can tolerate calling a command line tool, is to call pmset noidle in a daemon thread.

Do you need to prevent display sleep or system sleep? If the latter, you can prevent system sleep with disk activity - e.g. read from or write to a file.
Hard disk activity does not prevent display sleep, but it does prevent
system sleep. It is not uncommon for the display to sleep before the
system goes to sleep if both types of sleep are set to occur after the
same length of inactivity.
http://support.apple.com/kb/ht1776
This answer points to Objective-C code that can be used to prevent sleep, using the UpdateSystemActivity call. Could you create a simple JNI wrapper on this which you could invoke as a native method from your Java code?
It also seems possible to disable and re-enable sleep using applescript. Could you invoke such a script using Runtime.exec() from your Java code?

Following JNI solution works for macOS 10.5 / GCC
I. Definition Java Native Interface
// File KalleInterface.java
public class KalleInterface
{
static
{
// java.library.path set to libKalleInterface.dylib
System.loadLibrary("KalleInterface");
}
public static native boolean preventSleep();
public static native boolean allowSleep();
}
II. Generate Equivalent C++ Interface
Assume compliled java class file is ./bin/KalleInterface.class
javah -jni -d "${PWD}" -classpath "${PWD}/bin" KalleInterface
generates a C++ header KalleInterface.h as
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class KalleInterface */
#ifndef _Included_KalleInterface
#define _Included_KalleInterface
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: KalleInterface
* Method: preventSleep
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_KalleInterface_preventSleep(JNIEnv *, jclass);
/*
* Class: KalleInterface
* Method: allowSleep
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_KalleInterface_allowSleep(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
III. Implement C++ Interface
#include "esc_util_KalleInterface.h"
#include <IOKit/pwr_mgt/IOPMLib.h>
// IOPMAssertionCreateWithName ...
// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep
//reasonForActivity is a descriptive string used by the system whenever it needs
// to tell the user why the system is not sleeping. For example,
// "Mail Compacting Mailboxes" would be a useful string.
// IOPMAssertionDeclareUserActivity ...
// claims user activity
#ifdef __cplusplus
extern "C" {
#endif
CFStringRef reasonForActivity= CFSTR("User Activity Type");
// NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
IOPMAssertionID assertionID;
static bool active = false;
/*
* Class: KalleInterface
* Method: preventSleep
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_esc_util_KalleInterface_preventSleep(JNIEnv*, jclass)
{
IOReturn success = 0;
if (active)
{
return (jboolean)true;
}
//success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
//success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
success = IOPMAssertionDeclareUserActivity(reasonForActivity, kIOPMUserActiveLocal, &assertionID);
active =(success == kIOReturnSuccess);
return (jboolean)active;
//Add the work you need to do without
// the system sleeping here.
}
/*
* Class: KalleInterface
* Method: allowSleep
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_esc_util_KalleInterface_allowSleep(JNIEnv *, jclass)
{
IOReturn success = 0;
if (!active)
{
return (jboolean)true;
}
success = IOPMAssertionRelease(assertionID);
active = !(success == kIOReturnSuccess);
//The system will be able to sleep again.
return (jboolean)(!active);
}
#ifdef __cplusplus
}
#endif
IV. Build Shared Library
Command line steps for building libKalleInterface.dylib are
g++ -c -fPIC -I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/darwin -o KalleInterfaceCPP.o KalleInterfaceCPP.cpp
g++ -dynamiclib -framework CoreFoundation -framework IOKit -o libKalleInterface.dylib KalleInterfaceCPP.o -lc
V. Test Class
// File KalleTest.java
public class KalleTest
{
public static void main(String...args)
{
boolean ok1 = KalleInterface.preventSleep();
System.out.println(ok1);
try
{
Thread.sleep(60*10*1000);
}
catch (Exception x)
{
x.printStackTrace();
}
boolean ok2 = KalleInterface.allowSleep();
System.out.println(ok2);
}
}
Assuming compiled class is ./bin/KalleTest.class and shared library is ./libKalleInterface.dylib, test is invoked by
java -Djava.library.path=${PWD} -classpath ./bin KalleTest
Expected behaviour: Mac remains active for 10 minutes, each of preventSleep(), allowSleep() produce result/output true.

Related

Java Invocation API: Call the C function back from the java code

I have a C (navive) program and a jar file with the main() method. From my native program I am initializing the JVM, and calling the main() method. I have no problems with this, everything is completely fine. But then I wanted to call back a C function from my java code.
The C function is defined in the native code in the same module as the one, that have created the JVM. The header is auto-generated, and the body is as simple as this:
JNIEXPORT void JNICALL Java_eu_raman_chakhouski_NativeUpdaterBus_connect0(JNIEnv* env, jclass clazz)
{
return;
}
So, from the java code I'm calling NativeUpdaterBus.connect0(), continuosly getting an UnsatisfiedLinkError. I have no System.loadLibrary() calls in my java code, because I thought, that there will be no problems calling the native code back from the java code if the target module is (possibly?) already loaded.
Well, maybe my approach is completely incorrect, but I can't see any obvious defects, maybe you could help?
What possibly could help (but I didn't tried any of these approaches, because I'm still not quite sure)
Use a kind of a "trampoline" dynamic library with these JNI methods, load it from the java code, then marshal native calls through it.
Define a java.lang.Runnable's anonymous inheritor, created with jni_env->DefineClass() but this involves some bytecode trickery.
Use an another, less invasive approach, like sockets, named pipes, etc. But in my case I'm using only one native process, so this might be an overkill.
I'm using OpenJDK 11.0.3 and Windows 10. My C program is compiled with the Microsoft cl.exe 19.16.27031.1 for x64 (Visual Studio 2017).
One possibility, as others have already mentioned, is to create a shared library (.dll) and call it from the native code and from Java to exchange data.
However, if you want to callback to a C function defined in the native code in the same module as the one the JVM originally created, you can use RegisterNatives.
Simple Example
C program creates JVM
it calls a Main of a class
the Java Main calls back a C function named connect0 in the calling C code
to have a test case the native C function constructs a Java string and returns it
the Java side prints the result
Java
package com.software7.test;
public class Main {
private native String connect0() ;
public static void main(String[] args) {
Main m = new Main();
m.makeTest(args);
}
private void makeTest(String[] args) {
System.out.println("Java: main called");
for (String arg : args) {
System.out.println(" -> Java: argument: '" + arg + "'");
}
String res = connect0(); //callback into native code
System.out.println("Java: result of connect0() is '" + res + "'"); //process returned String
}
}
C Program
One can create the Java VM in C as shown here
(works not only with cygwin but still with VS 2019) and then register with RegisterNatives native C callbacks. So using the function invoke_class from the link above it could look like this:
#include <stdio.h>
#include <windows.h>
#include <jni.h>
#include <stdlib.h>
#include <stdbool.h>
...
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
helloWorldClass = (*env)->FindClass(env, "com/software7/test/Main");
mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "one argument");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}
jstring connect0(JNIEnv* env, jobject thiz);
static JNINativeMethod native_methods[] = {
{ "connect0", "()Ljava/lang/String;", (void*)connect0 },
};
jstring connect0(JNIEnv* env, jobject thiz) {
printf("C: connect0 called\n");
return (*env)->NewStringUTF(env, "Some Result!!");
}
static bool register_native_methods(JNIEnv* env) {
jclass clazz = (*env)->FindClass(env, "com/software7/test/Main");
if (clazz == NULL) {
return false;
}
int num_methods = sizeof(native_methods) / sizeof(native_methods[0]);
if ((*env)->RegisterNatives(env, clazz, native_methods, num_methods) < 0) {
return false;
}
return true;
}
int main() {
printf("C: Program starts, creating VM...\n");
JNIEnv* env = create_vm();
if (env == NULL) {
printf("C: creating JVM failed\n");
return 1;
}
if (!register_native_methods(env)) {
printf("C: registering native methods failed\n");
return 1;
}
invoke_class(env);
destroy_vm();
getchar();
return 0;
}
Result
Links
Creating a JVM from a C Program: http://www.inonit.com/cygwin/jni/invocationApi/c.html
Registering Native Methods: https://docs.oracle.com/en/java/javase/11/docs/specs/jni/functions.html#registering-native-methods
System.loadLibrary() is essential for the jni lookup to work. You also have a more flexible System.load() alternative.
Make sure that the native method implementation is declared with extern "C" and is not hidden by linker.

Create a shared library in C for java

When I try to use my library.dll with java I have this error :
java.lang.UnsatisfiedLinkError: C:\Users\ledev\Desktop\library.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
at java.base/java.lang.ClassLoader$NativeLibrary.load0(Native Method)
at java.base/java.lang.ClassLoader$NativeLibrary.load(ClassLoader.java:2424)
at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2481)
at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2678)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2643)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:876)
at java.base/java.lang.System.loadLibrary(System.java:1875)
at JNI.HelloJNI.<clinit>(HelloJNI.java:6)
Exception in thread "main"
Here is my code main.c :
#include "main.h"
#include <stdio.h>
#include<stdlib.h>
DLL_EXPORT void hello_jni()
{
printf("Hello, JNI !");
}
Here is my main.h :
#ifndef __MAIN_H__
#define __MAIN_H__
#define BUILDING_DLL
#include <windows.h>
#ifdef BUILD_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
#ifdef __c
extern "C"
{
#endif
DLL_EXPORT void hello_jni();
#ifdef __c
}
#endif
#endif // __MAIN_H__
And here is my java code :
package JNI;
public class HelloJNI {
static {
System.loadLibrary("library");
}
private native void hello_jni();
public static void main(String[] args) {
new HelloJNI().hello_jni(); // invoke the native method
}
}
This is how I created my dll :
g++ -c -BUILD_DLL main.c
g++ -shared -o library.dll main.o -Wl,--out-implib,libexample_dll.a
I'm working under intellij IDEA for java and none particular IDE for my c code. My os is windows 10.
I don't really know what I have to modifiy to make it works. None of the topics I read helped me (for exemple : Create a valid shared library in C). If you need anything else, just tell me.

Java JVMTI Agent crashes on GetEnv()

I'm at a total loss here. I'm trying to get a JVMTI agent library running but it keeps crashing for some reason.
I've narrowed it down this line:
(*jvm)->GetEnv(jvm, (void**)jvmti, JVMTI_VERSION_1_0);
this is the full code of the agent lib (in C):
#include <jvmti.h>
#include <stdlib.h>
jvmtiEnv* jvmti = NULL;
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
printf("Agent started.\n");
_flushall();
jint err = (*jvm)->GetEnv(jvm, (void**)jvmti, JVMTI_VERSION_1_0);
if (err != JNI_OK)
{
printf("Failed to get JVMTI env!\n");
_flushall();
return err;
}
return JNI_OK;
}
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved)
{
return JNI_OK;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
As I tried to isolate what the issue was I wrote a very simple java app to test
this with:
public class Test
{
public static void main(String[] args)
{
System.out.println("Hello from java!");
}
}
If I run this from netbeans with the VM arg -agentpath set to my .dllcontaining the code above, the app seems to crash when it tries to call GetEnv().
I've made sure of the following things:
- The JVM and the dll are both 64bit.
- The library is most definitely being found and loaded (the printf output is visible before the crash.)
I don't know what else could probably be causing this, do I have to link against some JVMTI API lib that I don't know about?
Or could this be an issue with the java installation on my PC?
Thanks
You should be passing address of jvmti to GetEnv() as in:
jint err = (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_1_0);

JNI Keep Object in C++ alive over multiple JNI calls

I have a problem in my Java native audio library but first, here is my current approach:
With a native method I'm opening a 'global' stream, which receives data over a callback function.
The callback function runs until there is no data.
If there is no data, the stream only stops, but does not get closed.
Now I wanted to feed the stream with data again [trying to start stream again(this operation is allowed)], but the stream has already been deleted.
So now I tried to figure out how to prevent deletion of the stream from C++ or Java.
One solution was to create a thread in the stream, which prevents the deletion.
But I don't like this solution...
So I searched how to keep such objects alive and found out, that there are so called "global references" which can be made with the JNI. But I did not understand whether they are only for java objects or for both.
Also I tried out whether another pointer type of C++ could help.
I appreciate any help or ideas, it does not have to be JNI only. C++ standard library methods/functions/classes etc. are also good :) !
System information:
Compiler: MinGW64 over MSYS2
JDK8u91
Of course 64bit operation system (Does not have to be named xD)
With global stream is meant, that the stream is accessible to all JNI methods.
EDIT:
Okay, 'to let the cat out of the back' I'm using RtAudio.
Realtime C++ Audio Library
Example:
//THIS IS C++ CODE
RtAudio audio(RtAudio::WASAPI);
int callback(//Buffer stuff etc.){
//do something
if(data.isEmpty())return 1;//invokes audio.closeStream() but this does NOT closes the stream!
else return 0; //Go on with the stream rather wait for the next call
}
JNIEXPORT void JNICALL openStream(jintArray data){
//This is a outputstream
audio.openStream(&outputParams,....., &callback,....);
audio.startStream();
}
JNIEXPORT void JNICALL fillData(jintArray data){
//filldata again!
stream.start(); //Starts the stream but does nothing, because the stream is deleted because of Java
}
If I would change the openStream method to this, the stream won't be deleted but I look for a better solution...
JNIEXPORT void JNICALL openStream(jintArray data){
//This is a outputstream
audio.openStream(&outputParams,....., &callback,....);
audio.startStream();
**while(true); //ADD THIS AND THE STREAM WON'T BE DELETED!**
}
Another solution is to add into the RtAudio API a "keepInstanceAliveThread" which is called after the stopStream() method and deleted after calling startStream() or closeStream(). I would rather prefer another solution but at all, there isn't any yet.
Pre-outcomes:
Thanks to #marcinj:
global object are known to cause many problems, its hard to control their construction/destruction.
EDIT:
I found out in the internet (also on stackoverflow), that the destructor is called after the return of a JNI method.
Use a long in the Java object to hold a pointer to the C++ object.
A Java long is 64 bits, and every platform Java runs on has either 32- or 64-bit pointers. And every platform Java is supplied for will support this, despite it not being strictly-conforming C or C++ code.
Java:
// class member
private long audio
// native functions
private native long openStream( int[] data );
private native void deleteStream( long audio );
private native void nativeFillData( long audio, int[] data );
public MyClass()
{
audio = openStream( data );
}
public void fillData( int[] data )
{
nativeFillData( this.audio, data );
}
// delete the C++ object - you may want to
// control this directly and not rely on
// finalize() getting called
protected void finalize()
{
deleteStream( audio );
super.finalize();
}
C++:
JNIEXPORT jlong JNICALL openStream(jintArray data)
{
RtAudio *audio = new RtAudio(RtAudio::WASAPI);
audio->openStream(&outputParams,....., &callback,....);
audio->startStream();
// C-style cast - JNI interface is C, not C++
return( ( jlong ) audio );
}
JNIEXPORT void JNICALL deleteStream(jlong jaudio)
{
RtAudio *audio = static_cast <RtAudio *>( jaudio );
delete audio;
}
JNIEXPORT void JNICALL nativeFillData(jlong jaudio, jintArray data)
{
RtAudio *audio = static_cast <RtAudio *>( jaudio );
audio->start();
...
}
1) JAVA THREAD WAY
We can create a new thread to keep running in a JNI function locked with a monitor or conditional while loop.
Then a separate call would stop the execution of the thread in your function by releasing the monitor or changing the condition in the while loop.
2) JAVA OBJECT REFERENCE WAY
Another option is to create a Global Reference of your object
Android JNI and NewGlobalRef.
Here a separate call on USB disconnect would do DeleteGlobalRef.
We can also move the life cycle of your C++ object into java by passing it back to the java layer
keep some sort of c++ object alive over multiple jni calls.
Here a separate call on USB disconnect would remove any reference to C++ object in your java code.
Implementation
Native File (mynative.cpp)
extern "C" JNIEXPORT jobject JNICALL
Java_com_android_nativecpp_MainActivity_createJniNativeReference(JNIEnv* env, jobject obj, jint size) {
void* buf = malloc(size);
jobject sharedbytebuffer = env->NewDirectByteBuffer(buf, size);
return env->NewGlobalRef(sharedbytebuffer);
}
extern "C" JNIEXPORT void JNICALL
Java_com_android_nativecpp_MainActivity_deleteJniNativeReference(JNIEnv* env, jobject obj, jobject sharedbytebuffer) {
env->DeleteGlobalRef(sharedbytebuffer);
void* buf = env->GetDirectBufferAddress(sharedbytebuffer);
free(buf);
return;
}
Java file (MainActivity.java)
import java.nio.ByteBuffer;
private ByteBuffer mJniReference;
public native ByteBuffer createJniNativeReference(int size);
public native void deleteJniNativeReference(ByteBuffer mJniReference);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mJniReference = createJniNativeReference(1000);
}
protected void onDestroy() {
deleteJniNativeReference(mJniReference);
super.onDestroy();
}
EXPLAINATION
The reason for either of 1) or 2) is otherwise, the creating frame is then exited from the stack and the JNI local references are deleted.
This will end all the threads in C++ when all std::thread references (which are not detached, ideally) are deleted.
In the non-detached case std::thread destructors are called on the main exit, or when a thread object goes out of scope and then terminate() is called.
In the detached case the detached threads exit on app close which kills the host process. They can also be garbage collected.

what is wrong with this call to the java method?

I am trying to call a Java method from the code. C code listens to either Escape, Shift, Ctrl key press, then it calls the Java method telling which key was pressed. Following are the snippets that play a role in this.
C Snippet:
mid = (*env)->GetMethodID(env,cls,"callBack","(Ljava/lang/String;)V");
Env = env;
if(called)
switch(param) {
case VK_CONTROL:
printf("Control pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"11"); // calling the java method
break;
case VK_SHIFT:
printf("Shift pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"10"); // calling the java method
break;
case VK_ESCAPE:
printf("Escape pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"1B"); // calling the java method
break;
default:
printf("The default case\n");
break;
}
Java Snippet:
public void callBack(String key) {
String x = KeyEvent.getKeyText(Integer.parseInt(key, 16));
System.out.println(x);
}
When I run the program and press the Escape key I get this on the console:
Escape pressed !
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x5c8b809a, pid=7588, tid=8088
#
# JRE version: 7.0
# Java VM: Java HotSpot(TM) Client VM (20.0-b01 mixed mode, sharing windows-x86 )
# Problematic frame:
# V [jvm.dll+0x19809a]
#
# An error report file with more information is saved as:
# W:\UnderTest\NetbeansCurrent\KeyLoggerTester\build\classes\hs_err_pid7588.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#
I know I am calling the Java function the wrong way, but I don't know where I am wrong. As from the output, it satisfies the case when I press the Escape key and then an unexpected error occurs.
Link to the LOG FILE
EDIT:
After the answer by mavroprovato I still get the same errors.
I edited this way:
(*Env)->CallVoidMethodA(Env,Obj,mid,(*Env)->NewStringUTF(Env,"1B"));
EDIT:
COMPLETE CODE version 1
COMPLETE CODE version 2
The JVM is crashing because the JNIEnv that is used is not a valid one. There are other issues with the code as well.
The Sun JNI documentation is providing very good information regarding threads.
Here comes some parts that are obvious:
Create a JNI_OnLoad function in your code. It will be called when the library is loaded. Then cache the JavaVM pointer because that is valid across threads. An alternative is to call (*env)->GetJavaVM in the initializeJNIVars function but I prefer the first one.
In your initializeJNIVars you can save the obj reference by calling Obj = (*env)->NewGlobalRef(obj).
In the LowLevelKeyboardProc you will have to get the env pointer:
AttachCurrentThread(JavaVM *jvm, JNIEnv &env, NULL);
Edit
OK, here are the code that you should add to get it working, I have tried it myself and it works. NB: I have not analyzed what your code is actually doing so I just did some fixes to get it working.
Add these variables among your other global variables:
static JavaVM *javaVM = NULL;
static jmethodID callbackMethod = NULL;
static jobject callbackObject = NULL;
You can remove your cls, mid, Env and Obj variables and use mine instead.
Create the JNI_OnLoad method where you cache the JavaVM pointer:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = 0;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)) {
return JNI_ERR;
}
javaVM = jvm;
return JNI_VERSION_1_4;
}
Alter your initializeJNIVars to look like the following:
void Java_keylogger_TestKeys_initializeJNIVars(JNIEnv *env, jobject obj) {
jclass cls = (*env)->GetObjectClass(env,obj);
callbackMethod = (*env)->GetMethodID(env, cls, "callBack", "(Ljava/lang/String;)V");
callbackObject = (*env)->NewGlobalRef(env, obj);
if(cls == NULL || callbackMethod == NULL) {
printf("One of them is null \n");
}
called = TRUE;
}
And finally in your LowLoevelKeyboardProc code you will have to add the following:
...
WPARAM param = kbhook->vkCode;
JNIEnv *env;
jint rs = (*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL);
if (rs != JNI_OK) {
return NULL; // Or something appropriate...
}
...
case VK_ESCAPE:
printf("Escape pressed !\n");
jstring message = (*env)->NewStringUTF(env, "1B");
(*env)->CallVoidMethod(env, callbackObject, callbackMethod, message);
break;
...
In your unregisterWinHook you should delete the global reference so that objects can be GC'd.
...
(*env)->DeleteGlobalRef(env, callbackObject);
And that's it.
I believe you cannot call a java method that takes a String parameter and pass it a char*. You should call NewStringUTF first.
I think it is due to the UAC feature enabled on your Operating System. This was a bug for Java 6. Read this for further reference.
The reason I say this is because the event to the escape key is fired correctly and the problem only begins as soon as the call to the java method is done.

Categories

Resources