Я изучаю настройку бесплатной бесплатной WinSxS для бесплатной регистрации с простым предоставлением файлов манифеста сборки, для стежки исполняемых файлов Delphi (COM-клиентов) и COM-библиотек .NET(С#) совместно отображаемых DLL вместе при развертывании и времени выполнения.
Я уже изучил документацию, доступную в MSDN "Взаимодействие с неуправляемым кодом" , разделы " COM Callable Wrapper" и Как сделать: Настройка COM-компонентов на основе .NET Framework для активации без регистрации.
После более чем одной недели исследований и повторного обращения в циклы недостаточной документации я решил разместить здесь 1-й вопрос.
Планируемая структура развертывания выглядит следующим образом:
./install-root
├───ProgramSuite1
│ ├───bin
│ │ DelphiNativeCOMClient1.exe
│ │ DelphiNativeCOMClient1.exe.config
│ │ DelphiNativeCOMClient2.exe
│ │ DelphiNativeCOMClient2.exe.config
│ | ...
│ │
│ └───data
│ ...
├───ProgramSuite2
│ ├───bin
│ │ DelphiNativeCOMClient3.exe
│ │ DelphiNativeCOMClient3.exe.config
│ │ DelphiNativeCOMClient4.exe
│ │ DelphiNativeCOMClient4.exe.config
│ | ...
│ │
│ └───data
│ ...
└───SharedLibs
├───MyCompany.Libs.Set1
│ MyCompany.Libs.Set1.manifest
│ SomeManagedCOMServerA.dll
│ SomeNativeCOMServerB.dll
│ SomeNativeCOMServerC.dll
│
└───MyCompany.Libs.Set2
MyCompany.Libs.Set2.manifest
SomeManagedCOMServerB.dll
SomeNativeCOMServerX.dll
SomeManagedCOMServerA.dll
Вот краткий очерк о реализации реализации встроенных исполняемых файлов Delphi и DLL С#.NET COM-сервера (я не использовал примеры для собственных COM-серверов, так как этот материал уже работает хорошо, и об этом не обошлось).
Я в основном следил за тем, что было предоставлено в "Без активации регистрации компонентов COM: пошаговое руководство" . Основное отличие заключается в том, что я использую Delphi, а не C, С++ или старый VB как собственный клиент.
TestDllConsoleApp.exe
TestDllConsoleApp.dpr
program TestDllConsoleApp;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
DllTests.Common,
WinApi.ActiveX,
WinApi.Windows,
// These were generated using the tlbimplib tool
CSharpCOMDll_TLB in 'CSharpCOMDll_TLB.pas',
mscorlib_TLB in 'mscorlib_TLB.pas';
var
comInterface1 : ICOMInterface1;
comInterface2 : ICOMInterface2;
intf1CoClass : _COMImplClass1;
intf2CoClass : _COMImplClass2;
res : HRESULT;
coInitializeRes : integer;
begin
//Initialize COM
coInitializeRes := CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
if (coInitializeRes <> S_OK) and (coInitializeRes <> S_FALSE) then begin
System.ExitCode := 1;
Exit(); // GUARD
end;
try
try
intf1CoClass := CoCOMImplClass1.Create();
res := intf1CoClass.QueryInterface(IID_ICOMInterface1,comInterface1);
System.WriteLn(comInterface1.GetModuleName());
intf2CoClass := CoCOMImplClass2.Create();
res := intf2CoClass.QueryInterface(IID_ICOMInterface2,comInterface2);
System.WriteLn(comInterface2.GetModuleName());
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
finally
//Uninitialize COM
CoUninitialize();
end;
end.
TestDllConsoleApp.manifest
(встроенный с идентификатором ресурса 1)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity name="MyCompany.Software.Application" processorArchitecture="x86" version="1.0.0.0" type="win32" />
<description>A native COM client application.</description>
<asmv3:trustInfo>
<asmv3:security>
<asmv3:requestedPrivileges>
<asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
</asmv3:requestedPrivileges>
</asmv3:security>
</asmv3:trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows Server 2016 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
<!-- Windows 8.1 and Windows Server 2012 R2 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 8 and Windows Server 2012 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 7 and Windows Server 2008 R2 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows Vista and Windows Server 2008 -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
</application>
</compatibility>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="MyCompany.Libs.Set1" version="1.0.0.0" processorArchitecture="x86" />
</dependentAssembly>
</dependency>
</assembly>
TestDllConsoleApp.exe.config
(развернуто в том же месте файла, что и исполняемый файл)
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="..\..\SharedLibs"/>
</assemblyBinding>
</runtime>
</configuration>
CSharpCOMDll.dll
(будет развернут в каталоге SharedLibs\MyCompany.Libs.Set1
)
Assemblyinfo.cs
#region Using directives
using System;
using System.Reflection;
using System.Runtime.InteropServices;
#endregion
[assembly: AssemblyTitle ("CSharpCOMDll")]
[assembly: AssemblyProduct ("CSharpCOMDll")]
[assembly: AssemblyCopyright ("Copyright 2018")]
[assembly: ComVisible (true)]
[assembly: AssemblyVersion ("1.0.0.0")]
[assembly: Guid ("045d53ab-a9e4-4036-a21b-4fe0cf433065")]
COMImplClass1.cs
// Using namespaces ...
namespace CSharpCOMDll
{
[Guid("6BDAF8DD-B0CF-4CBE-90F5-EA208D5A2BB0")]
public interface ICOMInterface1
{
string GetModuleName();
}
[Guid("4CD39F25-0EB9-4CD0-9B4C-6F5DB5C14805")]
public class COMImplClass1 : ICOMInterface1
{
public string GetModuleName()
{
return typeof(COMImplClass1).Module.FullyQualifiedName;
}
}
}
COMImplClass2.cs
// Using namespaces ...
namespace CSharpCOMDll
{
[Guid("BE69E9C7-1B37-4CA8-A3C1-10BFA9230940")]
public interface ICOMInterface2
{
string GetModuleName();
}
[Guid("067E5980-0C46-49C7-A8F0-E830877FB29C")]
public class COMImplClass2 : ICOMInterface2
{
public string GetModuleName()
{
return typeof(COMImplClass1).Module.FullyQualifiedName;
}
}
}
CSharpCOMDll.manifest
(встроен в DLL с идентификатором ресурса 2)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<assemblyIdentity
type="win32"
processorArchitecture="x86"
name="CSharpCOMDll"
version="1.0.0.0" />
<clrClass
clsid="{4CD39F25-0EB9-4CD0-9B4C-6F5DB5C14805}"
progid="CSharpCOMDll.COMImplClass1"
threadingModel="Both"
name="CSharpCOMDll.COMImplClass1"
runtimeVersion="v4.0.30319">
</clrClass>
<clrClass
clsid="{067E5980-0C46-49C7-A8F0-E830877FB29C}"
progid="CSharpCOMDll.COMImplClass2"
threadingModel="Both"
name="CSharpCOMDll.COMImplClass2"
runtimeVersion="v4.0.30319">
</clrClass>
</assembly>
И, наконец, манифест сборки разрешен из записей TestDllConsoleApp.manifest
dependency
:
MyCompany.Libs.Set1.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="MyCompany.Libs.Set1" version="1.0.0.0" processorArchitecture="x86" />
<file name="CSharpCOMDll.dll">
<comClass
clsid="{4CD39F25-0EB9-4CD0-9B4C-6F5DB5C14805}"
threadingModel="Both"
/>
<comClass
clsid="{067E5980-0C46-49C7-A8F0-E830877FB29C}"
threadingModel="Both"
/>
<comInterfaceProxyStub
name="ICOMInterface1"
iid="{6BDAF8DD-B0CF-4CBE-90F5-EA208D5A2BB0}"
proxyStubClsid32="????"
/>
<comInterfaceProxyStub
name="ICOMInterface2"
iid="{BE69E9C7-1B37-4CA8-A3C1-10BFA9230940}"
proxyStubClsid32="????"
/>
</file>
</assembly>
Кажется, я нахожусь на полпути, но до сих пор не могу диагностировать реальную проблему.
Сейчас есть два варианта сбоя ( Обратите внимание, что развертывание управляемых DLL-серверов COM-сервера рядом с исполняемым файлом вместо обращения к разрешенному файлу манифеста работает отлично и по назначению):
-
Я полностью удаляю атрибут
proxyStubClsid32
в глобальном манифесте:- Запуск исполняемого файла заканчивается исключением
EOleSysError: Error in dll, clsid = {4CD39F25-0EB9-4CD0-9B4C-6F5DB5C14805}
-
Отладка исключения приводит к значению
HRESULT
Error in the DLL (Exception from HRESULT: 0x800401F9 (CO_E_ERRORINDLL))
- Запуск исполняемого файла заканчивается исключением
-
Я предоставляю атрибут
proxyStubClsid32
в глобальном манифесте:- Я не уверен, что GUID действительно необходим для этого атрибута.
Как упоминалось в документации, естественно, это соответствует соответствующему "идентификатору класса" (CLSID
), как указано в атрибутеcomClass
CLSID
. - Я также попытался предоставить идентификатор LIBID из сгенерированного файла
,pas
.
Оба варианта оставляют меня с довольно бесполезной ошибкой, прослеживаемой с помощью инструмента
sxstrace
1:... INFORMATION: Manifestdatei ".\install-root\SharedLibs\MyCompany.Libs.Set1\MyCompany.Libs.Set1.MANIFEST" wird analysiert. INFORMATION: Die Manifestsdefinitionsidentität ist ",processorArchitecture="x86",type="win32",version="1.0.0.0"". FEHLER: Bei der Generierung des Aktivierungskontextes ist ein Fehler aufgetreten. Beendet die Generierung des Aktivierungskontextes.
Обратите внимание, что не было никакого краткого сообщения об ошибке/информации, например
... cannot resolve assembly XY ...
до генерации Контекста активации. Там много ссылок, указывающих на эту конкретную ошибку. Также здесь не помогают повселокальные упоминания о недостающей инфраструктуре, распространяемой на Visual С++. Я звоню из Delphi, и что-то другое.
- Я не уверен, что GUID действительно необходим для этого атрибута.
-
Еще одна попытка явно ссылаться на
CSharpCOMDll.dll
(другая зависимость в исполняемом манифесте) и просто поместить его вSharedLibs
, получив успешно созданный контекст активации, но не выполняет несколько иное исключение, чем раньшеEOleSysError: Cannot find file, clsid = {4CD39F25-0EB9-4CD0-9B4C-6F5DB5C14805}
Кто-нибудь знает, как делать то, что я хочу просто, или что можно сделать дополнительно (помимо sxstrace
), чтобы более подробно диагностировать проблему.
Я почти уверен, что возможно обеспечить такое развертывание.
TL; DR;
- Возможно ли предоставить структуру развертывания, как упомянуто выше, и поддерживать определенные DLL-серверы .NET COM-сервера за пределами ссылочных мест исполняемых файлов?
Обновление:
Изучая дальнейшие сегодня, я понял, что (несмотря на очень похожую терминологию), разрешение ActivationContext с помощью частного SxS и разрешение размещения .NET DLL, которые служат для создания COM-конвейерной оболочки, представляют собой два совершенно разных и разделенных механизма. Я в основном получил это от этих 2 и еще несколько Jufeng Zhang's, блестящих и подробно объясняющих статьи в блогах:
- "Активация COM, регистрация Бесплатная активация COM, COM/.Net Interop, регистрация бесплатно COM/.Net Interop"
- "Регистрация Free COM/.Net interop"
Проблема с размещением незарегистрированных сборников .NET(управляемых COM-серверов DLL) заключается в том, что это произойдет только в каталоге развертывания приложений и ниже.
Используя любой метод, например указание элемента <codebase>
или <probing>
внутри раздела <runtime>
, указывающего вне каталога, в котором развернут файл .config
, просто не работает.
Я проверил, что с помощью Sysinternals Process Monitor и инструмента просмотра журнала Fusion 2.
Я не отправляю это как окончательный ответ, потому что я попытаюсь сделать что-то иначе, чтобы обмануть этот механизм .NET, чтобы найти управляемые DLL-серверы COM-сервера, используя манифест сборки или собственную DLL, специфицирующую зависимости, и <probing>
/<codebase>
, чтобы перенаправить механизм определения местоположения.
В качестве последнего средства (sic!), возможно, даже можно предоставить свои собственные настроенные appDomainManagerAssembly
и appDomainManagerType
в конфигурации приложения под элементом <runtime>
.
Обновление II:
Я боюсь, что нам нужно пойти на управление AppDomain
самостоятельно, используя CLR API из собственного CLR Host.
Требуется дальнейшее расследование. Один многообещающий ресурс, как это сделать, я нашел здесь:
" Настройка среды выполнения Common Language Runtime Microsoft.NET Framework
1) Извините, пожалуйста, немецкие сообщения об ошибках. У меня нет компилятора на английском языке. Но перевод, указанный в google, должен хорошо работать.
2) Таким образом, вопрос о лучших инструментах для диагностики проблем можно считать разрешенным.