using System.Runtime.InteropServices; using DiscordBotCore; using DiscordBotCore.Others; namespace CppCompatibilityModule.Extern { public sealed class ExternLibrary { public string LibraryPath { get; init; } public IntPtr LibraryHandle { get; private set; } public ExternLibrary(string libraryPath) { LibraryPath = libraryPath; LibraryHandle = IntPtr.Zero; } public Result InitializeLibrary() { if(LibraryHandle != IntPtr.Zero) { return Result.Success(); } Application.Logger.Log($"Loading library {LibraryPath}"); if(!NativeLibrary.TryLoad(LibraryPath, out IntPtr hModule)) { return Result.Failure(new DllNotFoundException($"Unable to load library {LibraryPath}")); } Application.Logger.Log($"Library {LibraryPath} loaded successfully [{hModule}]"); LibraryHandle = hModule; return Result.Success(); } public void FreeLibrary() { if(LibraryHandle == IntPtr.Zero) { return; } NativeLibrary.Free(LibraryHandle); LibraryHandle = IntPtr.Zero; Application.Logger.Log($"Library {LibraryPath} freed successfully"); } private IntPtr GetFunctionPointer(string functionName) { if(LibraryHandle == IntPtr.Zero) { throw new InvalidOperationException("Library is not loaded"); } if(!NativeLibrary.TryGetExport(LibraryHandle, functionName, out IntPtr functionPointer)) { throw new EntryPointNotFoundException($"Unable to find function {functionName}"); } return functionPointer; } public T GetDelegateForFunctionPointer(string methodName) where T : Delegate { IntPtr functionPointer = GetFunctionPointer(methodName); Application.Logger.Log($"Function pointer for {methodName} obtained successfully [address: {functionPointer}]"); T result = (T)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(T)); Application.Logger.Log($"Delegate for {methodName} created successfully"); return result; } private IntPtr GetFunctionPointerForDelegate(T functionDelegate) where T : Delegate { IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(functionDelegate); Application.Logger.Log($"Function pointer for delegate {functionDelegate.Method.Name} obtained successfully [address: {functionPointer}]"); return functionPointer; } /// /// Tells the extern setter function to point its function to this C# function instead. /// This function takes the name of the extern setter function and the C# function to be executed. /// How it works: /// Find the external setter method by its name. It should take one parameter, which is the pointer to the function to be executed. /// Take the delegate function that should be executed and get its function pointer. /// Call the external setter with the new function memory address. This should replace the old C++ function with the new C# function. /// /// The setter function name /// The function that the C++ setter will make its internal function to point to /// A delegate that reflects the executable function structure /// The Setter delegate /// A response if it exists as an object public object? SetExternFunctionSetterPointerToCustomDelegate(string setterExternFunctionName, ExecuteDelegate executableFunction) where ExecuteDelegate : Delegate where SetDelegate : Delegate { SetDelegate setterDelegate = GetDelegateForFunctionPointer(setterExternFunctionName); IntPtr executableFunctionPtr = GetFunctionPointerForDelegate(executableFunction); var result = setterDelegate.DynamicInvoke(executableFunctionPtr); Application.Logger.Log($"Function {setterExternFunctionName} bound to local action successfully"); return result; } public void CallFunction(string methodName, ref object parameter) { var functionDelegate = GetDelegateForFunctionPointer(methodName); functionDelegate(ref parameter); Application.Logger.Log($"Function {methodName} called successfully with parameter"); } public void CallFunction(string methodName) { var functionDelegate = GetDelegateForFunctionPointer(methodName); functionDelegate(); Application.Logger.Log($"Function {methodName} called successfully"); } } }