1 module app; 2 3 import std.conv; 4 import std.stdio; 5 import std.string; 6 import std.exception; 7 import std.file : thisExePath; 8 9 import nethost; 10 import hostfxr; 11 import coreclr_delegates; 12 13 hostfxr_initialize_for_runtime_config_fn init_fptr; 14 hostfxr_get_runtime_delegate_fn get_delegate_fptr; 15 hostfxr_close_fn close_fptr; 16 17 void main() 18 { 19 // STEP 1: Load HostFxr and get exported hosting functions 20 enforce(loadHostFXR(), "Failed to load hostfxr"); 21 22 string libname = "DotNetLib"; 23 string fw = "net6.0"; 24 25 // STEP 2: Initialize and start the .NET Core runtime 26 string configpath = libname ~ "/bin/Debug/" ~ fw ~ "/" ~ libname ~ ".runtimeconfig.json"; 27 load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = null; 28 load_assembly_and_get_function_pointer = getDotnetLoadAssembly(configpath.toStringz); 29 assert(load_assembly_and_get_function_pointer, "Failure: get_dotnet_load_assembly()"); 30 31 // STEP 3: Load managed assembly and get function pointer to a managed method 32 component_entry_point_fn hello = null; 33 string dotnetlibpath = libname ~ "/bin/Debug/" ~ fw ~ "/" ~ libname ~ ".dll"; 34 string dotnettype = "DotNetLib.Lib, DotNetLib"; 35 string dotnettypemethod = "Hello"; 36 int rc = load_assembly_and_get_function_pointer( 37 dotnetlibpath.toStringz, 38 dotnettype.toStringz, 39 dotnettypemethod.toStringz, 40 null, 41 null, 42 cast(void**)&hello); 43 assert(rc == 0 && hello, "Failure: load_assembly_and_get_function_pointer()"); 44 45 struct lib_args 46 { 47 const char_t* message; 48 int number; 49 } 50 51 for (int i = 0; i < 3; ++i) 52 { 53 lib_args args = {"Hello from D", i}; 54 hello(&args, args.sizeof); 55 } 56 57 // TODO: 58 /* 59 #ifdef NET5_0 60 // Function pointer to managed delegate with non-default signature 61 typedef void (CORECLR_DELEGATE_CALLTYPE *custom_entry_point_fn)(lib_args args); 62 custom_entry_point_fn custom = nullptr; 63 rc = load_assembly_and_get_function_pointer( 64 dotnetlib_path.c_str(), 65 dotnet_type, 66 STR("CustomEntryPointUnmanaged"), 67 UNMANAGEDCALLERSONLY_METHOD, 68 nullptr, 69 (void**)&custom); 70 assert(rc == 0 && custom != nullptr && "Failure: load_assembly_and_get_function_pointer()"); 71 #else 72 // Function pointer to managed delegate with non-default signature 73 typedef void (CORECLR_DELEGATE_CALLTYPE *custom_entry_point_fn)(lib_args args); 74 custom_entry_point_fn custom = nullptr; 75 rc = load_assembly_and_get_function_pointer( 76 dotnetlib_path.c_str(), 77 dotnet_type, 78 STR("CustomEntryPoint"), 79 STR("DotNetLib.Lib+CustomEntryPointDelegate, DotNetLib"), 80 nullptr, 81 (void**)&custom); 82 assert(rc == 0 && custom != nullptr && "Failure: load_assembly_and_get_function_pointer()"); 83 #endif 84 85 lib_args args 86 { 87 STR("from host!"), 88 -1 89 }; 90 custom(args); 91 */ 92 } 93 94 hostfxr_initialize_parameters runtime_params; 95 // Using the nethost library, discover the location of hostfxr and get exports 96 bool loadHostFXR() 97 { 98 // Pre-allocate a large buffer for the path to hostfxr 99 char_t[1024] bf; 100 size_t bfsize = bf.sizeof / char_t.sizeof; 101 102 // For whatever reason, this is needed, unlike in the C++ example 103 // Without it, it'll compain about no runtimes being found 104 string libpath = "/usr/share/dotnet"; 105 immutable char_t* exec_path = thisExePath().toStringz; 106 runtime_params = hostfxr_initialize_parameters(hostfxr_initialize_parameters.sizeof, exec_path, "/usr/share/dotnet"); 107 108 int rc = get_hostfxr_path(cast(char*) bf, &bfsize, null); 109 if (rc) 110 return false; 111 112 // Load hostfxr and get desired exports 113 void* lib = load_library(cast(char*) bf); 114 init_fptr = cast(hostfxr_initialize_for_runtime_config_fn) get_export(lib, "hostfxr_initialize_for_runtime_config"); 115 get_delegate_fptr = cast(hostfxr_get_runtime_delegate_fn) get_export(lib, "hostfxr_get_runtime_delegate"); 116 close_fptr = cast(hostfxr_close_fn) get_export(lib, "hostfxr_close"); 117 118 return init_fptr && get_delegate_fptr && close_fptr; 119 } 120 121 // Load and initialize .NET Core and get desired function pointer for scenario 122 load_assembly_and_get_function_pointer_fn getDotnetLoadAssembly(const char_t* config_path) 123 { 124 // Load .NET Core 125 void* load_assembly_and_get_function_pointer = null; 126 hostfxr_handle ctx = null; 127 int rc = init_fptr(config_path, &runtime_params, &ctx); 128 if (rc || ctx is null) 129 { 130 stderr.writeln("Init failed: " ~ rc.to!string); 131 close_fptr(ctx); 132 return null; 133 } 134 135 // Get the load assembly function pointer 136 rc = get_delegate_fptr( 137 ctx, 138 hostfxr_delegate_type.hdt_load_assembly_and_get_function_pointer, 139 &load_assembly_and_get_function_pointer); 140 141 if (rc || load_assembly_and_get_function_pointer) 142 stderr.writeln("Get delegate failed: " ~ rc.to!string); 143 144 close_fptr(ctx); 145 return cast(load_assembly_and_get_function_pointer_fn) load_assembly_and_get_function_pointer; 146 } 147 148 void* load_library(const char_t* path) 149 { 150 version (Posix) 151 { 152 import core.sys.posix.dlfcn; 153 154 void* h = dlopen(path, RTLD_LAZY | RTLD_LOCAL); 155 assert(h); 156 return h; 157 } 158 else version (Windows) 159 { 160 import core.sys.windows.windows; 161 162 void* h = LoadLibraryW(path); 163 assert(h); 164 return h; 165 } 166 } 167 168 void* get_export(void* h, const char_t* path) 169 { 170 version (Posix) 171 { 172 import core.sys.posix.dlfcn; 173 174 void* f = dlsym(h, path); 175 assert(f); 176 return f; 177 } 178 else version (Windows) 179 { 180 import core.sys.windows.windows; 181 182 void* f = GetProcAddress(h, path); 183 assert(f); 184 return f; 185 } 186 }