如何实现C# COM Server类全局注册,实现C#与C++跨进程数据共享
解决方案:跨进程持久化注册状态(无需SQL等外部存储)
当前实现中RegisterPull.dictionary是进程内静态成员,进程退出后所有数据会被销毁,因此重启应用后IsRegister返回false。要实现应用重启后仍保留注册状态,且无需SQL等外部数据库,可借助Windows系统提供的跨进程存储机制,以下是两种可行方案:
方案1:内存映射文件(Memory-Mapped Files)
利用全局命名的内存映射文件存储注册数据,数据会被持久化到系统页文件,进程重启后可重新读取。
实现步骤:
- 标记
ClientDetails为可序列化:
[Serializable] public class ClientDetails { public string Address { get; set; } public DateTime LastRegister { get; set; } }
- 修改
RegisterPull类,通过内存映射文件读写数据:
using System.IO.MemoryMappedFiles; using System.Runtime.Serialization.Formatters.Binary; using System.Threading; using System.Collections.Concurrent; public static class RegisterPull { // 全局命名的内存映射文件和互斥锁,确保跨进程访问安全 private const string _mapName = "Global_GameCheater_RegisterStore"; private const int _mapSize = 1024 * 1024; // 分配1MB内存,可按需调整 private static readonly Mutex _syncMutex = new Mutex(false, "Global_GameCheater_RegisterMutex"); public static ConcurrentDictionary<string, ClientDetails> dictionary; static RegisterPull() { LoadDataFromMap(); } private static void LoadDataFromMap() { _syncMutex.WaitOne(); try { using var mmf = MemoryMappedFile.OpenOrCreate(_mapName, _mapSize); using var stream = mmf.CreateViewStream(); var formatter = new BinaryFormatter(); dictionary = stream.Length > 0 ? (ConcurrentDictionary<string, ClientDetails>)formatter.Deserialize(stream) : new ConcurrentDictionary<string, ClientDetails>(); } finally { _syncMutex.ReleaseMutex(); } } public static void SaveDataToMap() { _syncMutex.WaitOne(); try { using var mmf = MemoryMappedFile.OpenOrCreate(_mapName, _mapSize); using var stream = mmf.CreateViewStream(); stream.SetLength(0); // 清空旧数据 var formatter = new BinaryFormatter(); formatter.Serialize(stream, dictionary); } finally { _syncMutex.ReleaseMutex(); } } }
- 更新
RegisterManager的Register方法,每次修改后保存数据:
public void Register(string address) { var clientDetails = new ClientDetails() { Address = address, LastRegister = DateTime.Now }; if (RegisterPull.dictionary.ContainsKey(address)) RegisterPull.dictionary[address] = clientDetails; else RegisterPull.dictionary.TryAdd(address, clientDetails); RegisterPull.SaveDataToMap(); // 同步到内存映射文件 }
注意事项:
- 全局命名(前缀
Global\)需要管理员权限,若仅需同一会话内进程共享,可去掉前缀使用本地命名。 - 若注册数据量大,建议更换更高效的序列化方式(如Protobuf)替代
BinaryFormatter,同时调整内存映射文件大小。 - 系统重启后,内存映射文件数据会丢失,若需系统重启后仍保留,可结合方案2。
方案2:Windows注册表
将注册信息写入Windows注册表,进程重启时从注册表读取,无需额外存储文件。
实现步骤:
- 修改
RegisterPull类,通过注册表读写数据:
using Microsoft.Win32; using System.Collections.Concurrent; public static class RegisterPull { private const string _regPath = @"Software\GameCheater\RegisterRecords"; public static ConcurrentDictionary<string, ClientDetails> dictionary; static RegisterPull() { LoadDataFromRegistry(); } private static void LoadDataFromRegistry() { dictionary = new ConcurrentDictionary<string, ClientDetails>(); using var key = Registry.CurrentUser.OpenSubKey(_regPath); if (key == null) return; foreach (var address in key.GetValueNames()) { var lastRegisterStr = key.GetValue(address) as string; if (DateTime.TryParse(lastRegisterStr, out var lastRegister)) { dictionary.TryAdd(address, new ClientDetails { Address = address, LastRegister = lastRegister }); } } } public static void SaveDataToRegistry() { using var key = Registry.CurrentUser.CreateSubKey(_regPath); foreach (var kvp in dictionary) { key.SetValue(kvp.Key, kvp.Value.LastRegister.ToString("o")); // 用ISO格式存储时间 } } }
- 更新
RegisterManager的Register方法:
public void Register(string address) { var clientDetails = new ClientDetails() { Address = address, LastRegister = DateTime.Now }; if (RegisterPull.dictionary.ContainsKey(address)) RegisterPull.dictionary[address] = clientDetails; else RegisterPull.dictionary.TryAdd(address, clientDetails); RegisterPull.SaveDataToRegistry(); // 同步到注册表 }
注意事项:
- 注册表操作需要对应权限,
HKEY_CURRENT_USER路径下的操作无需管理员权限。 - 存储数据格式需统一,避免解析失败。
结论
两种方案均无需使用SQL等外部数据库,可实现应用重启后保留注册状态,C++通过COM调用RegisterManager的IsRegister和Register方法即可正常获取/修改注册结果。
内容的提问来源于stack exchange,提问作者Alex Soryn
相关产品推荐
相关产品推荐

