C# 从Java调用.NET程序集:JVM崩溃

C# 从Java调用.NET程序集:JVM崩溃,c#,java,integration,C#,Java,Integration,我有一个第三方.NET程序集和一个大型Java应用程序。我需要从Java应用程序调用.NET类库提供的方法。程序集未启用COM。 我已经搜索了网络,目前为止我有以下信息: C代码(cslib.cs): 使用编译(使用.net 3.5,但使用2.0时也会发生这种情况): C++代码(clib.cpp): Java代码(CallCS.Java): 当我尝试运行java类时,java VM在调用该方法时崩溃(它能够加载库): # #Java运行时环境检测到意外错误: # #内部错误(0xe0434f4

我有一个第三方.NET程序集和一个大型Java应用程序。我需要从Java应用程序调用.NET类库提供的方法。程序集未启用COM。 我已经搜索了网络,目前为止我有以下信息:

C代码(cslib.cs):

使用编译(使用.net 3.5,但使用2.0时也会发生这种情况):

C++代码(clib.cpp):

Java代码(CallCS.Java):

当我尝试运行java类时,java VM在调用该方法时崩溃(它能够加载库):

# #Java运行时环境检测到意外错误: # #内部错误(0xe0434f4d),pid=3144,tid=3484 # #Java虚拟机:Java热点(TM)客户端虚拟机(10.0-b19混合模式,共享windows-x86) #有问题的框架: #C[kernel32.dll+0x22366] # ... Java框架:(J=编译的Java代码,J=解释的,Vv=虚拟机代码) j CallCS.CallCS()V+0 jcallcs.main([Ljava/lang/String;)V+0 v~StubRoutines::调用_stub 但是,如果我创建一个加载clib.dll并调用导出函数Java\u CallCS\u CallCS的普通cpp应用程序,那么一切都正常。 我在x86和x64环境中都试过,结果是一样的。我没有试过其他版本的Java,但我需要代码在1.5.0上运行

此外,如果我将clib.cpp修改为仅调用系统方法,那么即使在Java中,一切都可以正常工作:

#include <jni.h>
#using <mscorlib.dll>

using namespace System;

extern "C" _declspec(dllexport) void Java_CallCS_callCS(JNIEnv* env, jclass cls) {
    System::Console::WriteLine("It works");
}
#包括
#使用
使用名称空间系统;
外部“C”\u declspec(dllexport)void Java\u CallCS\u CallCS(JNIEnv*env,jclass cls){
系统::控制台::写线(“它工作”);
}
总结:

  • 我能够从Java->clib.dll->mscorlib.dll调用系统方法
  • 我可以从CPPApp->clib.dll->cslib.dll调用任何方法
  • 我无法从Java->clib.dll->cslib.dll调用任何方法
  • 我知道有一种解决方法使用了1.1。我可以使用反射来加载assmebly并只使用系统调用调用所需的方法,但是代码会变得混乱,我希望有更好的解决方案

    我知道dotnetfromjava项目,它使用反射方法,但不希望增加比需要更多的复杂性

    我也看过ikvm.net,但我的理解是它使用自己的JVM(用C#编写)来实现这一神奇的功能。然而,在它的VM下运行整个Java应用程序对我来说是没有选择的


    谢谢。

    你看过ikvm.NET了吗?它允许在.NET和Java代码之间进行调用。

    好的,谜团已经解开了

    JVM崩溃是由未经处理的System.IO.FileNotFoundException引起的。引发该异常是因为在调用exe文件所在的文件夹中搜索.NET程序集

  • mscorlib.dll位于全局程序集缓存中,因此可以正常工作
  • CPP应用程序exe与程序集位于同一文件夹中,因此它也可以工作
  • cslib.dll程序集既不在java.exe文件夹中,也不在GAC中,因此无法工作

  • 看来我唯一的选择是在GAC中安装.NET程序集(第三方dll确实有一个强名称)。

    看,这将为您完成艰巨的工作。

    我很高兴找到这篇文章,因为我遇到了难题。 我想贡献一些代码,这有助于克服这个问题。 在Java构造函数中,调用init方法,该方法添加resolve事件。 我的经验是,在C++代码中不只是在调用到库之前调用init,因为由于时序问题,它可能会崩溃。 我已经将init调用放入映射JNI调用的java类构造函数中,这非常有效

        //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++代码

        // 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
    
    }
    

    C++代码实际上是C++ + CLI,是的,/CLR选项是指定的WOW,谢谢分享,我一直在尝试解决这个完全相同的问题。我真的想避免GAC的各种原因,所以我找到了一个方法来使用汇编语言解析事件手动加载程序集的路径。事件发生在C++/CLI层,因为C#程序集尚未加载。无论如何,希望这对另一个Google用户有所帮助…感谢分享此消息,事实上,我最终使用的正是AssemblyResolve事件,但忘记了更新答案。哇,很好!感谢链接。
    cl /clr /LD clib.cpp
    mt -manifest clib.dll.manifest -outputresource:clib.dll;2
    
    class CallCS {
        static {
           System.loadLibrary("clib");
        }
        private static native void callCS();
        public static void main(String[] args) {
            callCS();
        }
    }
    
    # # An unexpected error has been detected by Java Runtime Environment: # # Internal Error (0xe0434f4d), pid=3144, tid=3484 # # Java VM: Java HotSpot(TM) Client VM (10.0-b19 mixed mode, sharing windows-x86) # Problematic frame: # C [kernel32.dll+0x22366] # ... Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) j CallCS.callCS()V+0 j CallCS.main([Ljava/lang/String;)V+0 v ~StubRoutines::call_stub
    #include <jni.h>
    #using <mscorlib.dll>
    
    using namespace System;
    
    extern "C" _declspec(dllexport) void Java_CallCS_callCS(JNIEnv* env, jclass cls) {
        System::Console::WriteLine("It works");
    }
    
        //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
    
    #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 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);
        }
    
    }