Skip to content

Commit dc8a697

Browse files
committed
feat: 统一反射接口(IRuntime*) + Mono P/Invoke 绑定 + 自动后端检测
- 新增 RuntimeAbstractions/ 抽象接口层 (IRuntimeAssembly/Class/Method/Field) - Il2Cpp/Mono 反射类均实现 IRuntime* 接口 - 新增 Mono P/Invoke 绑定 (Mono.cs, MonoFunctions.cs, MonoTypes.cs) - MonoAssembly/MonoClass/MonoMethod/MonoField 反射封装 - RuntimeManager 自动检测 Mono/Il2Cpp 后端 - GraphicsDevice 统一 SystemInfo 图形设备检测 - MonoSystemInfo / UnitySystemInfo 分别支持 Mono/Il2Cpp 路径 - MonoMethod 添加 Invoke/InvokeStatic/InvokeUnbox - 源生成器支持 MonoHookAttribute + InstallHooks 返回 bool - D3D11 hook: 从 swapchain 获取窗口句柄修复 hwnd=null
1 parent e9278ff commit dc8a697

21 files changed

Lines changed: 6431 additions & 71 deletions

File tree

‎StArray.ModManager.SourceGenerator/HookGenerator.cs‎

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class HookGenerator : IIncrementalGenerator
1616
{
1717
private const string NativeHookAttrName = "StArray.ModManager.Hooks.NativeHookAttribute";
1818
private const string Il2CppHookAttrName = "StArray.ModManager.Hooks.Il2CppHookAttribute";
19+
private const string MonoHookAttrName = "StArray.ModManager.Hooks.MonoHookAttribute";
1920

2021
public void Initialize(IncrementalGeneratorInitializationContext context)
2122
{
@@ -58,6 +59,7 @@ private sealed class HookInfo
5859
{
5960
NativeHookAttrName => "NativeHook",
6061
Il2CppHookAttrName => "Il2CppHook",
62+
MonoHookAttrName => "MonoHook",
6163
_ => null,
6264
};
6365
if (attrType == null) continue;
@@ -127,7 +129,6 @@ private static void GenerateCode(SourceProductionContext ctx, Compilation compil
127129
var sf = Sanitize(h.MethodName);
128130
var pd = string.Join(", ", h.Parameters.Select(p => $"{SimplifyType(p.Type)} @{p.Name}"));
129131
var pn = string.Join(", ", h.Parameters.Select(p => $"@{p.Name}"));
130-
var pt = string.Join(", ", h.Parameters.Select(p => SimplifyType(p.Type)));
131132
var cv = GetConv(h);
132133
var ucoCallConv = GetUCOCallConv(h);
133134

@@ -140,20 +141,12 @@ private static void GenerateCode(SourceProductionContext ctx, Compilation compil
140141
sb.AppendLine($" private static nint _{sf}_origPtr;");
141142
sb.AppendLine($" private static _{sf}Delegate? _{sf}_orig;");
142143

143-
// ── 私有 wrapper(委托包装,作为 detour) ──
144+
// ── 持久引用(防止 delegate 被 GC) ──
144145
sb.AppendLine($" private static _{sf}Delegate? _{sf}_wrap;");
145-
sb.AppendLine();
146-
sb.AppendLine($" private static {rt} _Wrap_{sf}({pd})");
147-
sb.AppendLine(" {");
148-
if (IsVoid(h))
149-
sb.AppendLine($" {h.MethodName}({pn});");
150-
else
151-
sb.AppendLine($" return {h.MethodName}({pn});");
152-
sb.AppendLine(" }");
153146

154147
// ── Install ──
155148
sb.AppendLine();
156-
sb.AppendLine($" private static void Install_{sf}()");
149+
sb.AppendLine($" private static bool Install_{sf}()");
157150
sb.AppendLine(" {");
158151
if (h.AttrType == "NativeHook")
159152
{
@@ -166,15 +159,16 @@ private static void GenerateCode(SourceProductionContext ctx, Compilation compil
166159
var s = h.CtorArgs.Length > 1 ? h.CtorArgs[1] : "\"\"";
167160
sb.AppendLine($" var t = global::StArray.ModManager.Runtime.HookHelper.GetFunction({l}, {s});");
168161
}
169-
sb.AppendLine($" if (t == nint.Zero) return;");
162+
sb.AppendLine($" if (t == nint.Zero) return false;");
170163
sb.AppendLine($" _{sf}_origPtr = t;");
171-
sb.AppendLine($" _{sf}_wrap = _Wrap_{sf};");
164+
sb.AppendLine($" _{sf}_wrap = {h.MethodName};");
172165
sb.AppendLine($" var d = global::System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(_{sf}_wrap);");
173166
sb.AppendLine($" var o = global::StArray.ModManager.Runtime.HookHelper.Hook(t, d);");
174-
sb.AppendLine($" if (o != nint.Zero)");
175-
sb.AppendLine($" _{sf}_orig = global::System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer<_{sf}Delegate>(o);");
167+
sb.AppendLine($" if (o == nint.Zero) return false;");
168+
sb.AppendLine($" _{sf}_orig = global::System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer<_{sf}Delegate>(o);");
169+
sb.AppendLine(" return true;");
176170
}
177-
else // Il2CppHook
171+
else if (h.AttrType == "Il2CppHook")
178172
{
179173
var a = h.CtorArgs.Length > 0 ? h.CtorArgs[0] : "\"\"";
180174
var c = h.CtorArgs.Length > 1 ? h.CtorArgs[1] : "\"\"";
@@ -187,20 +181,53 @@ private static void GenerateCode(SourceProductionContext ctx, Compilation compil
187181
if (n.Key is "ParameterCount" or "parameterCount") pc = n.Value;
188182

189183
sb.AppendLine($" var asm = global::StArray.ModManager.Il2Cpp.Il2CppAssembly.Get({a});");
190-
sb.AppendLine($" if (asm == null) return;");
184+
sb.AppendLine($" if (asm == null) return false;");
191185
sb.AppendLine($" var cls = asm.GetClass(\"\", {c});");
192-
sb.AppendLine($" if (cls == null) return;");
186+
sb.AppendLine($" if (cls == null) return false;");
187+
if (ptn != null)
188+
sb.AppendLine($" var method = cls.GetMethod({m}, {ptn});");
189+
else
190+
sb.AppendLine($" var method = cls.GetMethod({m}, {pc});");
191+
sb.AppendLine($" if (method == null) return false;");
192+
sb.AppendLine($" var t = method.FunctionPtr;");
193+
sb.AppendLine($" _{sf}_origPtr = t;");
194+
sb.AppendLine($" _{sf}_wrap = {h.MethodName};");
195+
sb.AppendLine($" var d = global::System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(_{sf}_wrap);");
196+
sb.AppendLine($" var o = global::StArray.ModManager.Runtime.HookHelper.Hook(t, d);");
197+
sb.AppendLine($" if (o == nint.Zero) return false;");
198+
sb.AppendLine($" _{sf}_orig = global::System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer<_{sf}Delegate>(o);");
199+
sb.AppendLine(" return true;");
200+
}
201+
else // MonoHook
202+
{
203+
var a = h.CtorArgs.Length > 0 ? h.CtorArgs[0] : "\"\"";
204+
var nsArg = h.CtorArgs.Length > 1 ? h.CtorArgs[1] : "\"\"";
205+
var c = h.CtorArgs.Length > 2 ? h.CtorArgs[2] : "\"\"";
206+
var m = h.CtorArgs.Length > 3 ? h.CtorArgs[3] : "\"\"";
207+
string? ptn = null;
208+
foreach (var n in h.NamedArgs)
209+
if (n.Key is "ParameterTypeNames" or "parameterTypeNames") ptn = n.Value;
210+
string pc = "-1";
211+
foreach (var n in h.NamedArgs)
212+
if (n.Key is "ParameterCount" or "parameterCount") pc = n.Value;
213+
214+
sb.AppendLine($" var asm = global::StArray.ModManager.Mono.MonoAssembly.Get({a});");
215+
sb.AppendLine($" if (asm == null) return false;");
216+
sb.AppendLine($" var cls = asm.GetClass({nsArg}, {c});");
217+
sb.AppendLine($" if (cls == null) return false;");
193218
if (ptn != null)
194219
sb.AppendLine($" var method = cls.GetMethod({m}, {ptn});");
195220
else
196221
sb.AppendLine($" var method = cls.GetMethod({m}, {pc});");
197-
sb.AppendLine($" if (method == null) return;");
222+
sb.AppendLine($" if (method == null) return false;");
198223
sb.AppendLine($" var t = method.FunctionPtr;");
199224
sb.AppendLine($" _{sf}_origPtr = t;");
225+
sb.AppendLine($" _{sf}_wrap = {h.MethodName};");
200226
sb.AppendLine($" var d = global::System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(_{sf}_wrap);");
201227
sb.AppendLine($" var o = global::StArray.ModManager.Runtime.HookHelper.Hook(t, d);");
202-
sb.AppendLine($" if (o != nint.Zero)");
203-
sb.AppendLine($" _{sf}_orig = global::System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer<_{sf}Delegate>(o);");
228+
sb.AppendLine($" if (o == nint.Zero) return false;");
229+
sb.AppendLine($" _{sf}_orig = global::System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer<_{sf}Delegate>(o);");
230+
sb.AppendLine(" return true;");
204231
}
205232
sb.AppendLine(" }");
206233

@@ -216,13 +243,12 @@ private static void GenerateCode(SourceProductionContext ctx, Compilation compil
216243
}
217244

218245
sb.AppendLine();
219-
sb.AppendLine(" public static void InstallHooks()");
246+
sb.AppendLine(" public static bool InstallHooks()");
220247
sb.AppendLine(" {");
221-
sb.AppendLine(" foreach (var m in new global::System.Action[]");
222-
sb.AppendLine(" {");
248+
sb.AppendLine(" bool ok = true;");
223249
foreach (var h in group)
224-
sb.AppendLine($" Install_{Sanitize(h.MethodName)},");
225-
sb.AppendLine(" }) m();");
250+
sb.AppendLine($" ok &= Install_{Sanitize(h.MethodName)}();");
251+
sb.AppendLine(" return ok;");
226252
sb.AppendLine(" }");
227253
sb.AppendLine();
228254
sb.AppendLine(" public static void UninstallHooks()");

‎StArray.ModManager.Windows/Managed.cs‎

Lines changed: 92 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
using System.Runtime.CompilerServices;
44
using System.Runtime.InteropServices;
55
using ImGuiNET;
6-
using StArray.ModManager.Il2Cpp;
76
using StArray.ModManager.Manager;
87
using StArray.ModManager.Runtime;
8+
using StArray.ModManager.RuntimeAbstractions;
99
using StArray.ModManager.Windows.UI;
1010
using StArray.ModManager.Windows.Native;
1111

@@ -38,7 +38,10 @@ private static IntPtr ResolveDll(string libraryName, Assembly assembly, DllImpor
3838
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl), typeof(CallConvStdcall)])]
3939
public static int Entry(int argc, IntPtr argv)
4040
{
41-
AppDomain.CurrentDomain.UnhandledException += (sender, e) => { Write($"UnhandledException: {e.ExceptionObject}\n"); };
41+
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
42+
{
43+
Write($"UnhandledException: {e.ExceptionObject}\n");
44+
};
4245
var totalSw = Stopwatch.StartNew();
4346

4447
Benchmark.Begin();
@@ -49,10 +52,7 @@ public static int Entry(int argc, IntPtr argv)
4952
args[i] = Marshal.PtrToStringUTF8(pStr)!;
5053
}
5154

52-
Logger.OnLog += (level, s, arg3) =>
53-
{
54-
Write($"[ModManager/{s}][{level}]: {arg3}\n");
55-
};
55+
Logger.OnLog += (level, s, arg3) => { Write($"[ModManager/{s}][{level}]: {arg3}\n"); };
5656
Logger.Info($"{nameof(Managed)}-Benchmark", $"Args parse ({argc} args): {Benchmark.End():F3}s");
5757

5858
Benchmark.Begin();
@@ -69,30 +69,56 @@ public static int Entry(int argc, IntPtr argv)
6969
Writer = new StreamWriter(Path.Combine(Path.GetDirectoryName(modsPath), "log.txt"), append: false);
7070
Writer.AutoFlush = true;
7171
Logger.Info($"{nameof(Managed)}-Benchmark", $"Path resolve: {Benchmark.End():F3}s");
72+
73+
// 检测运行时后端 + 图形设备类型
74+
RuntimeManager.Detect();
75+
Logger.Info("Runtime", $"Detected backend: {RuntimeManager.Backend}");
76+
7277
ModManagerUI modManagerUI = new(new ModLoader(modsPath), managedDir);
7378

7479
// Detect backend and pick renderer
75-
int mask = NativeApi.GetAvailableBackends();
76-
Logger.Info("Backend", $"GetAvailableBackends=0x{mask:X2} (1=D3D12 2=D3D11 4=D3D9 8=GL 16=VK)");
77-
78-
if ((mask & 2) != 0) // D3D11
79-
{ NativeApi.SetBackend(1); ImGuiRenderer.OnRender += modManagerUI.Render; ImGuiRenderer.Install(); }
80-
else if ((mask & 1) != 0) // D3D12
81-
{ NativeApi.SetBackend(0); ImGuiRenderer.OnRender += modManagerUI.Render; ImGuiRenderer.Install(); }
82-
else if ((mask & 4) != 0) // D3D9
83-
{ NativeApi.SetBackend(2); ImGuiRenderer.OnRender += modManagerUI.Render; ImGuiRenderer.Install(); }
84-
else if ((mask & 8) != 0) // OpenGL
85-
{ ImGUIGLRenderer.OnRender += modManagerUI.Render; ImGUIGLRenderer.Install(); }
80+
var renderer = GetGameRenderer();
81+
Logger.Info("Backend", $"Detected renderer: {renderer}");
82+
83+
if ((renderer & Renderer.D3D11) != 0)
84+
{
85+
NativeApi.SetBackend(1); // D3D11
86+
ImGuiRenderer.OnRender += modManagerUI.Render;
87+
ImGuiRenderer.Install();
88+
}
89+
else if ((renderer & Renderer.D3D12) != 0)
90+
{
91+
NativeApi.SetBackend(0); // D3D12
92+
ImGuiRenderer.OnRender += modManagerUI.Render;
93+
ImGuiRenderer.Install();
94+
}
95+
else if ((renderer & Renderer.D3D9) != 0)
96+
{
97+
NativeApi.SetBackend(2); // D3D9
98+
ImGuiRenderer.OnRender += modManagerUI.Render;
99+
ImGuiRenderer.Install();
100+
}
101+
else if ((renderer & Renderer.OpenGL) != 0)
102+
{
103+
ImGUIGLRenderer.OnRender += modManagerUI.Render;
104+
ImGUIGLRenderer.Install();
105+
}
86106
else
87-
{ Logger.Error("Backend", $"No supported backend"); return 1; }
107+
{
108+
Logger.Error("Backend", "No supported backend");
109+
return 1;
110+
}
111+
88112
totalSw.Stop();
89113
Logger.Info($"{nameof(Managed)}-Benchmark", $"=== Startup total: {totalSw.Elapsed.TotalSeconds:F3}s ===");
90114
return 0;
91115
}
92-
116+
93117
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
94118
static extern bool WriteConsoleW(nint hConsole, string text, uint len, out uint written, nint reserved);
119+
95120
const int STD_OUTPUT_HANDLE = -11;
121+
96122
[DllImport("kernel32.dll")]
97123
static extern nint GetStdHandle(int nStdHandle);
98124

@@ -101,4 +127,51 @@ static void Write(string s)
101127
Writer?.Write(s);
102128
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), s, (uint)s.Length, out _, 0);
103129
}
130+
131+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
132+
private static extern IntPtr GetModuleHandle(string lpModuleName);
133+
134+
[Flags]
135+
public enum Renderer
136+
{
137+
None = 0,
138+
D3D12 = 1,
139+
D3D11 = 2,
140+
D3D9 = 4,
141+
OpenGL = 8,
142+
Vulkan = 16
143+
}
144+
145+
public static Renderer GetGameRenderer()
146+
{
147+
Renderer result = Renderer.None;
148+
149+
// Unity 2020+ GraphicsDeviceType 枚举值:
150+
// Direct3D9 = 0, Direct3D11 = 2, Direct3D12 = 3
151+
// OpenGLCore = 11, Vulkan = 13
152+
int gdt = GraphicsDevice.GetGraphicsDeviceType();
153+
switch (gdt)
154+
{
155+
case 0: result |= Renderer.D3D9; break;
156+
case 2: result |= Renderer.D3D11; break;
157+
case 3: result |= Renderer.D3D12; break;
158+
case 11: result |= Renderer.OpenGL; break;
159+
case 13: result |= Renderer.Vulkan; break;
160+
default:
161+
// 回退到模块名检测(非 Unity 或未知版本)
162+
if (GetModuleHandle("d3d12.dll") != IntPtr.Zero)
163+
result |= Renderer.D3D12;
164+
if (GetModuleHandle("d3d11.dll") != IntPtr.Zero)
165+
result |= Renderer.D3D11;
166+
if (GetModuleHandle("d3d9.dll") != IntPtr.Zero)
167+
result |= Renderer.D3D9;
168+
if (GetModuleHandle("opengl32.dll") != IntPtr.Zero)
169+
result |= Renderer.OpenGL;
170+
if (GetModuleHandle("vulkan-1.dll") != IntPtr.Zero)
171+
result |= Renderer.Vulkan;
172+
break;
173+
}
174+
175+
return result;
176+
}
104177
}

‎StArray.ModManager.Windows/Native/CMakeLists.txt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ add_library(${PROJECT_NAME} SHARED
4747
)
4848
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") # no "lib" prefix
4949
target_include_directories(${PROJECT_NAME} PRIVATE . kiero2 minhook/include)
50-
target_link_libraries(${PROJECT_NAME} PRIVATE kiero MinHook d3d12 dxgi ${CMAKE_CURRENT_SOURCE_DIR}/libcimgui.a)
50+
target_link_libraries(${PROJECT_NAME} PRIVATE kiero MinHook d3d11 d3d12 dxgi ${CMAKE_CURRENT_SOURCE_DIR}/libcimgui.a)
5151

5252
# Copy DLL to C# build output
5353
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
@@ -57,5 +57,5 @@ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
5757
"${CMAKE_CURRENT_SOURCE_DIR}/../bin/${CMAKE_BUILD_TYPE}/net10.0/"
5858
COMMAND ${CMAKE_COMMAND} -E copy_if_different
5959
"$<TARGET_FILE:${PROJECT_NAME}>"
60-
"D:/Development/RiderProjects/StArray.ModManager/tests/Corehold/managed"
60+
"D:/Development/RiderProjects/StArray.ModManager/tests/build/Corehold/managed"
6161
)

‎StArray.ModManager.Windows/Native/hook_common.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace DX12 {
3030
}
3131
namespace DX11 { extern ID3D11Device* Device; extern ID3D11DeviceContext* Context; extern ID3D11RenderTargetView* MainRTV;
3232
extern bool Initialized;
33-
void Init(IDXGISwapChain* sc); void Render(IDXGISwapChain* sc); }
33+
bool Install(); }
3434
namespace DX9 { extern IDirect3DDevice9* Device;
3535
void Init(IDXGISwapChain* sc); void Render(IDXGISwapChain* sc); }
3636

0 commit comments

Comments
 (0)