Как создать COM-объект из свободной библиотеки COM C#, зарегистрированной без регистрации, в неуправляемой / нативной DLL C ++ - PullRequest
0 голосов
/ 16 января 2020

Я в процессе создания плагина Heartrate Monitor для OBS Studio. Идея состоит в том, чтобы получить доступ к службам Bluetooth GATT на стороне C#, чтобы получить данные от Smart BT HRM, обработать их и затем передать их в C ++ dll, который действует как сам плагин OBS Studio.

Мне удалось чтобы все заработало, и я могу вызвать функцию на стороне C# из C ++, которая возвращает массив с данными изображения, который я впоследствии могу передать OBS. Однако для этого требуется, чтобы DLL-библиотека C# была зарегистрирована. Я бы предпочел не загрязнять реестр бесполезной информацией DLL, поэтому я заглянул в регистрацию Free COM. (Это также сделало бы распространение / установку много проще)

После долгих хлопот я зашел в тупик. С sxstrace я знаю, что параллельная система подбирает нужную библиотеку:

=================
Begin Activation Context Generation.
Input Parameter:
    Flags = 0
    ProcessorArchitecture = AMD64
    CultureFallBacks = en-US;en
    ManifestPath = C:\Program Files\obs-studio\obs-plugins\64bit\HrmPlugin.dll
    AssemblyDirectory = C:\Program Files\obs-studio\obs-plugins\64bit\
    Application Config File = 
-----------------
INFO: Parsing Manifest File C:\Program Files\obs-studio\obs-plugins\64bit\HrmPlugin.dll.
    INFO: Manifest Definition Identity is HrmPlugin,processorArchitecture="AMD64",type="win32",version="1.0.0.0".
    INFO: Reference: OBSBluetoothHeartrate,processorArchitecture="*",type="win32",version="1.0.0.0"
INFO: Resolving reference OBSBluetoothHeartrate,processorArchitecture="*",type="win32",version="1.0.0.0".
    INFO: Resolving reference for ProcessorArchitecture AMD64.
        INFO: Resolving reference for culture Neutral.
            INFO: Applying Binding Policy.
                INFO: No binding policy redirect found.
            INFO: Begin assembly probing.
                INFO: Did not find the assembly in WinSxS.
                INFO: Attempt to probe manifest at C:\Program Files\obs-studio\obs-plugins\64bit\OBSBluetoothHeartrate.DLL.
                INFO: Manifest found at C:\Program Files\obs-studio\obs-plugins\64bit\OBSBluetoothHeartrate.DLL.
            INFO: End assembly probing.
INFO: Resolving reference OBSBluetoothHeartrate.mui,language="*",processorArchitecture="AMD64",type="win32",version="1.0.0.0".
    INFO: Resolving reference for ProcessorArchitecture AMD64.
        INFO: Resolving reference for culture en-US.
            INFO: Applying Binding Policy.
                INFO: No binding policy redirect found.
            INFO: Begin assembly probing.
                INFO: Did not find the assembly in WinSxS.
                INFO: Did not find manifest for culture en-US.
            INFO: End assembly probing.
        INFO: Resolving reference for culture en.
            INFO: Applying Binding Policy.
                INFO: No binding policy redirect found.
            INFO: Begin assembly probing.
                INFO: Did not find the assembly in WinSxS.
                INFO: Did not find manifest for culture en.
            INFO: End assembly probing.
INFO: Parsing Manifest File C:\Program Files\obs-studio\obs-plugins\64bit\OBSBluetoothHeartrate.DLL.
    INFO: Manifest Definition Identity is OBSBluetoothHeartrate,processorArchitecture="AMD64",type="win32",version="1.0.0.0".
INFO: Activation Context generation succeeded.
End Activation Context Generation.

Однако, когда я пытаюсь создать класс из самого COM-объекта, я получаю класс, не найдена ошибка. Код для его создания выглядит следующим образом:

static void Initialize(hrm_source *context)
{
    if (context->init_failed) return;

    try
    {
        HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
        IHrmPluginPtr pHRM(CLSID_HrmPlugin);
        context->pHRM = pHRM;
    }
    catch (_com_error _com_err)
    {
        MessageBox(NULL, _com_err.ErrorMessage(), L"Failed to initialize COM", MB_OK);
        context->pHRM = NULL;
        context->init_failed = (context->pHRM == NULL);
    }
}

Проблема в том, что все это работает, если либо A) я регистрирую сборку, либо B) я использую из exe. На самом деле, если я изменяю тип вывода .exe, добавляю main () к plugin-dll-source и создаю его оттуда, я могу запустить exe, и он работает правильно.

                 | C++ Exe | C++ DLL 
-------------------------------------
Registered       | Works   | Works
Register Free    | Works   | Doesn't Work

Там это, очевидно, какой-то флаг конфигурации (ну, скрестив пальцы) или параметр или атрибут, отсутствующий в файлах манифеста.

Итак, вот вопрос: как заставить регистрацию без COM работать с управляемым C# COM-сервером и собственной C ++ DLL в качестве COM-клиента?

Вот также файлы манифеста:

COM-сервер (C# DLL)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32"
                    name="OBSBluetoothHeartrate" 
                    version="1.0.0.0" 
                    processorArchitecture="amd64"/>
  <clrClass clsid="{54667DC4-13CD-4D25-BB9E-C3BDDFAF019F}" 
            progid="OBSBluetoothHeartrate.HrmPlugin" 
            threadingModel="Both" 
            name="OBSBluetoothHeartrate.HrmPlugin" 
            runtimeVersion="v4.0.30319"/>
</assembly>

COM-клиент (C ++ DLL )

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32"
                    name="HrmPlugin"
                    version="1.0.0.0"
                    processorArchitecture="amd64"/>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32"
                        name="OBSBluetoothHeartrate"
                        version="1.0.0.0"
                        processorArchitecture="amd64"/>
    </dependentAssembly>
  </dependency>
</assembly>

Обновление от 17 января 2020

Я также добавил AssemblyIdentity в C ++ DLL на случай, если это поможет (как за вопрос в комментариях). Единственное, что изменилось, так это то, что теперь, когда он загружается параллельной системой, он получает идентификацию вместо (null) - идентификация не требуется, поскольку я не использую C ++ DLL в качестве COM-сервера. Однако я обновил манифест C ++ DLL и журнал sxstrace - теперь это технически «более правильно», поэтому, по крайней мере, мы знаем, что это не причина.

Обновление от 29 января 2020

Я случайно скопировал манифест DLL-сервера DLL C# как манифест C ++ DLL-клиента C ++. Теперь это обновлено до правильного. Удостоверьтесь, что дважды проверили это, но класс все еще не найден, даже если sxstrace говорит, что правильный dll найден и загружен. Я попробую создать контекст активации вручную, который был предоставлен как ответ, и доложу (и, конечно, пометьте его как правильный ответ, если я смогу заставить его работать с этим)

1 Ответ

0 голосов
/ 27 января 2020

Вот скелет ответа, использующего контексты ручной активации ... функция ReportError () является заглушкой ... делайте с ней что хотите ... регистрируйте, отслеживайте, окно сообщения или удаляйте его ...

   ACTCTX ctx = { sizeof(ctx),  ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID, _T("NameOfYourDotNet.manifest")};
   ctx.lpAssemblyDirectory = szPath; // path is drive and directory where your COM DLL is


   HANDLE hCtx = CreateActCtx(&ctx);
   if (hCtx != INVALID_HANDLE_VALUE)
   {
      DWORD_PTR dwCookie = 0;
      BOOL bActivated = ActivateActCtx(hCtx, &dwCookie);
      if (bActivated)
      {
         wcout << L"After activating ActCtx..." << endl;


     // Do stuff with your .NET COM server here

         DeactivateActCtx(0, dwCookie);
      }
      else
      {
         ReportError(L"Could not activate context: ", GetLastError());
      }
      ReleaseActCtx(hCtx);
   }
   else
   {
      ReportError(_T("Problem creating ActCtx: "), GetLastError());

   }
...