Added new module for cpp compatibility

This commit is contained in:
2024-09-19 13:44:05 +03:00
parent be75ef03cb
commit 49403e70fd
10 changed files with 393 additions and 1 deletions

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\DiscordBotCore\DiscordBotCore.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,75 @@
using DiscordBotCore;
using DiscordBotCore.Interfaces.Modules;
using DiscordBotCore.Others;
namespace CppCompatibilityModule;
public class Entry : IModule
{
public ModuleType ModuleType => ModuleType.Compatibility;
public string Name => "CppCompatibility";
public IDictionary<string, string> MethodMapping => new Dictionary<string, string>()
{
{"create_application", "CreateApplication"},
{"stop_application", "StopApplication"},
{"execute_function_with_parameter", "CallFunctionWithParameter"},
{"execute_function_without_parameter", "CallFunctionWithoutParameter"}
};
private ExternalApplicationManager? _ExternalApplicationManager;
public Task Initialize()
{
_ExternalApplicationManager = new ExternalApplicationManager();
return Task.CompletedTask;
}
public Guid CreateApplication(string dllFilePath)
{
if(_ExternalApplicationManager is null)
{
Application.Logger.Log("Failed to create application because the manager is not initialized. This should have never happened in the first place !!!", this, LogType.Critical);
return Guid.Empty;
}
if(_ExternalApplicationManager.TryCreateApplication(dllFilePath, out Guid appId))
{
return appId;
}
return Guid.Empty;
}
public void StopApplication(Guid applicationId)
{
if(_ExternalApplicationManager is null)
{
Application.Logger.Log("Failed to stop application because the manager is not initialized. This should have never happened in the first place!!!", this, LogType.Critical);
return;
}
_ExternalApplicationManager.FreeApplication(applicationId);
}
public void CallFunctionWithParameter(Guid appId, string functionName, ref object parameter)
{
if(_ExternalApplicationManager is null)
{
Application.Logger.Log("Failed to call function because the manager is not initialized. This should have never happened in the first place!!!", this, LogType.Critical);
return;
}
_ExternalApplicationManager.ExecuteApplicationFunctionWithParameter(appId, functionName, ref parameter);
}
public void CallFunctionWithoutParameter(Guid appId, string functionName)
{
if(_ExternalApplicationManager is null)
{
Application.Logger.Log("Failed to call function because the manager is not initialized. This should have never happened in the first place!!!", this, LogType.Critical);
return;
}
_ExternalApplicationManager.ExecuteApplicationFunctionWithoutParameter(appId, functionName);
}
}

View File

@@ -0,0 +1,18 @@
using System.Runtime.InteropServices;
namespace CppCompatibilityModule.Extern;
public static class Delegates
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ProcessObject(ref object obj);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ExecuteDelegateFunction();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetExternFunctionPointerDelegate(IntPtr funcPtr);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CsharpFunctionDelegate();
}

View File

@@ -0,0 +1,133 @@
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<T>(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>(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;
}
/// <summary>
/// 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.
/// <para><b>How it works:</b></para>
/// 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.
/// </summary>
/// <param name="setterExternFunctionName">The setter function name</param>
/// <param name="executableFunction">The function that the C++ setter will make its internal function to point to</param>
/// <typeparam name="ExecuteDelegate">A delegate that reflects the executable function structure</typeparam>
/// <typeparam name="SetDelegate">The Setter delegate </typeparam>
/// <returns>A response if it exists as an object</returns>
public object? SetExternFunctionSetterPointerToCustomDelegate<SetDelegate, ExecuteDelegate>(string setterExternFunctionName, ExecuteDelegate executableFunction) where ExecuteDelegate : Delegate where SetDelegate : Delegate
{
SetDelegate setterDelegate = GetDelegateForFunctionPointer<SetDelegate>(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<Delegates.ProcessObject>(methodName);
functionDelegate(ref parameter);
Application.Logger.Log($"Function {methodName} called successfully with parameter");
}
public void CallFunction(string methodName)
{
var functionDelegate = GetDelegateForFunctionPointer<Delegates.ExecuteDelegateFunction>(methodName);
functionDelegate();
Application.Logger.Log($"Function {methodName} called successfully");
}
}
}

View File

@@ -0,0 +1,55 @@
using DiscordBotCore;
using DiscordBotCore.Others;
namespace CppCompatibilityModule.Extern;
public class ExternalApplication
{
public Guid ApplicationId { get; private set; }
private readonly ExternLibrary _ExternLibrary;
private ExternalApplication(Guid applicationGuid, ExternLibrary library)
{
this.ApplicationId = applicationGuid;
this._ExternLibrary = library;
}
internal void CallFunction(string methodName, ref object parameter)
{
_ExternLibrary.CallFunction(methodName, ref parameter);
}
internal void CallFunction(string methodName)
{
_ExternLibrary.CallFunction(methodName);
}
internal T GetDelegateForFunctionPointer<T>(string methodName) where T : Delegate
{
return _ExternLibrary.GetDelegateForFunctionPointer<T>(methodName);
}
internal void SetExternFunctionToPointToFunction(string externalFunctionName, Delegates.CsharpFunctionDelegate localFunction)
{
_ExternLibrary.SetExternFunctionSetterPointerToCustomDelegate<Delegates.SetExternFunctionPointerDelegate, Delegates.CsharpFunctionDelegate>(externalFunctionName, localFunction);
}
internal void FreeLibrary()
{
_ExternLibrary.FreeLibrary();
}
public static ExternalApplication? CreateFromDllFile(string dllFilePath)
{
ExternLibrary library = new ExternLibrary(dllFilePath);
var result = library.InitializeLibrary();
return result.Match<ExternalApplication?>(
() => new ExternalApplication(Guid.NewGuid(), library),
(ex) => {
Application.Logger.Log(ex.Message, LogType.Error);
library.FreeLibrary();
return null;
});
}
}

View File

@@ -0,0 +1,72 @@
using CppCompatibilityModule.Extern;
using DiscordBotCore;
namespace CppCompatibilityModule;
public class ExternalApplicationManager
{
private List<ExternalApplication> _ExternalApplications;
public ExternalApplicationManager()
{
_ExternalApplications = new List<ExternalApplication>();
}
public bool TryCreateApplication(string applicationFileName, out Guid applicationId)
{
ExternalApplication? externalApplication = ExternalApplication.CreateFromDllFile(applicationFileName);
if(externalApplication is null)
{
applicationId = Guid.Empty;
return false;
}
_ExternalApplications.Add(externalApplication);
applicationId = externalApplication.ApplicationId;
return true;
}
public void FreeApplication(Guid applicationId)
{
var application = _ExternalApplications.FirstOrDefault(app => app.ApplicationId == applicationId, null);
if(application is null)
{
Application.Logger.Log($"Couldn't find application with id {applicationId}");
return;
}
application.FreeLibrary();
_ExternalApplications.Remove(application);
Application.Logger.Log($"Application with id {applicationId} freed successfully");
}
public void ExecuteApplicationFunctionWithParameter(Guid appId, string functionName, ref object parameter)
{
var application = _ExternalApplications.FirstOrDefault(app => app.ApplicationId == appId);
if(application is null)
{
Application.Logger.Log($"Couldn't find application with id {appId}");
return;
}
application.CallFunction(functionName, ref parameter);
}
public void ExecuteApplicationFunctionWithoutParameter(Guid appId, string functionName)
{
var application = _ExternalApplications.FirstOrDefault(app => app.ApplicationId == appId);
if(application is null)
{
Application.Logger.Log($"Couldn't find application with id {appId}");
return;
}
application.CallFunction(functionName);
}
}