Thursday, April 2, 2009

Calling a .NET Method from Java via JNI

One of my friends asked me for a fast and painless solution in order to call a .NET method from Java side. The best way (according to me) is web services to establish interoperability but however they especially asked for me not to use a web service. Although there are some frameworks we can use, for a fast and temporary development you do not need anything. Here’s how you can achieve it in 24 minutes :)

They only sent a dll to me without any information so I used a .NET Reflector to see what methods are in the dll and what the signature of the methods are there. In order to keep things simple, let me say I only determined following method:

int compute();

So I created a Java class, if we skip error handling and fa├žade patterns, the most two important lines were:

System.load("C:\\netBridge.dll");
private static native int callNativeCompute();
netBridge.dll is the dll that we are going to create it’s header with JNI and implement/compile with .NET.
callNativeCompute is the method that any third java party will call to invoke that .NET method.

After compiling your java class, generate the C++ header for implementing your native method (callNativeCompute)with javah. I will write an article about using JNI but believe me it is easy; just use javah as javac after compiling. But now I am so lazy to describe using JNI but at least let me share a JNI tutorial: http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html#gen

Corresponding lines of the header file for our native method are:

JNIEXPORT jint JNICALL Java_com_yamanyar_j2n_FormalisTech_callNativeCompute
(JNIEnv *, jclass);

Now go to your Visiual Studio an create a DLL project from scratch and add the generated header file. Do not forget to include JNI include libraries to your project. (For example in my case: C:\Program Files\Java\jdk1.6.0_11\include).

Okay here is the .NET implementation:

JNIEXPORT jint JNICALL Java_com_yamanyar_j2n_FormalisTech_callNativeCompute
(JNIEnv *env, jclass myclass) {

try {
System::Reflection::Assembly ^ assembly = System::Reflection::Assembly::LoadFrom("c:\\Friterm.FrtCoils.dll");
System::Type ^ theType =  assembly->GetType("Friterm.FrtCoils.Calculator");
System::Console::WriteLine(theType->ToString());
System::Reflection::MethodInfo ^ mi =     theType->GetMethod("Compute");
System::Object ^ instance = System::Activator::CreateInstance(theType);
cli::array<System::Object ^,1> ^ arr = {};
return (int) mi->Invoke(instance,arr);
} catch (System::Exception ^ e){
System::Console::WriteLine(e->Message);
System::Console::WriteLine(e->StackTrace);
throw(e);
}

}


As you see in the above unmanaged C++ code, we are loading the dll and calling the Compute method. All the namespaces, location of dll, method name are hard-coded. You can make it configurable via text file too. And if an error occurs, it will be displayed in the terminal where your java application is running.

As you see we used reflection of .NET (do .NET developers call it reflection? I do not know). I am not a .NET expert, so if you recommend better solutions at the .NET side I will be glad to learn. Compile this dll and rename it to netBridge.dll so that our java class can load it.

4 comments:

  1. have you tried a javonet solution? Plase check us out :)

    ReplyDelete
  2. Great set of tips from the master himself. Excellent ideas. Anyone wishing to take their blogging forward must read these tips. Thank you .ReLoader Activator 3.0 For Windows

    ReplyDelete