Genere archivos de manifiesto para COM sin registro

87

Tengo algunas aplicaciones (algunas nativas, algunas .NET) que usan archivos de manifiesto para que se puedan implementar en completo aislamiento , sin requerir ningún registro COM global. Por ejemplo, la dependencia del servidor com dbgrid32.ocx se declara de la siguiente manera en el archivo myapp.exe.manifest que se encuentra en la misma carpeta que myapp.exe:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="myapp.exe" version="1.2.3.4" />
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="dbgrid32.ocx" version="5.1.81.4" />
    </dependentAssembly>
  </dependency>
</assembly>

El dbgrid32.ocx se implementa en la misma carpeta, junto con su propio archivo dbgrid32.ocx.manifest:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="dbgrid32.ocx" version="5.1.81.4" />
  <file name="dbgrid32.ocx">
     <typelib
        tlbid="{00028C01-0000-0000-0000-000000000046}"
        version="1.0"
        helpdir=""/>
    <comClass progid="MSDBGrid.DBGrid"
       clsid="{00028C00-0000-0000-0000-000000000046}"
       description="DBGrid  Control" />
  </file>
</assembly>

Todo esto funciona bien, pero mantener estos archivos de manifiesto manualmente es un poco complicado. ¿Hay alguna forma de generar estos archivos automáticamente? Idealmente, solo me gustaría declarar la dependencia de la aplicación en una lista de servidores COM (tanto nativos como .NET) y luego dejar que el resto se genere automáticamente. ¿Es posible?

Wim Coenen
fuente
+1 también: reetiquetado regfreecom ya que esa etiqueta es más común para COM sin registro
MarkJ
¿Puedo utilizar una versión superior mstscax.dll en mi propia carpeta de instalación por archivo de manifiesto?
Acewind
@acewind sí. (Es posible que desee publicar una nueva pregunta con más detalles).
UuDdLrLrSs
@UuDdLrLrSs ¡Buenas noticias!
Publico

Respuestas:

63

Parece que la solución perfecta aún no existe. Para resumir algunas investigaciones:

Hacer mi manifiesto ( enlace )

Esta herramienta escanea un proyecto VB6 para buscar dependencias COM, pero también es compatible con la declaración manual de dependencias COM vinculadas en fecha tardía (es decir, las utilizadas a través de CreateObject).

Curiosamente, esta herramienta pone toda la información sobre las dependencias dentro del manifiesto de la aplicación. El exe de la aplicación y sus dependencias se describen como un solo ensamblado que consta de varios archivos. No me había dado cuenta antes de que esto era posible.

Parece una muy buena herramienta, pero a partir de la versión 0.6.6 tiene las siguientes limitaciones:

  • solo para aplicaciones VB6, comienza desde el archivo de proyecto VB6. Es una pena, porque mucho de lo que hace realmente no tiene nada que ver con VB6.
  • aplicación estilo asistente, no apta para integrar en un proceso de construcción. Este no es un gran problema si sus dependencias no cambian mucho.
  • freeware sin fuente, es arriesgado confiar en él porque podría convertirse en abandonware en cualquier momento.

No probé si es compatible con las bibliotecas com .NET.

regsvr42 ( enlace de codeproject )

Esta herramienta de línea de comandos genera archivos de manifiesto para bibliotecas COM nativas. Invoca DllRegisterServer y luego espía el registro automático a medida que agrega información al registro. También puede generar un manifiesto de cliente para aplicaciones.

Esta utilidad no es compatible con las bibliotecas COM de .NET, ya que no exponen una rutina DllRegisterServer.

La utilidad está escrita en C ++. El código fuente está disponible.

mt.exe

Parte del SDK de Windows (se puede descargar desde MSDN ), que ya tiene si tiene instalado Visual Studio. Está documentado aquí . Puede generar archivos de manifiesto para bibliotecas COM nativas con él de esta manera:

mt.exe -tlb:mycomlib.ocx -dll:mycomlib.ocx -out:mycomlib.ocx.manifest

Puede generar archivos de manifiesto para las bibliotecas COM .NET de esta manera:

mt.exe -managedassemblyname:netlib.dll -nodependency -out:netlib.dll.manifest

Sin embargo, existen algunos problemas con esta herramienta:

  • El primer fragmento no generará atributos progid, lo que interrumpirá a los clientes que usen CreateObject con progids.
  • El segundo fragmento generará <runtime>y <mvid>elementos que necesitan ser despojado de delante de los manifiestos de hecho trabajar.
  • No se admite la generación de manifiestos de cliente para aplicaciones.

Quizás las futuras versiones del SDK mejoren esta herramienta, probé la del SDK de Windows 6.0a (vista).

Wim Coenen
fuente
1
Creo que te perdiste una opción: mazecomputer.com, pero no sé nada que el sitio web no describa.
Bob
MMM también redirigirá los archivos DLL que no son COM (estándar). No estoy seguro de que las otras herramientas hagan esto.
Bob
Solo una nota para los nerviosos: la fuente de MMM ha sido publicada. En el lado negativo, esto parece deberse a que el autor ha decidido dejar de trabajar en él. Sigue siendo una señal positiva.
Gavin
2
El sitio para MMM es nolonger arriba pero el lugar en el que el código fuente se puso todavía está disponible para v0.9 y v0.12 .
Scott Chamberlain
1
Solo probé mt.exe para las bibliotecas .NET COM como se describe anteriormente y funcionó sin modificaciones en el manifiesto usando v7.1A. Además, los enlaces para MMM no funcionaron, pero Unattented Make My Manifest parece hacer un trabajo decente.
bzuillsmith
28

Con la tarea GenerateApplicationManifest de MSBuild, generé un manifiesto en la línea de comandos idéntico al manifiesto que genera Visual Studio. Sospecho que Visual Studio usa GenerateApplicationManifest durante la compilación. A continuación se muestra mi script de compilación que se puede ejecutar desde la línea de comandos usando msbuild "msbuild build.xml"

Gracias a Dave Templin y su publicación que me indicó la tarea GenerateApplicationManifest y la documentación adicional de MSDN de la tarea .

build.xml

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="Build">
        <ItemGroup>
            <File Include='MyNativeApp.exe'/>
            <ComComponent Include='Com1.ocx;Com2.ocx'/>
        </ItemGroup>
        <GenerateApplicationManifest
            AssemblyName="MyNativeApp.exe"
            AssemblyVersion="1.0.0.0"
            IsolatedComReferences="@(ComComponent)"
            Platform="x86"
            ManifestType="Native">
            <Output
                ItemName="ApplicationManifest"
                TaskParameter="OutputManifest"/>
        </GenerateApplicationManifest>
    </Target>   
</Project>
mcdon
fuente
Creo que esto realmente debería marcarse como la respuesta a esta pregunta. Ahora estoy usando esto para automatizar toda nuestra generación de manifiestos. Gracias @mcdon, me has ahorrado mucho trabajo.
Pete Magsig
Estoy de acuerdo en que esta es la mejor solución cuando se crea con Visual Studio. Probablemente no tenga una calificación más alta solo porque se publicó mucho más tarde que las otras respuestas
dschaeffer
¿cómo agregar esto en un csproj?
jle
@jle Creo que podrías agregarlo a tu csproj en el destino AfterBuild. Aquí hay un enlace de msdn y otra publicación sobre el tema de eventos previos y posteriores a la compilación. Tenga en cuenta que no he probado incluir esto en el csproj, pero sospecho que funcionaría.
mcdon
¿Se puede generar el manifiesto para una DLL COM de C #, de modo que cualquier exe pueda consumirlo (en lugar de generar un manifiesto en cada exe e incluir las DLL)?
GilesDMiddleton
9

Make My Manifest (MMM) es una buena herramienta para hacer esto. También es posible escribir un script para procesar todos sus archivos DLL / OCX usando mt.exe para generar un manifiesto para cada uno y luego fusionarlos todos juntos. MMM suele ser mejor / más fácil, porque también maneja muchos casos especiales / extraños.

jdve
fuente
3
Estoy un poco nervioso por esto de MMM; es solo un blog, software gratuito pero no hay código fuente disponible, solo un enlace a un "exe autoextraíble" y veo comentarios sobre la utilidad que hace que XP se bloquee. mmm ...
Wim Coenen
Esos "fallos" fueron la muerte de la propia utilidad MMM. Esto se corrigió en la versión 0.6.5, pero querrá la 0.6.6 de todos modos, ya que aunque todavía es una versión beta, ya no caduca. Siempre puede usar MT.EXE en su lugar, como ya se sugirió.
Bob
mt.exe no genera progid cuando lo uso en servidores com nativos como dbgrid32.ocx
Wim Coenen
El uso de COM sin registro con componentes creados por .NET aparentemente puede hacer que XP se bloquee; consulte este stackoverflow.com/questions/617253/…
MarkJ
8

Puede utilizar el spin-off Unattended Make My Manifest para generar manifiestos directamente en compilaciones automatizadas. Utiliza un archivo de secuencia de comandos para agregar componentes COM dependientes. Este es un extracto del ejemplo ini con los comandos disponibles:

# Unattended MMM script
#
# Command names are case-insensitive. Reference of supported commands:
#
# Command: Identity
#
#   Appends assemblyIdentity and description tags.
#
#   Parameters       <exe_file> [name] [description]
#      exe_file      file name can be quoted if containing spaces. The containing folder 
#                    of the executable sets base path for relative file names
#      name          (optional) assembly name. Defaults to MyAssembly
#      description   (optional) description of assembly
#
# Command: Dependency
#
#   Appends dependency tag for referencing dependent assemblies like Common Controls 6.0, 
#     VC run-time or MFC
#
#   Parameters       {<lib_name>|<assembly_file>} [version] [/update]
#     lib_name       one of { comctl, vc90crt, vc90mfc }
#     assembly_file  file name of .NET DLL exporting COM classes
#     version        (optional) required assembly version. Multiple version of vc90crt can
#                    be required by a single manifest
#     /update        (optional) updates assembly_file assembly manifest. Spawns mt.exe
#
# Command: File
#
#   Appends file tag and collects information about coclasses and interfaces exposed by 
#     the referenced COM component typelib.
#
#   Parameters       <file_name> [interfaces]
#     file_name      file containing typelib. Can be relative to base path
#     interfaces     (optional) pipe (|) separated interfaces with or w/o leading 
#                    underscore
#
# Command: Interface
#
#   Appends comInterfaceExternalProxyStub tag for inter-thread marshaling of interfaces
#
#   Parameters       <file_name> <interfaces>
#     file_name      file containing typelib. Can be relative to base path
#     interfaces     pipe (|) separated interfaces with or w/o leading underscore
#
# Command: TrustInfo
#
#   Appends trustInfo tag for UAC user-rights elevation on Vista and above
#
#   Parameters       [level] [uiaccess]
#     level          (optional) one of { 1, 2, 3 } corresponding to { asInvoker, 
#                    highestAvailable, requireAdministrator }. Default is 1
#     uiaccess       (optional) true/false or 0/1. Allows application to gain access to 
#                    the protected system UI. Default is 0
#
# Command: DpiAware
#
#   Appends dpiAware tag for custom DPI aware applications
#
#   Parameters       [on_off]
#     on_off         (optional) true/false or 0/1. Default is 0
#
# Command: SupportedOS
#
#   Appends supportedOS tag
#
#   Parameters       <os_type>
#     os_type        one of { vista, win7 }. Multiple OSes can be supported by a single 
#                    manifest
#

Se ejecutará en Windows de 32 o 64 bits.

wqw
fuente
+1 Interesante, especialmente porque el código fuente está disponible. Estoy un poco confundido por la similitud del nombre, aparentemente "hacer mi manifiesto" y "hacer mi manifiesto desatendido" son herramientas diferentes de diferentes autores.
Wim Coenen
2
Nota: a partir de 2017 (8 años después ...) este proyecto todavía está activo con actualizaciones de mantenimiento ocasionales. github.com/wqweto/UMMM/commits/master . Funciona bien y lo uso habitualmente.
UuDdLrLrSs
0

Para completar los ProgID que mt.exe no incluye, puede llamar ProgIDFromCLSIDpara buscarlos en el registro. Esto requiere el registro COM tradicional antes de completar el archivo de manifiesto, pero posteriormente, el archivo de manifiesto será autosuficiente.

Este código C # agrega los ProgID a todas las clases COM en un manifiesto:

var manifest = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("s", "urn:schemas-microsoft-com:asm.v1");
foreach (var classElement in manifest.XPathSelectElements("s:assembly/s:file/s:comClass", namespaceManager)) {
    var clsid = Guid.Parse(classElement.Attribute("clsid").Value);
    int result = ProgIDFromCLSID(ref clsid, out string progId); if (result != S_OK) throw new COMException($"ProgID lookup failed for {clsid}.", result);
    classElement.SetAttributeValue("progid", progId);
}
manifest.Save(fileName);

El código se basa en estas definiciones de interoperabilidad:

[DllImport("ole32.dll")] static extern int ProgIDFromCLSID([In] ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)] out string lplpszProgID);
const int S_OK = 0;
Edward Brey
fuente