Wednesday, April 18, 2012

NetBeans Development 7 - Windows 7 64-bit … JNI native calls ... a how to guide

I provide this for you to hopefully save you some time and pain.



As part of my expereince in getting to know NB Development v7 on my Windows 64-bit workstation I found another frustrating adventure in trying to get the JNI (Java Native Interface) abilities up and working in my project. As such, I am including a brief summary of steps required (as all the documentation I found was completely incorrect for these versions of Windows and NetBeans on how to do JNI). It took a couple of days of experimentation and reviewing every webpage I could find that included these technologies as keyword searches. Yuk!! Not fun.



To begin, as NetBeans Development is "all about modules" if you are reading this you probably have a need for one, or more, of your modules to perform JNI calls. Most of what is available on this site or the Internet in general (not to mention the help file in NB7) is either completely wrong for these versions, or so sparse as to be essentially unuseful to anyone other than a JNI expert.



Here is what you are looking for ... the "cut to the chase" - "how to guide" to get a JNI call up and working on your NB7 / Windows 64-bit box.



1) From within your NetBeans Module (not the host appliation) declair your native method(s) and make sure you can compile the Java source without errors.



Example:



package org.mycompanyname.nativelogic;

public class NativeInterfaceTest
{
static
{
try
{
if (System.getProperty( "os.arch" ).toLowerCase().equals( "amd64" ) )
System.loadLibrary( <64-bit_folder_name_on_file_system>/<file_name.dll> );
else
System.loadLibrary( <32-bit_folder_name_on_file_system>/<file_name.dll> );
}
catch (SecurityException se) {}
catch (UnsatisfieldLinkError ule) {}
catch (NullPointerException npe) {}
}

public NativeInterfaceTest() {}

native String echoString(String s);
}


Take notice to the fact that we only load the Assembly once (as it's in a static block), because othersise you will throw exceptions if attempting to load it again. Also take note of our single (in this example) native method titled "echoString". This is the method that our C / C++ application is going to implement, then via the majic of JNI we'll call from our Java code.



2) If using a 64-bit version of Windows (which we are here) we need to open a 64-bit Visual Studio Command Prompt (versus the standard 32-bit version), and execute the "vcvarsall" BAT file, along with an "amd64" command line argument, to set the environment up for 64-bit tools.



Example:



<path_to_Microsoft_Visual_Studio_10.0>/VC/vcvarsall.bat amd64


Take note that you can use any version of the C / C++ compiler from Microsoft you wish. I happen to have Visual Studio 2005, 2008, and 2010 installed on my box so I chose to use "v10.0" but any that support 64-bit development will work fine. The other important aspect here is the "amd64" param.



3) In the Command Prompt change drives \ directories on your computer so that you are at the root of the fully qualified Class location on the file system that contains your native method declairation.



Example:
The fully qualified class name for my natively declair method is "org.mycompanyname.nativelogic.NativeInterfaceTest". As we successfully compiled our Java in Step 1 above, we should find it contained in our NetBeans Module something similar to the following:



"/build/classes/org/mycompanyname/nativelogic/NativeInterfaceTest.class"



We need to make sure our Command Prompt sets, as the current directly, "/build/classes" because of our next step.



4) In this step we'll create our C / C++ Header file that contains the JNI required statments. Type the following in the Command Prompt:



javah -jni org.mycompanyname.nativelogic.NativeInterfaceTest and hit enter. If you receive any kind of error that states this is an unrecognized command that simply means your Windows computer does not know the PATH to that command (it's in your /bin folder). Either run the command from there, or include the fully qualified path name when invoking this application, or set your computer's PATH environmental variable to include that path in its search.



This should produce a file called "org_mycompanyname_nativelogic_NativeInterfaceTest.h" ... a C Header file. I'd make a copy of this in case you need a backup later.



5) Edit the NativeInterfaceTest.h header file and include an implementation for the echoString() method.



Example:



JNIEXPORT jstring JNICALL Java_org_mycompanyname_nativelogic_NativeInterfaceTest_echoString
(JNIEnv *env, jobject jobj, jstring js)
{
return((*env)->NewStringUTF(env, "My JNI is up and working after lots of research"));
}


Notice how you can't simply return a normal Java String (because you're in C at the moment). You have to tell the passed in JVM variable to create a Java String for you that will be returned back. Check out the following Oracle web page for other data types and how to create them for JNI purposes.



6) Close and Save your changes to the Header file. Now that you've added an implementation to the Header change the file extention from ".h" to ".c" as it's now a C source code file that properly implements the JNI required interface.



Example:
NativeInterfaceTest.c



7) We need to compile the newly created source code file and Link it too. From within the Command Prompt type the following:



cl /I"path_to_my_jdks_include_folder" /I"path_to_my_jdks_include_win32_folder" /D:AMD64=1 /LD NativeInterfaceTest.c /FeNativeInterfaceTest.dll /link /machine:x64



Example:



cl /I"D:/Program Files/Java/jdk1.6.0_21/include" /I"D:/Program Files/java/jdk1.6.0_21/include/win32" /D:AMD64=1 /LD NativeInterfaceTest.c /FeNativeInterfaceTest.dll /link /machine:x64


Notice the quotes around the paths to the 'include" and 'include/win32' folders is required because I have spaces in my folder names ... 'Program Files'. You can include them if you have no spaces without problems, but they are mandatory if you have spaces when using a command prompt.



This will generate serveral files, but it's the DLL we're interested in. This is what the System.loadLirbary() java method is looking for.



8) Congratuations! You're at the last step. Simply take the DLL Assembly and paste it at the following location:



<path_of_NetBeansProjects_folder>/<project_name>/<module_name>/build/cluster/modules/lib/x64


Note that you'll probably have to create the "lib" and "x64" folders.



Example:
C:\Users\<user_name>\Documents\NetBeansProjects\<application_name>\<module_name>\build\cluster\modules\lib\x64\NativeInterfaceTest.dll


Java code ... notice how we don't inlude the ".dll" file extension in the loadLibrary() call?



System.loadLibrary( "/x64/NativeInterfaceTest" );


Now, in your Java code you can create a NativeInterfaceTest object and call the echoString() method and it will return the String value you typed in the NativeInterfaceTest.c source code file.



Hopefully this will save you the brain damage I endured trying to figure all this out on my own. Good luck and happy coding!





No comments:

Post a Comment