11

サード パーティの .NET アセンブリと大規模な Java アプリケーションがあります。.NET クラス ライブラリが提供するメソッドを Java アプリケーションから呼び出す必要があります。アセンブリは 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 ランタイム環境によって予期しないエラーが検出されました:
#
# 内部エラー (0xe0434f4d)、pid=3144、tid=3484
#
# Java VM: Java HotSpot(TM) クライアント 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# で記述) を使用して魔法を実行しています。ただし、Java アプリケーション全体をその VM の下で実行することは、私にとって選択肢ではありません。

ありがとう。

4

4 に答える 4

10

よし、謎が解けた。

JVM クラッシュは、未処理の System.IO.FileNotFoundException が原因で発生します。呼び出し元の exe ファイルが存在するフォルダーで .NET アセンブリが検索されるため、例外がスローされます。

  1. mscorlib.dll はグローバル アセンブリ キャッシュにあるため、機能します。
  2. CPP アプリケーションの exe は、アセンブリと同じフォルダーにあるため、これも機能します。
  3. cslib.dll アセンブリは、java.exe のフォルダーにも、GAC のフォルダーにもないため、機能しません。

私の唯一のオプションは、.NETアセンブリをGACにインストールすることです(サードパーティのdllには厳密な名前があります)。

于 2008-09-26T11:05:41.357 に答える
4

jni4netを見てください。大変な作業をしてくれます。

于 2009-10-31T18:06:50.347 に答える
2

.NET と Java コード間の呼び出しを可能にする ikvm.NET を見たことがありますか?

于 2008-09-26T08:57:28.030 に答える
0

私は立ち往生し、まさにその問題を抱えていたので、この記事を見つけてとてもうれしかったです. この問題を克服するのに役立つコードを提供したいと思います。Java コンストラクターで、解決イベントを追加する init メソッドを呼び出します。私の経験では、タイミングの問題によりクラッシュする可能性があるため、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 コード

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

}
于 2013-07-09T19:27:18.073 に答える