├── .gitmodules ├── CMakeLists.txt ├── CoreCLRHost.cpp ├── CoreCLRHost.hpp ├── Managed.cs ├── Managed.csproj ├── README.md ├── Sample1.cpp ├── Sample1.h ├── Sample1.java ├── azure-pipelines.yml ├── build.yml └── utils.hpp /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dynamicLinker"] 2 | path = dynamicLinker 3 | url = https://github.com/passedbylove/dynamicLinker 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.8) 2 | project (ASA4J) 3 | 4 | # TODO wall 5 | set(CMAKE_CXX_STANDARD 14) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | # 64bit compile 9 | SET(CMAKE_CXX_FLAGS "-m64") 10 | SET(CMAKE_C_FLAGS "-m64") 11 | SET(CMAKE_EXE_LINKER_FLAGS "-m64") 12 | SET(CMAKE_MODULE_LINKER_FLAGS "-m64") 13 | SET(CMAKE_SHARED_LINKER_FLAGS "-m64") 14 | 15 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) # fPIC 16 | 17 | find_package(JNI REQUIRED) 18 | find_package(Java REQUIRED) 19 | include(UseJava) 20 | 21 | message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}") 22 | message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}") 23 | 24 | add_library(tar SHARED CoreCLRHost.cpp CoreCLRHost.hpp utils.hpp Sample1.h 25 | dynamicLinker/dynamicLinker.cpp) 26 | target_link_libraries(tar dl stdc++fs) 27 | target_include_directories(tar PUBLIC ${JNI_INCLUDE_DIRS} dynamicLinker) 28 | 29 | add_jar(sample1 Sample1.java GENERATE_NATIVE_HEADERS sample-native) 30 | 31 | execute_process(COMMAND dotnet build) 32 | -------------------------------------------------------------------------------- /CoreCLRHost.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Hubert Jarosz. All rights reserved. 3 | * Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | */ 5 | #include 6 | #include 7 | #include "CoreCLRHost.hpp" 8 | #include "utils.hpp" 9 | #include "Sample1.h" 10 | 11 | int runFromEntryPoint( 12 | std::string currentExeAbsolutePath, 13 | std::string clrFilesAbsolutePath, 14 | std::string managedAssemblyAbsoluteDir, 15 | std::string assemblyName, 16 | std::string entryPointType, 17 | std::string entryPointName) 18 | { 19 | std::cout<<"currentExeAbsolutePath="<= PATH_MAX ) { 25 | std::cerr << "Path to libcoreclr.so too long!" << std::endl; 26 | return -1; 27 | } 28 | 29 | std::string nativeDllSearchDirs = managedAssemblyAbsoluteDir + ":" + clrFilesAbsolutePath; 30 | 31 | std::string tpaList; 32 | AddFilesFromDirectoryToTpaList( clrFilesAbsolutePath, tpaList ); 33 | std::cout<<"line 32"<getFunction("coreclr_initialize"); 36 | auto coreclr_shutdown = dl->getFunction("coreclr_shutdown"); 37 | auto coreclr_create_delegate = dl->getFunction("coreclr_create_delegate"); 38 | std::cout<<"line 37"<open(); 41 | coreclr_initialize.init(); 42 | coreclr_shutdown.init(); 43 | coreclr_create_delegate.init(); 44 | } catch ( dynamicLinker::openException e ) { 45 | std::cerr << "Cannot find " << coreClrDll << "Path that was searched: " 46 | << coreClrDllPath << std::endl; 47 | std::cerr << e.what() << std::endl; 48 | return -1; 49 | } catch ( dynamicLinker::symbolException e ) { 50 | std::cerr << "Probably your libcoreclr is broken or too old." << std::endl; 51 | std::cerr << e.what() << std::endl; 52 | return -1; 53 | } catch ( dynamicLinker::dynamicLinkerException e ) { 54 | std::cerr << "dynamicLinkerEx: " << e.what() << std::endl; 55 | return -1; 56 | } 57 | 58 | const char *propertyKeys[] = { 59 | "TRUSTED_PLATFORM_ASSEMBLIES", 60 | "APP_PATHS", 61 | "APP_NI_PATHS", 62 | "NATIVE_DLL_SEARCH_DIRECTORIES", 63 | "AppDomainCompatSwitch" 64 | }; 65 | 66 | const char *propertyValues[] = { 67 | tpaList.c_str(), 68 | managedAssemblyAbsoluteDir.c_str(), 69 | managedAssemblyAbsoluteDir.c_str(), 70 | nativeDllSearchDirs.c_str(), 71 | "UseLatestBehaviorWhenTFMNotSpecified" 72 | }; 73 | 74 | void* hostHandle = NULL; 75 | unsigned int domainId = 0; 76 | int status = -1; 77 | 78 | // initialize coreclr 79 | try { 80 | status = coreclr_initialize ( 81 | currentExeAbsolutePath.c_str(), 82 | "simpleCoreCLRHost", 83 | sizeof(propertyKeys) / sizeof(propertyKeys[0]), 84 | propertyKeys, 85 | propertyValues, 86 | &hostHandle, 87 | &domainId 88 | ); 89 | } catch ( dynamicLinker::dynamicLinkerException e ) { 90 | std::cerr << e.what() << std::endl; 91 | return -1; 92 | } 93 | 94 | if ( status < 0 ) { 95 | std::cerr << "ERROR! coreclr_initialize status: 0x" << std::hex << status << std::endl; 96 | return -1; 97 | } 98 | 99 | // Fancy modern C++ code. You can also just use void *. 100 | auto no_del = []( auto x ) { (void)(x); }; 101 | auto csharp_runIt = std::unique_ptr(nullptr, no_del); 102 | 103 | try { 104 | // create delegate to our entry point 105 | status = coreclr_create_delegate ( 106 | hostHandle, 107 | domainId, 108 | assemblyName.c_str(), 109 | entryPointType.c_str(), 110 | entryPointName.c_str(), 111 | reinterpret_cast(&csharp_runIt) 112 | ); 113 | } catch ( dynamicLinker::dynamicLinkerException e ) { 114 | std::cerr << e.what() << std::endl; 115 | return -1; 116 | } 117 | 118 | if ( status < 0 ) { 119 | std::cerr << "ERROR! coreclr_create_delegate status: 0x" << std::hex << status << std::endl; 120 | return -1; 121 | } 122 | 123 | myClass tmp = myClass(); 124 | tmp.question(); 125 | 126 | /* 127 | * If arguments are in in different order then second arg is 0 in C#. 128 | * probably something with padding/offset/ptr byte size 129 | */ 130 | (*csharp_runIt)( tmp, std::mem_fun_ref(&myClass::print) ); 131 | 132 | try { 133 | status = coreclr_shutdown ( hostHandle, domainId ); 134 | } catch ( dynamicLinker::dynamicLinkerException e ) { 135 | std::cerr << e.what() << std::endl; 136 | return -1; 137 | } 138 | 139 | if ( status < 0 ) { 140 | std::cerr << "ERROR! coreclr_shutdown status: 0x" << std::hex << status << std::endl; 141 | return -1; 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | 148 | JNIEXPORT jint JNICALL Java_Sample1_intMethod 149 | (JNIEnv *env, jobject obj, jint num){ 150 | return num * num; 151 | } 152 | JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod 153 | (JNIEnv *env, jobject obj, jboolean boolean){ 154 | return !boolean; 155 | } 156 | JNIEXPORT jstring JNICALL Java_Sample1_stringMethod 157 | (JNIEnv *env, jobject obj, jstring string){ 158 | const char *str = env->GetStringUTFChars(string,0); 159 | char cap[128]; 160 | strcpy(cap,str); 161 | env->ReleaseStringUTFChars(string , str); 162 | return env->NewStringUTF(cap); 163 | } 164 | 165 | JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod 166 | (JNIEnv *env, jobject obj, jintArray array){ 167 | int i, sum=0; 168 | jsize len = env->GetArrayLength(array); 169 | jint *body = env ->GetIntArrayElements(array,0); 170 | for (i=0;iReleaseIntArrayElements(array, body,0); 175 | return sum; 176 | } 177 | JNIEXPORT jint JNICALL Java_Sample1_coreClrHost(JNIEnv *env, jobject obj, jstring string) 178 | { 179 | std::cout<<"haha"<GetStringUTFChars(string,0); 181 | char cap[128]; 182 | strcpy(cap,str); 183 | std::string dllpath(cap); //"./Managed.dll"; 184 | env->ReleaseStringUTFChars(string , str); 185 | // std::string dllpath = env->NewStringUTF(cap); 186 | std::string cwd = SCCH_fs::current_path(); 187 | cwd += "/"; 188 | std::cout<<"dll path :"< 13 | #include 14 | #include "dynamicLinker/dynamicLinker.hpp" 15 | 16 | // getcwd on Linux 17 | #include 18 | 19 | // PATH_MAX on Linux 20 | #include 21 | 22 | #if not defined PATH_MAX 23 | #warning Is this GNU/Hurd? Then this code could occupy a lot of memory. 24 | #include 25 | #define PATH_MAX FILENAME_MAX 26 | #endif 27 | 28 | #if defined(__APPLE__) 29 | std::string coreClrDll = "libcoreclr.dylib"; 30 | #else 31 | std::string coreClrDll = "libcoreclr.so"; 32 | #endif 33 | 34 | class myClass { 35 | private: 36 | int value; 37 | public: 38 | void question() { value = 42; } 39 | void print() { std::cout << "Value: " << value << std::endl; } 40 | }; 41 | 42 | typedef void (csharp_runIt_t)( myClass&, std::mem_fun_ref_t ); 43 | -------------------------------------------------------------------------------- /Managed.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Hubert Jarosz. All rights reserved. 3 | * Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | */ 5 | 6 | using System; 7 | using System.Runtime.InteropServices; 8 | 9 | public class Managed { 10 | 11 | [UnmanagedFunctionPointer(CallingConvention.ThisCall)] 12 | unsafe delegate void myDelegate( IntPtr thisptr ); 13 | 14 | public static unsafe void runIt( IntPtr thisptr, IntPtr mem_fun ) { 15 | Console.WriteLine("Here's C# code:"); 16 | 17 | myDelegate fun = (myDelegate) Marshal.GetDelegateForFunctionPointer( mem_fun, typeof(myDelegate) ); 18 | 19 | fun(thisptr); // first argument of member functions in C++ is "this", but it's hidden from us :-) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Managed.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | library 4 | netcoreapp2.2 5 | true 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://dev.azure.com/marcozo0520/JavaCallCSharp/_apis/build/status/eisber.JavaCallCSharp?branchName=master)](https://dev.azure.com/marcozo0520/JavaCallCSharp/_build/latest?definitionId=1?branchName=master) 2 | 3 | 4 | # JavaCallCSharp 5 | Java call C# lib build with .NET CORE 2.0 via C++ as wraper 6 | The code is based on [example from coreCLR](https://github.com/dotnet/coreclr/tree/master/src/coreclr/hosts/unixcoreruncommon) 7 | 8 | Java using JNI to call C++ code. then C++ host a core CLR to run C# code. 9 | # pre-require 10 | 11 | .NET CORE SDK 2.0: I only tested it in Ubuntu 18.04 x64 (Follow https://dotnet.microsoft.com/download/linux-package-manager/ubuntu18-04/sdk-current) 12 | 13 | gcc 6: std C++ 14 for the filesystem 14 | 15 | # usage: 16 | git submodule init 17 | git submodule update 18 | 19 | cmake . 20 | cmake --build . 21 | 22 | java -cp sample1.jar Sample1 23 | 24 | 25 | `You should keep the compiled file in the same fold.` 26 | -------------------------------------------------------------------------------- /Sample1.cpp: -------------------------------------------------------------------------------- 1 | #include "Sample1.h" 2 | #include 3 | #include 4 | JNIEXPORT jint JNICALL Java_Sample1_intMethod 5 | (JNIEnv *env, jobject obj, jint num){ 6 | return num * num; 7 | } 8 | 9 | JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod 10 | (JNIEnv *env, jobject obj, jboolean boolean){ 11 | return !boolean; 12 | } 13 | 14 | JNIEXPORT jstring JNICALL Java_Sample1_stringMethod 15 | (JNIEnv *env, jobject obj, jstring string){ 16 | const char *str = env->GetStringUTFChars(string,0); 17 | char cap[128]; 18 | strcpy(cap,str); 19 | env->ReleaseStringUTFChars(string , str); 20 | return env->NewStringUTF(cap); 21 | } 22 | 23 | JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod 24 | (JNIEnv *env, jobject obj, jintArray array){ 25 | int i, sum=0; 26 | jsize len = env->GetArrayLength(array); 27 | jint *body = env ->GetIntArrayElements(array,0); 28 | for (i=0;iReleaseIntArrayElements(array, body,0); 34 | return sum; 35 | } 36 | -------------------------------------------------------------------------------- /Sample1.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class Sample1 */ 4 | 5 | #ifndef _Included_Sample1 6 | #define _Included_Sample1 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: Sample1 12 | * Method: intMethod 13 | * Signature: (I)I 14 | */ 15 | JNIEXPORT jint JNICALL Java_Sample1_intMethod 16 | (JNIEnv *, jobject, jint); 17 | 18 | /* 19 | * Class: Sample1 20 | * Method: booleanMethod 21 | * Signature: (Z)Z 22 | */ 23 | JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod 24 | (JNIEnv *, jobject, jboolean); 25 | 26 | /* 27 | * Class: Sample1 28 | * Method: stringMethod 29 | * Signature: (Ljava/lang/String;)Ljava/lang/String; 30 | */ 31 | JNIEXPORT jstring JNICALL Java_Sample1_stringMethod 32 | (JNIEnv *, jobject, jstring); 33 | 34 | /* 35 | * Class: Sample1 36 | * Method: intArrayMethod 37 | * Signature: ([I)I 38 | */ 39 | JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod 40 | (JNIEnv *, jobject, jintArray); 41 | 42 | /* 43 | * Class: Sample1 44 | * Method: coreClrHost 45 | * Signature: (Ljava/lang/String;)I 46 | */ 47 | JNIEXPORT jint JNICALL Java_Sample1_coreClrHost 48 | (JNIEnv *, jobject, jstring); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | #endif 54 | -------------------------------------------------------------------------------- /Sample1.java: -------------------------------------------------------------------------------- 1 | public class Sample1 2 | { 3 | public native int intMethod(int n); 4 | public native boolean booleanMethod(boolean bool); 5 | public native String stringMethod(String text); 6 | public native int intArrayMethod(int[] intArray); 7 | public native int coreClrHost(String dllpath); 8 | public static void main(String[] args) 9 | { 10 | //System.loadLibrary("Sample1"); 11 | System.load(System.getProperty("user.dir") + java.io.File.separator + "libtar.so"); 12 | 13 | Sample1 sample = new Sample1(); 14 | int square = sample.intMethod(5); 15 | boolean bool = sample.booleanMethod(true); 16 | String text = sample.stringMethod("JAVA"); 17 | int sum = sample.intArrayMethod(new int[]{1,1,2,3,5,8,13}); 18 | 19 | int success=0; 20 | success= sample.coreClrHost("./bin/Debug/netcoreapp2.2/Managed.dll"); 21 | System.out.println("intMethod: "+ square); 22 | System.out.println("boolMethod: "+ bool); 23 | System.out.println("stringMethod: "+ text); 24 | System.out.println("intArrayMethod: "+ sum); 25 | System.out.println("status:"+success); 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: build.yml 3 | parameters: 4 | name: macOS 5 | pool: 6 | vmImage: 'macOS-10.13' 7 | 8 | - template: build.yml 9 | parameters: 10 | name: Linux 11 | pool: 12 | vmImage: 'Ubuntu-16.04' 13 | 14 | # - template: build.yml 15 | # parameters: 16 | # name: Windows 17 | # pool: 18 | # vmImage: 'vs2017-win2016' 19 | -------------------------------------------------------------------------------- /build.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | 3 | - job: ${{ parameters.name }} 4 | pool: ${{ parameters.pool }} 5 | 6 | steps: 7 | - checkout: self 8 | submodules: true 9 | - script: | 10 | cmake . 11 | cmake --build . 12 | java -cp sample1.jar Sample1 13 | displayName: 'Build & run' 14 | -------------------------------------------------------------------------------- /utils.hpp: -------------------------------------------------------------------------------- 1 | //#pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // Prototype of the coreclr_initialize function from the libcoreclr.so 9 | typedef int (coreclrInitializeFunction)( 10 | const char* exePath, 11 | const char* appDomainFriendlyName, 12 | int propertyCount, 13 | const char** propertyKeys, 14 | const char** propertyValues, 15 | void** hostHandle, 16 | unsigned int* domainId); 17 | 18 | // Prototype of the coreclr_shutdown function from the libcoreclr.so 19 | typedef int (coreclrShutdownFunction)( 20 | void* hostHandle, 21 | unsigned int domainId); 22 | 23 | // Prototype of the coreclr_execute_assembly function from the libcoreclr.so 24 | typedef int (coreclrCreateDelegateFunction)( 25 | void* hostHandle, 26 | unsigned int domainId, 27 | const char* entryPointAssemblyName, 28 | const char* entryPointTypeName, 29 | const char* entryPointMethodName, 30 | void** delegate); 31 | 32 | 33 | //#if not defined ( __GNUC__ ) || __GNUC__ < 5 || ( __GNUC__ == 5 && __GNUC_MINOR__ < 3 ) 34 | // #error THIS SOFTWARE CURRENTLY BUILDS ONLY ON GCC 5.3 OR NEWER! 35 | //#endif 36 | 37 | #include 38 | namespace SCCH_fs = std::experimental::filesystem; 39 | 40 | void AddFilesFromDirectoryToTpaList( std::string directory, std::string& tpaList ) { 41 | 42 | for ( auto& dirent : SCCH_fs::directory_iterator(directory) ) { 43 | std::string path = dirent.path(); 44 | 45 | if ( ! path.compare(path.length() - 4, 4, ".dll") ) { 46 | tpaList.append(path + ":"); 47 | } 48 | } 49 | 50 | } 51 | --------------------------------------------------------------------------------