seguro nombre necesita manifiesto firma ensamblado encuentra con certificados certificado almacén c# .net delphi com winsxs

c# - nombre - ¿Cómo proporcionar un manifiesto privado de lado a lado que localice correctamente una DLL de.NET como proveedor COM?



no se encuentra el certificado de firma del manifiesto en el almacén de certificados (1)

  • ¿Es incluso posible proporcionar una estructura de implementación como la mencionada anteriormente, y mantener ciertas DLL de servidor COM de .NET fuera de las ubicaciones de los ejecutables de referencia?

Definitivamente no es posible (!) Resolver los ensamblajes provistos para el mecanismo de alojamiento de CLR intrínseco fuera del directorio ejecutable de AppDomain .

Puedes usar el

<probing privatePath="<some directory below your executable''s location>" />`

Pero la etiqueta <probing> funciona de manera diferente para la resolución SxS (que aparece bajo la etiqueta <windows> del manifiesto), y el mecanismo de CLR para instanciar envoltorios que se pueden llamar COM que aparecen bajo la etiqueta <runtime> .

Incluso está sin documentar, pero especificando

<windows> <probing privatePath="../<xxx>" /> </windows>

para resolver las dependencias de SxS, se admiten rutas relativas para <xxx> hasta 3 ../ niveles de directorio principal desde la ubicación de su ejecutable para cualquier servidor COM nativo , mientras que

<runtime> <probing privatePath="../<xxx>" /> <!-- ^^^ --> </runtime>

o

<runtime> <codebase href="../<xxx>/xyz.dll" version="1.0.0.0"/> <!-- ^^^ --> </runtime>

no le permitirá especificar ubicaciones de ensamblaje que apunten a ubicaciones hacia arriba fuera del directorio de hospedaje de su dominio de aplicación usando los mecanismos estándar de Windows .NET para resolver que los candidatos se instalen como envoltorios que se pueden llamar COM (alojados por mscoreee.dll ).
Descender más profundamente del directorio de implementación de su ejecutable funciona bien y según lo previsto.

Una forma (probablemente la más fácil) de interceptar el mecanismo de sondeo de CLR, es proporcionar una implementación personalizada de AppDomainManager y especificarla en los elementos <appDomainManagerAssembly> y <appDomainManagerType> del archivo de configuración de la aplicación:

<configuration> <runtime> <appDomainManagerAssembly value="MyAppDomainMgr" /> <appDomainManagerType value="MyAppDomainMgr.MyCustomAppDomainMgr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </runtime> <configuration>

La implementación de la clase MyAppDomainMgr.MyCustomAppDomainMgr debe estar en un ensamblado .NET, por ejemplo, escrito en C #:

namespace MyAppDomainMgr { [ComVisible(true)] public class MyCustomAppDomainMgr : AppDomainManager { public MyCustomAppDomainMgr() { } public override void InitializeNewDomain(AppDomainSetup appDomainInfo) { Console.Write("Initialize new domain called: "); Console.WriteLine(AppDomain.CurrentDomain.FriendlyName); InitializationFlags = AppDomainManagerInitializationOptions.RegisterWithHost; // Several ways to control settings of the AppDomainSetup class, // or add a delegate for the AppDomain.CurrentDomain.AssemblyResolve // event. } } }

Tan pronto como su aplicación no administrada intente acceder a alguna interfaz COM (COM Callable Wrapper) a través del CLR (es decir, una llamada a CoCreateInstance() ), la clase MyCustomAppDomainMgr creará una instancia y se llamará primero a la función InitializeNewDomain() .

La forma menos intrusiva parece ser agregar esa función de delegado:

public override void InitializeNewDomain(AppDomainSetup appDomainInfo) { // ... AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyCustomAssemblyResolver); } static Assembly MyCustomAssemblyResolver(object sender, ResolveEventArgs args) { // Resolve how to find the requested Assembly using args.Name // Assembly.LoadFrom() would be a good way, as soon you found // some matching Assembly manifest or DLL whereever you like to look up for it }

El ensamblaje resultante ( MyAppDomainMgr.dll ) debe colocarse debajo de la aplicación ejecutable no administrada.

Estoy investigando sobre la configuración de un registro privado WinSxS gratuito con la provisión simple de archivos de manifiesto de ensamblaje, para unir ejecutables Delphi (clientes COM) y .NET (C #) DLL visibles COM juntos en la implementación y el tiempo de ejecución.

Ya estudié la documentación disponible en MSDN "Interoperación con código no administrado" , las secciones sobre " COM Callable Wrapper " y " Cómo: Configurar los componentes COM basados ​​en .NET Framework para la activación sin registro " en particular.

Después de más de una semana de investigación y de ser re-dirigido en ciclos de documentación insuficiente, decidí colocar mi primera pregunta aquí.

La estructura de despliegue planificada se ve como sigue:

./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

Aquí hay un breve bosquejo de la implementación de la implementación de los ejecutables nativos de Delphi y las DLL del servidor COM de C # .NET (omití los ejemplos para los servidores COM nativos, ya que esto ya funciona bien y está fuera de discusión).
Principalmente seguí lo que se proporcionó en "Activación sin registro de componentes COM: un tutorial" . La principal diferencia es que estoy utilizando Delphi en lugar de C, C ++ o VB anterior como cliente nativo .

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

(incrustado con ID de recurso 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

(implementado en la misma ubicación de archivo que el ejecutable)

<configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="../../SharedLibs"/> </assemblyBinding> </runtime> </configuration>

CSharpCOMDll.dll

(se implementará en el directorio 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

(Incrustado en la DLL con ID de recurso 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>

Y finalmente, el ensamblado se resuelve a partir de las entradas de dependency TestDllConsoleApp.manifest :

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>

Parece que estoy a mitad de camino, pero aún no puedo diagnosticar el problema real.

Hay dos variantes de fallas ahora mismo ( tenga en cuenta que la implementación de las DLL del servidor COM administrado junto al ejecutable en lugar de referirse al directorio de manifiestos resueltos simplemente funciona bien y según lo previsto):

  1. proxyStubClsid32 completamente el atributo proxyStubClsid32 en el manifiesto global:

    • Iniciar el ejecutable termina con una excepción
      EOleSysError: Error in dll, clsid = {4CD39F25-0EB9-4CD0-9B4C-6F5DB5C14805}
    • La depuración de la excepción lleva a un valor HRESULT

      Error in the DLL (Exception from HRESULT: 0x800401F9 (CO_E_ERRORINDLL))

  2. Proporciono un atributo proxyStubClsid32 en el manifiesto global:

    • No estoy seguro de qué GUID es realmente necesario para ese atributo.
      Como se menciona en la documentación, naturalmente parece ser un "Id. De clase de co" ( CLSID ) correspondiente como se menciona en el atributo comClass elementos clsid .
    • Intenté alternativamente proporcionar el GUID LIBID del archivo de ,pas generado.

    Ambas variantes me dejan con un error bastante inútil rastreable con la herramienta 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.

    Tenga en cuenta que no hubo ningún error conciso / mensaje de información como

    ... cannot resolve assembly XY ...

    antes de que la Generación de Contexto de Activación fallara. Hay muchas referencias que indican esta situación particular de error.
    Además, las omnipresentes menciones del marco redistribuible de Visual C ++ faltante no ayudan aquí. Llamo desde Delphi, y eso es algo diferente.

  3. Otro intento de hacer referencia a CSharpCOMDll.dll explícitamente (otra dependencia en el manifiesto ejecutable) y simplemente colocarlo en SharedLibs obtuvo un Contexto de activación creado con éxito, pero falla con una excepción ligeramente diferente que antes

    EOleSysError: Cannot find file, clsid = {4CD39F25-0EB9-4CD0-9B4C-6F5DB5C14805}

¿Alguien aquí sabe cómo hacer lo que quiero directamente, o lo que se puede hacer adicionalmente (además de sxstrace ) para diagnosticar el problema con mayor profundidad?

Estoy casi seguro de que debe ser posible proporcionar una implementación como esta.

TL; DR;

  • ¿Es incluso posible proporcionar una estructura de implementación como la mencionada anteriormente, y mantener ciertas DLL de servidor COM de .NET fuera de las ubicaciones de los ejecutables de referencia?

Actualizar:

Investigando más a fondo hoy, me di cuenta de que (a pesar de una terminología muy similar), resolver el Cactor de Activación con un SxS privado y resolver la ubicación de las DLL de .NET que sirven para una instanciación del contenedor de llamadas COM son dos mecanismos completamente distintos y separados. Principalmente obtuve eso de estos 2 y algunos más de los brillantes y profundos artículos de Jufeng Zhang que explican el blog:

El problema con la ubicación de los ensamblados .NET no registrados (DLL de servidor COM administrado) es que esto solo ocurrirá dentro del directorio de implementación de aplicaciones y debajo.

Usando cualquier método como especificar un elemento <codebase> o <probing> dentro de la sección de configuración <runtime> que apunta fuera del directorio donde se .config archivo .config , simplemente no funciona.

Verifiqué que utilizando el Monitor de proceso Sysinternals y la herramienta del visor de registros de Fusion 2 .

No lo estoy publicando como una respuesta final, porque a continuación intentaré engañar a ese mecanismo .NET para ubicar las DLL del servidor COM administrado, usando un manifiesto de ensamblado o una DLL nativa que especifique las dependencias y <probing> / <codebase> Elemento para redireccionar el mecanismo de localización.

Como último recurso (¡sic!) Parece incluso posible proporcionar su propio appDomainManagerAssembly y appDomainManagerType en la configuración de la aplicación bajo el elemento <runtime> .

Actualización II:

Me temo que tenemos que ir por la administración de AppDomain utilizando la API de CLR desde un CLR Host nativo.

Necesita más investigación. Un recurso prometedor sobre cómo hacer que encontré aquí:

"Personalización del Common Language Runtime de Microsoft .NET Framework"

1) Disculpe los mensajes de error alemanes por favor. No tengo un compilador de versión en inglés a la mano. Pero la traducción dada en google debería funcionar bien.

2) Por lo tanto, la pregunta sobre mejores herramientas para diagnosticar los problemas puede considerarse resuelta.