mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 13:05:25 -05:00
impr: Merge in script loader structure improvements from python branch
This commit is contained in:
6
plugins/script_loader/support/dotnet/AssemblyLoader/.gitignore
vendored
Normal file
6
plugins/script_loader/support/dotnet/AssemblyLoader/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.vs/
|
||||
bin/
|
||||
obj/
|
||||
*.dll
|
||||
*.pdb
|
||||
*.json
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyName>AssemblyLoader</AssemblyName>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<SelfContained>true</SelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RollForward>Major</RollForward>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
178
plugins/script_loader/support/dotnet/AssemblyLoader/Program.cs
Normal file
178
plugins/script_loader/support/dotnet/AssemblyLoader/Program.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace ImHex
|
||||
{
|
||||
|
||||
public class EntryPoint
|
||||
{
|
||||
|
||||
private static void Log(string message)
|
||||
{
|
||||
Console.WriteLine($"[.NET Script] {message}");
|
||||
}
|
||||
public static int ExecuteScript(IntPtr argument, int argumentLength)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ExecuteScript(Marshal.PtrToStringUTF8(argument, argumentLength));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log($"Exception in AssemblyLoader: {e}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<string> LoadedPlugins = new();
|
||||
private static int ExecuteScript(string args)
|
||||
{
|
||||
// Parse input in the form of "execType||path"
|
||||
var splitArgs = args.Split("||");
|
||||
var type = splitArgs[0];
|
||||
var methodName = splitArgs[1];
|
||||
var path = splitArgs[2];
|
||||
|
||||
// Get the parent folder of the passed path
|
||||
string? basePath = Path.GetDirectoryName(path);
|
||||
if (basePath == null)
|
||||
{
|
||||
Log("Failed to get base path");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create a new assembly context
|
||||
AssemblyLoadContext? context = new("ScriptDomain_" + basePath, true);
|
||||
|
||||
int result = 0;
|
||||
try
|
||||
{
|
||||
if (type is "LOAD")
|
||||
{
|
||||
// If the script has been loaded already, don't do it again
|
||||
if (LoadedPlugins.Contains(path))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if the plugin is already loaded
|
||||
LoadedPlugins.Add(path);
|
||||
}
|
||||
|
||||
// Load all assemblies in the parent folder
|
||||
foreach (var file in Directory.GetFiles(basePath, "*.dll")) {
|
||||
// Skip main Assembly
|
||||
if (new FileInfo(file).Name == "Main.dll")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load the Assembly
|
||||
try
|
||||
{
|
||||
context.LoadFromStream(new MemoryStream(File.ReadAllBytes(file)));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log($"Failed to load assembly: {file} - {e}");
|
||||
}
|
||||
}
|
||||
|
||||
// Load the script assembly
|
||||
var assembly = context.LoadFromStream(new MemoryStream(File.ReadAllBytes(path)));
|
||||
|
||||
// Find ImHexLibrary module
|
||||
var libraryModule = Array.Find(context.Assemblies.ToArray(), module => module.GetName().Name == "ImHexLibrary");
|
||||
if (libraryModule == null)
|
||||
{
|
||||
Log("Refusing to load non-ImHex script");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load Library type
|
||||
var libraryType = libraryModule.GetType("Library");
|
||||
if (libraryType == null)
|
||||
{
|
||||
Log("Failed to find Library type in ImHexLibrary");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load Initialize function in the Library type
|
||||
var initMethod = libraryType.GetMethod("Initialize", BindingFlags.Static | BindingFlags.Public);
|
||||
if (initMethod == null)
|
||||
{
|
||||
Log("Failed to find Initialize method");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Execute it
|
||||
initMethod.Invoke(null, null);
|
||||
}
|
||||
|
||||
// Find classes derived from IScript
|
||||
var entryPointTypes = Array.FindAll(assembly.GetTypes(), t => t.GetInterface("IScript") != null);
|
||||
|
||||
if (entryPointTypes.Length == 0)
|
||||
{
|
||||
Log("Failed to find Script entrypoint");
|
||||
return 1;
|
||||
} else if (entryPointTypes.Length > 1)
|
||||
{
|
||||
Log("Found multiple Script entrypoints");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var entryPointType = entryPointTypes[0];
|
||||
|
||||
if (type is "EXEC" or "LOAD")
|
||||
{
|
||||
// Load the function
|
||||
var method = entryPointType.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
|
||||
if (method == null)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Execute it
|
||||
method.Invoke(null, null);
|
||||
}
|
||||
else if (type == "CHECK")
|
||||
{
|
||||
// Check if the method exists
|
||||
return entryPointType.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public) != null ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log($"Exception in AssemblyLoader: {e}");
|
||||
return 3;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (type != "LOAD")
|
||||
{
|
||||
// Unload all assemblies associated with this script
|
||||
context.Unload();
|
||||
context = null;
|
||||
|
||||
// Run the garbage collector multiple times to make sure that the
|
||||
// assemblies are unloaded for sure
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>bin\Release\net8.0\publish\</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<_TargetId>Folder</_TargetId>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<History>True|2023-06-16T15:24:52.9876162Z;</History>
|
||||
<LastFailureDetails />
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
25
plugins/script_loader/support/dotnet/CMakeLists.txt
Normal file
25
plugins/script_loader/support/dotnet/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
project(dotnet_assemblies)
|
||||
|
||||
function(add_dotnet_assembly name)
|
||||
set(OUTPUT_DLL ${CMAKE_CURRENT_BINARY_DIR}/../../${name}.dll)
|
||||
set(OUTPUT_RUNTIMECONFIG ${CMAKE_CURRENT_BINARY_DIR}/../../${name}.runtimeconfig.json)
|
||||
|
||||
file(GLOB_RECURSE sources ${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cs)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../../${name}.dll
|
||||
COMMAND ${DOTNET_EXECUTABLE} build ${CMAKE_CURRENT_SOURCE_DIR}/${name}/${name}.csproj -nologo -consoleLoggerParameters:NoSummary -verbosity:quiet -c Release -o ${CMAKE_CURRENT_BINARY_DIR}/../..
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}/${name}.csproj ${sources}
|
||||
COMMENT "Building ${name}.dll"
|
||||
)
|
||||
|
||||
add_custom_target(${name} ALL DEPENDS ${OUTPUT_DLL})
|
||||
|
||||
install(FILES
|
||||
${OUTPUT_DLL}
|
||||
${OUTPUT_RUNTIMECONFIG}
|
||||
DESTINATION
|
||||
${PLUGINS_INSTALL_LOCATION}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
add_dotnet_assembly(AssemblyLoader)
|
||||
Reference in New Issue
Block a user