Loading unregistered .NET assembly from unmanaged native C++ library within Java application

371 views Asked by At

I am using a Java application that can be extended with custom packages that may include native code, as such I have no control over the Java environment other than the ability to add extension jars.

In this instance, I need to be able to call C# code from a Java extension. This is C# code that has previously been used from a purely native context using registration free COM - which works fine. Now I need to be able to make the same code usable from this Java context, but have hit upon the issue that there seems to be no way to control where the DLL should be found. Requiring the DLL to be installed into the JRE/JDK bin directory is clearly not an option, and requiring the DLL to be registered into the GAC would not be ideal.

At present my hope would be that all DLL dependencies would be packaged into the extension jar, and would be loaded when required at runtime (no manual installation step).

I was hoping that I could use the Activation Context API to get this working, and have tested a number of variations on this code, with the DLLs in D:\somepath\:

ACTCTX actctx = {sizeof(actctx)};
actctx.lpSource = "D:\\somepath\\dll.manifest";
actctx.lpAssemblyDirectory = "D:\\somepath\\";
actctx.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;

HANDLE hActCtx = CreateActCtx(&actctx);
ULONG_PTR ulpCookie = 0;
ActivateActCtx(hActCtx, &ulpCookie);

CoCreateInstance(...);

The manifest contains a single dependentAssembly with the details of the assembly containing the C# class referenced by the call to CoCreateInstance.

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity name="[assemblyname]" processorArchitecture="msil" publicKeyToken="[token]" version="[version]" />
    </dependentAssembly>
  </dependency>
</assembly>

Also tried with manifest in this format dumped from the DLL using mt.exe:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity name="[assemblyname]" version="[version]" publicKeyToken="[token]" processorArchitecture="msil"/>
    <clrClass clsid="..." progid="..." threadingModel="Both" name="..." runtimeVersion="v4.0.30319"/>
    ...
    <file name="[assemblyname].dll" hashalg="SHA1"/>
</assembly>

The calls to CreateActCtx and ActivateActCtx are successful, but the CoCreateInstance call always fails, returning 0x80070002, unless I put the DLL in C:\Program Files\Java...

Is there any solution out there (other than using the GAC) that would allow a completely unmanaged C++ DLL to load and create an instance of a C# class from a DLL in an arbitrary known location?

1

There are 1 answers

0
Hardy On

I work on an application with similar requirements, i.e. I need to activate a reg-free managed COM server (located it a DLL written in C# / .net v4.8) from an unmanaged client process (written in C++), and the C# DLL should be located in directory outside the C++ application dir.

As of now, reg-free COM server activation of the managed COM server DLL only works for me when I both

  • activate the COM server using a custom activation context (CreateActCtx / ActivateActCtx etc.) with actctx.lpAssemblyDirectory = ... set to the COM DLL directory and

  • change the assembly probing algorithm on the .net side so that it's able to locate my C# DLL, which it doesn't do by default.

Regarding the latter, I currently do this by adding an application configuration file for the C++ EXE in which I enabe developmentMode and I also set the DEVPATH environment variable to the same value as `actctx.lpAssemblyDirectory'. This works for me because I only need the C# DLL directory to be outside the C++ EXE directory during development.

Note that there are also other options to change the .net assembly probing algorithm. For example you could

Maybe (if it's an option for you) have a look at .Net DllExport which allows you to export .net methods as DLL functions from a .net assembly. This enables you to interact with the .net assembly just like with a a native DLL (using LoadLibrary, GetProcAddress etc.)