Click here to Skip to main content
Click here to Skip to main content

Debugging a JNI Application using Netbeans and Visual Studio

By , 14 May 2012
 

Introduction

This article explains how to create a Java swing application using the Netbeans IDE and how to use JNI to communicate with native C++ code. It also shows how to debug both the Java application as well as the native C++ DLL.

Background

Many times, we may be in a situation where the major part of an application including the GUI needs to be developed in Java and some legacy library already written in C or C++ needs to be used for some functionality of the application. In such a situation, Java Native Interface (JNI) is used to write wrappers around the C/C++ library calls and then interface those wrapper DLLs with Java code.

Let's Begin...

First let us start by creating a simple desktop application using the Netbeans IDE.

Start a new Java project in Netbeans and select "Java Desktop application" from the New Project wizard.

DebugJavaJNIApplication/01_NewJavaProj.jpg

In the next page, set the name of the project as NativeGUI, you can choose the name you like.

DebugJavaJNIApplication/02_NewJavaProjName.jpg

The wizard will populate the project with the code for a basic Desktop Application and open up the interactive GUI editor.

DebugJavaJNIApplication/03_DesktopApp.jpg

Select and delete the menubar and the statusbar. Add components to the GUI as shown in the image below:

DebugJavaJNIApplication/04_DesktopAppMod.jpg

Delete lines 29 to 81 in the NativeGUIView.java file by switching into the Source view mode. This was the code autogenerated for the statusbar animation which needs to be removed. Also remove the unused member variables. Netbeans IDE will visually help you do all this. Optionally, you may choose to keep the menubar and statusbar to avoid hassles of deleting the code correctly.

Switch back to Design view mode and right click on the Add button and click Set Action from the context menu. You will get the Set Action dialog. Drop down the Action combo-box and select Create New Action.

DebugJavaJNIApplication/05_AddListener.jpg

Enter the action name as onAddClicked and press OK.

DebugJavaJNIApplication/06_ActionName.jpg

The wizard will add the code for the Listener and you will be taken to the code editor where you will enter the following code -- shown in bold.

@Action
public void onAddClicked() {
    double d = 0.0;
    try
    {
        d = NativeAdd.add(Double.parseDouble(jSpinner1.getValue().toString()), 
        Double.parseDouble(jSpinner2.getValue().toString()));
    }
    catch(Exception e)
    {
        JOptionPane.showMessageDialog(mainPanel, e.getMessage());
    }
    jTextField1.setText(Double.toString(d));
} 

Similarly create an Action and set it for the Quit button too. You can switch back to the UI editor by clicking the Design button on the panel just above the editor.

DebugJavaJNIApplication/07_DesignView.jpg

Add a Listener using the same procedure.

@Action
public void onQuitClicked() {
        System.exit(0);
}  

Now right-click the nativegui package in the Projects view and click Add -> Java Class. In the Class dialog, enter the name NativeAdd and click Finish.

DebugJavaJNIApplication/08_AddJavaClass.jpg

The code editor will open up with the newly added class code. Add the following static method lines to the class (shown in bold). This class is the wrapper class for loading and executing the native DLL and execute a method in it.

public class NativeAdd {
    native public static double add(double n1, double n2);
    static
    {
        System.loadLibrary( "NativeAdd" );
    }
}

Run the project once by clicking the green arrow in the toolbar above.

DebugJavaJNIApplication/10_RunProj.jpg

This will serve two purposes for us:

  1. We can see how the GUI looks like so that we can make necessary changes to the layout
  2. It will generate the class files for all the java files. We need the NativeAdd.class file for further processing for JNI.

Click the Add button and you will see lots of errors in the Netbeans output window. These are related to the missing DLL that we are supposed to provide so that the line System.loadLibrary( "NativeAdd" ); finds the DLL to load. This will be solved when we create the DLL with JNI code and place it in a locatable folder by the JVM.

Now open a command prompt window and navigate to the <Your Project Folder>\NativeGUI\build\classes\nativegui folder. All the built classes would be found here. chdir to one level up and issue the following command:

javah nativegui.NativeAdd 

javah.exe is the JNI header generating tool found in the bin folder of your Java SDK installation. Ensure it to be in your path.

You will find a C header file in the name nativegui_NativeAdd.h generated in the folder. The file name is in the format packagename_classname.h. Open it to find a function in name Java_nativegui_NativeAdd_add. The name is in the format Java_packagename_classname_methodname. This function would actually be loaded from a DLL named NativeAdd.dll. Remember the argument we had given for the System.LoadLibrary method? This is the content of the nativegui_NativeAdd.h file.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class nativegui_NativeAdd */

#ifndef _Included_nativegui_NativeAdd
#define _Included_nativegui_NativeAdd
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     nativegui_NativeAdd
 * Method:    add
 * Signature: (DD)D
 */
JNIEXPORT jdouble JNICALL Java_nativegui_NativeAdd_add
  (JNIEnv *, jclass, jdouble, jdouble);

#ifdef __cplusplus
}
#endif
#endif 

We will be using this code to create the JNI DLL that implements the function. Remember, only the header has been generated by javah.exe. We need to provide the function implementation. Remember, the code we implement in the NativeAdd.dll is only a wrapper around the native/legacy software API we wish to use with Java. Here, for demonstration purposes, we simply write code to add two numbers. In real life applications, the code for native APIs are called. The design of JNI classes and methods should be done after careful study of the native API functions or classes that you wish to call. You can also call COM APIs on Windows.

Now let us start implementing the code for the DLL.

Start Visual Studio and create a new Win32 Project with the name NativeAdd.

DebugJavaJNIApplication/11_NewWin32Proj.jpg

Select DLL in Application type and click Finish.

DebugJavaJNIApplication/12_DllProj.jpg

The project will be created and populated with code to create a DLL. The file NativeAdd.cpp is also created with only the inclusion of stdafx.h. Add a new header file to the project in the name NativeAdd.h. Now copy the contents of the file nativegui_NativeAdd.h (generated in the netbeans folder) and paste it into the Nativeadd.h file. Implement the function in the Nativeadd.cpp file as shown below:

#include "NativeAdd.h"

jdouble JNICALL Java_nativegui_NativeAdd_add
  (JNIEnv *env, jclass cls, jdouble n1, jdouble n2)
{
    return n1 + n2;
}

Add the java include folder path to Visual Studio either by the following steps ..

1) In the Solution Explorer, right click the project -> Properties.

2) In the property pages, expand C/C++ tree and select General node.

3) In the page add the "<path_to_jdk>\include" and "<path_to_jdk>\include\win32" folder path to "Additional Include Directories".

or by the steps below...

1) Click on Tools->Options in the menu bar.

2) In the Options dialog navigate to the Projects and Solutions->VC++ Directories node.

3) In the page, use the drop down box labelled "Show Directories For" to select "Include Files".

4) Create a new entry for "<path_to_jdk>\include" and "<path_to_jdk>\include\win32" folder path.

The second option is more prefered to the first one which is project specific only.

Now we are good to build the project.

Build the project. The file NativeAdd.dll is created in the Debug folder in the DLL Project path.

Note: You also have to add the library path and jvm.lib library entry to the linker if you would be accessing the JVM inside the C++ code.

Now we have the JNI code DLL and the Java GUI that uses it. But how will the DLL get loaded? For that, you have to copy the DLL in the <Your Java SDK Folder>\bin. But if we are running the GUI project from Netbeans, it is not required. We can simply set the working directory in the Run settings of the Project Properties dialog. You can right-click the project in the Netbeans Project View window and click on Properties to get the dialog.

DebugJavaJNIApplication/13_RunProperties.jpg

Be sure to choose the path of your project... Not mine! :)

Now run the GUI project and test the addition of two numbers. Hurrah! You have just interfaced native C code to a Java application using JNI. Now let us get into the debugging of the application and see the native C code getting called. Start debugging the Java program by clicking the Debug button on the toolbar. Add a breakpoint in the onAddClicked method of the NativeGUIView class. Enter two values in the spinboxes of the application and click Add.

The debugger halts at the breakpoint.

DebugJavaJNIApplication/14_NetbeansDebug.jpg

Now we must set breakpoint in the native DLL code. In Visual Studio, click Debug -> Attach to Process.

DebugJavaJNIApplication/15_VSDebug.jpg

From the Attach to Process dialog, select the java.exe that runs your application. Remember, there may be more than one instances of java.exe running on your system if some other Java application is running. Be careful to select the java.exe running your application. Click Attach.

DebugJavaJNIApplication/16_VSDebugProcess.jpg


Then add a breakpoint, in Visual Studio, in the native JNI function implementation.

DebugJavaJNIApplication/17_VSBreakpoint.jpg

Note: The breakpoint will not be hit since the DLL has not been loaded by the JVM. The breakpoint symbol is with a small warning icon and the tooltip states that it won't be hit.

Go back to Netbeans IDE and step over each block of code by pressing F8. After you step over the line calling the NativeADD.add method, the DLL will be dynamically loaded by the JVM and Visual Studio will automatically come on top with the breakpoint hit.

DebugJavaJNIApplication/18_BreakpointHit.jpg

Step through the code in Visual Studio by pressing F10 and finally F5 when Visual Studio shows up the warning message box for showing disassembly. The Java application will be shown back on top. Then to further debug the application, go to Netbeans IDE and continue the debugging.

Hope you enjoyed this article!

Points of Interest

COM Server Applications can also be debugged in a similar fashion by attaching the Server EXE to the debugger.

Tip

This is an additional tip from CodeProject member wc2009 (follow the comments portion). If you want to compile the DLL project with MinGW compiler, you must add "-Wl,--add-stdcall-alias" to additional compiler options.

History

  • 31st March, 2010: First release
  • 12th April, 2010: Added tip for MinGW compiler

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sharjith
Engineer Tata Technologies Inc
United States United States
Member
Sharjith is a Mechanical Engineer with strong passion for Automobiles, Aircrafts and Software development.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionConcerning the DLLmemberMohamedMansour28 Jun '12 - 22:32 
I made the tutorial and made the JNI messaging successfully, thank you Smile | :)
But when I try to add header and source files of my project in the DLL, I get an error in loading the library.
"Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError:"
 
How could I make the DLL have my whole project and work successfully?
AnswerRe: Concerning the DLLmemberSharjith29 Jun '12 - 11:26 
Can you please give me stepwise details of what you are trying to do exactly?
RegardsSmile | :)
N. Sharjith

GeneralRe: Concerning the DLLmemberMohamedMansour1 Jul '12 - 10:19 
I when i try to read a file, it makes an exception. Except for this, it all works well.
GeneralRe: Concerning the DLL [modified]memberMohamedMansour1 Jul '12 - 23:56 
I have a problem in reading files using the FileHandle in boost c++,
do you know how to solve this? Smile | :)
I get this exception:
 
/usr/lib/jvm/jdk1.7.0/bin/java: symbol lookup error: ../NetBeansProjects/TRIONative/dist/libNativeAdd.so: undefined symbol: _ZN5Gdsii9GdsParserC1EPKcN7SoftJin10FileHandle8FileTypeEN5boost8functionIFvS2_ESaIvEEE
Java Result: 127

 
thanks in advance Smile | :)

modified 3 Jul '12 - 7:06.

GeneralExcellent TutorialmemberMohamedMansour27 Jun '12 - 10:46 
Thank you so much Smile | :)
GeneralMy vote of 5memberVolynsky Alex16 May '12 - 11:00 
Good Article
GeneralRe: My vote of 5memberSharjith17 May '12 - 14:47 
Thanks!
RegardsSmile | :)
N. Sharjith

QuestionGreat Article, One SuggestionmemberDavid_Eisner14 May '12 - 10:04 
This is a terrific article -- great job. I was able to get everything working the first time through.
 
One suggestion: Because the jni header #includes , it's necessary to add a few include paths to the Visual Studio project configuration:
C:\path_to_jdk\include\
and
C;\path_to_jdk\include\win32\
That was the only step I had to fill in for myself.
AnswerRe: Great Article, One SuggestionmemberSharjith14 May '12 - 10:52 
Sorry about not having that mentioned in the article. I assumed that you would have added the java include and library path to your global VC Directories. Updating the article soon with this piece of information.
RegardsSmile | :)
N. Sharjith

QuestionCaused by: java.lang.UnsatisfiedLinkError: desktopapplication1.NativeAdd.add(DD)D ErrormemberJBoinker26 Mar '12 - 9:58 
Hello,
 
Thanks for the tutorial, as I have followed the instructions, but when I hit the Add button I get the following error.
 
Caused by: java.lang.UnsatisfiedLinkError: desktopapplication1.NativeAdd.add(DD)D
 
I am running VS 2010 on a Windows 7 System, and can't seem to figure out the cuase of the error.
 
Any assistance would be GREAT!
 
Thanks in advance
 
JJ

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 14 May 2012
Article Copyright 2010 by Sharjith
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid