Подтвердить что ты не робот

Создать Out-Of-Process COM в С#/. Net?

Мне нужно создать COM-сервер вне процесса (.exe) в С#, к которому будут доступны несколько других процессов в одном и том же поле. Компонент должен быть одним процессом, поскольку он кэширует информацию, которую он предоставляет своим потребителям в памяти.

Примечание. Процессы, которые будут обращаться к моему серверу COM, - это в основном процессы Matlab, поэтому необходимость для COM-интерфейса.

Я видел потоки относительно создания COM-компонентов in-process в .Net при переполнении стека (Создать COM...) и в Интернете, но у меня есть трудно найти способ создания внепроцессных компонентов с помощью .Net.

Как это достижимо? Любые предлагаемые ссылки?

Спасибо.

4b9b3361

Ответ 2

У нас также были некоторые проблемы много лет назад с 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-идентификаторами COM. Вы можете использовать меню "Инструменты" в 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 вручную создает параметры реестра для COM-объекта Local EXE Server, и это можно сравнить с regasm, если вы используете REGEDIT и находите соответствующие ключи.

Я прокомментировал три метода вызова \\Registry.ClassesRoot.CreateSubKey, поскольку это была еще одна причина, по которой нам нужно было регистрировать тип самостоятельно, поскольку это был OPC-сервер, а сторонние OPC-клиенты использовали эти реализованные категории для сканирования совместимых OPC-серверов. REGASM не добавит их нам, если мы сами не сделаем эту работу.

Вы можете легко увидеть, как это работает, если вы положите точки останова на функции при запуске.

В нашей реализации использовался интерфейс, который уже был зарегистрирован в COM. Для вашего приложения вам потребуется либо: -

  • Расширьте перечисленные выше методы регистрации, чтобы зарегистрировать интерфейс с помощью COM
  • Или создайте отдельную DLL с определением интерфейса, а затем экспортируйте это определение интерфейса в библиотеку типов и зарегистрируйте это, как описано в ссылке StackOverflow, добавленной в вопрос.

Ответ 4

IMO, один из способов, которым это можно сделать, - создать обычную COM-Dll в соответствии с методом, указанным в ссылке, а затем после регистрации вашей DLL-библиотеки заменить его на суррогатную DLL. Это можно сделать очень легко с помощью утилиты OLEView, хотя вы также можете сделать это вручную, изменив записи реестра, а также метод, упомянутый в http://msdn.microsoft.com/en-us/library/ms686606(VS.85).aspx.

Сделав это суррогатной DLL, она запустит в нее собственный dllhost.exe и, следовательно, будет вне процесса.

Ответ 5

Это можно сделать, используя устаревшую структуру 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;
    }
}

Часть С# (класс MyCSharpClass) живет в отдельном проекте с типом Class Class типа вывода.

Ответ 6

Вы можете использовать RegistrationServices.RegisterTypeForComClients, который является управляемым эквивалентом CoRegisterClassObject - для примера кода см. http://www.andymcm.com/blog/2009/10/managed-dcom-server.html

Ответ 8

Проект визуальной студии "CSExeCOMServer", который вы можете найти здесь ( "Все-в-одном" ), дает полный пример.

Ответ 9

ActiveX.NET - это настоящая реализация COM-сервера Out-Of-Proc (EXE) в С#.СЕТЬ. У этого есть более чистая реализация по сравнению с оригинальным CSExeCOMServer, опубликованным в Code.MSDN.

ActiveX.NET имеет такие функции, как он использует .NET Message Pump (вместо собственного) и использует модель плагина MEF, так что EXE-сервер отключен и может использоваться совместно несколькими плагинами COM, которые могут быть разработаны независимо