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