如何将.NET 5现有进程内COM服务器改造为进程外COM服务器
.NET Core/.NET 进程内COM服务器改造为进程外实现指南
核心前提说明
进程外COM服务器必须提供类型库(TLB),这是COM跨进程参数列集的固有要求,和.NET实现无关,但你不需要手写IDL文件,用.NET自带工具即可从现有ComVisible代码直接生成TLB。
改造必备步骤
你已有的进程内COM代码不需要大改,只需按以下步骤调整即可:
- 第一步:给所有公开的COM接口、实现类显式标注固定
[Guid]特性,禁止.NET自动生成GUID,避免每次编译后ID变化导致注册失效 - 第二步:将原类库项目调整为可执行项目(控制台程序、Windows服务均可,作为进程外COM的宿主),保留原有
[ComVisible(true)]、<EnableComHosting>true</EnableComHosting>配置 - 第三步:补充进程外COM必须的类工厂实现、类工厂注册逻辑、Windows消息循环(用于处理COM跨进程调用请求)
- 第四步:用工具生成TLB文件
- 第五步:卸载原有进程内COM注册,完成进程外COM注册
简化代码示例
已有COM接口和实现类(无需大改,仅补充Guid即可)
using System.Runtime.InteropServices; // COM接口,替换为你自己生成的GUID [ComVisible(true)] [Guid("12345678-1234-1234-1234-1234567890AB")] [InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface IMyComService { // 你的现有方法,此处仅为示例 int Add(int a, int b); } // COM实现类,替换为你自己生成的GUID [ComVisible(true)] [Guid("87654321-4321-4321-4321-BA0987654321")] [ClassInterface(ClassInterfaceType.None)] public class MyComService : IMyComService { public int Add(int a, int b) => a + b; }
新增进程外COM宿主逻辑
using System.Runtime.InteropServices; namespace ComServerHost { class Program { // Win32 API导入 [DllImport("ole32.dll")] private static extern int CoRegisterClassObject( [MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [MarshalAs(UnmanagedType.IUnknown)] object pUnk, uint dwClsContext, uint flags, out uint lpdwRegister); [DllImport("ole32.dll")] private static extern int CoRevokeClassObject(uint dwRegister); [DllImport("user32.dll")] private static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); [DllImport("user32.dll")] private static extern IntPtr DispatchMessage([In] ref MSG lpmsg); [StructLayout(LayoutKind.Sequential)] private struct MSG { public IntPtr hwnd; public uint message; public IntPtr wParam; public IntPtr lParam; public uint time; public POINT pt; } [StructLayout(LayoutKind.Sequential)] private struct POINT { public int x; public int y; } private const uint CLSCTX_LOCAL_SERVER = 0x4; private const uint REGCLS_MULTIPLEUSE = 0x2; static void Main(string[] args) { // 替换为你自己的COM实现类的GUID Guid comClsid = new Guid("87654321-4321-4321-4321-BA0987654321"); uint registerCookie; // 注册COM类工厂 int hr = CoRegisterClassObject( comClsid, new ComClassFactory<MyComService>(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, out registerCookie); if (hr < 0) { Marshal.ThrowExceptionForHR(hr); return; } try { // 启动消息循环处理COM请求 MSG msg; while (GetMessage(out msg, IntPtr.Zero, 0, 0)) { DispatchMessage(ref msg); } } finally { // 退出前撤销类工厂注册 CoRevokeClassObject(registerCookie); } } } // 通用COM类工厂实现,无需修改 [ComVisible(false)] internal class ComClassFactory<T> : IClassFactory where T : new() { public int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject) { ppvObject = IntPtr.Zero; if (pUnkOuter != IntPtr.Zero) { return unchecked((int)0x80040110); // CLASS_E_NOAGGREGATION 不支持聚合 } T instance = new T(); IntPtr unkPtr = Marshal.GetIUnknownForObject(instance); try { return Marshal.QueryInterface(unkPtr, ref riid, out ppvObject); } finally { Marshal.Release(unkPtr); } } public int LockServer(bool fLock) { return 0; // S_OK 简单场景直接返回成功即可 } } // IClassFactory系统接口定义,无需修改 [ComImport] [Guid("00000001-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IClassFactory { [PreserveSig] int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject); [PreserveSig] int LockServer(bool fLock); } }
生成与注册步骤
- 先卸载原有进程内COM注册:
regsvr32 /u <原项目名>.comhost.dll - 编译项目得到宿主exe和对应程序集dll
- 生成TLB文件:打开VS开发者命令提示符,执行
tlbexp <你的程序集名称>.dll /out:MyCom.tlb - 一键注册:执行
regasm <你的程序集名称>.dll /tlb:MyCom.tlb /codebase,会自动完成TLB注册、LocalServer32注册表项配置,无需手动改注册表
测试时客户端直接按照原有方式创建COM对象即可,系统会自动启动你的COM宿主exe进程。
内容的提问来源于stack exchange,提问作者cogumel0
相关产品推荐
相关产品推荐

