我需要在C#中创建一个进程外COM服务器(.exe),它将被同一个盒子上的多个其他进程访问.组件必须是单个进程,因为它会将其提供的信息缓存到内存中的使用者.
注意:访问我的COM服务器的进程主要是Matlab进程,因此需要 COM接口.
我已经看到了关于在.Net上创建进程内COM组件的线程溢出(创建COM ...)和Web上的线程,但我很难找到一种方法来创建.Net的进程外组件.
这怎么可以实现?任何建议的参考?
谢谢.
多年前我们也遇到了一些问题,包括regasm并将COM类作为本地EXE服务器运行.
这是一个黑客,我欢迎任何建议,使其更优雅.它是在.NET 1.0天后的一个项目中实现的,从那时起就没有被触及过!
基本上,每次应用程序启动时它都会执行一种regasm注册方式(在COM容器应用程序中实例化COM对象之前,需要运行一次以生成注册表项).
我从我们的实现中复制了以下重要的部分,并重命名了几个类来说明这个例子.
从Form Loaded事件调用以下方法来注册COM类(MyCOMClass
为此示例重命名为)
private void InitialiseCOM() { System.Runtime.InteropServices.RegistrationServices services = new System.Runtime.InteropServices.RegistrationServices(); try { System.Reflection.Assembly ass = Assembly.GetExecutingAssembly(); services.RegisterAssembly(ass, System.Runtime.InteropServices.AssemblyRegistrationFlags.SetCodeBase); Type t = typeof(MyCOMClass); try { Registry.ClassesRoot.DeleteSubKeyTree("CLSID\\{" + t.GUID.ToString() + "}\\InprocServer32"); } catch(Exception E) { Log.WriteLine(E.Message); } System.Guid GUID = t.GUID; services.RegisterTypeForComClients(t, ref GUID ); } catch ( Exception e ) { throw new Exception( "Failed to initialise COM Server", e ); } }
对于有问题的类型MyCOMObject
,需要一些特殊属性才能兼容COM.一个重要的属性是指定一个固定的GUID,否则每次编译注册表都会填满孤立的COM GUID.您可以使用VisualStudio中的"工具"菜单为您创建唯一的GUID.
[GuidAttribute("D26278EA-A7D0-4580-A48F-353D1E455E50"), ProgIdAttribute("My PROGID"), ComVisible(true), Serializable] public class MyCOMClass : IAlreadyRegisteredCOMInterface { public void MyMethod() { } [ComRegisterFunction] public static void RegisterFunction(Type t) { AttributeCollection attributes = TypeDescriptor.GetAttributes(t); ProgIdAttribute ProgIdAttr = attributes[typeof(ProgIdAttribute)] as ProgIdAttribute; string ProgId = ProgIdAttr != null ? ProgIdAttr.Value : t.FullName; GuidAttribute GUIDAttr = attributes[typeof(GuidAttribute)] as GuidAttribute; string GUID = "{" + GUIDAttr.Value + "}"; RegistryKey localServer32 = Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\LocalServer32", GUID)); localServer32.SetValue(null, t.Module.FullyQualifiedName); RegistryKey CLSIDProgID = Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\ProgId", GUID)); CLSIDProgID.SetValue(null, ProgId); RegistryKey ProgIDCLSID = Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}", ProgId)); ProgIDCLSID.SetValue(null, GUID); //Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\Implemented Categories\\{{63D5F432-CFE4-11D1-B2C8-0060083BA1FB}}", GUID)); //Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\Implemented Categories\\{{63D5F430-CFE4-11d1-B2C8-0060083BA1FB}}", GUID)); //Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\Implemented Categories\\{{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}}", GUID)); } [ComUnregisterFunction] public static void UnregisterFunction(Type t) { AttributeCollection attributes = TypeDescriptor.GetAttributes(t); ProgIdAttribute ProgIdAttr = attributes[typeof(ProgIdAttribute)] as ProgIdAttribute; string ProgId = ProgIdAttr != null ? ProgIdAttr.Value : t.FullName; Registry.ClassesRoot.DeleteSubKeyTree("CLSID\\{" + t.GUID + "}"); Registry.ClassesRoot.DeleteSubKeyTree("CLSID\\" + ProgId); } }
InitialiseCOM
主窗体中的方法RegistrationServices
用于注册类型.然后,框架使用反射来查找使用该ComRegisterFunction
属性标记的方法,并使用要注册的类型调用该函数.
该ComRegisterFunction
标记的方法,一方面为本地EXE服务器的COM对象创建的注册表设置,这可以与regasm如果使用进行对比REGEDIT
,发现有问题的钥匙.
我已经注释掉了三个\\Registry.ClassesRoot.CreateSubKey
方法调用,因为这是我们自己需要注册类型的另一个原因,因为这是OPC服务器,第三方OPC客户端使用这些实现的类别来扫描兼容的OPC服务器.除非我们自己完成工作,否则REGASM不会为我们添加这些内容.
如果在函数启动时对函数设置断点,则可以很容易地看到它是如何工作的.
我们的实现使用了已在COM中注册的接口.对于您的应用,您将需要: -
扩展上面列出的注册方法以向COM注册接口
或者使用接口定义创建一个单独的DLL,然后将该接口定义导出到类型库,并按照您在问题中添加的StackOverflow链接中的说明进行注册.
官方答案在KB文章中如何使用Visual C++,Visual C#或Visual Basic .NET开发进程外COM组件.
IMO,可以这样做的方法之一是按照你在链接中提到的方法创建一个普通的COM Dll,然后在注册你的COM dll后,将它改为代理DLL.这可以通过OLEView实用程序轻松完成,尽管您也可以通过更改注册表项以及http://msdn.microsoft.com/en-us/library/ms686606(VS.85)中提到的方法手动完成此操作.).aspx.
通过使这个代理DLL,它将在它自己的dllhost.exe中运行,因此将是进程外的.
一个选项是服务组件 - 即将它作为shell exe托管在COM +中.另见这里的方法.
ActiveX.NET是C#.NET中真正的 Pro(COM)COM Server实现。与在 Code.MSDN中发布的原始 CSExeCOMServer相比,此实现更干净。
ActiveX.NET具有一些功能,例如它确实使用.NET消息泵(而不是本机)并使用MEF插件模型,因此EXE服务器是分离的,可以在多个COM插件之间共享,可以独立开发