c # — управляемый Reg-Free COM-сервер не активируется

Я начал с очень сложной системы клиентов и серверов с ссылками на COM и другими вещами, и урезал, пока не понял, что не могу даже заставить пример кода Microsoft работать для бесплатной регистрации COM-активации управляемого COM-сервера. написано в C #.

Код сервера:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace ClassLibrary1
{
[Guid("A7AC6D8C-FF17-4D2C-A3B1-2C8690A8EA04")
,ComVisible(true)]
public interface IClass1
{
[DispId(1)]
string DummyFunction(string inputValue);
}

[Guid("81723475-B5E3-4FA0-A3FE-6DE66CEE211C"),
ClassInterface(ClassInterfaceType.None),
ComDefaultInterface(typeof(IClass1)),
ComVisible(true)]
public class Class1 : IClass1
{
public string DummyFunction(string inputValue)
{
return inputValue.Substring(0, 1) + " Inserted " + inputValue.Substring(1);
}
}
}

Код клиента VB6:

Dim c As ClassLibrary1.Class1
Set c = New Class1
MsgBox c.DummyFunction("Ben")

Код клиента C ++:

#include "stdafx.h"
#import <ClassLibrary1.tlb> raw_interfaces_only

using namespace ClassLibrary1;

int _tmain(int argc, _TCHAR* argv[])
{
IClass1Ptr p;

HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = CoCreateInstance(__uuidof(Class1), NULL, CLSCTX_INPROC_SERVER, __uuidof(IClass1), (void **)&p);
if (FAILED(hr))
{
_tprintf_s(_T("Error %x\n"), hr);
CoUninitialize();
return 1;
}
_bstr_t b = _T("Bobby");
BSTR b2;
p->DummyFunction(b, &b2);
wprintf_s(L"%s\n", b2);
p->Release();
CoUninitialize();
return 0;
}

Оба клиента работают нормально, когда я удаляю весь Reg-Free COM-код и регистрирую ClassLibrary1.dll с помощью regasm / codebase.

Затем я отменяю регистрацию ClassLibrary1 и пытаюсь представить Reg-Free COM для клиента VB6 с файлом Project1.exe.manifest:

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

И ClassLibrary1.manifest:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="ClassLibrary1" />
<clrClass clsid="{81723475-B5E3-4FA0-A3FE-6DE66CEE211C}" name="ClassLibrary1.Class1" tlbid="{F8A2D334-5BBB-4007-8308-A1417052E6D6}"></clrClass>
<file name="ClassLibrary1.dll" ></file>
</assembly>

Теперь я получаю ошибку 429 (компонент ActiveX не может создать объект) иногда и (необъяснимо) ошибку автоматизации в других случаях:

Ошибка времени выполнения ‘-2146234304 (80131040)’:
Ошибка автоматизации

затем я пытаюсь ввести COM Isolation в клиент C ++:
Изолированная библиотека типов COM = F: \ Lib \ ClassLibrary1.tlb - Имя файла компонента = ClassLibrary1.dll

Теперь, когда я запускаю клиент C ++, вывод просто

Ошибка 800401f9

7

Решение

После многих испытаний, работающих с различными образцами при поддержке Microsoft, я определил много Подводные камни, возникающие при попытке реализовать управляемый COM-сервер с неуправляемым C ++ COM-клиентом. Вот ключевые фрагменты информации, которые я помню, которые можно применить к примеру кода в вопросе, чтобы убедиться, что он работает.

  1. Клиент не должен использовать параметры изолированного COM в проекте C ++. Моя память исчезает, но поддержка Microsoft говорит мне, что это для чего-то другого — я думаю для разработки изолированного COM-интерфейса для неуправляемого COM-сервера вместо управляемого COM-сервера. Хотя это не совсем понятно из его описания в https://msdn.microsoft.com/en-us/library/zzbcs3x5(v=vs.120).aspx.
  2. Клиент может выбрать «Да» или «Нет» для параметра «Встроить манифест», но если выбрано «Да», то манифест, который включает в себя информацию о зависимой сборке, должен быть предоставлен как файл входного манифеста. Если встроенный манифест не содержит зависимой информации о сборке, то любой внешний файл манифеста будет игнорироваться. Также убедитесь, что редактируемая конфигурация (например, Debug) соответствует тестируемой конфигурации!
    введите описание изображения здесь
  3. Если COM-сервер подписан с помощью ключа (со строгим именем), то элемент assemblyIdentity как в манифесте клиента, так и в манифесте сервера должен содержат publicKeyToken, в противном случае ошибка HRESULT 0x80131040 произойдет во время CoCreateInstance.
  4. Внедрение ресурса RT_MANIFEST в качестве ресурса Win32 в управляемый код не так просто с Visual Studio 2013, потому что проекты C # и VB.NET, как правило, хотят встроить ресурсы как управляемые ресурсы .NET, а не ресурсы Win32 (это можно проверить, открыв вывод DLL файл с помощью средства просмотра ресурсов и обратите внимание на то, что исполняемые файлы .NET обычно получают ресурс версии, а не многое другое, даже если в проект включен файл манифеста). Один из способов обойти это — создать RC-файл примерно так:

#define RT_MANIFEST 24
#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST ClassLibrary1.manifest

Затем добавьте шаг предварительной сборки следующим образом:

"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\rc.exe"  "$(ProjectDir)ClassLibrary1.rc"

Затем в настройках проекта на вкладке «Приложение» измените «Ресурсы», чтобы использовать ClassLibrary1.res вместо «Значок и манифест». Но это связано с проблемами: во-первых, путь к RC.EXE нелегко определить без его жесткого кодирования; во-вторых, информация о версии из AssemblyInfoCommon будет игнорироваться, поскольку ресурсы в файле RC полностью заменяют все ресурсы Win32, которые будут сгенерированы компилятором .NET.

Другая возможность состоит в том, чтобы просто хранить файл манифеста COM DLL сервера отдельно и не встраивать его в качестве ресурса. Я читал, что это может быть ненадежным, но он работает на Windows 7 Enterprise 64-bit SP1.

  1. Чтобы гарантировать, что неуправляемый клиент загружает правильную среду выполнения .NET, ему нужен файл конфигурации (ConsoleApplication1.exe.config), который определяет, как загрузить .NET. Для .NET 4.5 я видел эту работу:

<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>

В то время как для .NET 3.5 кажется, что useLegacyV2RuntimeActivationPolicy необходимо переключить:

<configuration>
<startup useLegacyV2RuntimeActivationPolicy="false">
<supportedRuntime version="v3.5"/>
</startup>
</configuration>
  1. Важно убедиться, что все версии фреймворка и CLR синхронизированы. И важно понимать, что версии CLR не совпадают с версиями фреймворка. Например, если вы хотите построить управляемый COM-сервер в .NET 3.5, версия CLR (runtimeVersion) должна быть «2.0.50727», а версия .NET (supportRuntime) должна быть «v3.5».

  2. Убедитесь, что целевая версия .NET Framework COM-сервера соответствует поддерживаемому клиенту Runtime. Если он создается из командной строки, он может не выбирать версию платформы из файла проекта Visual Studio (например, если вы используете компилятор C # VB.NET напрямую, а не вызываете MSBuild), убедитесь, что сборка ориентирована на правильную версию фреймворка.

Я еще не проверил все вышеперечисленное, но собираюсь вскоре пройти весь этот процесс, чтобы убедиться, что я все поймал. Вот что я закончил тем, что еще не упомянул:

ConsoleApplication1.exe.manifest (в исходном каталоге копируется или внедряется в выходной каталог во время сборки)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity
type = "win32"name = "ConsoleApplication1"version = "1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"name="ClassLibrary1"version="1.0.0.0"publicKeyToken="541b4aff0f04b60a"/>
</dependentAssembly>
</dependency>
</assembly>

ClassLibrary1.manifest

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity type="win32" name="ClassLibrary1" version="1.0.0.0" publicKeyToken="541b4aff0f04b60a" />
<clrClass clsid="{81723475-B5E3-4FA0-A3FE-6DE66CEE211C}" progid="ClassLibrary1.Class1" threadingModel="both" name="ClassLibrary1.Class1" runtimeVersion="v2.0.50727"></clrClass>
</assembly>

РЕДАКТИРОВАТЬ:

Теперь, чтобы пройти и проверить каждую деталь с полной информацией об ошибке и т. Д.

Я начинаю с создания единого решения, содержащего два проекта со всеми значениями по умолчанию и кодом, показанным в вопросе. Я начну с файлов манифеста или с любых настроек проекта, упомянутых в вопросе, и буду явно вызывать, когда я внесу эти изменения в процесс ниже. Это шаги и ошибки, которые находятся на пути к созданию этого проекта.

  1. Ошибка: «Class1: необъявленный идентификатор». Необходимо получить командную строку разработчика и выполнить следующую командную строку, чтобы получить файл TLB, который может импортировать код C ++: tlbexp ClassLibrary1.dll
  2. Переместите файл TLB в каталог проекта ConsoleApplication1 и выполните повторную сборку. Та же ошибка
  3. Заменить угловые скобки на #import <ClassLibrary1.tlb> raw_interfaces_only с кавычками, так что читает #import "ClassLibrary1.tlb" raw_interfaces_only, Восстановить: успех.
  4. На этом этапе, если мы бежим, мы получаем Error 80040154 (Класс не зарегистрирован), потому что мы не зарегистрировали компонент и не установили COM без регистрации.
  5. Знание того, что попытка установить изолированную COM в клиенте будет представлять Error 800401f9 мы пропустим это и просто попробуем создать манифест клиента. Создайте новый текстовый файл со следующим содержимым и сохраните его как ConsoleApplication1.exe.manifest в каталоге проекта ConsoleApplication1:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="ConsoleApplication1" version="1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity name="ClassLibrary1" version="1.0.0.0" />
</dependentAssembly>
</dependency>
</assembly>
  1. На данный момент кажется, что шаги, упомянутые ранее в этом решении, немного усложнены. Вы можете просто включить файл манифеста в проект, показывая скрытые файлы и используя команду «Включить в проект» в файле манифеста.
  2. Запуск в этот момент приведет к появлению сообщения об ошибке «Приложение не удалось запустить, поскольку его параллельная конфигурация неверна. Пожалуйста, обратитесь к журналу событий приложения или используйте инструмент командной строки sxstrace.exe для получения более подробной информации». Это отчасти потому, что мы нигде не поместили ClassLibrary1.dll там, где ConsoleApplication1.exe может его найти. Анализируемый вывод sxstrace на этом этапе выглядит так:

INFO: Parsing Manifest File C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ConsoleApplication1.exe.
INFO: Manifest Definition Identity is ConsoleApplication1,type="win32",version="1.0.0.0".
INFO: Reference: ClassLibrary1,version="1.0.0.0"INFO: Resolving reference ClassLibrary1,version="1.0.0.0".
INFO: Resolving reference for ProcessorArchitecture ClassLibrary1,version="1.0.0.0".
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:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1.DLL.
INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1.MANIFEST.
INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1\ClassLibrary1.DLL.
INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1\ClassLibrary1.MANIFEST.
INFO: Did not find manifest for culture Neutral.
INFO: End assembly probing.
ERROR: Cannot resolve reference ClassLibrary1,version="1.0.0.0".
ERROR: Activation Context generation failed.
End Activation Context Generation.
  1. Копирование файла ClassLibrary1.dll в тот же каталог, что и ConsoleApplication1.exe, ничего не меняет, поскольку мы не предоставили никакого манифеста для файла, по которому можно определить зависимость COM. Поэтому следующим шагом является создание манифеста для ClassLibrary1. Одна версия ClassLibrary1.manifest уже присутствует в вопросе. Давайте попробуем это сделать, создав текстовый файл с этим содержимым и сохранив его в каталоге проекта ClassLibrary1 как ClassLibrary1.manifest. Чтобы включить его в проект, давайте попробуем ту же самую простую команду «Включить в проект» (снова включив видимость скрытых файлов, чтобы сделать это возможным). Теперь, что происходит, когда копируется новый ClassLibrary1.dll в каталог с ConsoleApplication1.exe и выполняется?
  2. Та же самая ошибка и результаты sxstrace происходят, потому что файл манифеста в управляемой DLL не внедряется как ресурс Win32, что можно проверить, открыв файл DLL в Visual Studio, которая показывает ресурсы Win32 файла. Показывает версию ресурса и ничего больше. Итак, давайте исключим манифест из ClassLibrary1 и просто скопируем файл манифеста в папку ConsoleApplication1.exe вместо внешнего файла.
  3. Успех! Программа работает и завершается нормально. Но что, если мы хотим использовать компонент, созданный с другой версией .NET Framework? Или, может быть, ваш тест не работает на данный момент, потому что ваша Visual Studio по умолчанию другой версии? Прямо сейчас я вижу, что мой проект ClassLibrary1 по умолчанию установлен на .NET 3.5. Что произойдет, если я изменю его на 4.0, пересоберу, скопирую и снова запустлю?
  4. Ошибка 8013101b. Это соответствует (согласно поиску Google) COR_E_NEWER_RUNTIME, что также означает «Модуль, указанный в манифесте, не найден». Это происходит, когда, например, EXE-файл, который загрузил .NET 2.0, пытается ссылаться на DLL, созданную с помощью .NET 4.0. Так что теперь мы должны сообщить неуправляемому клиентскому EXE, какую версию платформы .NET загружать, когда он разрешает свою ссылку COM. Это делается с помощью файла конфигурации с именем ConsoleApplication1.exe.config. Просто создайте новый текстовый файл и сохраните его с этим именем в каталоге ConsoleApplication1.exe. Он имеет следующее содержание:

<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>

Та же самая ошибка все еще произошла бы, если useLegacyV2RuntimeActivationPolicy были исключены в этом случае. К сожалению, я не совсем понимаю, почему, но я подозреваю, что это как-то связано с более новой политикой активации среды выполнения v4.0, по умолчанию загружающей CLR v2.0, если загружаемый исполняемый файл явно не ссылается на .NET 4.0 (который не управляемого кода нет, потому что он явно не ссылается на период .NET).

  1. Удачи снова! Но подождите, это еще не все. Что делать, если ваша COM-DLL подписана ключом (имеет строгое имя)? Давайте добавим ключ к ClassLibrary1, настроим его для использования при подписывании DLL на вкладке «Подписание» проекта и посмотрим, что произойдет, когда мы скопируем обновленную DLL в каталог ConsoleApplication1.exe.
  2. Теперь мы получаем ошибку 80131040 («Определение манифеста обнаруженной сборки не соответствует ссылке на сборку»). И sxstrace, и fuslogvw, к сожалению, бесполезны здесь, уступая любой информация о том, что происходит. К счастью, теперь я знаю, что в этом конкретном сценарии reg-free-com это вызвано отсутствием атрибута publicKeyToken в элементах assemblyIdentity, описывающих ClassLibrary1 (в обоих файлах манифеста). Но как вы получаете значение publicKeyToken? Бежать sn -T ClassLibrary1.dll из командной строки разработчика. После обновления ClassLibrary1.manifest и ConsoleApplication1.exe.manifest не забудьте перестроить ConsoleApplication1.exe, если манифест встроен, и скопировать ClassLibrary1.manifest в каталог ConsoleApplication1.exe. Запустить снова и?
  3. Я прошел еще несколько кругов решения ошибок с помощью sxstrace, но это было из-за глупых ошибок. В интересах тех, кто делает глупые ошибки, вот еще несколько вещей, о которых следует знать, если вы получаете ошибки sxstrace: a) убедитесь, что вы используете атрибут publicKeyToken а не какое-то другое смешное имя, как privateKeyToken; б) Убедитесь, что все атрибуты, указанные вами в assemblyIdentity на манифесте на стороне сервера, соответствуют атрибутам на манифесте на стороне клиента, и что у вас нет type="win32" указано на одном, но не на другом.
  4. Успех! Выход B Inserted obby

Следует также отметить, что клиент VB6 также работает с использованием следующих файлов вместе с клиентом VB6:

Project1.exe.config:

<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>

Project1.exe.manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="Project1" version="1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity name="ClassLibrary1" version="1.0.0.0" publicKeyToken="541b4aff0f04b60a" />
</dependentAssembly>
</dependency>
</assembly>

Однако, по-видимому, существует тенденция сообщать «Компонент ActiveX не может создать объект» (ошибка времени выполнения 429) в тех редких случаях, когда исполняемый файл создается и запускается до создания файлов конфигурации и манифеста. Восстановление exe-файла, кажется, исправляет это, но тогда я не могу решить проблему, поэтому трудно определить конкретную причину.

Я думал, что я был достаточно хорошим решением проблем, но кое-что из-за многочисленных движущихся частей и многочисленных бесполезных кодов ошибок и сообщений об ошибках, о которых сообщалось в проблемах с конфигурацией без рег. код. Надеюсь, этот ответ поможет другим приобрести аналогичный опыт. Пожалуйста, расширьте этот ответ, если вы узнаете больше!

Манифест управляемого COM-сервера Можно быть правильно и легко внедренным, если вы используете команду «Добавить» -> «Новый элемент …» в проекте, чтобы добавить «Файл манифеста приложения». Это добавляет файл с именем app.manifest в проект. Но настоящая хитрость заключается в том, что это происходит так, что не может быть реплицировано любым другим способом через пользовательский интерфейс Visual Studio, кроме как через один винтик. Поскольку поле «Манифест» на вкладке «Приложение» окна настроек проекта отключено для проектов типа «Библиотека классов», манифест, который обычно устанавливается здесь, не может быть установлен для библиотеки классов. Но вы можете временно изменить проект на приложение Windows, изменить здесь выбор Manifest, а затем восстановить его в библиотеке классов. Настройка будет придерживаться, поэтому выбранный манифест будет правильно встроен. Вы можете проверить настройку в текстовом редакторе, просмотрев файл проекта. Ищу:

<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>

Ошибка 0x80131040 по-прежнему может возникнуть при соблюдении всех вышеуказанных мер предосторожности. Чтобы сузить причину этого, с помощью средства просмотра журнала Fusion можно увидеть больше информации о том, что происходит, когда сборки загружаются и разрешаются. Google «Fuslogvw» для получения дополнительной информации о том, как просмотреть этот журнал (fuslogvw.exe — это утилита, предоставляемая при установке Visual Studio). Также важно понимать, что это приложение, по умолчанию, по-видимому, не показывает никакой информации, пока вы не настроите его для регистрации информации в файлах, воспроизведения проблемы, а затем перезапустите приложение, чтобы прочитать файлы журнала после их создания. И, согласно документации MSDN, также важно помнить, чтобы запустить эту утилиту от имени администратора.

После того, как вы преодолели все препятствия для запуска fuslogvw.exe, вы можете увидеть что-то вроде этого в журнале:

WRN: Comparing the assembly name resulted in the mismatch: Major Version
ERR: The assembly reference did not match the assembly definition found.
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

Несмотря на то, что в файле манифеста COM-сервера указана версия 1.0.0.0, это не единственная версия, используемая при привязке ссылки COM-клиента к серверу. Мой клиентский EXE-файл пытался ссылаться на 1.0.0.0, что точно соответствовало версии в файле манифеста COM-сервера, но не совпадало с версией .NET DLL. После исправления файлов манифеста клиента и сервера, чтобы они отражали версию, фактически отображаемую в DLL-библиотеке сервера .NET, ошибка 0x80131040 исчезла, и fuslogvw.exe стал ключом к определению источника ошибки.

Если манифест клиента синхронизирован с фактической версией .NET DLL, но файл манифеста серверной DLL не отражает эту версию, возникнет другая ошибка:

Приложение не удалось запустить, потому что его бок о бок
Конфигурация неверна. Пожалуйста, смотрите журнал событий приложения или
используйте инструмент командной строки sxstrace.exe для более подробной информации.

Ошибка 0xc0150002 или следующее сообщение может быть сообщено:

Приложение не удалось запустить правильно (0xc0150002), нажмите кнопку ОК
закрыть приложение.

Я видел это в случае, когда манифест клиента был встроен в неуправляемую DLL, а не в неуправляемый EXE, а также манифест assemblyIdentity элемент не точно соответствует серверу assemblyIdentity, У клиента был дополнительный processorArchitecture="x86" в этом то, что сервер не указал, вызвав несоответствие. К сожалению, я не знаю, как это узнать, не подумав, чтобы проверить файлы манифеста, чтобы убедиться, что они совпадают (или не читал эту статью). Эта ошибка явно не указывает на то, что источником проблемы является файл манифеста, поэтому вам просто нужно знать, что существует возможная корреляция между этим сообщением об ошибке и этой причиной.

Я видел, как внешние файлы манифеста полностью игнорировались, приводя к совершенно пустому журналу sxstrace, даже когда у исполняемых файлов нет встроенных манифестов. По-видимому, это может произойти в результате кеширования контекста активации (проблема, задокументированная в http://csi-windows.com/blog/all/27-csi-news-general/245-find-out-why-your-external-manifest-is-being-ignored). Чтобы обойти эту проблему, вы можете использовать следующую команду, чтобы коснуться отметки даты файла, манифест которого игнорируется:

copy /b myfile.exe+,,

Я видел еще одну трудную для объяснения ошибку Class Not Registered (0x80040154REGDB_E_CLASSNOTREG) что происходит при вызове CoCreateInstance при следующих условиях:

  1. Файл CPP содержит конструктор для класса, который создается в глобальной области видимости, поэтому динамическая инициализация будет вызывать конструктор во время DllMain если /clr переключатель не применяется к файлу CPP или во время .cctor если /clr переключатель является применяется к файлу.
  2. В DLL есть встроенный манифест, позволяющий ссылаться на класс COM, создаваемый через Reg-Free COM.
  3. COM DLL реализована в управляемом коде (с COM-Callable Wrapper aka CCW) в .NET 2.0.
  4. EXE, который загрузил DLL делает не иметь Reg-Free Manifest, ссылающийся на созданный класс COM.
  5. COM DLL есть не зарегистрирован в regasm,
  6. Вызов файла CPP CoCreateInstance имеет /clr Переключатель применяется к настройкам компилятора C ++.

Если какое-либо из 3 последних условий будет изменено, проблема исчезнет. (Кроме того, если последнее условие изменено, вы можете получить блокировку загрузчика из-за # 1 — читайте о блокировке загрузчика и ее отношении к CLR в Инициализация смешанных сборок). Поэтому, если вы сталкиваетесь с ошибкой Class Not Registered в подобных обстоятельствах, подумайте, можете ли вы изменить какое-либо из этих трех последних условий для устранения ошибки.
Замечания: Я с трудом фиксирую поведение # 6. Кажется, эффект переключения это также зависит от состояния # 1. Это похоже на вызов конструктора (включая его CoCreateInstance) после полной загрузки DLL все еще вызывает Class Not Registered, тогда как вызов конструктора во время инициализации DLL будет успешным, если /clr переключатель не указан. Мое решение на данный момент состоит в том, чтобы перекодировать клиентский файл CPP в управляемом C ++, поскольку это был относительно простой интерфейсный класс между компонентом COM и остальной частью неуправляемого кода. Так что теперь в этом клиенте больше нет COM, только ссылка .NET.

10

Другие решения

Несмотря на все мои разочарования, объяснение BlueMonkMN было действительно полезным, но все же я столкнулся с Class not registered сообщение.

У меня есть .NET 4.6 COM взаимодействия Dll и клиент C ++, который использует Регистрация Free COM с помощью технологии Windows SxS. Манифест сборки был встроен в COM Dll, а манифест приложения помещен наружу в виде отдельного файла .manifest.

Все работало так, как рекламировалось, но начало получать Class not registered сообщение во время CreateInstance когда происходит одно из следующего.

  • Скопируйте COM Dll взаимодействия вместе с исполняемым файлом клиента C ++ и
    файл манифеста в другой каталог.
  • Внесите не связанные с COM изменения в клиент C ++, а затем пересоберите
    приложение.

В этой ситуации, чтобы добавить оскорбление травмы, sxstrace журнал был пуст, журнал событий приложения (eventvwr) не сообщал об ошибке SideBySide и программе просмотра журнала привязки сборки (FUSLOGVW.exe) ничего не показывал из клиентского приложения C ++.

Позвольте мне добавить еще одно Дополнение к ответу BlueMonkMN.

Из моих экспериментов я понимаю, что в таком случае контекст активации для клиентского приложения C ++ не создается.

В такой ситуации необходимо создать контекст активации самостоятельно.

Следующий код помещается в клиентское приложение C ++ после вызова CoInitialize(0); и прежде чем звонить CreateInstance,

ACTCTX context;
memset(&context, 0, sizeof(context));

context.cbSize = sizeof(context);
context.lpSource = L"CPPClient.exe.manifest";
context.lpAssemblyDirectory = L"D:\\code\\vs2017\\CPPClient\\Debug";
context.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;

HANDLE hActCtx = CreateActCtx(&context);

ULONG_PTR cookie = 0;
BOOL result = ActivateActCtx(hActCtx, &cookie);

//Rest of the code goes here.

В качестве меры очистки следующий фрагмент кода помещается непосредственно перед вызовом CoUninitialize();

DeactivateActCtx(0, cookie);
ReleaseActCtx(hActCtx);

Теперь это звучит хорошо. Бок о бок работает очень хорошо. Если что-то пойдет не так, то это можно увидеть в sxstrace или в журнале событий приложения или в программе просмотра журнала привязки сборки.

1

По вопросам рекламы [email protected]