2008-09-26 6 views
11

제 3 자 .NET 어셈블리와 대규모 Java 응용 프로그램이 있습니다. Java 애플리케이션에서 .NET 클래스 라이브러리에 의해 제공되는 mothod를 호출해야한다. 어셈블리가 COM을 사용할 수 없습니다. 나는 인터넷을 검색하고 지금까지 나는 다음과 같습니다자바에서 .NET 어셈블리 호출 : JVM이 충돌 함

C# 코드 (cslib.cs) :

using System; 

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

(3.5 NET을 사용하지만 같은 2.0를 사용하는 경우 발생) 컴파일 :

csc /target:library cslib.cs 

C++ 코드 (clib.cpp) : (VC 2008 도구를 사용하여 컴파일

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

using namespace CSLib; 

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

하지만 일 2003 도구를 사용하는 경우 동일한 전자)가 발생합니다

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

자바 코드 (CallCS.java) :

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

나는 자바 클래스를 실행하려고하면, 자바 VM 충돌이 방법을 (호출하는 동안) 라이브러리를로드 할 수 있습니다 :

 
# 
# 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 

그러나, 나는 clib.dll을로드하고 내 보낸 함수 Java_CallCS_callCS를 호출하는 일반 CPP 응용 프로그램을 만드는 경우, 모든 것이 OK입니다. x86 및 x64 환경에서이 작업을 시도했으며 그 결과는 같습니다. 다른 Java 버전을 사용해 보진 않았지만 1.5.0에서 실행하려면 코드가 필요합니다. 난 단지 시스템 메소드를 호출 할 clib.cpp을 수정하면

또한, 모든 심지어 자바에서 잘 작동 :

#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. 을 나는 자바에서 시스템 메소드를 호출 할 수 있어요 -> clib.dll -> mscorlib.dll에서
  2. 내가 CPPApp에서 모든 메소드를 호출 할 수 있어요
  3. -> clib.dll -> cslib.dll
  4. 나는 자바에서 어떤 메소드를 호출 할 수 없습니다입니다
  5. -> clib.dll - > cs lib.dll

위의 1.을 사용하는 해결 방법을 알고 있습니다 - 나는 assmebly를로드하고 원하는 메서드를 시스템 호출 만 사용하여 호출 할 수 있지만 코드가 지저분 해지고 더 나은 결과를 기대합니다. 해결책.

나는 반사 방식을 사용하는 dotnetfromjava 프로젝트에 대해 알고 있지만, 필요 이상으로 복잡성을 추가하지 않는 것을 선호합니다. 그러나 다른 방법이 없다면 이런 식으로 사용할 것입니다.

나는 또한 ikvm.net을 보았지만 나의 이해는 자신의 JVM (C#으로 작성)을 사용하여 마법을 수행한다는 것이다. 그러나 VM에서 전체 Java 응용 프로그램을 실행하는 것은 선택 사항이 아닙니다.

감사합니다.

+0

는 C++ 코드는 실제로 바로 C++/CLI입니까? – Gili

+0

예,/clr 옵션이 지정되었습니다. – Kcats

답변

10

확인을하면 문제가 해결됩니다.

JVM 충돌은 처리되지 않은 System.IO.FileNotFoundException으로 인해 발생합니다. 호출 exe 파일이있는 폴더에서 .NET 어셈블리를 검색하기 때문에 예외가 throw됩니다.

  1. mscorlib.dll은 전역 어셈블리 캐시에 있으므로 작동합니다.
  2. CPP 응용 프로그램 exe는 어셈블리와 동일한 폴더에 있으므로 작동합니다.
  3. cslib.dll 어셈블리는 GAC의 java.exe 폴더에서 NEITH이므로 작동하지 않습니다.

내 유일한 옵션은 .NET 어셈블리를 GAC에 설치하는 것입니다. 타사 DLL에는 강력한 이름이 있습니다.

+7

와우 덕분에이 문제를 해결하려고 노력했습니다. 여러 가지 이유로 GAC를 피하고 싶었 기 때문에 AssemblyResolve 이벤트를 사용하여 원하는 경로에서 어셈블리를 수동으로로드하는 방법을 찾았습니다. http://www.devcity.net/Articles/254/1/.aspx . C# 어셈블리가 아직로드되지 않았으므로 C++/CLI 계층에서이 이벤트를 처리해야합니다. 어쨌든 이것은 다른 Google 직원에게 도움이 될 것입니다. – Jason

+0

공유 해 주셔서 감사합니다. 실제로 AssemblyResolve 이벤트를 정확히 사용했지만 결과를 잊어 버렸습니다. – Kcats

2

.NET과 Java 코드 간 호출을 허용하는 ikvm.NET을 보셨습니까?

4

jni4net을 보면 열심히하겠습니다.

+0

와우. 좋은! 링크를 가져 주셔서 감사합니다. – dthorpe

+0

Windows에서만 – eomeroff

0

내가 붙어서 정확히 그 문제가 있었기 때문에 나는이 기사를 발견하게되어 기뻤다. 이 문제를 극복하는 데 도움이되는 몇 가지 코드를 제공하고 싶습니다. Java 생성자에서 init 이벤트를 호출하면 resolve 이벤트가 추가됩니다. 내 경험에 비추어 볼 때, C++ 코드로 라이브러리에 호출하기 전에 init을 호출해야합니다. 그럼에도 불구하고 타이밍 문제로 인해 충돌이 발생할 수 있습니다. JNI 호출을 매핑하는 Java 클래스 생성자에 init 호출을 두었습니다.

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

}