I have a third party .NET Assembly and a large Java application. I need to call mothods provided by the .NET class library from the Java application. The assembly is not COM
Have you looked at ikvm.NET, which allows calls between .NET and Java code?
I was so glad to find this article since I got stuck and had exactly that problem. I want to contribute some code, which helps to overcome this problem. In your Java constructor call the init method, which adds the resolve event. My experience it is necessary to call init NOT just before the call into your library in c++ code, since due to timing problems it may crash nonetheless. I've put the init call into my java class constructor of mapping the JNI calls, which works great.
//C# code
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Security.Permissions;
using System.Runtime.InteropServices;
namespace JNIBridge
{
public class Temperature
{
[SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode | SecurityPermissionFlag.Assertion | SecurityPermissionFlag.Execution)]
[ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
[FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
public static double toFahrenheit(double value)
{
return (value * 9) / 5 + 32;
}
[SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode | SecurityPermissionFlag.Assertion | SecurityPermissionFlag.Execution)]
[ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
[FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
public static double toCelsius(double value)
{
return (value - 32) * 5 / 9;
}
}
}
C++ Code
// C++ Code
#include "stdafx.h"
#include "JNIMapper.h"
#include "DotNet.h"
#include "stdio.h"
#include "stdlib.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: DotNet
* Method: toFahrenheit
* Signature: (D)D
*/
static bool initialized = false;
using namespace System;
using namespace System::Reflection;
/***
This is procedure is always needed when the .NET dll's arent in the actual directory of the calling exe!!!
It loads the needed assembly from a predefined path, if found in the directory and returns the assembly.
*/
Assembly ^OnAssemblyResolve(Object ^obj, ResolveEventArgs ^args)
{
//System::Console::WriteLine("In OnAssemblyResolve");
#ifdef _DEBUG
/// Change to your .NET DLL paths here
String ^path = gcnew String("d:\\WORK\\JNIBridge\\x64\\Debug");
#else
String ^path = gcnew String(_T("d:\\WORK\\JNIBridge\\x64\\Release"));
#endif
array<String^>^ assemblies =
System::IO::Directory::GetFiles(path, "*.dll");
for (long ii = 0; ii < assemblies->Length; ii++) {
AssemblyName ^name = AssemblyName::GetAssemblyName(assemblies[ii]);
if (AssemblyName::ReferenceMatchesDefinition(gcnew AssemblyName(args->Name), name)) {
// System::Console::WriteLine("Try to resolve "+ name);
Assembly ^a = Assembly::Load(name);
//System::Console::WriteLine("Resolved "+ name);
return a;
}
}
return nullptr;
}
/**
This procedure adds the Assembly resolve event handler
*/
void AddResolveEvent()
{
AppDomain::CurrentDomain->AssemblyResolve +=
gcnew ResolveEventHandler(OnAssemblyResolve);
}
/*
* Class: DotNet
* Method: init
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_DotNet_init
(JNIEnv *, jobject)
{
printf("In init\n");
AddResolveEvent();
printf("init - done.\n");
return true;
}
/*
* Class: DotNet
* Method: toFahrenheit
* Signature: (D)D
*/
JNIEXPORT jdouble JNICALL Java_DotNet_toFahrenheit
(JNIEnv * je, jobject jo, jdouble value)
{
printf("In Java_DotNet_toFahrenheit\n");
double result = 47;
try{
result = JNIBridge::Temperature::toFahrenheit(value);
} catch (...){
printf("Error caught");
}
return result;
}
/*
* Class: DotNet
* Method: toCelsius
* Signature: (D)D
*/
JNIEXPORT jdouble JNICALL Java_DotNet_toCelsius
(JNIEnv * je, jobject jo , jdouble value){
printf("In Java_DotNet_toCelsius\n");
double result = 11;
try{
result = JNIBridge::Temperature::toCelsius(value);
} catch (...){
printf("Error caught");
}
return result;
}
#ifdef __cplusplus
}
Java code
/***
** Java class file
**/
public class DotNet {
public native double toFahrenheit (double d);
public native double toCelsius (double d);
public native boolean init();
static {
try{
System.loadLibrary("JNIMapper");
} catch(Exception ex){
ex.printStackTrace();
}
}
public DotNet(){
init();
}
public double fahrenheit (double v) {
return toFahrenheit(v);
}
public double celsius (double v) {
return toCelsius(v);
}
}
OK, the mystery is solved.
The JVM crash is caused by unhandled System.IO.FileNotFoundException. The exception is thrown because the .NET assembly is searched in the folder where the calling exe file resides.
It seems my only option is to install the .NET assembly in GAC (the third-party dll does have a strong name).
Look at jni4net, it will do the hard work for you.