Gerar arquivos de manifesto para COM sem registro

87

Tenho alguns aplicativos (alguns nativos, alguns .NET) que usam arquivos de manifesto para que possam ser implantados de forma totalmente isolada , sem exigir nenhum registro COM global. Por exemplo, a dependência do servidor dbgrid32.ocx com é declarada da seguinte maneira no arquivo myapp.exe.manifest, que fica na mesma pasta 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>

O dbgrid32.ocx é implantado na mesma pasta, junto com seu próprio arquivo 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>

Tudo isso funciona bem, mas manter esses arquivos de manifesto manualmente é um pouco chato. Existe uma maneira de gerar esses arquivos automaticamente? Idealmente, gostaria apenas de declarar a dependência do aplicativo em uma lista de servidores COM (nativos e .NET) e, em seguida, deixar o resto ser gerado automaticamente. É possível?

Wim Coenen
fonte
+1 também: regfreecom retagged como essa tag é mais comum para registro grátis COM
MarkJ
Posso usar uma versão superior do mstscax.dll em minha própria pasta de instalação por arquivo de manifesto?
Acewind
@acewind yes. (Você pode postar uma nova pergunta com mais detalhes.)
UuDdLrLrSs
@UuDdLrLrSs Boas notícias! Eu posto uma nova pergunta aqui: stackoverflow.com/questions/63575746/…
Acewind

Respostas:

63

Parece que a solução perfeita ainda não existe. Para resumir algumas pesquisas:

Make My Manifest ( link )

Esta ferramenta varre um projeto VB6 para procurar dependências COM, mas também suporta a declaração manual de dependências COM de ligação tardia (ou seja, aquelas usadas via CreateObject).

Curiosamente, essa ferramenta coloca todas as informações sobre as dependências dentro do manifesto do aplicativo. O aplicativo exe e suas dependências são descritos como um único assembly que consiste em vários arquivos. Eu não tinha percebido antes que isso era possível.

Parece uma ferramenta muito boa, mas a partir da versão 0.6.6 tem as seguintes limitações:

  • apenas para aplicativos VB6, começa a partir do arquivo de projeto VB6. Uma pena, porque muito do que ele faz realmente não tem nada a ver com o VB6.
  • aplicativo de estilo de assistente, não adequado para integração em um processo de construção. Isso não é um grande problema se suas dependências não mudarem muito.
  • freeware sem fonte, é arriscado confiar nele porque pode se tornar abandonware a qualquer momento.

Não testei se ele oferece suporte a bibliotecas .NET com.

regsvr42 ( link codeproject )

Esta ferramenta de linha de comando gera arquivos de manifesto para bibliotecas COM nativas. Ele invoca DllRegisterServer e, em seguida, espiona o autorregistro à medida que adiciona informações ao registro. Ele também pode gerar um manifesto de cliente para aplicativos.

Este utilitário não oferece suporte a bibliotecas .NET COM, pois elas não expõem uma rotina DllRegisterServer.

O utilitário foi escrito em C ++. O código-fonte está disponível.

mt.exe

Parte do Windows SDK (pode ser baixado do MSDN ), que você já possui se tiver o Visual Studio instalado. Está documentado aqui . Você pode gerar arquivos de manifesto para bibliotecas COM nativas assim:

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

Você pode gerar arquivos de manifesto para bibliotecas .NET COM assim:

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

No entanto, existem alguns problemas com esta ferramenta:

  • O primeiro snippet não gerará atributos progid, interrompendo clientes que usam CreateObject com progids.
  • O segundo snippet irá gerar elementos <runtime>e <mvid>que precisam ser removidos antes que os manifestos realmente funcionem.
  • A geração de manifestos de cliente para aplicativos não é suportada.

Talvez versões futuras do SDK aprimorem essa ferramenta, testei a do Windows SDK 6.0a (vista).

Wim Coenen
fonte
1
Acho que você perdeu uma opção: mazecomputer.com, mas não sei nada sobre isso que o site não descreve.
Bob
O MMM também redirecionará DLLs não COM (padrão). Não tenho certeza se as outras ferramentas fazem isso.
Bob
Apenas uma nota para os nervosos: a fonte do MMM foi divulgada. No lado negativo, isso parece ser porque o autor decidiu parar de trabalhar nisso. Ainda é um sinal positivo.
Gavin
2
O site para MMM não está mais disponível, mas o local onde o código-fonte foi colocado ainda está disponível para v0.9 e v0.12 .
Scott Chamberlain
1
Tentei mt.exe para bibliotecas .NET COM conforme descrito acima e funcionou sem nenhuma modificação no manifesto usando a v7.1A. Além disso, os links para MMM não funcionaram, mas Unattented Make My Manifest parece fazer um trabalho decente.
bzuillsmith
28

Com a tarefa MSBuild GenerateApplicationManifest , gerei um manifesto na linha de comando idêntico ao manifesto gerado pelo Visual Studio. Suspeito que o Visual Studio usa GenerateApplicationManifest durante a compilação. Abaixo está o meu script de construção que pode ser executado a partir da linha de comando usando msbuild "msbuild build.xml"

Agradeço a Dave Templin e sua postagem que me indicou a tarefa GenerateApplicationManifest e a documentação adicional do MSDN sobre a tarefa .

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
fonte
Eu acho que isso realmente deveria ser marcado como a resposta a esta pergunta. Agora estou usando isso para automatizar toda a nossa geração de manifesto. Obrigado @mcdon, você me poupou muito trabalho.
Pete Magsig
Concordo que essa é a melhor solução ao compilar com o Visual Studio. Provavelmente não recebeu uma classificação mais alta apenas porque foi postado muito depois das outras respostas
dschaeffer
como adicionar isso em um csproj?
jle
@jle Acho que você pode adicioná-lo ao csproj no destino AfterBuild. Aqui está um link do msdn e outro post sobre o tópico de eventos pré-construção e pós-construção. Observação Não testei a inclusão disso no csproj, mas suspeito que funcionaria.
mcdon
O manifesto pode ser gerado para uma DLL C # COM, para que qualquer exe possa consumi-lo (em vez de gerar um manifesto em cada exe, incluindo as DLLs?)
GilesDMiddleton
9

Make My Manifest (MMM) é uma boa ferramenta para fazer isso. Também é possível escrever um script para processar todos os seus arquivos DLL / OCX usando mt.exe para gerar um manifesto para cada um e, em seguida, mesclá-los todos juntos. MMM geralmente é melhor / mais fácil, porque também lida com muitos casos especiais / estranhos.

jdve
fonte
3
Estou um pouco nervoso com essa coisa de MMM; é apenas um blog, freeware, mas nenhum código-fonte disponível, apenas um link para um "exe de extração automática" e vejo comentários sobre o utilitário que causa o travamento do XP. mmm ...
Wim Coenen
Essas "falhas" foram o próprio utilitário MMM morrendo. Isso foi corrigido na versão 0.6.5, mas você vai querer a 0.6.6 de qualquer maneira, já que ainda é uma versão beta, ela não expira mais. Você sempre pode usar MT.EXE como já sugerido.
Bob
mt.exe não está gerando progid quando eu o uso em servidores de com nativos como dbgrid32.ocx
Wim Coenen
O uso de COM reg free com componentes de autoria .NET pode aparentemente causar travamento do XP - consulte stackoverflow.com/questions/617253/…
MarkJ
8

Você pode usar o spin off Unattended Make My Manifest para gerar manifestos diretamente em compilações automatizadas. Ele usa um arquivo de script para adicionar componentes COM dependentes. Este é um trecho do exemplo de ini com os comandos disponíveis:

# 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
#

Ele será executado no Windows de 32 ou 64 bits.

wqw
fonte
+1 Interessante, principalmente porque o código-fonte está disponível. Estou um pouco confuso com a semelhança do nome, aparentemente "make my manifest" e "autônomo make my manifest" são ferramentas diferentes de diferentes autores.
Wim Coenen de
2
Nota - a partir de 2017 (8 anos depois ...) este projeto ainda está ativo com atualizações de manutenção ocasionais. github.com/wqweto/UMMM/commits/master . Funciona bem e eu o uso rotineiramente.
UuDdLrLrSs
0

Para preencher os ProgIDs que o mt.exe não inclui, você pode ligar ProgIDFromCLSIDpara consultá-los no registro. Isso requer o registro COM tradicional antes de completar o arquivo de manifesto, mas posteriormente, o arquivo de manifesto será autossuficiente.

Este código C # adiciona os ProgIDs a todas as classes COM em um manifesto:

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);

O código depende dessas definições de interoperabilidade:

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