У нас также были некоторые проблемы много лет назад с 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
, создает параметры реестра для COM-объекта локального EXE-сервера, и его можно сравнить с regasm, если вы используете REGEDIT
и найдете соответствующие ключи.
Я прокомментировал три вызова метода \\Registry.ClassesRoot.CreateSubKey
, поскольку это было еще одной причиной, по которой нам нужно было зарегистрировать тип самостоятельно, поскольку это был OPC-сервер, и сторонние OPC-клиенты используют эти реализованные категории для поиска совместимых OPC-серверов. REGASM не добавит их для нас, если мы сами не выполним эту работу.
Вы можете легко увидеть, как это работает, если поставить точки останова на функции при запуске.
В нашей реализации использовался интерфейс, который уже был зарегистрирован в COM. Для подачи заявления вам необходимо: -
- Расширьте методы регистрации, перечисленные выше, чтобы зарегистрировать интерфейс с помощью COM
- Или создайте отдельную DLL с определением интерфейса, а затем экспортируйте это определение интерфейса в библиотеку типов и зарегистрируйте его, как описано в ссылке StackOverflow, которую вы добавили в вопросе.