Java Forum / General / March 2006
creating a JFrame via JNI
HenStepper - 14 Mar 2006 02:39 GMT I get an unhandled native exception when trying to create a JFrame from C via JNI. The error message is unhelpful and untraceable even on Google.
Here is the Java line it claims to bomb on: JFrame frame = new JFrame("test frame");
Here is the result: JavaAWT: NSException not handled by native method. Passing to Java. java.lang.RuntimeException: Non-Java exception raised, not handled! (Original problem: Error (1002) creating CGSWindow) at apple.awt.OSXOffScreenSurfaceData._copyNSImagePixels(Native Method) at apple.awt.OSXOffScreenSurfaceData.copyNSImagePixels(OSXOffScreenSurfaceData.java:1017) at apple.awt.CImage.createImage(CImage.java:47) at apple.laf.AquaImageFactory.makeAlertIcon(AquaImageFactory.java:111) at apple.laf.AquaImageFactory.getConfirmImageIcon(AquaImageFactory.java:92) at apple.laf.AquaLookAndFeel.initComponentDefaults(AquaLookAndFeel.java:616) at apple.laf.AquaLookAndFeel.getDefaults(AquaLookAndFeel.java:360) at javax.swing.UIManager.setLookAndFeel(UIManager.java:445) at javax.swing.UIManager.setLookAndFeel(UIManager.java:485) at javax.swing.UIManager.initializeDefaultLAF(UIManager.java:1178) at javax.swing.UIManager.initialize(UIManager.java:1265) at javax.swing.UIManager.maybeInitialize(UIManager.java:1253) at javax.swing.UIManager.getUI(UIManager.java:859) at javax.swing.JPanel.updateUI(JPanel.java:104) at javax.swing.JPanel.<init>(JPanel.java:64) at javax.swing.JPanel.<init>(JPanel.java:87) at javax.swing.JPanel.<init>(JPanel.java:95) at javax.swing.JRootPane.createGlassPane(JRootPane.java:482) at javax.swing.JRootPane.<init>(JRootPane.java:313) at javax.swing.JFrame.createRootPane(JFrame.java:247) at javax.swing.JFrame.frameInit(JFrame.java:228) at javax.swing.JFrame.<init>(JFrame.java:195) at GUIApp.startup(GUIApp.java:8) startup displaying message
Apparently there is an error when native C calls Java which in turn calls a native method _copyNSImagePixels. When I start the same class using "java" on the command line, everything works fine. So why is _copyNSImagePixels sensitive to the origin of this request?
An extra oddity is that line 8 in GUIApp.java is indeed the JFrame constructor (as the traceback shows), but line 9 is a print statement: System.out.println("startup displaying message"); and you can see that its output arrived too. I cannot interpret this. Does anyone know what this means?
Or, more to the point... Does anyone know how to start a GUI from C? TIA, hs
Gordon Beaton - 14 Mar 2006 09:48 GMT > I get an unhandled native exception when trying to create a JFrame > from C via JNI. The error message is unhelpful and untraceable even > on Google. > > Here is the Java line it claims to bomb on: > JFrame frame = new JFrame("test frame"); You said from JNI, but that looks like Java to me. At any rate, this native code works for me:
jframe_cls = (*env)->FindClass(env,"javax/swing/JFrame"); mid = (*env)->GetMethodID(env,jframe_cls,"<init>","(Ljava/lang/String;)V") jframe = (*env)->NewObject(env,jframe_cls,mid,title);
mid = (*env)->GetMethodID(env,jframe_cls,"pack","()V"); (*env)->CallVoidMethod(env,jframe,mid);
mid = (*env)->GetMethodID(env,jframe_cls,"setVisible","(Z)V"); (*env)->CallVoidMethod(env,jframe,mid,JNI_TRUE);
Similarly, invoking an equivalent Java method from C also works. I suspect a coding error.
/gordon
 Signature [ do not email me copies of your followups ] g o r d o n + n e w s @ b a l d e r 1 3 . s e
HenStepper - 14 Mar 2006 15:50 GMT >> Here is the Java line it claims to bomb on: >> JFrame frame = new JFrame("test frame");
> You said from JNI, but that looks like Java to me. Ummm, yes... I forgot to include my code. Here is the C program:
#include "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Headers/jni.h" #include <string.h> #include <stdlib.h>
void bomb(int errno, char * msg) { printf("ERROR: %s\n", msg); exit(errno); }
int main(int argc, char *args[]) { jclass cls; JNIEnv *env; JavaVM *jvm; JavaVMOption options[10]; JavaVMInitArgs vm_args; jmethodID mid;
options[0].optionString= "-Djava.class.path=."; memset(&vm_args, 0, sizeof(vm_args)); vm_args.version = JNI_VERSION_1_4; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized= 0; long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); if (status == JNI_ERR) bomb(1000,"JNI_ERR; Could not launch JVM"); printf("no JNI_ERR\n");
cls = (*env)->FindClass(env, "GUIApp"); // find our class if (cls==0) bomb(1003, "JVM cannot find GUIApp class.");
mid = (*env)->GetStaticMethodID(env, cls, "startup", "()V"); if (mid==0) bomb(1004, "couldn't find method"); printf("calling startup method\n"); (*env)->CallStaticVoidMethod(env, cls, mid); printf("got started\n");
return 0; }
and the Java class it relies on to create the JFrame is here:
import javax.swing.JFrame; import javax.swing.JOptionPane;
public class GUIApp {
public static void startup() { System.out.println("startup creating frame"); JFrame frame = new JFrame("test frame"); System.out.println("startup displaying message"); JOptionPane.showMessageDialog(frame, "I'm up!"); System.out.println("startup message acknowledged"); System.exit(1234); } public static void main(String[] args) { startup(); } }
HenStepper - 14 Mar 2006 16:08 GMT Thanks for the code you supplied, Gordon. I ran it. It bombed in exactly the same way. Am I having a Mac problem? Here is my whole program (this time without any supporting java):
#include "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Headers/jni.h" #include <string.h> #include <stdlib.h>
void bomb(int errno, char * msg) { printf("ERROR: %s\n", msg); exit(errno); }
int main(int argc, char *args[]) { jclass jframe_cls; JNIEnv *env; JavaVM *jvm; JavaVMOption options[10]; JavaVMInitArgs vm_args; jmethodID mid; jobject jframe;
options[0].optionString= "-Djava.class.path=."; memset(&vm_args, 0, sizeof(vm_args)); vm_args.version = JNI_VERSION_1_4; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized= 0; long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); printf("no JNI_ERR\n");
jframe_cls = (*env)->FindClass(env, "javax/swing/JFrame"); if (jframe_cls==0) bomb(1003, "JVM cannot find GUIApp class.");
mid = (*env)->GetMethodID(env,jframe_cls,"<init>","(Ljava/lang/String;)V"); if (mid==0) bomb(1004, "couldn't find constructor");
jframe = (*env)->NewObject(env,jframe_cls,mid,"JFrame from C!"); printf("jframe created\n");
return 0; }
The traceback is entirely similar: JavaAWT: NSException not handled by native method. Passing to Java. java.lang.RuntimeException: Non-Java exception raised, not handled! (Original problem: Error (1002) creating CGSWindow) ... at javax.swing.JFrame.<init>(JFrame.java:195) jframe created
Note that the exception was thrown in native code, the taceback got printed, and then the message "jframe created" arrived... apparently out of the blue again. I still cannot interpret this sequencing. There is no second thread here, and I doubt that the return statement following the printf("jframe created") caused the native exception.
Gordon Beaton - 14 Mar 2006 16:26 GMT > Thanks for the code you supplied, Gordon. > I ran it. It bombed in exactly the same way. > Am I having a Mac problem? Here's one thing that is likely causing problems:
> jframe = (*env)->NewObject(env,jframe_cls,mid,"JFrame from C!"); That title text needs to be a jstring object, not a char*.
Also (now I'm guessing), have you compiled your launcher with the necessary CFLAGS for a multithreaded program (e.g. -D_REENTRANT), and linked with the appropriate thread library (e.g. -pthread)?
/gordon
 Signature [ do not email me copies of your followups ] g o r d o n + n e w s @ b a l d e r 1 3 . s e
HenStepper - 15 Mar 2006 04:35 GMT I had not use D_REENTRANT and -pthread, but I did as soon as you suggested (to no avail). My concept of creating a JFrame does not involve any new Threads. Do you know differently? or are you just suggesting safe practices?
You are right about the jstring, and I've fixed it. But the exception was happening before that line, so it hasn't mattered yet.
I attach the new code here. I'm running on Mac OS 10.4; I'd be delighted if people on other OS's would run it and see what happens. Put the file in GUIdooey.c, then do something like this: gcc -framework JavaVM -pthread GUIdooey.c a.out and report if it crashes or not. You may have to fiddle with the first line. TIA, hs
#include "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Headers/jni.h" //#include "jni.h" #include <string.h> #include <stdlib.h>
void bomb(int errno, char * msg) { printf("ERROR: %s\n", msg); exit(errno); }
int main(int argc, char *args[]) { jclass jframe_cls; JNIEnv *env; JavaVM *jvm; JavaVMOption options[10]; JavaVMInitArgs vm_args; jmethodID mid; jobject jframe;
options[0].optionString= "-Djava.class.path=."; memset(&vm_args, 0, sizeof(vm_args)); vm_args.version = JNI_VERSION_1_4; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized= 0; long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); printf("no JNI_ERR\n");
jframe_cls = (*env)->FindClass(env, "javax/swing/JFrame"); if (jframe_cls==0) bomb(1003, "JVM cannot find GUIApp class.");
mid = (*env)-> GetMethodID(env,jframe_cls,"<init>","(Ljava/lang/String;)V"); if (mid==0) bomb(1004, "couldn't find constructor");
jstring title= (*env)->NewStringUTF(env, "JFrame from C!"); if (title==NULL) bomb(1005,"JNI failed to get memory for string"); jframe = (*env)->NewObject(env,jframe_cls,mid,title); printf("jframe created\n"); return 0; }
Gordon Beaton - 15 Mar 2006 08:57 GMT > I had not use D_REENTRANT and -pthread, but I did as soon as you > suggested (to no avail). My concept of creating a JFrame does not > involve any new Threads. Do you know differently? or are you just > suggesting safe practices? [...]
> I attach the new code here. I'm running on Mac OS 10.4; I'd be > delighted if people on other OS's would run it and see what happens. Your code works for me on Linux/x86. This is how I built it:
gcc -Wall -D_REENTRANT -I $JDK/linux -I $JDK/linux/include launch.c -L $JDK/jre/lib/i386 -L $JDK/jre/lib/i386/client -ljvm -lpthread -o launch
The -D_REENTRANT and -lpthread arguments are not just a good idea; the JVM expects to run in a multithreaded environment. I don't know whether you need to specify these (or something equivalent) on MacOSX.
To run the program I made sure that the two directories containing the JVM libraries (indicated with -L above) are in my LD_LIBRARY_PATH so they can be found at runtime. Again, how you do this on MacOSX I don't know.
Note that you fail to check the return value from JNI_CreateJavaVM(), but you print "no JNI_ERR" regardless. Check that the JVM actually starts before continuing. It will fail to start if the necessary libraries are not found at runtime.
/gordon
 Signature [ do not email me copies of your followups ] g o r d o n + n e w s @ b a l d e r 1 3 . s e
Chris Uppal - 14 Mar 2006 15:33 GMT > I get an unhandled native exception when trying to create a JFrame from > C via JNI. One possible problem may be that you are creating this component from the "wrong" thread. AFAIK, Sun now expect you to do all your GUI work on the EDT thread. See: http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
Unfortunately there is no JNI equivalent of: java.awt.EventQueue.invokeLater() and/or: javax.swing.SwingUtilities.invokeLater() so it's messy to change the code to ensure that your C code is running in a JNI method which has been invoked from Java via one of the above methods, and therefore on the correct thread.
-- chris
HenStepper - 14 Mar 2006 16:25 GMT Chris, You are right to worry about the Thread issue. I did my share of fretting about it too, and eventually took *out* the EDT stuff that I originally had. The removal made no difference in the error caused.
See the Java code in my message of Mar 14 2006 9:50 am; it runs reliably fine if you invoke it via "java GUIApp". The only call to any of the GUI stuff is in this line: JOptionPane.showMessageDialog(frame, "I'm up!"); This method blocks and waits for user confirmation, so there is no requirement for an event thread.
But all that seems moot anyway, because I can't get the JFrame to start at all. hs
Chris Uppal - 15 Mar 2006 11:09 GMT > The only call to any of the GUI stuff is in this line: > JOptionPane.showMessageDialog(frame, "I'm up!"); > This method blocks and waits for user confirmation, so > there is no requirement for an event thread. Whether, when, and why the platform-specific AWT implementation sees fit to start an EDT (or any other threads necessary for its internal implementatin) is not something you can determine on that basis. Since you mentioned that your debugging output continued even though there was a stack trace caused apparently at the previous line, it seems very likely that the Mac AWT implementation does ensure that the EDT is running in this instance.
-- chris
HenStepper - 17 Mar 2006 23:35 GMT Thank you, Chris and Gordon. You both sent me in the right direction. It was a threading issue (in spite of my mental model) and it was a Mac-specific issue (which the Linux code brought to light). I ended up with all the final details to solve this problem at: http://developer.apple.com/samplecode/simpleJavaLauncher/simpleJavaLauncher.html hs
Chris Uppal - 18 Mar 2006 11:28 GMT > It was a threading issue (in spite of my mental model) and it was > a Mac-specific issue (which the Linux code brought to light). > I ended up with all the final details to solve this problem at: http://developer.apple.com/samplecode/simpleJavaLauncher/simpleJavaLauncher.html
Thanks for the follow-up. Interesting to see that they force a new thread in their launcher. Unfortunate that the (otherwise) well-commented code doesn't explain why.
-- chris
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|