我需要在C#中创建一个进程外COM服务器(.exe),它将被同一个盒子上的多个其他进程访问。该组件必须是一个进程,因为它会将它提供的信息缓存到内存中的消费者。
注意:访问我的COM服务器的进程主要是Matlab进程,因此COM接口的必需。
我已经看到了关于在堆栈溢出(Create COM ...)和Web上创建.Net中的进程内COM组件的线程,但我很难找到创建进程外组件的方法与.Net。
这是如何实现的?有建议的参考文献吗?
感谢。
答案 0 :(得分:12)
多年前我们也遇到了一些问题,包括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
标记方法,hand为本地EXE服务器COM对象创建注册表设置,如果您使用REGEDIT
并查找相关密钥,则可以将此与regasm进行比较。
我已经注释掉了三个\\Registry.ClassesRoot.CreateSubKey
方法调用,因为这是我们自己注册类型的另一个原因,因为这是OPC服务器,第三方OPC客户端使用这些实现的类别来扫描兼容的OPC服务器。除非我们自己完成工作,否则REGASM不会为我们添加这些内容。
如果你在函数开始时给它们设置断点,你可以很容易地看到它是如何工作的。
我们的实现使用了已在COM注册的接口。对于您的应用,您需要: -
答案 1 :(得分:11)
官方答案在KB文章How to develop an out-of-process COM component by using Visual C++, Visual C#, or Visual Basic .NET中。
答案 2 :(得分:5)
IMO,其中一种方法是按照你在链接中提到的方法创建一个普通的COM Dll,然后在注册你的COM dll之后,将它改为代理DLL。这可以通过OLEView实用程序轻松完成,但您也可以通过更改注册表项以及http://msdn.microsoft.com/en-us/library/ms686606(VS.85).aspx中提到的方法手动完成。
通过使它成为代理DLL,它将在它自己的dllhost.exe中运行,因此将处于进程外。
答案 3 :(得分:5)
一个选项是serviced components - 即将它作为shell exe托管在COM +中。另请参阅howto here。
答案 4 :(得分:5)
ActiveX.NET,是C#.NET中真正的Out-Of-Proc(EXE)COM服务器实现。与在Code.MSDN中发布的原始CSExeCOMServer相比,这个实现更清晰。
ActiveX.NET 具有使用.NET消息泵(而不是本机)并使用MEF插件模型的功能,因此EXE服务器可以解耦并可以在多个COM插件之间共享,可以独立开发
答案 5 :(得分:3)
您可以使用RegistrationServices.RegisterTypeForComClients,它是CoRegisterClassObject的托管等效项 - 示例代码见http://www.andymcm.com/blog/2009/10/managed-dcom-server.html
答案 6 :(得分:2)
可以使用非托管ATL框架并使用托管代码进行管理 (通过将结果项目属性更改为/ clr来简单)。
以下是说明性摘要:
.H-part:
\#include < vcclr.h >
\#using < MyCSharpModule.dll >
using namespace System;
class ATL_NO_VTABLE MyCSharpProxyServer :
public CComObjectRootEx< CComMultiThreadModel >,
.....
{
HRESULT FinalConstruct();
STDMETHODIMP LoadMyFile(BSTR FileName);
.....
gcroot<MyCSNamespace::MyCSharpClass^> m_CSClass;
}
.CPP-part:
using namespace System::Collections;
using namespace MyCSNamespace;
HRESULT MyCSharpProxyServer::FinalConstruct()
{
m_CSClass = gcnew MyCSharpClass();
}
STDMETHODIMP MyCSharpProxyServer::LoadMyFile(BSTR FileName)
{
try {
int hr = m_CSClass->LoadFile(gcnew String( FileName));
return hr;
}
catch( Exception^ e ) {
return E_FAIL;
}
}
C#部分(MyCSharpClass类)位于一个单独的项目中,输出类型为Class Library。
答案 7 :(得分:2)
视觉工作室项目&#34; CSExeCOMServer&#34;你可以找到here(All-in-One),给出一个完整的例子。
答案 8 :(得分:1)
将标志REGCLS_MULTIPLEUSE与CoRegisterClassObject()一起使用,使coclass成为一个多用途类。以下是更多信息:http://support.microsoft.com/kb/169321。