linux下java调用netcore程序

发布时间 2023-12-29 12:04:52作者: 你不知道的浪漫

代码备份仅供参考

自述文件

# JavaCallCSharp
Java call C# lib build with .NET CORE 2.0 via C++ as wraper
The code is based on [example from coreCLR](https://github.com/dotnet/coreclr/tree/master/src/coreclr/hosts/unixcoreruncommon)

Java using JNI to call C++ code. then C++ host a core CLR to run C# code.
# pre-require
JDK: You should edit the Makefile and set the JAVALIB PATH

.NET CORE SDK 2.0: I only tested it in Ubuntu 16.04 x64

gcc 6: std C++ 14 for the filesystem

# usage:
javac Sample1.java

javah Sample1

make

cd cs

dotnet restore

dotnet build 

java Sample1 


`You should keep the compiled file in the same fold.`

CoreCLRHost.cpp

/*
 *  Copyright (c) Hubert Jarosz. All rights reserved.
 *  Licensed under the MIT license. See LICENSE file in the project root for full license information.
 */
#include <iostream>
#include <string.h>
#include "CoreCLRHost.hpp"
#include "utils.hpp"
#include "Sample1.h"

int runFromEntryPoint(
            std::string currentExeAbsolutePath,
            std::string clrFilesAbsolutePath,
            std::string managedAssemblyAbsoluteDir,
            std::string assemblyName,
            std::string entryPointType,
            std::string entryPointName)
{
    std::cout<<"currentExeAbsolutePath="<<currentExeAbsolutePath<<std::endl<<"clrFilesAbsolutePath="<<clrFilesAbsolutePath<<std::endl<<"managedAssemblyAbsoluteDir="<<managedAssemblyAbsoluteDir<<std::endl<<"assemblyName="<<assemblyName<<std::endl;
    std::cout<<"entryPointType="<<entryPointType<<std::endl<<"entryPointName="<<entryPointName<<std::endl;
  std::string coreClrDllPath = clrFilesAbsolutePath + "/" + coreClrDll;


  if ( coreClrDllPath.size() >= PATH_MAX ) {
      std::cerr << "Path to libcoreclr.so too long!" << std::endl;
      return -1;
  }

  std::string nativeDllSearchDirs = managedAssemblyAbsoluteDir + ":" + clrFilesAbsolutePath;

  std::string tpaList;
  AddFilesFromDirectoryToTpaList( clrFilesAbsolutePath, tpaList );
  std::cout<<"line 32"<<std::endl;
  auto dl = dynamicLinker::dynamicLinker::make_new( coreClrDllPath );
  auto coreclr_initialize = dl->getFunction<coreclrInitializeFunction>("coreclr_initialize");
  auto coreclr_shutdown = dl->getFunction<coreclrShutdownFunction>("coreclr_shutdown");
  auto coreclr_create_delegate = dl->getFunction<coreclrCreateDelegateFunction>("coreclr_create_delegate");
 std::cout<<"line 37"<<std::endl;
  try {
    dl->open();
    coreclr_initialize.init();
    coreclr_shutdown.init();
    coreclr_create_delegate.init();
  } catch ( dynamicLinker::openException e ) {
    std::cerr << "Cannot find " << coreClrDll << "Path that was searched: "
              << coreClrDllPath << std::endl;
    std::cerr << e.what() << std::endl;
    return -1;
  } catch ( dynamicLinker::symbolException e ) {
    std::cerr << "Probably your libcoreclr is broken or too old." << std::endl;
    std::cerr << e.what() << std::endl;
    return -1;
  } catch ( dynamicLinker::dynamicLinkerException e ) {
    std::cerr << e.what() << std::endl;
    return -1;
  }

  const char *propertyKeys[] = {
      "TRUSTED_PLATFORM_ASSEMBLIES",
      "APP_PATHS",
      "APP_NI_PATHS",
      "NATIVE_DLL_SEARCH_DIRECTORIES",
      "AppDomainCompatSwitch"
  };

  const char *propertyValues[] = {
      tpaList.c_str(),
      managedAssemblyAbsoluteDir.c_str(),
      managedAssemblyAbsoluteDir.c_str(),
      nativeDllSearchDirs.c_str(),
      "UseLatestBehaviorWhenTFMNotSpecified"
  };

  void* hostHandle = NULL;
  unsigned int domainId = 0;
  int status = -1;

  // initialize coreclr
  try {
    status = coreclr_initialize (
      currentExeAbsolutePath.c_str(),
      "simpleCoreCLRHost",
      sizeof(propertyKeys) / sizeof(propertyKeys[0]),
      propertyKeys,
      propertyValues,
      &hostHandle,
      &domainId
    );
  } catch ( dynamicLinker::dynamicLinkerException e ) {
    std::cerr << e.what() << std::endl;
    return -1;
  }

  if ( status < 0 ) {
    std::cerr << "ERROR! coreclr_initialize status: 0x" << std::hex << status << std::endl;
    return -1;
  }

  // Fancy modern C++ code. You can also just use void *.
  auto no_del = []( auto x ) { (void)(x); };
  auto csharp_runIt = std::unique_ptr<csharp_runIt_t, decltype(no_del)>(nullptr, no_del);

  try {
    // create delegate to our entry point
    status = coreclr_create_delegate (
      hostHandle,
      domainId,
      assemblyName.c_str(),
      entryPointType.c_str(),
      entryPointName.c_str(),
      reinterpret_cast<void**>(&csharp_runIt)
    );
  } catch ( dynamicLinker::dynamicLinkerException e ) {
    std::cerr << e.what() << std::endl;
    return -1;
  }

  if ( status < 0 ) {
    std::cerr << "ERROR! coreclr_create_delegate status: 0x" << std::hex << status << std::endl;
    return -1;
  }

  myClass tmp = myClass();
  tmp.question();

  /*
   *  If arguments are in in different order then second arg is 0 in C#.
   *  probably something with padding/offset/ptr byte size
   */
  (*csharp_runIt)( tmp, std::mem_fun_ref(&myClass::print) );

  try {
    status = coreclr_shutdown ( hostHandle, domainId );
  } catch ( dynamicLinker::dynamicLinkerException e ) {
    std::cerr << e.what() << std::endl;
    return -1;
  }

  if ( status < 0 ) {
    std::cerr << "ERROR! coreclr_shutdown status: 0x" << std::hex << status << std::endl;
    return -1;
  }

  return 0;
}


JNIEXPORT jint JNICALL Java_Sample1_intMethod
       (JNIEnv *env, jobject obj, jint num){
                return num * num;
            }
     JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
          (JNIEnv *env, jobject obj, jboolean boolean){
                  return !boolean;
              }
    JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
        (JNIEnv *env, jobject obj, jstring string){
                const char *str = env->GetStringUTFChars(string,0);
                char cap[128];
               strcpy(cap,str);
               env->ReleaseStringUTFChars(string , str);
                return env->NewStringUTF(cap);
            }

    JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod
        (JNIEnv *env, jobject obj, jintArray array){
                int i, sum=0;
                jsize len = env->GetArrayLength(array);
                jint *body = env ->GetIntArrayElements(array,0);
                for (i=0;i<len;i++)
                {
                            sum+=body[i];
                        }
                env->ReleaseIntArrayElements(array, body,0);
                return sum;
            }
JNIEXPORT jint JNICALL Java_Sample1_coreClrHost(JNIEnv *env, jobject obj, jstring string)
    {
  std::cout<<"haha"<<std::endl;
  const char *str = env->GetStringUTFChars(string,0);
  char cap[128];
  strcpy(cap,str);
  std::string dllpath(cap); //"./Managed.dll";
  env->ReleaseStringUTFChars(string , str);
 // std::string dllpath = env->NewStringUTF(cap);
  std::string cwd = SCCH_fs::current_path();
  cwd += "/";
  std::cout<<"dll path :"<<dllpath<<std::endl;

  std::string assemblyName(dllpath);
  std::string assemblyDir(assemblyName);


  if( !assemblyName.size() ) {
    std::cerr << "ERROR: Bad ASSEMBLY_PATH !" << std::endl;
    return 0;
  }

  size_t find = assemblyName.rfind('/');
  if( find == std::string::npos )
    find = 0;

  assemblyName = assemblyName.substr( find+1, assemblyName.size() );

  if( assemblyName.size() < 5 ||
      assemblyName.substr( assemblyName.size()-4,
                           assemblyName.size()) != ".dll" ) {
    std::cerr << "ERROR: Assembly is not .dll !" << std::endl;
    return 0;
  }

  assemblyName = assemblyName.substr( 0, assemblyName.size()-4 );

  assemblyDir.erase(find);  // get dir of assembly
  assemblyDir = cwd + assemblyDir;

  int exitCode = runFromEntryPoint(
                          cwd+std::string("./tar.so"), // absolute path to this exe
                          std::string("/usr/share/dotnet/shared/Microsoft.NETCore.App/2.0.0-preview1-002111-00/"),     // absolute path to coreCLR DLLs
                          assemblyDir, // absolute path to DLL to run
                          assemblyName,
                          std::string("Managed"),
                          std::string("runIt"));

  if ( exitCode < 0 )
    std::cout << "Exit Code: " << exitCode << std::endl;


  return 1;
}

int main( int argc, char* argv[] ) {  return 0;}

CoreCLRHost.hpp

/*
 *  Copyright (c) Hubert Jarosz. All rights reserved.
 *  Licensed under the MIT license. See LICENSE file in the project root for full license information.
 */

//#pragma once

//#if not defined (__unix__) && not defined(__unix) && not defined (unix) && ( (not defined (__APPLE__) || not defined (__MACH__)) )
//  #error THIS SOFTWARE IS ONLY FOR UNIX-LIKE SYSTEMS!
//#endif

#include <functional>
#include <iostream>
#include "dynamicLinker/dynamicLinker.hpp"

// getcwd on Linux
#include <unistd.h>

// PATH_MAX on Linux
#include <limits.h>

#if not defined PATH_MAX
  #warning Is this GNU/Hurd? Then this code could occupy a lot of memory.
  #include <stdio.h>
  #define PATH_MAX FILENAME_MAX
#endif

//#if defined(__APPLE__)
//  std::string coreClrDll = "libcoreclr.dylib";
//#else
  std::string coreClrDll = "libcoreclr.so";
//#endif

class myClass {
private:
  int value;
public:
  void question() { value = 42; }
  void print() { std::cout << "Value: " <<  value << std::endl; }
};

typedef void (csharp_runIt_t)( myClass&, std::mem_fun_ref_t<void, myClass> );

Makefile

OS_NAME = $(shell uname -s)
ifeq ($(OS_NAME), Darwin)
  CXX = g++-6
  ifeq (, $(shell which $(CPP)))
    $(error "$(CPP) not found! You need to install gcc 6 to build this!")
    endif
else
  CXX = g++
endif

CXXFLAGS = -Wall  -std=c++14 -m64 -fPIC
LDLIBS = -ldl -lstdc++fs
JAVALIB= -I/usr/lib/jvm/java-9-openjdk-amd64/include -I/usr/lib/jvm/java-9-openjdk-amd64/include/linux/
.PHONY: all clean

all: tar.so   Makefile

tar.so: CoreCLRHost.cpp CoreCLRHost.hpp utils.hpp Sample1.h  Makefile
    git -C dynamicLinker pull || git clone https://github.com/Marqin/dynamicLinker
    make -C dynamicLinker CXX=$(CXX)
    $(CXX) $(JAVALIB) $(CXXFLAGS) CoreCLRHost.cpp -shared -o tar.so -LdynamicLinker/ -ldynamicLinker $(LDLIBS)


clean:
    rm -rf  tar.so  
    sh -c "stat dynamicLinker/ &> /dev/null && make -C dynamicLinker clean" || true

distclean: clean
    rm -rf dynamicLinker/

Managed.cs

/*
 *  Copyright (c) Hubert Jarosz. All rights reserved.
 *  Licensed under the MIT license. See LICENSE file in the project root for full license information.
 */

using System;
using System.Runtime.InteropServices;

public class Managed {

[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
unsafe delegate void myDelegate( IntPtr thisptr );

public static unsafe void runIt( IntPtr thisptr, IntPtr mem_fun ) {
    Console.WriteLine("Here's C# code:");

    myDelegate fun = (myDelegate) Marshal.GetDelegateForFunctionPointer( mem_fun, typeof(myDelegate) );

    fun(thisptr);  // first argument of member functions in C++ is "this", but it's hidden from us :-)
  }
}

Sample1.cpp

#include "Sample1.h"
#include <string.h>
#include <iostream>
JNIEXPORT jint JNICALL Java_Sample1_intMethod
(JNIEnv *env, jobject obj, jint num){
    return num * num;
}

JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
(JNIEnv *env, jobject obj, jboolean boolean){
    return !boolean;
}

JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
(JNIEnv *env, jobject obj, jstring string){
    const char *str = env->GetStringUTFChars(string,0);
    char cap[128];
   strcpy(cap,str);
   env->ReleaseStringUTFChars(string , str);
    return env->NewStringUTF(cap);
}

JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod
(JNIEnv *env, jobject obj, jintArray array){
    int i, sum=0;
    jsize len = env->GetArrayLength(array);
    jint *body = env ->GetIntArrayElements(array,0);
    for (i=0;i<len;i++)
    {
        sum+=body[i];
    }

    env->ReleaseIntArrayElements(array, body,0);
    return sum;
}

Sample1.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Sample1 */

#ifndef _Included_Sample1
#define _Included_Sample1
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Sample1
 * Method:    intMethod
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_Sample1_intMethod
  (JNIEnv *, jobject, jint);

/*
 * Class:     Sample1
 * Method:    booleanMethod
 * Signature: (Z)Z
 */
JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
  (JNIEnv *, jobject, jboolean);

/*
 * Class:     Sample1
 * Method:    stringMethod
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
  (JNIEnv *, jobject, jstring);

/*
 * Class:     Sample1
 * Method:    intArrayMethod
 * Signature: ([I)I
 */
JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod
  (JNIEnv *, jobject, jintArray);

/*
 * Class:     Sample1
 * Method:    coreClrHost
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_Sample1_coreClrHost
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

Sample1.java

public class Sample1
{
    public native int intMethod(int n);
    public native boolean booleanMethod(boolean bool);
    public native String stringMethod(String text);
    public native int intArrayMethod(int[] intArray);
    public native int coreClrHost(String dllpath);
    public static void main(String[] args)
{
    //System.loadLibrary("Sample1");
    System.load("./tar.so");

    Sample1 sample = new Sample1();
    int square = sample.intMethod(5);
    boolean bool = sample.booleanMethod(true);
    String text =  sample.stringMethod("JAVA");
    int sum = sample.intArrayMethod(new int[]{1,1,2,3,5,8,13});

    int success=0;
    success= sample.coreClrHost("./Managed.dll");
    System.out.println("intMethod: "+ square);
    System.out.println("boolMethod: "+ bool);
    System.out.println("stringMethod: "+ text);
    System.out.println("intArrayMethod: "+ sum);
    System.out.println("status:"+success);

}

}

utils.cpp

//#pragma once

#include <cstdlib>
#include <set>
#include <string>
#include <cstring>

// Prototype of the coreclr_initialize function from the libcoreclr.so
typedef int (coreclrInitializeFunction)(
            const char* exePath,
            const char* appDomainFriendlyName,
            int propertyCount,
            const char** propertyKeys,
            const char** propertyValues,
            void** hostHandle,
            unsigned int* domainId);

// Prototype of the coreclr_shutdown function from the libcoreclr.so
typedef int (coreclrShutdownFunction)(
            void* hostHandle,
            unsigned int domainId);

// Prototype of the coreclr_execute_assembly function from the libcoreclr.so
typedef int (coreclrCreateDelegateFunction)(
              void* hostHandle,
              unsigned int domainId,
              const char* entryPointAssemblyName,
              const char* entryPointTypeName,
              const char* entryPointMethodName,
              void** delegate);


//#if not defined ( __GNUC__ ) || __GNUC__ < 5 || ( __GNUC__ == 5 && __GNUC_MINOR__ < 3 )
//  #error THIS SOFTWARE CURRENTLY BUILDS ONLY ON GCC 5.3 OR NEWER!
//#endif

#include <experimental/filesystem>
namespace SCCH_fs = std::experimental::filesystem;

void AddFilesFromDirectoryToTpaList( std::string directory, std::string& tpaList ) {

  for ( auto& dirent : SCCH_fs::directory_iterator(directory) ) {
    std::string path = dirent.path();

    if ( ! path.compare(path.length() - 4, 4, ".dll") ) {
      tpaList.append(path + ":");
    }
  }

}