代码备份仅供参考
自述文件
# 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 + ":"); } } }