From ad1b37dc64edd734764debabead611cf37c932ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sat, 23 Apr 2022 01:15:33 +0200 Subject: [PATCH] Some more dotnet stuff --- GlitchyEditor/src/Editor.bf | 82 ++++++++++++++- GlitchyEngineHelper/BeefProj.toml | 2 +- GlitchyEngineHelper/DotNetRuntime.cpp | 121 ++++++++++++++++++----- GlitchyEngineHelper/src/DotNetRuntime.bf | 66 ++++++++++++- 4 files changed, 240 insertions(+), 31 deletions(-) diff --git a/GlitchyEditor/src/Editor.bf b/GlitchyEditor/src/Editor.bf index e9ad866..82ec8bd 100644 --- a/GlitchyEditor/src/Editor.bf +++ b/GlitchyEditor/src/Editor.bf @@ -27,16 +27,94 @@ namespace GlitchyEditor public ComponentEditWindow ComponentEditWindow => _componentEditWindow; public SceneViewportWindow SceneViewportWindow => _sceneViewportWindow; + [CRepr] + struct lib_args + { + public char16* Message; + public int32 number; + }; + + [CRepr] + struct CreateInstanceArgs + { + //public StringView TypeName; + public char8* TypeName; + public int TypeNameLength; + }; + + struct ManagedReference; + + public typealias CreateInstanceDelegate = function [CallingConvention(.Stdcall)] ManagedReference*(CreateInstanceArgs* arg, int32 arg_size_in_bytes); + + public typealias FreeInstanceDelegate = function [CallingConvention(.Stdcall)] void(ManagedReference* arg); + + public typealias InstanceMethodDelegate = function [CallingConvention(.Stdcall)] void(ManagedReference* arg); + /// Creates a new editor for the given world public this(Scene scene) { String str = @"D:\Development\Projects\Beef\GlitchyEngine\build\Debug_Win64\GlitchyEditor\"; + //char16* ptr = str.ToScopedNativeWChar!(); - char16* ptr = str.ToScopedNativeWChar!(); + String runtimeConfig = scope String(str, "DotNetTest.runtimeconfig.json"); + String assemblyPath = scope String(str, "DotNetTest.dll"); DotNetRuntime.Init(); - DotNetRuntime.TestRunDotNet(1, &ptr); + DotNetRuntime.LoadRuntime(runtimeConfig); + + + + DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.Lib, DotNetTest", "Hello", let hello); + DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.ScriptableEntity, DotNetTest", "Hello2", let hello2); + + + DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "Test", let test); + + CreateInstanceDelegate createInstance; + //DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "CreateInstance", "DotNetTest.InteropHelper+CreateInstanceEntryPoint, DotNetTest", out createInstance); + DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "CreateInstance", out createInstance); + + FreeInstanceDelegate freeInstance; + //DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "FreeInstance", "DotNetTest.InteropHelper+FreeInstanceEntryPoint, DotNetTest", out freeInstance); + DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "FreeInstance", out freeInstance); + + InstanceMethodDelegate update; + //DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.ScriptableEntity, DotNetTest", "InvokeUpdate", "DotNetTest.ScriptableEntity+InstanceMethodEntryPoint, DotNetTest", out update); + DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.ScriptableEntity, DotNetTest", "InvokeUpdate", out update); + + //DotNetRuntime.TestRunDotNet(1, &ptr); + + lib_args args = . + { + Message = "from host!".ToScopedNativeWChar!(), + number = 1337 + }; + + hello(&args, sizeof(lib_args)); + + hello2(&args, sizeof(lib_args)); + + test(null, 0); + + String typeName = "DotNetTest.ScriptableEntity, DotNetTest"; + + CreateInstanceArgs instanceArgs = . + { + TypeName = typeName.Ptr, + TypeNameLength = typeName.Length + }; + + ManagedReference* instance = createInstance(&instanceArgs, sizeof(CreateInstanceArgs)); + + for (int i < 10) + { + update(instance); + } + + freeInstance(instance); + + DotNetRuntime.Deinit(); _scene = scene; _ecsWorld = _scene.[Friend]_ecsWorld; diff --git a/GlitchyEngineHelper/BeefProj.toml b/GlitchyEngineHelper/BeefProj.toml index 8c18bb1..cf2ed81 100644 --- a/GlitchyEngineHelper/BeefProj.toml +++ b/GlitchyEngineHelper/BeefProj.toml @@ -9,5 +9,5 @@ StartupObject = "GlitchyEngineHelper.Program" [Configs.Debug.Win64] BuildCommandsOnCompile = "IfFilesChanged" BuildCommandsOnRun = "IfFilesChanged" -LibPaths = ["$(ProjectDir)/out/build/x64-debug/GlitchyEngineHelper.lib", "D:\\Development\\Projects\\Beef\\GlitchyEngine\\GlitchyEngineHelper\\vendor\\dotnethost\\nethost.lib"] +LibPaths = ["$(ProjectDir)/out/build/x64-debug/GlitchyEngineHelper.lib"] PreBuildCmds = ["$(ProjectDir)\\..\\bin\\vscmake.bat x64 $(ProjectDir) x64-debug", "CopyToDependents(\"$(ProjectDir)/out/build/x64-debug/*.dll\")"] diff --git a/GlitchyEngineHelper/DotNetRuntime.cpp b/GlitchyEngineHelper/DotNetRuntime.cpp index 86cd76e..d5798df 100644 --- a/GlitchyEngineHelper/DotNetRuntime.cpp +++ b/GlitchyEngineHelper/DotNetRuntime.cpp @@ -48,20 +48,43 @@ namespace hostfxr_get_runtime_delegate_fn get_delegate_fptr; hostfxr_close_fn close_fptr; + load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer; + + hostfxr_handle hostContext; + // Forward declarations bool load_hostfxr(); load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* assembly); } -GE_EXPORT int GE_CALLTYPE DotNet_Init() +struct lib_args { - if (!load_hostfxr()) - { - assert(false && "Failure: load_hostfxr()"); - return EXIT_FAILURE; - } + const char_t* message; + int number; +}; - return 0; +typedef void (CORECLR_DELEGATE_CALLTYPE* custom_entry_point_fn)(lib_args args); + +custom_entry_point_fn GetFunctionPointer(load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer, const string_t& dotnetlib_path, const char_t* dotnet_type, const char_t* method_name) +{ + // Function pointer to managed delegate with non-default signature + custom_entry_point_fn custom = nullptr; + int rc = load_assembly_and_get_function_pointer( + dotnetlib_path.c_str(), + dotnet_type, + STR("CustomEntryPointUnmanaged") /*method_name*/, + UNMANAGEDCALLERSONLY_METHOD, + nullptr, + (void**)&custom); + assert(rc == 0 && custom != nullptr && "Failure: load_assembly_and_get_function_pointer()"); + + return custom; +} + +void LoadRuntime(const char_t* configPath) +{ + load_assembly_and_get_function_pointer = get_dotnet_load_assembly(configPath); + assert(load_assembly_and_get_function_pointer != nullptr && "Failure: get_dotnet_load_assembly()"); } GE_EXPORT int GE_CALLTYPE TestRunDotNet(int argc, wchar_t* argv[]) @@ -121,11 +144,11 @@ GE_EXPORT int GE_CALLTYPE TestRunDotNet(int argc, wchar_t* argv[]) // // STEP 4: Run managed code // - struct lib_args + /*struct lib_args { const char_t* message; int number; - }; + };*/ for (int i = 0; i < 3; ++i) { // @@ -140,18 +163,24 @@ GE_EXPORT int GE_CALLTYPE TestRunDotNet(int argc, wchar_t* argv[]) } // Function pointer to managed delegate with non-default signature - typedef void (CORECLR_DELEGATE_CALLTYPE* custom_entry_point_fn)(lib_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**)&custom); - assert(rc == 0 && custom != nullptr && "Failure: load_assembly_and_get_function_pointer()"); + //typedef void (CORECLR_DELEGATE_CALLTYPE* custom_entry_point_fn)(lib_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**)&custom); + //assert(rc == 0 && custom != nullptr && "Failure: load_assembly_and_get_function_pointer()"); - lib_args args + custom_entry_point_fn custom = GetFunctionPointer( + load_assembly_and_get_function_pointer, + dotnetlib_path, + dotnet_type, + STR("CustomEntryPointUnmanaged") /*method_name*/); + + lib_args args { STR("from host!"), -1 @@ -227,25 +256,65 @@ namespace { // Load .NET Core void* load_assembly_and_get_function_pointer = nullptr; - hostfxr_handle cxt = nullptr; - int rc = init_fptr(config_path, nullptr, &cxt); - if (rc != 0 || cxt == nullptr) + hostContext = nullptr; + int rc = init_fptr(config_path, nullptr, &hostContext); + if (rc != 0 || hostContext == nullptr) { std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl; - close_fptr(cxt); + close_fptr(hostContext); return nullptr; } // Get the load assembly function pointer rc = get_delegate_fptr( - cxt, + hostContext, hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer); if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl; - close_fptr(cxt); + // TODO: close_fptr(hostContext); ??? return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer; } // } + + +GE_EXPORT int GE_CALLTYPE DotNet_Init() +{ + if (!load_hostfxr()) + { + assert(false && "Failure: load_hostfxr()"); + return EXIT_FAILURE; + } + + return 0; +} + +GE_EXPORT int GE_CALLTYPE DotNet_Deinit() +{ + int result = close_fptr(hostContext); + hostContext = nullptr; + + return result; +} + +GE_EXPORT void GE_CALLTYPE DotNet_LoadRuntime(const char_t* configPath) +{ + LoadRuntime(configPath); +} + +GE_EXPORT int GE_CALLTYPE DotNet_LoadAssemblyAndGetFunctionPointer(const char_t* assemblyPath, const char_t* typeName, const char_t* methodName, const char_t* delegateTypeName, void** outDelegate) +{ + int rc = load_assembly_and_get_function_pointer( + assemblyPath, + typeName, + methodName, + delegateTypeName, + nullptr, + outDelegate); + + assert(rc == 0 && outDelegate != nullptr && "Failure: load_assembly_and_get_function_pointer()"); + + return rc; +} diff --git a/GlitchyEngineHelper/src/DotNetRuntime.bf b/GlitchyEngineHelper/src/DotNetRuntime.bf index 839acf8..d32e0a0 100644 --- a/GlitchyEngineHelper/src/DotNetRuntime.bf +++ b/GlitchyEngineHelper/src/DotNetRuntime.bf @@ -1,13 +1,75 @@ using System; using System.Interop; + namespace GlitchyEngineHelper { static class DotNetRuntime { [LinkName("TestRunDotNet"), CallingConvention(.Cdecl)] - public static extern int TestRunDotNet(c_int argc, c_wchar** argv); + public static extern c_int TestRunDotNet(c_int argc, c_wchar** argv); [LinkName("DotNet_Init"), CallingConvention(.Cdecl)] - public static extern int Init(); + public static extern c_int Init(); + + [LinkName("DotNet_Deinit"), CallingConvention(.Cdecl)] + public static extern c_int Deinit(); + + [LinkName("DotNet_LoadRuntime"), CallingConvention(.Cdecl)] +#if BF_PLATFORM_WINDOWS + private static extern void Internal_LoadRuntime(char16* configPath); +#else + private static extern void Internal_LoadRuntime(char8* configPath); +#endif + + public static void LoadRuntime(StringView configPath) + { +#if BF_PLATFORM_WINDOWS + Internal_LoadRuntime(configPath.ToScopedNativeWChar!()); +#else + Internal_LoadRuntime(configPath.Ptr); +#endif + } + + [LinkName("DotNet_LoadAssemblyAndGetFunctionPointer"), CallingConvention(.Cdecl)] +#if BF_PLATFORM_WINDOWS + private static extern c_int DotNet_LoadAssemblyAndGetFunctionPointer(char16* assemblyPath, char16* typeName, char16* methodName, char16* delegateName, void** outDelegate); +#else + private static extern c_int DotNet_LoadAssemblyAndGetFunctionPointer(char8* assemblyPath, char8* typeName, char8* methodName, char8* delegateName, void** outDelegate); +#endif + + public typealias ManagedDelegate = function int32(void *arg, int32 arg_size_in_bytes); + + public static c_int LoadAssemblyAndGetFunctionPointer(StringView assemblyPath, StringView typeName, StringView methodName, out ManagedDelegate outDelegate) + { + outDelegate = ?; + +#if BF_PLATFORM_WINDOWS + return DotNet_LoadAssemblyAndGetFunctionPointer(assemblyPath.ToScopedNativeWChar!(), typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), null, (void**)&outDelegate); +#else + +#endif + } + + public static c_int LoadAssemblyAndGetFunctionPointer(StringView assemblyPath, StringView typeName, StringView methodName, StringView delegateName, out T outDelegate) where T : var + { + outDelegate = ?; + +#if BF_PLATFORM_WINDOWS + return DotNet_LoadAssemblyAndGetFunctionPointer(assemblyPath.ToScopedNativeWChar!(), typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), delegateName.ToScopedNativeWChar!(), (void**)&outDelegate); +#else + +#endif + } + + public static c_int LoadAssemblyAndGetFunctionPointer(StringView assemblyPath, StringView typeName, StringView methodName, out T outDelegate) where T : var + { + outDelegate = ?; + +#if BF_PLATFORM_WINDOWS + return DotNet_LoadAssemblyAndGetFunctionPointer(assemblyPath.ToScopedNativeWChar!(), typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), (char16*)(void*)-1, (void**)&outDelegate); +#else + +#endif + } } } \ No newline at end of file