Created
February 4, 2022 16:22
-
-
Save Ohjurot/a10dceea7da1e957003e0405c8594553 to your computer and use it in GitHub Desktop.
C# .NET 6 Embedding in c++ (Portable version - Build your own runtime and distribute the shared folder)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"runtimeOptions": { | |
"tfm": "net6.0", | |
"framework": { | |
"name": "Microsoft.NETCore.App", | |
"version": "6.0.0" | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Runtime.InteropServices; | |
namespace CsCode | |
{ | |
public static class CsMain | |
{ | |
[UnmanagedCallersOnly] | |
public static int Test(IntPtr str) | |
{ | |
int result = 0; | |
String csstr = ""; | |
if (str.ToInt64() != 0) | |
{ | |
String t_csstr = Marshal.PtrToStringAnsi(str); | |
if(t_csstr != null) | |
{ | |
if (t_csstr.Length != 0) | |
{ | |
csstr = t_csstr; | |
result = 1; | |
} | |
} | |
} | |
Console.WriteLine(csstr); | |
return result; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define NOMINMAX | |
#define WIN32_LEAN_AND_MEAN | |
#include <Windows.h> | |
#include <iostream> | |
#include <hostfxr.h> | |
#include <coreclr_delegates.h> | |
#define HOSTFXR_PATH_LEN 1024 | |
struct HostFxr | |
{ | |
// Functions | |
inline int32_t initializeForRuntimeConfig( | |
const char_t* runtime_config_path, | |
const hostfxr_initialize_parameters* parameters, | |
hostfxr_handle* host_context_handle) | |
{ | |
return _initialize_for_runtime_config(runtime_config_path, parameters, host_context_handle); | |
} | |
inline int32_t getRuntimeDelegate( | |
const hostfxr_handle host_context_handle, | |
hostfxr_delegate_type type, | |
void** delegate) | |
{ | |
return _get_runtime_delegate(host_context_handle, type, delegate); | |
} | |
inline int32_t close( | |
const hostfxr_handle host_context_handle) | |
{ | |
return _close(host_context_handle); | |
} | |
inline int loadAssemblyAndGetFunctionPointer( | |
const char_t* assembly_path, | |
const char_t* type_name, | |
const char_t* method_name, | |
const char_t* delegate_type_name, | |
void* reserved, | |
void** delegate) | |
{ | |
return _load_assembly_and_get_function_pointer(assembly_path, type_name, method_name, delegate_type_name, reserved, delegate); | |
} | |
// FPTR-TABLE | |
hostfxr_initialize_for_runtime_config_fn _initialize_for_runtime_config = nullptr; | |
hostfxr_get_runtime_delegate_fn _get_runtime_delegate = nullptr; | |
hostfxr_close_fn _close = nullptr; | |
load_assembly_and_get_function_pointer_fn _load_assembly_and_get_function_pointer = nullptr; | |
}; | |
int main() | |
{ | |
const wchar_t* fxrPath = L"./hostfxr.dll"; | |
SetEnvironmentVariable(L"DOTNET_MULTILEVEL_LOOKUP", L"0"); // Prevent .NET from loading an installed runtime aka. force the embeded one | |
// Load FXR | |
HMODULE lib = LoadLibrary(fxrPath); | |
if (lib != NULL) | |
{ | |
std::cout << "loaded hostfxr.dll module" << std::endl; | |
// Init host fxr | |
HostFxr hostFxr; | |
hostFxr._initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)GetProcAddress(lib, "hostfxr_initialize_for_runtime_config"); | |
hostFxr._get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)GetProcAddress(lib, "hostfxr_get_runtime_delegate"); | |
hostFxr._close = (hostfxr_close_fn)GetProcAddress(lib, "hostfxr_close"); | |
// Check if host fxr loaded | |
if (hostFxr._initialize_for_runtime_config && hostFxr._get_runtime_delegate && hostFxr._close) | |
{ | |
std::cout << "loaded hostfxr.dll functions" << std::endl; | |
// Get module file name | |
WCHAR exeFile[1024]; | |
GetModuleFileName(NULL, exeFile, 1024); | |
// Execution dir | |
WCHAR workDir[1024]; | |
GetCurrentDirectory(1024, workDir); | |
// Describe runtime path | |
hostfxr_initialize_parameters fxrParams; | |
fxrParams.size = sizeof(hostfxr_initialize_parameters); | |
fxrParams.host_path = exeFile; // The host of the application (not required) | |
fxrParams.dotnet_root = workDir; // I have builded and installed a custom .NET runtime to make this app portable | |
// Create context | |
hostfxr_handle ctx = nullptr; | |
if (hostFxr.initializeForRuntimeConfig(L"./conf.runtimeconfig.json", &fxrParams, &ctx) == 0) | |
{ | |
std::cout << "loaded .NETCore" << std::endl; | |
// Get hdt_load_assembly_and_get_function_pointer | |
if (hostFxr.getRuntimeDelegate(ctx, hdt_load_assembly_and_get_function_pointer, (void**)&hostFxr._load_assembly_and_get_function_pointer) == 0) | |
{ | |
std::cout << "loaded hdt_load_assembly_and_get_function_pointer" << std::endl; | |
// Build assembly path | |
WCHAR dllFile[1024]; | |
wcscpy_s<1024>(dllFile, exeFile); | |
auto* lsts = wcsrchr(dllFile, L'\\'); | |
if (lsts) *lsts = L'\0'; | |
wcscat_s<1024>(dllFile, L"\\CsCode.dll"); | |
// Load the assembly and get function | |
typedef int(*csFunction)(const char*); | |
csFunction ptrFuc = nullptr; | |
if (hostFxr.loadAssemblyAndGetFunctionPointer( | |
dllFile, | |
L"CsCode.CsMain, CsCode", | |
L"Test", | |
UNMANAGEDCALLERSONLY_METHOD, | |
nullptr, | |
(void**)&ptrFuc) == 0) | |
{ | |
std::cout << "loaded CsCode.CsMain.ComponentEntryPoint" << std::endl; | |
std::cout << "Calling CS: "; | |
int result = ptrFuc("Hello World!"); | |
std::cout << "CS Returned: #" << result << std::endl; | |
} | |
int i = 0; | |
} | |
// Free context | |
hostFxr.close(ctx); | |
} | |
} | |
// Close FXR | |
FreeLibrary(lib); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment