P2: Calling C# from Java

Ever wondered how to call a C# method from Java? Here’s how….

Calling native code from Java involves using JNI (Java Native Interface), this piece of JVM is pretty old and hasn’t changed lately (it’s probably that good). JNI is meant to be used with native C/C++ code (aka unmanaged) and there’s plenty of documentation how that can be accomplished. But when trying to call C# code (aka managed) problems tend to surface. There are a couple of tutorials online but following them step by step doesn’t lead to a working application šŸ˜¦Ā I’ll try to be as brief as possible and produce a working application. All the other tutorials start with Java code, I’ll do it the other way (seems more natural). This will be a simple application that calls a C# method which returns a String.

Development environment

  • Microsoft Windows XP SP3
  • Oracle Java Development Kit 1.7.0.5
  • Microsoft Visual C# 2010 Express
  • Microsoft Visual C++ 2010 Express

Numero uno: C#

I didn’t use a MS Visual C# for this because it’s dead simple and because I couldn’t manage to compile this the right way from Visual Studio. Command line rulez! Make a file “Runner.cs” with this content:

using System;
public class Runner{
  public Runner(){}
  public String ping(){
    return "Alive (C#)";
  }
}

Compile this class with this command:
csc.exe /target:module Runner.cs Ā 
You can find csc.exe in .NET framework install folder

This command will produce a file Runner.netmodule in the current directory. It’s a special form of DLL which can be used in managed C++ code. Hopefully ping() method will be called from Java and it’s output printed using System.out.println.

Numero due: Java

In order to build a C++ application that calls a C# method we need a JNI header file. To produce a JNI header file write a Java class:

public class Runner{

  public native String ping();
  public static void main(String[] args){
    System.load("<absolute-path-to-a-dll>");
    System.out.println("Java RUNNER");
    Runner r = new Runner();
    System.out.println("Trying to ping C# assembly: " + r.ping());
  }
}

Command-line ops:
javac Runner.java
This generates a Runner.class
javah -jni Runner
This generates a Runner.h file which will be included in C++ code.

This class has a native method ping() which is implemented in C#. System.load() is responsible for loading the appropriate DLL which holds the implementation of ping().

Numero tre: C++

For this you’ll need a MS Visual C++ 2010 Express tool (or a full MS Visual Studio). Open up this tool and create a Win32 Project called Runner. You need to configure the project since we’ll be referencing JNI header file and .netmodule file (csharp) with other configuration options. In order to have a working application correct project configuration is a must! Follow this procedure:

  1. Include JVM header files (jni.h and related) – Project: Properties > Configuration Properties > C/C++ > General > Additional Include Directories. Add folders “include” and “include\win32” which are located in the JDK installation folder. This is needed for resolvingĀ #include <jni.h>.
  2. Reference Runner.netmodule – Project: Properties > Configuration Properties > C/C++ > General > Resolve #using References. Add folder where Runner.netmodule is located. This is needed for resolvingĀ #using “Runner.netmodule”.
  3. Specific project properties
    1. Project: Properties > Configuration Properties > C/C++ > Command Line = /clr
    2. Project: Properties > Configuration Properties > Linker > Command Line =Ā /NOENTRY /NODEFAULTLIB:nochkclr.obj /NODEFAULTLIB:LIBCMTD
    3. Project: Properties > Configuration Properties > C/C++ > Code Generation
      1. Enable Minimal Rebuild = No (/Gm-)
      2. Enable C++ Exceptions = No
      3. Smaller Type Checks = No
      4. Basic Runtime Checks = Default
      5. Runtime Library = Multi-threaded DLL (/MD)
      6. Struct Member Alignment = Default
      7. Buffer Security Check = Yes (/GS)
      8. Enable Function-Level Linking = No (/Gy-)
      9. Enable Enhanced Instruction Set = Not Set

In the project folder create 2 subfolders “JAVA” and “MCPP”. In the JAVA folder put Runner.h file that was generated with javah command and in MCPP folder put Runner.h file which you created in step 2 (Numero due). In Visual C++ Express create a C++ file “Runner.cpp” with this content:

#include "stdafx.h"
#include "JAVA/Runner.h"
#include "MCPP/Runner.h"
#pragma once
#using <mscorlib.dll>
#using "Runner.netmodule"
JNIEXPORT jstring JNICALL Java_Runner_ping(JNIEnv *env, jobject obj){
 Runner^ t = gcnew Runner();
 String^ ping = t->ping();
 char* str = static_cast<char*>((System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(ping)).ToPointer());

 char cap[128];
 strcpy_s(cap, str);

 return env->NewStringUTF(cap);
}

Here we get a reference to Runner class (C#) with ping() implementation. Before returning the output from C# we need to convert the System::String to char* (using Marshal) and to jstring (using env).

Build the project and there you have it: Runner.dll!

Numero quattro: Finals

In the folder with Runner.class put previously generated Runner.dll and Runner.netmodule files. Run the Java: java Runner and the output should be like this:

c:\>java Runner
Java RUNNER
Trying to ping C# assembly: Alive (C#)

Cheers!

Tags: , , , , ,

4 responses to “P2: Calling C# from Java”

  1. Onur says :

    Hello!
    when I try to compile code in VS2010, first I’ve got /Zi and /clr incompatible options error and I fixed it by setting Project properties>C/C++>General>Debug Information Format = Program Database(/Zi)
    but then I’ve got these errors in compiling:
    Error 1 error C2065: ‘String’ : undeclared identifier c:\Users\Onur\documents\visual studio 2010\Projects\Runner\Runner\Runner.cpp 13 1 Runner
    Error 2 error C2065: ‘ping’ : undeclared identifier c:\Users\Onur\documents\visual studio 2010\Projects\Runner\Runner\Runner.cpp 13 1 Runner
    Error 3 error C2065: ‘ping’ : undeclared identifier c:\Users\Onur\documents\visual studio 2010\Projects\Runner\Runner\Runner.cpp 14 1 Runner
    Error 4 error C2228: left of ‘.ToPointer’ must have class/struct/union c:\Users\Onur\documents\visual studio 2010\Projects\Runner\Runner\Runner.cpp 14 1 Runner

    so how can I fix it ?

  2. John Pickard says :

    Not so much a reply as a supporting comment. I have, also, hit this stumbling block 2 years later and I have been unable to find an answer any where on the web. Did you ever succeed in getting over the errors?

Leave a comment