【问题标题】:Calling .NET assembly from Java: JVM crashes从 Java 调用 .NET 程序集:JVM 崩溃
【发布时间】:2010-09-13 09:26:58
【问题描述】:

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

C#代码(cslib.cs):

using System;

namespace CSLib
{
    public class CSClass
    {
        public static void SayHi()
        {
            System.Console.WriteLine("Hi");
        }
    }
}

编译(使用 .net 3.5,但使用 2.0 时也是如此):

csc /target:library cslib.cs

C++ 代码(clib.cpp):

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

using namespace CSLib;

extern "C" _declspec(dllexport) void Java_CallCS_callCS(JNIEnv* env, jclass cls) {
    CSLib::CSClass::SayHi();
}

编译时使用(使用 VC 2008 工具,但使用 2003 工具时也是如此):

cl /clr /LD clib.cpp
mt -manifest clib.dll.manifest -outputresource:clib.dll;2

Java 代码(CallCS.java):

class CallCS {
    static {
       System.loadLibrary("clib");
    }
    private static native void callCS();
    public static void main(String[] args) {
        callCS();
    }
}

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

# # Java Runtime Environment 检测到意外错误: # # 内部错误 (0xe0434f4d), pid=3144, tid=3484 # # Java VM:Java HotSpot(TM) Client VM(10.0-b19混合模式,共享windows-x86) # 有问题的框架: #C [kernel32.dll+0x22366] # ... Java 框架:(J=编译的 Java 代码,j=解释的,Vv=VM 代码) j CallCS.callCS()V+0 j CallCS.main([Ljava/lang/String;)V+0 v ~StubRoutines::call_stub

但是,如果我创建一个加载 clib.dll 并调用导出函数 Java_CallCS_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");
}

总结一下:

  1. 我能够从 Java -> clib.dll -> mscorlib.dll 调用系统方法
  2. 我可以从 CPPApp -> clib.dll -> cslib.dll 调用任何方法
  3. 我无法从 Java -> clib.dll -> cslib.dll 调用任何方法

我知道使用上述 1. 的解决方法 - 我可以使用反射来加载程序集并仅使用系统调用来调用所需的方法,但代码变得混乱,我希望有更好的解决方案。

我知道 dotnetfromjava 项目,它使用反射方法,但不希望增加不必要的复杂性。但是,如果没有其他方法,我会使用类似的东西。

我也看过 ikvm.net,但我的理解是它使用自己的 JVM(用 C# 编写)来发挥作用。但是,在其 VM 下运行整个 Java 应用程序对我来说是没有选择的。

谢谢。

【问题讨论】:

  • C++ 代码实际上是 C++/CLI 对吧?
  • 是的,指定了/clr选项

标签: c# java integration


【解决方案1】:

好的,谜团解开了。

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

  1. mscorlib.dll 位于全局程序集缓存中,因此可以正常工作。
  2. CPP 应用程序 exe 与程序集位于同一文件夹中,因此它也可以工作。
  3. cslib.dll 程序集不在 java.exe 文件夹中,在 GAC 中也不在,所以它不起作用。

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

【讨论】:

  • 哇,感谢分享这个,我一直在试图找出这个完全相同的问题。由于各种原因,我真的很想避开 GAC,所以我找到了一种方法,可以使用 AssemblyResolve 事件从您选择的路径手动加载程序集:devcity.net/Articles/254/1/.aspx。您必须在 C++/CLI 层中处理此事件,因为尚未加载 C# 程序集。无论如何,希望这对其他 Google 员工有所帮助...
  • 感谢分享,确实我最终使用了 AssemblyResolve 事件,但忘记更新答案。
【解决方案2】:

看看jni4net,它会为你做辛苦的工作。

【讨论】:

  • 哇。好的!感谢您的链接。
【解决方案3】:

您是否看过 ikvm.NET,它允许在 .NET 和 Java 代码之间进行调用?

【讨论】:

    【解决方案4】:

    我很高兴找到这篇文章,因为我遇到了这个问题。 我想贡献一些代码,这有助于克服这个问题。 在您的 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
    
    }
    

    Java 代码

        /***
        ** 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);
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-09
      • 1970-01-01
      • 2010-10-06
      • 2012-07-04
      相关资源
      最近更新 更多