Possible to use `IInitializeWithWindow` (or alternative) to load UI from .NET `.dll` into `HINSTANCE`/`HWND` of window provided by parent `.exe`?
See original GitHub issueHeya, this might sound a bit convoluted but I’d really like to use .NET to write the UI for a plugin, that will be loaded as a .dll
from a closed-source parent .exe
.
The host application loads the plugin DLL and calls a named entrypoint function, it passes an hinstance
/hwnd
to a window it’s allocated for the plugin to use as an arg.
I was thinking of adding an [UnmanagedCallersOnly]
attribute to a function in .NET, and using Dotnet Native Exports (https://github.com/AaronRobinsonMSFT/DNNE) and compiling as a shared library + using the assembly directly:
[UnmanagedCallersOnlyAttribute(EntryPoint = "InitDll")]
public static int InitDll(IntPtr hwnd)
{
// initialize the WinUI application here, use the hwnd that was passed with IInitializeWithWindow
}
The other alternative that I was thinking might be possible was using the nethost
and hostfxr
libraries to write a small C++ app to work as a middleman to load + initialize the CLR and the WinUI assembly, and then act as the entrypoint used to start the app:
Parent .exe --> Loads MyAppWrapper.dll --> uses nethost.dll and hostfxr.dll to load MyAppAssembly.dll
--> initializes the CLR and calls the entrypoint method of the .NET app in C/C++ exported function
--> Entrypoint of .NET app takes an "IntPtr HWND" and uses this to set the render handle target
#define DLL_EXPORT __declspec(dllexport)
extern "C"
{
// Entrypoint function called by host .exe when .dll is loaded
DLL_EXPORT int InitDll(HWND hwnd) {
load_hostfxr();
// Initialize and start the .NET Core runtime
const string_t config_path = root_path + STR("MyAppAssembly.runtimeconfig.json");
load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = nullptr;
load_assembly_and_get_function_pointer = get_dotnet_load_assembly(config_path.c_str());
// Load app assembly, that has an "UnmanagedCallersOnly" exported entrypoint function taking "IntPtr hwnd"
const string_t dotnetlib_path = root_path + STR("MyAppAssembly.dll");
const char_t *dotnet_type = STR("MyAppAssembly.Lib, MyAppAssembly");
struct dotnet_managed_entrypoint_args { HWND hwnd };
typedef void (CORECLR_DELEGATE_CALLTYPE *custom_entry_point_fn)(dotnet_managed_entrypoint_args args);
custom_entry_point_fn custom = nullptr;
rc = load_assembly_and_get_function_pointer(
dotnetlib_path.c_str(),
dotnet_type,
STR("CustomEntryPointUnmanaged") /*method_name*/,
UNMANAGEDCALLERSONLY_METHOD,
nullptr,
(void**)&winui_app_entrypoint_fn);
// Call .NET entrypoint, give it the HWND handle
dotnet_managed_entrypoint_args args{ .hwnd = hwnd };
winui_app_entrypoint_fn(args);
return EXIT_SUCCESS;
}
}
Would be grateful for any wisdom on this, I don’t know very much about .NET Thank you! 😃
Issue Analytics
- State:
- Created 2 years ago
- Comments:7 (3 by maintainers)
Top GitHub Comments
This kind of scenario could be used for VST plugins for Digital Audio Workstation software
If you use NativeAOT or LLVM NativeAOT for .NET and there’s no dynamically loaded runtime, then is there still an issue?
Or could you also using Mono and
mkbundle
to statically embed the entire runtime? https://www.mono-project.com/docs/tools+libraries/tools/mkbundle https://www.mono-project.com/docs/advanced/runtime/#bundles