Home » Java » Java .NET interop

Java .NET interop

Posted by: admin December 28, 2021 Leave a comment

Questions:

Alright, I’ve been digging at this for awhile and am looking for input.

I need a Java application that can load and unload native libraries, simple enough, C/C++/Java/Scripts/Executables/etc. is no real problem using JNI and other built in features.

However, I need the ability to also load .NET libraries, this has been crazy frustrating. My first attept was to use JNI and call a C++ wrapper as is below:

Java:

this.Lib = (LibHandler)Native.loadLibrary("MyLib", LibHandler.class);

The CPP:

#include <jni.h>
#using <MyLibCSharp.dll>
#include <vcclr.h>
#include <msclr\marshal.h>

using namespace msclr::interop;
using namespace System::Runtime::InteropServices;
using namespace System;

extern "C" 
{
    JNIEXPORT jstring JNICALL Java_Test(JNIEnv * env)
    { 
        marshal_context ^ context = gcnew marshal_context();
        const char* str4 = context->marshal_as<const char*>(CSharp::Class1::Test());

        jstring js = env->NewStringUTF(str4);


        return js;
    }

    JNIEXPORT jstring JNICALL Java_Test2(JNIEnv * env, jobject jobj)
    { 
        marshal_context ^ context = gcnew marshal_context();
        const char* str4 = context->marshal_as<const char*>(CSharp::Class1::Test());

        jstring js = env->NewStringUTF(str4);


        return js;
    }
}

This would continously fail to even be loaded by the system, I can swap the files so that MyLib.dll is actually the C# one, and it successfully loads it (but fails to find any functions being as it’s not a native C library and I don’t think .NET can export like C++ can), so I’m not having file location issues.

Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'MyLib.dll': The specified module could not be found.
    at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:163)
    at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:236)
    at com.sun.jna.Library$Handler.<init>(Library.java:140)
    at com.sun.jna.Native.loadLibrary(Native.java:379)
    at com.sun.jna.Native.loadLibrary(Native.java:364)
    at EntryPoint.main(EntryPoint.java:31)

I figured I’d try to compile the C# library as a COM object and call it that way, alas:

ActiveXComponent comp = new ActiveXComponent("MyLib.Class1");

Fails with:

Exception in thread "main" com.jacob.com.ComFailException: Can't co-create object
    at com.jacob.com.Dispatch.createInstanceNative(Native Method)
    at com.jacob.com.Dispatch.<init>(Dispatch.java:99)
    at com.jacob.activeX.ActiveXComponent.<init>(ActiveXComponent.java:58)
    at EntryPoint.main(EntryPoint.java:33)

C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;



namespace MyLib
{
    [Guid("4F3A0A13-4D2B-4DE6-93EA-D6861C230290"),
    ComVisible(true)]
    public interface ITest
    {
        [DispId(1)]
        string Test();
    }

    [Guid("A78C5820-3E4B-49B3-8C8E-63DD12346410"),
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
    ComVisible(true)]
    public interface ITestEvents
    {
    }

    [Guid("3ECD46AE-E8F4-4B62-B9DC-DD7F6CB435E2"),
    ClassInterface(ClassInterfaceType.None),
    ComSourceInterfaces(typeof(ITestEvents)),
    ComVisible(true)]
    public class Class1 : ITest
    {
        public string Test()
        {
            return "This is my C# DLL COM returning a string! heh!";
        }
    }
}

I can see the COM is registered, I can browse it with oleview.exe, but I can’t call it from vbScript… I’m confused on that one, so I’m completely out of ideas, this has really been racking my brain all day.

I’d like to get away from COM, but I need to keep the implmentation fairly simple (the libraries wont be developed by us, so I don’t want to drop a bunch of C/C++ code into the lap of VB6 developers).

I’d be fine with going back to the CLI C++ implementation method, being as it’s so straightforward almost anyone can do it.

Any ideas would be extremely appreciated, thanks.

Edit:

I can’t use System.LoadLibrary, wont let me unload libraries like Native.LoadLibrary with an assigned class does.

Answers:

If you have single method to expose your approach is reasonable. Definetely going through JNI to C++/CLI and then .NET is much more reliable solution and easier to maintain than trying to handle such communication using COM objects.

Anyway for those who have more complex requirements like calling any method, passing ref/out arguments, calling generic methods, getting setting fields, subscribing .NET events handling exceptions, getting indexed properties etc..etc.. I think custom native wrapping will not work, at least not in rational time and cost.

If you have such requirements or just to be aware of the possbilities please have a look at third-party Java to .NET Bridges like:

These two are more appropriate for this case as mentioned IKVM. Those Bridges let you use ANY .NET library directly in your JAVA code. They handle all the mentioned operations/scenarios and much more, including built-in data type translations and other mechanisms for known traps.

JNBridge is a lit bit more heavy and more expensive. However it has some dedicated plugins for enterprise systems like BizTalk. On the other hand Javonet is very light one jar-file solution half priced with very easy API. The other major difference is that Javonet works without proxy classes, so you do not have to generate any wrappers you just call .NET with reflection style whereas by JNBridge you can generate proxy classes that gives you strongly typed interface but it takes more time and effort. In my opinion it reduces flexibility a little bit as I like to have control over this what happens under the hood and with Javonet you can easily make your own strongly typed wrapped if you like.

I think this is not popular still but for single machine solution it is great approach that seamlessly covers the gap between .NET and JAVA with native performance. It is fraction of the percent of execution time taken by webservices and do not require any high level client-server infrastructures. In my tests it takes around 30% more time than executing particular code directly in .NET (it is very attractive).

It is good to hear you solve your case and I hope this post will help others with Java to .NET interop issues.

Below you can find sample code of Javonet using .NET Random class (it’s important to notice that with third-party bridge you get access not only to your custom DLL but full .NET framework):

public void GenerateRandomNumber() throws JavonetException 
{
        NObject objRandom = Javonet.New("System.Random");
        int value = objRandom.invoke("Next",10,20);

        System.out.println(value); 
}

###

Ended up getting JNI working correctly with C++ code, I was compiling the C++ as 32-bit, but I’m running a 64-bit JVM which was causing this extremely vague error.

I also ran into errors from .NET (Library wasn’t in the GAC), so be sure to catch/report those correctly to Java, seems that uncaught exceptions cannot be wrapped in Java I believe.

I’ll probably be posting this online as a resource soon, being as many JNI <-> .NET interop tutorials are way overcomplicated (usually adding what seems to be extremely unnecessary layers).

###

I do not know if this helps, but the open source project IKVM allows you to do the opposite, converting your Java application to .Net:

IKVM.NET is an implementation of Java
for Mono and the Microsoft .NET
Framework. It includes the following
components:

* A Java Virtual Machine implemented in .NET
* A .NET implementation of the Java class libraries
* Tools that enable Java and .NET interoperability

###

Your COM interop code looks almost right. However, you can’t instantiate the coclass late-bound until you define the default interface on the class like this:

[Guid("3ECD46AE-E8F4-4B62-B9DC-DD7F6CB435E2"),
ClassInterface(ClassInterfaceType.None),
ComDefaultInterface(typeof(ITest)),
ComSourceInterfaces(typeof(ITestEvents)),
ComVisible(true)]
public class Class1 : ITest