Crear COM fuera de proceso en C # /. Net?

Necesito crear un servidor COM fuera de proceso (.exe) en C # al que accederán muchos otros procesos en el mismo cuadro. El componente tiene que ser un proceso único porque almacenará en memoria caché la información que proporciona a sus consumidores.

Nota: los procesos que accederán a mi servidor COM son en su mayoría procesos de Matlab, por lo tanto, la necesidad de una interfaz COM.

He visto temas relacionados con la creación de componentes COM en proceso en .Net en desbordamiento de stack ( Crear COM … ) y en la web, pero estoy teniendo dificultades para encontrar una manera de crear componentes fuera de proceso con .Net .

¿Cómo se puede lograr esto? ¿Alguna referencia sugerida?

Gracias.

Una opción son los componentes de servicio , es decir, alojarlo en COM + como el administrador de shell. Vea también el howto aquí .

Nosotros también tuvimos algunos problemas hace muchos años con regasm y ejecutamos la clase COM como un servidor EXE local.

Esto es un poco complicado y agradecería cualquier sugerencia para hacerlo más elegante. ¡Fue implementado para un proyecto en .NET 1.0 días y no ha sido tocado desde entonces!

Básicamente, realiza un registro regasm cada vez que se inicia la aplicación (debe ejecutarse una vez para realizar las entradas de registro antes de que el objeto COM se cree una instancia en la aplicación de contenedor COM).

Copié los siguientes bits importantes de nuestra implementación y cambié el nombre de algunas clases para ilustrar el ejemplo.

El siguiente método se llama desde el evento Form Loaded para registrar la clase COM (renombrado a MyCOMClass para este ejemplo)

 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 ); } } 

Para el tipo en cuestión, MyCOMObject , necesitará algunos atributos especiales para ser compatible con COM. Un atributo importante es especificar un GUID fijo; de lo contrario, cada vez que compile el registro se llenará con GUID COM huérfanos. Puede usar el menú Herramientas en VisualStudio para crear un GUID único.

  [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); } } 

El método InitialiseCOM en el formulario principal utiliza RegistrationServices para registrar el tipo. A continuación, el marco utiliza la reflexión para encontrar el método marcado con el atributo ComRegisterFunction y las llamadas que funcionan con el tipo que se está registrando.

El método marcado ComRegisterFunction , hand crea la configuración de registro para un objeto COM del servidor EXE local y esto se puede comparar con regasm si usa REGEDIT y encuentra las claves en cuestión.

Comenté las tres llamadas al método \\Registry.ClassesRoot.CreateSubKey , ya que este era otro motivo por el que necesitábamos registrar el tipo nosotros mismos, ya que este era un servidor OPC y los clientes OPC de terceros utilizan estas categorías implementadas para buscar servidores OPC compatibles. REGASM no nos agregaría esto a menos que hiciéramos el trabajo nosotros mismos.

Puedes ver cómo funciona esto fácilmente si pones puntos de corte en las funciones cuando está comenzando.

Nuestra implementación utilizó una interfaz que ya estaba registrada en COM. Para su aplicación, necesitará:

  1. Extienda los métodos de registro enumerados anteriormente para registrar la interfaz con COM
  2. O cree una DLL separada con la definición de interfaz y luego exporte esa definición de interfaz a una biblioteca de tipos y registre eso como se explica en el enlace StackOverflow que agregó en la pregunta.

La respuesta oficial está en el artículo de KB Cómo desarrollar un componente COM fuera de proceso mediante el uso de Visual C ++, Visual C # o Visual Basic .NET .

IMO, una de las formas en que esto se puede hacer es crear un COM Dll normal según el método que mencionó en el enlace y luego de registrar su dll COM, cambiarlo a un DLL sustituto. Esto se puede hacer muy fácilmente a través de la utilidad OLEView, aunque también puede hacerlo manualmente cambiando las entradas de registro a través del método mencionado en http://msdn.microsoft.com/en-us/library/ms686606(VS.85 ) .aspx .

Al hacer esto una DLL sustituta, se ejecutará en su propio dllhost.exe y, por lo tanto, estará fuera de proceso.

ActiveX.NET , es una verdadera implementación de servidor COM fuera de progtwig (EXE) en C # .NET. Éste tiene una implementación más limpia en comparación con el CSExeCOMServer original, publicado en Code.MSDN.

ActiveX.NET tiene características como usar un .NET Message Pump (en lugar de nativo) y utiliza el modelo de complemento MEF, por lo que el servidor EXE está desacoplado y se puede compartir entre múltiples complementos COM, que se pueden desarrollar de forma independiente

Puede utilizar RegistrationServices.RegisterTypeForComClients, que es el equivalente administrado de CoRegisterClassObject; para ver el código de ejemplo, consulte http://www.andymcm.com/blog/2009/10/managed-dcom-server.html.

Se puede hacer usando un marco ATL administrado u orientándolo con el código administrado (simple al cambiar las propiedades del proyecto resultante a / clr).

Aquí hay fragmentos ilustrativos:

 .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 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; } } 

La parte C # (clase MyCSharpClass) vive en un proyecto separado con el tipo de salida Class Library.

Utilice el indicador REGCLS_MULTIPLEUSE con CoRegisterClassObject () para hacer que el coclass sea una clase multiuso. Aquí hay más información: http://support.microsoft.com/kb/169321 .

El proyecto de estudio visual “CSExeCOMServer” que puede encontrar aquí (All-in-One), brinda un ejemplo completo.