返回提交历史
Modified
XFEExtension.NetCore.MemoryEditor/MemoryEditor.cs
+115
-32
Modified
XFEExtension.NetCore.MemoryEditor/MemoryListener.cs
+102
-17
Modified
XFEExtension.NetCore.MemoryEditor/MemoryValue.cs
+3
-1
XFEstudio/XFEExtension.NetCore.MemoryEditor
大幅度优化结构,完善监听器
87452f5
代码差异
3 个文件
+220
-50
@@ -1,5 +1,4 @@
1
1
using System.Diagnostics;
2
using System.Net;
3
2
using System.Runtime.InteropServices;
4
3
using XFEExtension.NetCore.DelegateExtension;
5
4
@@ -8,12 +7,14 @@ namespace XFEExtension.NetCore.MemoryEditor;
8
7
/// <summary>
9
8
/// 内存编辑器
10
9
/// </summary>
11
public partial class MemoryEditor
10
public partial class MemoryEditor : IDisposable
12
11
{
12
private bool disposedValue;
13
13
14
/// <summary>
14
15
/// 当指定地址的内存值变化时触发
15
16
/// </summary>
16
public event XFEEventHandler<nint, MemoryValue>? ValueChanged;
17
public event XFEEventHandler<nint?, MemoryValue>? ValueChanged;
17
18
/// <summary>
18
19
/// 目标进程
19
20
/// </summary>
@@ -35,15 +36,17 @@ public partial class MemoryEditor
35
36
/// </summary>
36
37
/// <typeparam name="T">目标类型(int,float,long等)</typeparam>
37
38
/// <param name="address">指定地址</param>
38
/// <returns></returns>
39
public T ReadMemory<T>(nint address) where T : struct => ReadMemory<T>(ProcessHandle, address);
39
/// <param name="result">读取结果</param>
40
/// <returns>是否读取成功</returns>
41
public bool ReadMemory<T>(nint address, out T result) where T : struct => ReadMemory<T>(ProcessHandle, address, out result);
40
42
/// <summary>
41
43
/// 在指定内存地址中写入数据
42
44
/// </summary>
43
45
/// <typeparam name="T">数据类型(int,float,long等)</typeparam>
44
46
/// <param name="address">目标地址</param>
45
47
/// <param name="source">待写入值</param>
46
public void WriteMemory<T>(nint address, T source) where T : struct => WriteMemory<T>(ProcessHandle, address, source);
48
/// <returns>是否写入成功</returns>
49
public bool WriteMemory<T>(nint address, T source) where T : struct => WriteMemory<T>(ProcessHandle, address, source);
47
50
/// <summary>
48
51
/// 添加监听器
49
52
/// </summary>
@@ -60,11 +63,40 @@ public partial class MemoryEditor
60
63
/// <param name="customName">自定义监听器标识名</param>
61
64
public void AddListener<T>(nint address, int delay, string customName = "") where T : struct => _ = Listener.StartListen<T>(address, delay, customName);
62
65
/// <summary>
66
/// 添加监听器
67
/// </summary>
68
/// <typeparam name="T">待监听的内存的数据类型(int,float,long等)</typeparam>
69
/// <param name="address">待监听的内存地址</param>
70
/// <param name="delay">检测频率</param>
71
/// <param name="customName">自定义监听器标识名</param>
72
public void AddListener<T>(nint address, TimeSpan delay, string customName = "") where T : struct => _ = Listener.StartListen<T>(address, delay, customName);
73
/// <summary>
74
/// 添加监听器
75
/// </summary>
76
/// <typeparam name="T">待监听的内存的数据类型(int,float,long等)</typeparam>
77
/// <param name="getMemoryAddressFunc">动态获取内存地址的方法</param>
78
/// <param name="delay">检测频率,以毫秒为单位</param>
79
/// <param name="customName">自定义监听器标识名</param>
80
public void AddListener<T>(Func<nint?> getMemoryAddressFunc, int delay, string customName = "") where T : struct => _ = Listener.StartListen<T>(getMemoryAddressFunc, delay, customName);
81
/// <summary>
82
/// 添加监听器
83
/// </summary>
84
/// <typeparam name="T">待监听的内存的数据类型(int,float,long等)</typeparam>
85
/// <param name="getMemoryAddressFunc">动态获取内存地址的方法</param>
86
/// <param name="delay">检测频率</param>
87
/// <param name="customName">自定义监听器标识名</param>
88
public void AddListener<T>(Func<nint?> getMemoryAddressFunc, TimeSpan delay, string customName = "") where T : struct => _ = Listener.StartListen<T>(getMemoryAddressFunc, delay, customName);
89
/// <summary>
63
90
/// 移除并停止指定监听器
64
91
/// </summary>
65
92
/// <param name="address">监听地址</param>
66
93
public void RemoveListener(nint address) => Listener.StopListener(address);
67
94
/// <summary>
95
/// 移除并停止指定监听器
96
/// </summary>
97
/// <param name="customName">自定义名称</param>
98
public void RemoveListener(string customName) => Listener.StopListener(customName);
99
/// <summary>
68
100
/// 移除所有监听器
69
101
/// </summary>
70
102
public void RemoveListeners() => Listener.StopListener();
@@ -75,7 +107,15 @@ public partial class MemoryEditor
75
107
/// <param name="baseAddress">基址的地址部分</param>
76
108
/// <param name="offsets">基址的偏移组</param>
77
109
/// <returns>实际地址</returns>
78
public nint ResolvePointerAddress(string moduleName, int baseAddress, params int[] offsets) => ResolvePointerAddress(TargetProcess.ProcessName, moduleName, baseAddress, ProcessBiteType, offsets);
110
public nint ResolvePointerAddress(string moduleName, int baseAddress, params nint[] offsets) => ResolvePointerAddress(TargetProcess, moduleName, baseAddress, ProcessBiteType, offsets);
111
/// <summary>
112
/// 解析基址对应的实际地址
113
/// </summary>
114
/// <param name="moduleBaseAddress">模块地址(基址的进程部分,可以是DLL等)</param>
115
/// <param name="baseAddress">基址的地址部分</param>
116
/// <param name="offsets">基址的偏移组</param>
117
/// <returns>实际地址</returns>
118
public nint ResolvePointerAddress(nint moduleBaseAddress, int baseAddress, params nint[] offsets) => ResolvePointerAddress(TargetProcess, moduleBaseAddress, baseAddress, ProcessBiteType, offsets);
79
119
/// <summary>
80
120
/// 内存编辑器
81
121
/// </summary>
@@ -118,6 +158,41 @@ public partial class MemoryEditor
118
158
Listener = new(ProcessHandle);
119
159
Listener.ValueChanged += (sender, e) => ValueChanged?.Invoke(sender, e);
120
160
}
161
/// <summary>
162
/// 释放资源
163
/// </summary>
164
/// <param name="disposing"></param>
165
protected virtual void Dispose(bool disposing)
166
{
167
if (!disposedValue)
168
{
169
if (disposing)
170
{
171
Listener.Dispose();
172
}
173
174
// TODO: 释放未托管的资源(未托管的对象)并重写终结器
175
// TODO: 将大型字段设置为 null
176
disposedValue = true;
177
}
178
}
179
180
// // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器
181
// ~MemoryEditor()
182
// {
183
// // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
184
// Dispose(disposing: false);
185
// }
186
187
/// <summary>
188
/// 释放资源
189
/// </summary>
190
public void Dispose()
191
{
192
// 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
193
Dispose(disposing: true);
194
GC.SuppressFinalize(this);
195
}
121
196
#region 静态方法
122
197
/// <summary>
123
198
/// 获取程序句柄
@@ -147,18 +222,20 @@ public partial class MemoryEditor
147
222
/// <typeparam name="T">目标类型(int,float,long等)</typeparam>
148
223
/// <param name="processHandle">进程句柄</param>
149
224
/// <param name="address">指定地址</param>
150
/// <returns></returns>
151
public static T ReadMemory<T>(nint processHandle, nint address) where T : struct
225
/// <param name="result">读取结果</param>
226
/// <returns>是否读取成功</returns>
227
public static bool ReadMemory<T>(nint processHandle, nint address, out T result) where T : struct
152
228
{
153
229
unsafe
154
230
{
155
231
var size = Marshal.SizeOf<T>();
156
232
var buffer = new byte[size];
157
ReadProcessMemory(processHandle, address, buffer, (uint)size, out _);
233
var boolResult = ReadProcessMemory(processHandle, address, buffer, (uint)size, out _);
158
234
fixed (byte* pBuffer = buffer)
159
235
{
160
return Marshal.PtrToStructure<T>((nint)pBuffer);
236
result = Marshal.PtrToStructure<T>((nint)pBuffer);
161
237
}
238
return boolResult;
162
239
}
163
240
}
164
241
/// <summary>
@@ -168,46 +245,52 @@ public partial class MemoryEditor
168
245
/// <param name="processHandle">进程句柄</param>
169
246
/// <param name="address">目标地址</param>
170
247
/// <param name="source">待写入值</param>
171
public static void WriteMemory<T>(nint processHandle, nint address, T source) where T : struct
248
/// <returns>是否写入成功</returns>
249
public static bool WriteMemory<T>(nint processHandle, nint address, T source) where T : struct
172
250
{
173
251
var buffer = StructureToByteArray(source);
174
WriteProcessMemory(processHandle, address, buffer, (uint)buffer.Length, out _);
252
return WriteProcessMemory(processHandle, address, buffer, (uint)buffer.Length, out _);
175
253
}
176
254
/// <summary>
177
255
/// 解析基址对应的实际地址
178
256
/// </summary>
179
/// <param name="processName">进程名称</param>
257
/// <param name="process">主进程</param>
180
258
/// <param name="moduleName">模块名称(基址的进程部分,可以是DLL等)</param>
181
259
/// <param name="baseAddress">基址的地址部分</param>
182
260
/// <param name="processType">目标进程类型</param>
183
261
/// <param name="offsets">基址的偏移组</param>
184
262
/// <returns>实际地址</returns>
185
public static nint ResolvePointerAddress(string processName, string moduleName, int baseAddress, ProcessType processType, params int[] offsets)
263
public static nint ResolvePointerAddress(Process process, string moduleName, int baseAddress, ProcessType processType, params nint[] offsets) => ResolvePointerAddress(process, GetModuleBaseAddress(process, moduleName), baseAddress, processType, offsets);
264
/// <summary>
265
/// 解析基址对应的实际地址
266
/// </summary>
267
/// <param name="process">主进程</param>
268
/// <param name="moduleBaseAddress">模块地址(基址的进程部分,可以是DLL等)</param>
269
/// <param name="baseAddress">基址的地址部分</param>
270
/// <param name="processType">目标进程类型</param>
271
/// <param name="offsets">基址的偏移组</param>
272
/// <returns>实际地址</returns>
273
public static nint ResolvePointerAddress(Process process, nint moduleBaseAddress, int baseAddress, ProcessType processType, params nint[] offsets)
186
274
{
187
var gameProcess = GetGameProcessByName(processName);
188
if (gameProcess == null)
189
{
190
Trace.WriteLine("游戏进程未找到!");
191
return nint.Zero;
192
}
193
var moduleBaseAddress = GetModuleBaseAddress(gameProcess, moduleName);
194
if (moduleBaseAddress == nint.Zero)
195
{
196
Trace.WriteLine($"未找到模块:{moduleName}");
197
return nint.Zero;
198
}
199
275
var resolvedAddress = nint.Add(moduleBaseAddress, baseAddress);
200
foreach (int offset in offsets)
276
if (processType == ProcessType.Bit32)
201
277
{
202
if (processType == ProcessType.Bit32)
278
foreach (int offset in offsets)
203
279
{
204
var nextAddress = ReadMemory<int>(gameProcess.Handle, resolvedAddress);
280
var success = ReadMemory(process.Handle, resolvedAddress, out int nextAddress);
281
if (!success || nextAddress == nint.Zero)
282
return nint.Zero;
205
283
var pointerValue = new nint(nextAddress);
206
284
resolvedAddress = nint.Add(pointerValue, offset);
207
285
}
208
else
286
}
287
else
288
{
289
foreach (int offset in offsets)
209
290
{
210
var nextAddress = ReadMemory<long>(gameProcess.Handle, resolvedAddress);
291
var success = ReadMemory(process.Handle, resolvedAddress, out long nextAddress);
292
if (!success || nextAddress == nint.Zero)
293
return nint.Zero;
211
294
var pointerValue = new nint(nextAddress);
212
295
resolvedAddress = nint.Add(pointerValue, offset);
213
296
}
@@ -10,10 +10,11 @@ public class MemoryListener : IDisposable
10
10
{
11
11
private bool disposedValue;
12
12
private readonly Dictionary<nint, bool> listeningList = [];
13
private readonly Dictionary<string, bool> listeningFuncList = [];
13
14
/// <summary>
14
15
/// 当监听值发生改变时触发
15
16
/// </summary>
16
public event XFEEventHandler<nint, MemoryValue>? ValueChanged;
17
public event XFEEventHandler<nint?, MemoryValue>? ValueChanged;
17
18
/// <summary>
18
19
/// 进程句柄
19
20
/// </summary>
@@ -29,13 +30,14 @@ public class MemoryListener : IDisposable
29
30
/// <param name="memoryAddress">待监听的内存地址</param>
30
31
/// <param name="customName">自定义监听器标识名</param>
31
32
/// <returns></returns>
32
public async Task StartListen<T>(nint memoryAddress, string customName = "") where T : struct
33
public async Task StartListen<T>(nint memoryAddress, string? customName = null) where T : struct
33
34
{
34
35
IsListening = true;
35
36
await Task.Run(() =>
36
37
{
38
bool lastSuccess = false;
37
39
var lastValue = default(T);
38
try { lastValue = MemoryEditor.ReadMemory<T>(ProcessHandler, memoryAddress); } catch { }
40
try { lastSuccess = MemoryEditor.ReadMemory(ProcessHandler, memoryAddress, out lastValue); } catch { }
39
41
listeningList.Add(memoryAddress, true);
40
42
ReStart:
41
43
try
@@ -44,11 +46,12 @@ public class MemoryListener : IDisposable
44
46
{
45
47
try
46
48
{
47
var currentValue = MemoryEditor.ReadMemory<T>(ProcessHandler, memoryAddress);
48
if (!currentValue.Equals(lastValue))
49
var currentSuccess = MemoryEditor.ReadMemory<T>(ProcessHandler, memoryAddress, out var currentValue);
50
if (!currentValue.Equals(lastValue) || lastSuccess != currentSuccess)
49
51
{
50
ValueChanged?.Invoke(memoryAddress, new(lastValue, currentValue, customName));
52
ValueChanged?.Invoke(memoryAddress, new(lastSuccess, currentSuccess, lastValue, currentValue, customName));
51
53
lastValue = currentValue;
54
lastSuccess = currentSuccess;
52
55
}
53
56
}
54
57
catch { }
@@ -69,13 +72,23 @@ public class MemoryListener : IDisposable
69
72
/// <param name="delay">检测频率,以毫秒为单位</param>
70
73
/// <param name="customName">自定义监听器标识名</param>
71
74
/// <returns></returns>
72
public async Task StartListen<T>(nint memoryAddress, int delay, string customName = "") where T : struct
75
public async Task StartListen<T>(nint memoryAddress, int delay, string? customName = null) where T : struct => await StartListen<T>(memoryAddress, TimeSpan.FromMilliseconds(delay), customName);
76
/// <summary>
77
/// 开始监听
78
/// </summary>
79
/// <typeparam name="T">待监听的内存的数据类型(int,float,long等)</typeparam>
80
/// <param name="memoryAddress">待监听的内存地址</param>
81
/// <param name="delay">检测频率</param>
82
/// <param name="customName">自定义监听器标识名</param>
83
/// <returns></returns>
84
public async Task StartListen<T>(nint memoryAddress, TimeSpan delay, string? customName = null) where T : struct
73
85
{
74
86
IsListening = true;
75
await Task.Run(() =>
87
await Task.Run(async () =>
76
88
{
89
bool lastSuccess = false;
77
90
var lastValue = default(T);
78
try { lastValue = MemoryEditor.ReadMemory<T>(ProcessHandler, memoryAddress); } catch { }
91
try { lastSuccess = MemoryEditor.ReadMemory(ProcessHandler, memoryAddress, out lastValue); } catch { }
79
92
listeningList.Add(memoryAddress, true);
80
93
ReStart:
81
94
try
@@ -84,23 +97,90 @@ public class MemoryListener : IDisposable
84
97
{
85
98
try
86
99
{
87
88
var currentValue = MemoryEditor.ReadMemory<T>(ProcessHandler, memoryAddress);
89
if (!currentValue.Equals(lastValue))
100
var currentSuccess = MemoryEditor.ReadMemory<T>(ProcessHandler, memoryAddress, out var currentValue);
101
if (!currentValue.Equals(lastValue) || lastSuccess != currentSuccess)
90
102
{
91
ValueChanged?.Invoke(memoryAddress, new(lastValue, currentValue, customName));
92
currentValue = lastValue;
103
ValueChanged?.Invoke(memoryAddress, new(lastSuccess, currentSuccess, lastValue, currentValue, customName));
104
lastValue = currentValue;
105
lastSuccess = currentSuccess;
93
106
}
94
Thread.Sleep(delay);
107
await Task.Delay(delay);
95
108
}
96
109
catch { }
97
110
}
111
listeningList.Remove(memoryAddress);
112
}
113
catch
114
{
115
goto ReStart;
116
}
117
});
118
}
119
/// <summary>
120
/// 开始监听
121
/// </summary>
122
/// <typeparam name="T">待监听的内存的数据类型(int,float,long等)</typeparam>
123
/// <param name="getMemoryAddressFunc">动态获取内存地址的方法</param>
124
/// <param name="delay">检测频率</param>
125
/// <param name="customName">自定义监听器标识名</param>
126
/// <returns></returns>
127
public async Task StartListen<T>(Func<nint?> getMemoryAddressFunc, int delay, string customName) where T : struct => await StartListen<T>(getMemoryAddressFunc, TimeSpan.FromMilliseconds(delay), customName);
128
/// <summary>
129
/// 开始监听
130
/// </summary>
131
/// <typeparam name="T">待监听的内存的数据类型(int,float,long等)</typeparam>
132
/// <param name="getMemoryAddressFunc">动态获取内存地址的方法</param>
133
/// <param name="delay">检测频率</param>
134
/// <param name="customName">自定义监听器标识名</param>
135
/// <returns></returns>
136
public async Task StartListen<T>(Func<nint?> getMemoryAddressFunc, TimeSpan delay, string customName) where T : struct
137
{
138
IsListening = true;
139
await Task.Run(async () =>
140
{
141
var memoryAddress = getMemoryAddressFunc.Invoke();
142
bool lastSuccess = false;
143
var lastValue = default(T);
144
if (memoryAddress is not null)
145
try { lastSuccess = MemoryEditor.ReadMemory(ProcessHandler, memoryAddress.Value, out lastValue); } catch { }
146
listeningFuncList.Add(customName, true);
147
ReStart:
148
try
149
{
150
while (listeningFuncList[customName] && IsListening)
151
{
152
try
153
{
154
memoryAddress = getMemoryAddressFunc.Invoke();
155
if (memoryAddress == 0)
156
{
157
if (lastSuccess)
158
ValueChanged?.Invoke(memoryAddress, new(lastSuccess, false, lastValue, null, customName));
159
lastSuccess = false;
160
lastValue = default;
161
while (memoryAddress == 0 && listeningFuncList[customName] && IsListening)
162
{
163
memoryAddress = getMemoryAddressFunc.Invoke();
164
await Task.Delay(delay);
165
}
166
}
167
var currentSuccess = MemoryEditor.ReadMemory<T>(ProcessHandler, memoryAddress!.Value, out var currentValue);
168
if (!currentValue.Equals(lastValue) || lastSuccess != currentSuccess)
169
{
170
ValueChanged?.Invoke(memoryAddress, new(lastSuccess, currentSuccess, lastValue, currentValue, customName));
171
lastValue = currentValue;
172
lastSuccess = currentSuccess;
173
}
174
await Task.Delay(delay);
175
}
176
catch { }
177
}
178
listeningFuncList.Remove(customName);
98
179
}
99
180
catch
100
181
{
101
182
goto ReStart;
102
183
}
103
listeningList.Remove(memoryAddress);
104
184
});
105
185
}
106
186
/// <summary>
@@ -110,9 +190,14 @@ public class MemoryListener : IDisposable
110
190
/// <summary>
111
191
/// 停止指定进程的监听
112
192
/// </summary>
113
/// <param name="memoryAddress"></param>
193
/// <param name="memoryAddress">内存地址</param>
114
194
public void StopListener(nint memoryAddress) => listeningList[memoryAddress] = false;
115
195
/// <summary>
196
/// 停止指定进程的监听
197
/// </summary>
198
/// <param name="customName">自定义名称</param>
199
public void StopListener(string customName) => listeningFuncList[customName] = false;
200
/// <summary>
116
201
/// 内存监听器
117
202
/// </summary>
118
203
/// <param name="processName">进程名称</param>
@@ -3,7 +3,9 @@
3
3
/// <summary>
4
4
/// 内存记录值
5
5
/// </summary>
6
/// <param name="PreviousValueGetSuccessful">上个值是否被成功获取</param>
7
/// <param name="CurrentValueGetSuccessful">当前值是否成功获取</param>
6
8
/// <param name="PreviousValue">改变前的值</param>
7
9
/// <param name="CurrentValue">当前值</param>
8
10
/// <param name="CustomName">自定义标识名</param>
9
public record struct MemoryValue(object PreviousValue, object CurrentValue, string? CustomName) { }
11
public record struct MemoryValue(bool PreviousValueGetSuccessful, bool CurrentValueGetSuccessful, object? PreviousValue, object? CurrentValue, string? CustomName) { }