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 }