JNI wrapper for NSSavePanel

Mike Swingler swingler at apple.com
Sun Oct 7 14:35:19 PDT 2012


On Oct 7, 2012, at 7:40 AM, Steve Hannah <steve at weblite.ca> wrote:

> Are you building/running the app from Xcode? You could try setting a breakpoint to inspect the local state.
> 
> I'm building the JNI library in XCode, but the application I'm building in Netbeans and packaging using Marco's port of AppBundler.
> 
> I've experimented with threading issues and don't think that is the problem now in this case, but still don't know what is the problem.  I have modified the example to make it simpler.
> 
> The following code does not work:
> JNIEXPORT jstring JNICALL Java_ca_weblite_mactools_Sandbox_saveDialog
> 
> (JNIEnv *env, jobject jthis, jstring title, jstring extension){
> 
>     //JNF_COCOA_ENTER(<#env#>);
> 
>     return JNFNSToJavaString(env, @"/Users/shannah/Downloads/out2.pdf");
> 
>     //JNF_COCOA_EXIT(env);
> 
>  }

This is not a valid C function based on the return type - in the case where an error occurs, the return value of this function is undefined and will likely be whatever garbage is left on the stack (isn't Xcode warning/erroring about this like crazy???). You need a "return NULL;" at the very end so that the JNI return value will be defined in the case where you don't make it to the first return.

> If I uncomment the JNF_COCOA_ENTER/EXIT lines, it works properly.
> 
> I have my Java logging working now so I can follow the progress with println statements right up until it calls the native method.  I get a crash at that point.  It doesn't even enter the native method (i.e. if I put a print statement first line, nothing happens.
> 
> It almost seems like putting the JNF_COCOA_ENTER/EXIT calls inside the method some somehow changes the method signature so that Java can no longer find the correct method to call at runtime.... but that doesn't seem possible.

Are you sure the library is even getting built and loaded? Can you make a plain JNI function next to this one that contains nothing but a simple "fprintf(stderr, "test\n");" in it? Call that first, and see if the library is even loaded.

> Given my inexperience with XCode, I suspect that it could be a problem with the way I'm building/linking the JNI library.  The fact that calls to JNFNSToJavaString() work makes me thing that the JavaNativeFoundation is correctly linked (?).

It may be getting hauled in by Java 7 itself, and not explicitly linked. If you don't have the JavaNativeFounation.framework explicitly linked in the target that builds your library, that would also cause the compiler to infer that the JNF_COCOA_ENTER/EXIT macros are actually functions, and treat them as undeclared/unlinked entities.

> Note:  I'm going through this exercise to try and write the code with JNF_COCOA_ENTER/EXIT because, despite the fact that the library worked perfectly the way it was on Mountain Lion, it doesn't work on Lion, so I'm trying to cover all of my bases to figure out why.  When I got stuck at this step, I figured it's worth finding out what this problem is just in case it's related to the Lion problem.
> 
> I'm building it with settings:
> Base SDK: Latest OS X (OS X 10.8)
> Deployment Target: OS X 10..7  (although I've tried 10.6 and 10.8 with same results).

Run otool -L on your library, and make sure the full path to JavaNativeFoundation.framework is printed out. If it isn't, you are only picking up the symbols inside of it by accident.

Regards,
Mike Swingler
Apple Inc.

> Any other suggestions or tips, much appreciated.
> 
> 
> Best regards
> 
> Steve
> 
>> 
>> JNF_COCOA_ENTER/EXIT do more than just setup an autorelease pool - they also catch ObjC exceptions and re-throw them as Java runtime exceptions.
>> 
>> Do you have the backtrace of where you are crashing when using JNF_COCOA_ENTER/EXIT?
>> I do, but I'm having difficulty making heads or tails from it.  I'm also struggling with getting logging information out on the Java side as I'm working on a Mountain Lion machine and I can't find where System.out and System.err are logged by default... and the sandbox is making it difficult for me to set a log location manually... (anything that runs or fails before or during the setting of a custom log location is not logged). 
> 
> I think (but I'm not certain) that this is the way it is now on Lion and Mountain Lion - output logged to stdout/stderr directly (including the JVM System.out/err) just goes to /dev/null unless you run the primary executable from a shell or some other parent process that gives it a TTY. Otherwise, the only thing that gets logged to the system console comes through ASL, like NSLog(). Of course, I may be completely off on this, but perhaps this is explaining what you are seeing.
> 
>> Also, are you doing anything special to run this JNI method on the main AppKit thread (or are you using SWT)?
>> Ah.. I hadn't thought of that, but yes.  I'm overriding the java.awt.FileDialog class's setVisible() method to display this as a modal dialog.
>>  
>> If not, you may want to use +[JNFRunLoop performOnMainThreadWaiting:withBlock:], which will block the current thread, run your block on the main thread (safe for AppKit), and then continue execution of your Java thread when the block returns.
>> 
>> I'll give this a shot.
>> 
>> Thanks for the suggestions.
> 
> No problem. Please do always think of the threading considerations when writing JNI. We try to let you not worry about exceptions and autorelease pools if you use JNF, but threading is still something you have to think through when you get JNI and Cocoa tangled together.
> 
> Best of luck,
> Mike Swingler
> Apple Inc.
> 
> 
> 
> 
> -- 
> Steve Hannah
> Web Lite Solutions Corp.
> 



More information about the macosx-port-dev mailing list