creating a JFrame via JNI

H

HenStepper

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
 
G

Gordon Beaton

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
 
C

Chris Uppal

HenStepper said:
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
 
H

HenStepper

Here is the Java line it claims to bomb on:
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(); }

}
 
H

HenStepper

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.
 
H

HenStepper

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
 
G

Gordon Beaton

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
 
H

HenStepper

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;
}
 
G

Gordon Beaton

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
 
C

Chris Uppal

HenStepper said:
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
 
C

Chris Uppal

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top