Question

J'ai un assemblage .NET tiers et une grande application Java. J'ai besoin d'appeler les méthodes fournies par la bibliothèque de classes .NET à partir de l'application Java. L'assembly n'est pas activé par COM. J'ai effectué des recherches sur le net et jusqu'à présent, j'ai les informations suivantes:

Code C # (cslib.cs):

using System;

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

compilé avec (en utilisant .net 3.5, mais la même chose se produit lorsque 2.0 est utilisé):

csc /target:library cslib.cs

Code 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();
}

compilé avec (à l'aide des outils VC 2008, mais la même chose se produit lorsque des outils 2003 sont utilisés):

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

Code Java (CallCS.java):

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

Lorsque j'essaie d'exécuter la classe java, la machine virtuelle Java se bloque lors de l'appel de la méthode (elle est capable de charger la bibliothèque):

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

Cependant, si je crée une application cpp ordinaire qui charge clib.dll et appelle la fonction exportée Java_CallCS_callCS, tout est en ordre. J'ai essayé ceci sur les environnements x86 et x64 et le résultat est le même. Je n'ai pas essayé d'autres versions de Java, mais j'ai besoin du code pour fonctionner sous 1.5.0.

De plus, si je modifie clib.cpp pour n’appeler que les méthodes système, tout fonctionne correctement, même à partir de 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");
}

Pour conclure:

  1. Je peux ABLE appeler des méthodes système de Java - > clib.dll - > mscorlib.dll
  2. Je peux appeler toutes les méthodes de CPPApp - > clib.dll - > cslib.dll
  3. Je suis incapable d'appeler des méthodes de Java - > clib.dll - > cslib.dll

Je connais une solution de contournement utilisant 1. ci-dessus. Je peux utiliser la réflexion pour charger l'assemment et appeler les méthodes souhaitées à l'aide d'appels système uniquement, mais le code devient confus et j'espère une meilleure solution.

Je connais le projet dotnetfromjava, qui utilise la méthode de réflexion, mais préfère ne pas ajouter plus de complexité que nécessaire. Je vais utiliser quelque chose comme ça s'il n'y a pas d'autre moyen, cependant.

J’ai également consulté ikvm.net, mais j’ai bien compris qu’il utilise sa propre JVM (écrite en C #) pour faire la magie. Toutefois, exécuter l'application Java dans son intégralité sous sa machine virtuelle n'est pas une option pour moi.

Merci.

Était-ce utile?

La solution

OK, le mystère est résolu.

Le blocage de la machine virtuelle Java est dû à une exception non gérée System.IO.FileNotFoundException. L'exception est levée car l'assembly .NET est recherché dans le dossier où réside le fichier exe appelant.

  1. Le fichier mscorlib.dll est dans le Global Assembly Cache, de sorte qu'il fonctionne.
  2. Le fichier exe de l'application CPP se trouve dans le même dossier que l'assembly, il fonctionne donc également.
  3. L'assembly cslib.dll n'est ni dans le dossier de java.exe, ni dans le GAC, donc cela ne fonctionne pas.

Il semble que ma seule option est d'installer l'assembly .NET dans GAC (la DLL tierce a un nom fort).

Autres conseils

Regardez jni4net , il fera le travail difficile pour vous.

Avez-vous examiné ikvm.NET, qui autorise les appels entre le code .NET et le code Java?

J'étais si heureux de trouver cet article car je me suis retrouvé coincé et j'avais exactement ce problème. Je veux contribuer un peu de code, ce qui aide à surmonter ce problème. Dans votre constructeur Java, appelez la méthode init, qui ajoute l'événement resol. D'après mon expérience, il est nécessaire d'appeler init NON avant l'appel de votre bibliothèque dans votre code c ++, car, en raison de problèmes de synchronisation, il risque néanmoins de tomber en panne. J'ai mis l'appel init dans le constructeur de ma classe java pour mapper les appels JNI, ce qui fonctionne très bien.

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


    }
}

Code 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

}

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

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top