From 711da7f63659b5db97fb0cd91c2af6a69922e7a6 Mon Sep 17 00:00:00 2001 From: Emmanuel AYME Date: Fri, 27 Feb 2026 22:50:18 +0100 Subject: [PATCH] Add methods to the SDK --- libs/Unity/MonoLoader.cpp | 69 ++++++++++++++++++++++-------- libs/Unity/MonoLoader.hpp | 88 ++++++++++++++++++++++++++++----------- 2 files changed, 114 insertions(+), 43 deletions(-) diff --git a/libs/Unity/MonoLoader.cpp b/libs/Unity/MonoLoader.cpp index 415b4f4..51ec44d 100644 --- a/libs/Unity/MonoLoader.cpp +++ b/libs/Unity/MonoLoader.cpp @@ -21,13 +21,13 @@ bool MonoLoader::Initialize(std::shared_ptr log, const char* mon } logger->info("Mono DLL found at {:p}", (void*)mono); - g_mono_get_root_domain = (MonoDomain * (*)())GetProcAddress(mono, "mono_get_root_domain"); - g_mono_thread_attach = (void(*)(MonoDomain*))GetProcAddress(mono, "mono_thread_attach"); - g_mono_domain_assembly_open = (MonoAssembly * (*)(MonoDomain*, const char*))GetProcAddress(mono, "mono_domain_assembly_open"); - g_mono_assembly_get_image = (MonoImage * (*)(MonoAssembly*))GetProcAddress(mono, "mono_assembly_get_image"); - g_mono_class_from_name = (MonoClass * (*)(MonoImage*, const char*, const char*))GetProcAddress(mono, "mono_class_from_name"); - g_mono_class_get_method_from_name = (MonoMethod * (*)(MonoClass*, const char*, int))GetProcAddress(mono, "mono_class_get_method_from_name"); - g_mono_compile_method = (void* (*)(MonoMethod*))GetProcAddress(mono, "mono_compile_method"); + mono_get_root_domain = (MonoDomain * (*)())GetProcAddress(mono, "mono_get_root_domain"); + mono_thread_attach = (MonoThread * (*)(MonoDomain*))GetProcAddress(mono, "mono_thread_attach"); + mono_domain_assembly_open = (MonoAssembly * (*)(MonoDomain*, const char*))GetProcAddress(mono, "mono_domain_assembly_open"); + mono_assembly_get_image = (MonoImage * (*)(MonoAssembly*))GetProcAddress(mono, "mono_assembly_get_image"); + mono_class_from_name = (MonoClass * (*)(MonoImage*, const char*, const char*))GetProcAddress(mono, "mono_class_from_name"); + mono_class_get_method_from_name = (MonoMethod * (*)(MonoClass*, const char*, int))GetProcAddress(mono, "mono_class_get_method_from_name"); + mono_compile_method = (void* (*)(MonoMethod*))GetProcAddress(mono, "mono_compile_method"); mono_assembly_foreach = (void(*)(void(*)(MonoAssembly*, void*), void*))GetProcAddress(mono, "mono_assembly_foreach"); mono_assembly_get_name = (mono_assembly_get_name_t)GetProcAddress(mono, "mono_assembly_get_name"); mono_assembly_name_get_name = (mono_assembly_name_get_name_t)GetProcAddress(mono, "mono_assembly_name_get_name"); @@ -35,22 +35,28 @@ bool MonoLoader::Initialize(std::shared_ptr log, const char* mon mono_method_get_name = (mono_method_get_name_t)GetProcAddress(mono, "mono_method_get_name"); mono_method_signature = (mono_method_signature_t)GetProcAddress(mono, "mono_method_signature"); mono_signature_get_param_count = (mono_signature_get_param_count_t)GetProcAddress(mono, "mono_signature_get_param_count"); + mono_class_get_field_from_name = (mono_class_get_field_from_name_t)GetProcAddress(mono, "mono_class_get_field_from_name"); + mono_runtime_invoke = (mono_runtime_invoke_t) GetProcAddress(mono, "mono_runtime_invoke"); + mono_field_get_offset = (mono_field_get_offset_t)GetProcAddress(mono, "mono_field_get_offset"); + mono_field_set_value = (mono_field_set_value_t)GetProcAddress(mono, "mono_field_set_value"); + mono_field_get_value = (mono_field_get_value_t)GetProcAddress(mono, "mono_field_get_value"); - if (!g_mono_get_root_domain || !g_mono_thread_attach || !g_mono_domain_assembly_open || - !g_mono_assembly_get_image || !g_mono_class_from_name || !g_mono_class_get_method_from_name || - !g_mono_compile_method || !mono_assembly_foreach || !mono_assembly_get_name || !mono_assembly_name_get_name || - !mono_class_get_methods || !mono_method_get_name || !mono_method_signature || !mono_signature_get_param_count) { + if (!mono_get_root_domain || !mono_thread_attach || !mono_domain_assembly_open || !mono_assembly_get_image || + !mono_class_from_name || !mono_class_get_method_from_name || !mono_compile_method || !mono_assembly_foreach || + !mono_assembly_get_name || !mono_assembly_name_get_name || !mono_class_get_methods || !mono_method_get_name || + !mono_method_signature || !mono_signature_get_param_count || !mono_class_get_field_from_name || + !mono_runtime_invoke || !mono_field_get_offset || !mono_field_set_value || !mono_field_get_value) { logger->error("Failed to resolve one or more Mono exports."); return false; } - g_monoDomain = g_mono_get_root_domain(); + g_monoDomain = mono_get_root_domain(); if (!g_monoDomain) { logger->error("Mono root domain is null."); return false; } - g_mono_thread_attach(g_monoDomain); + mono_thread_attach(g_monoDomain); logger->info("Attached to Mono domain {:p}", (void*)g_monoDomain); return true; } @@ -92,17 +98,44 @@ MonoClass* MonoLoader::FindClass(const std::string& assemblyName, const std::str MonoAssembly* assembly = FindAssembly(assemblyName); if (!assembly) return nullptr; - MonoImage* image = g_mono_assembly_get_image(assembly); + MonoImage* image = mono_assembly_get_image(assembly); if (!image) return nullptr; - return g_mono_class_from_name(image, namespaceName.c_str(), className.c_str()); + return mono_class_from_name(image, namespaceName.c_str(), className.c_str()); } uint8_t* MonoLoader::GetCompiledMethod(MonoClass* monoClass, const std::string& methodName, int paramCount) { if (!monoClass) return nullptr; - MonoMethod* method = g_mono_class_get_method_from_name(monoClass, methodName.c_str(), paramCount); + MonoMethod* method = mono_class_get_method_from_name(monoClass, methodName.c_str(), paramCount); if (!method) return nullptr; - return (uint8_t*)g_mono_compile_method(method); + return (uint8_t*)mono_compile_method(method); +} + +MonoThread* MonoLoader::AttachCurrentThread() { + MonoThread* monoThread = mono_thread_attach(mono_get_root_domain()); + if (!monoThread) return nullptr; + return monoThread; +} + +void MonoLoader::DetachCurrentThread(MonoThread* thread) { + if (!thread || !mono_thread_detach) return; + mono_thread_detach(thread); +} + +MonoMethod* MonoLoader::GetMethod(MonoClass* monoClass, const std::string& methodName, int paramCount) { + if (!monoClass) return nullptr; + MonoMethod* method = mono_class_get_method_from_name(monoClass, methodName.c_str(), paramCount); + if (!method) return nullptr; + return method; +} + +MonoObject* MonoLoader::InvokeMethod(MonoMethod* method, MonoObject* obj, void** params, MonoObject** exception) { + if (!method) return nullptr; + MonoThread* thread = AttachCurrentThread(); + if (!thread) return nullptr; + MonoObject* invoke = mono_runtime_invoke(method, obj, params, exception); + DetachCurrentThread(thread); + return invoke; } void MonoLoader::DumpMethods(MonoClass* monoClass, std::shared_ptr logger) { @@ -116,7 +149,7 @@ void MonoLoader::DumpMethods(MonoClass* monoClass, std::shared_ptrerror("DumpMethods: Required Mono exports not resolved."); diff --git a/libs/Unity/MonoLoader.hpp b/libs/Unity/MonoLoader.hpp index cf7f123..535597c 100644 --- a/libs/Unity/MonoLoader.hpp +++ b/libs/Unity/MonoLoader.hpp @@ -9,6 +9,9 @@ struct MonoImage; struct MonoClass; struct MonoMethod; struct MonoMethodSignature; +struct MonoClassField; +struct MonoObject; +struct MonoThread; // Typedefs pour les exports Mono using mono_assembly_foreach_t = void(*)(void(*)(MonoAssembly*, void*), void*); @@ -18,32 +21,25 @@ using mono_class_get_methods_t = MonoMethod * (*)(MonoClass*, void**); using mono_method_get_name_t = const char* (*)(MonoMethod*); using mono_method_signature_t = MonoMethodSignature * (*)(MonoMethod*); using mono_signature_get_param_count_t = uint32_t(*)(MonoMethodSignature*); +using mono_class_get_field_from_name_t = MonoClassField * (*)(MonoClass*, const char*); +using mono_runtime_invoke_t = MonoObject * (*)(MonoMethod*, void*, void**, MonoObject**); +using mono_field_get_offset_t = int (*)(MonoClassField*); +using mono_field_set_value_t = void (*)(MonoObject*, MonoClassField*, void*); +using mono_field_get_value_t = void (*)(MonoObject*, MonoClassField*, void*); class MonoLoader { public: MonoLoader() = default; ~MonoLoader() = default; - /** - * @brief Callback used during Mono assembly enumeration. - * - * This function is passed to `mono_assembly_foreach` and checks each loaded assembly - * to find the one matching a specific name (provided via userData). - * Can be used to initialize pointers to target assemblies. - * - * @param assembly Pointer to the current assembly. - * @param userData User data passed to `mono_assembly_foreach`, typically a structure - * containing the target name and a location to store the result. - */ + bool Initialize(std::shared_ptr log = nullptr, const char* monoName = "mono-2.0-bdwgc.dll"); MonoDomain* GetDomain() const { return g_monoDomain; } /** * @brief Finds a Mono assembly by its name. - * * This function iterates over all loaded assemblies in the Mono domain and * returns a pointer to the assembly matching the given name. - * * @param name Name of the assembly to search for. * @return MonoAssembly* Pointer to the found assembly, or nullptr if not found. */ @@ -67,24 +63,70 @@ public: */ uint8_t* GetCompiledMethod(MonoClass* monoClass, const std::string& methodName, int paramCount); + /** + * @brief Attach the current thread to the Mono domain (required before invoking methods). + * @return MonoThread* pointer to the attached thread. + */ + MonoThread* AttachCurrentThread(); + + /** + * @brief Detach the current thread from the Mono runtime. + * Should be called if AttachCurrentThread() was used on a manually created thread. + * For the main thread, Mono typically handles detachment automatically. + * @param thread Pointer to the MonoThread previously returned by AttachCurrentThread(). + */ + void DetachCurrentThread(MonoThread* thread); + + /** + * @brief Retrieve a method from a Mono class by name and parameter count. + * @param monoClass The MonoClass to search in. + * @param methodName The name of the method. + * @param paramCount Number of parameters (use -1 for any). + * @return MonoMethod* pointer, or nullptr if not found. + */ + MonoMethod* GetMethod(MonoClass* monoClass, const std::string& methodName, int paramCount); + + /** + * @brief Invoke a Mono method safely. + * @param method MonoMethod* to invoke. + * @param obj MonoObject* instance, or nullptr for static methods. + * @param params Array of pointers to arguments, or nullptr if none. + * @param exception Optional pointer to MonoObject* to catch exceptions. + * @return MonoObject* result of the method call, or nullptr. + */ + MonoObject* InvokeMethod(MonoMethod* method, MonoObject* obj, void** params = nullptr, MonoObject** exception = nullptr); + + /** + * @brief Dump all methods of a MonoClass to the log. + * Iterates over all methods defined in the given MonoClass and logs their + * names and signatures. Useful for debugging or exploring Mono assemblies. + * @param monoClass Pointer to the MonoClass whose methods will be dumped. + * @param log Optional logger to output the method information. If nullptr, + * no logging is performed. + */ void DumpMethods(MonoClass* monoClass, std::shared_ptr log = nullptr); // Exports mono_assembly_foreach_t mono_assembly_foreach = nullptr; mono_assembly_get_name_t mono_assembly_get_name = nullptr; mono_assembly_name_get_name_t mono_assembly_name_get_name = nullptr; - MonoDomain* (*g_mono_get_root_domain)() = nullptr; - void (*g_mono_thread_attach)(MonoDomain*) = nullptr; - MonoAssembly* (*g_mono_domain_assembly_open)(MonoDomain*, const char*) = nullptr; - MonoImage* (*g_mono_assembly_get_image)(MonoAssembly*) = nullptr; - MonoClass* (*g_mono_class_from_name)(MonoImage*, const char*, const char*) = nullptr; - MonoMethod* (*g_mono_class_get_method_from_name)(MonoClass*, const char*, int) = nullptr; - void* (*g_mono_compile_method)(MonoMethod*) = nullptr; + MonoDomain* (*mono_get_root_domain)() = nullptr; + MonoThread* (*mono_thread_attach)(MonoDomain*) = nullptr; + void* (*mono_thread_detach)(MonoThread*) = nullptr; + MonoAssembly* (*mono_domain_assembly_open)(MonoDomain*, const char*) = nullptr; + MonoImage* (*mono_assembly_get_image)(MonoAssembly*) = nullptr; + MonoClass* (*mono_class_from_name)(MonoImage*, const char*, const char*) = nullptr; + MonoMethod* (*mono_class_get_method_from_name)(MonoClass*, const char*, int) = nullptr; + void* (*mono_compile_method)(MonoMethod*) = nullptr; mono_class_get_methods_t mono_class_get_methods = nullptr; mono_method_get_name_t mono_method_get_name = nullptr; mono_method_signature_t mono_method_signature = nullptr; mono_signature_get_param_count_t mono_signature_get_param_count = nullptr; - + mono_class_get_field_from_name_t mono_class_get_field_from_name = nullptr; + mono_runtime_invoke_t mono_runtime_invoke = nullptr; + mono_field_get_offset_t mono_field_get_offset = nullptr; + mono_field_set_value_t mono_field_set_value = nullptr; + mono_field_get_value_t mono_field_get_value = nullptr; private: MonoDomain* g_monoDomain = nullptr; @@ -92,22 +134,18 @@ private: /** * @brief Waits for the Mono module to be loaded in the process and returns its handle. - * * This function loops every 200 ms until the "mono-2.0-bdwgc.dll" module * is present in the current process. Useful to ensure that Mono is initialized * before resolving its exports. - * * @return HMODULE Handle of the Mono module, or nullptr if not found (practically never). */ HMODULE WaitForMono(const char* monoName); /** * @brief Callback used during Mono assembly enumeration. - * * This function is passed to `mono_assembly_foreach` and checks each loaded assembly * to find the one matching a specific name (provided via userData). * Can be used to initialize pointers to target assemblies. - * * @param assembly Pointer to the current assembly. * @param userData User data passed to `mono_assembly_foreach`, typically a structure * containing the target name and a location to store the result.