creating a 'listener' on a native C device

Greetings,

I have posted something related to this in the thread "accessing a method through its mapping address". I thought I should generalise the problem a bit, so that others might benefit.

The general problem:

I have a device with an SDK written in C++. To access incoming data from the device, we wrote a JNI wrapper that would receive callbacks from the device, store the data, then allow us to make calls to retrieve it from Java. Much better would be if there were a way to have callbacks from its SDK go directly to our Java appliation. I've seen JNative (http://jnative.free.fr/SPIP-v1-8-3/), but it seems much more compliated than what I need here. Here's how we've tried to write this:

1. create an 'init' function, called from Java, which will open the connection to the device. Calling this will also give a pointer to the JVM, which we store.

2. when the sdk calls back, turn around and invoke a java method on the JVM we stored on the call to init.

Unfortunately, this doesn't quite work (or, rather, our implementation doesn't work!). We've tried to setup a *very* basic callback: the Java method takes only a single int as a param. The code for our C function is below. We get an Access Exception (way up in the stack) on our call to CallVoidMethod. Anyone out there do something like this before?

//Note: cached_jvm is the JVM object we received on the init, and TheObject is the jobject called on init:

void javaCallback() {

JNIEnv *env;

(cached_jvm)->AttachCurrentThread((void **)&env,NULL );

jclass cls =env->FindClass("com/merl/forlines/input/fingerworks/FingerWorksDeviceJNI");

javaMethod = (env)->GetMethodID(cls, "cliffCallback", "(I)V");

// '17' here is the value we're trying to pass to our method, which takes an

// int.

env->CallVoidMethod(TheObject, javaMethod, 17);

(cached_jvm)->DetachCurrentThread();

}

[1979 byte] By [dwigdora] at [2007-11-26 20:58:41]
# 1

Hi,

if you want to keep TheObject from the init to the callback, then you must be sure to create a GlobalRef for that object. If you don't do, then it is just a LocalRef which is out of scope when you execute the callback.

Beneath the init, you should also write a terminate method which frees the GlobalRef again.

martin@worka at 2007-7-10 2:28:27 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 2

Cool, thanks. I'm sorry to have to ask this follow-up, but I'm working in C++ only because we can't get at the device in Java, and I have very little experience. Visual studio 2005 does not recognize the keyword 'GlobalRef'. Is there some other way to make this global?

Thanks!

Daniel

dwigdora at 2007-7-10 2:28:27 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 3

Sorry - just a follow-up. Is the issue that my variable, 'TheObject', is going out of scope? Or the object passed to me in the 'init' method is no longer pointing to the right place? If it's the former, it seems I just need some syntax. If it's the latter, then is there another way for me to register a callback object? I would normally expect objects to register themseves as 'listeners', but there doesn't seem to be a good way to pass a reference to the C++ side.

dwigdora at 2007-7-10 2:28:27 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 4

Found it! Here's the code for my 'registerListener' function. Works like a charm. Thanks much for your help!

-Daniel

JNIEXPORT void JNICALL Java_com_merl_forlines_input_fingerworks_FingerWorksDeviceJNI_registerListener

(JNIEnv *env, jobject obj1, jobject objref) {

TheObject = env->NewGlobalRef(objref);

}

dwigdora at 2007-7-10 2:28:27 > top of Java-index,Java HotSpot Virtual Machine,Specifications...