web-dev-qa-db-fra.com

Générer des fichiers de manifeste pour COM sans inscription

Certaines de mes applications (certaines natives, certaines .NET) utilisent des fichiers manifestes pour pouvoir être déployées de manière totalement isolée , sans nécessiter un enregistrement COM global. Par exemple, la dépendance sur le serveur com dbgrid32.ocx est déclarée comme suit dans le fichier myapp.exe.manifest qui se trouve dans le même dossier 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>

Le fichier dbgrid32.ocx est déployé dans le même dossier, avec son propre fichier 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>

Tout cela fonctionne bien, mais la maintenance manuelle de ces fichiers manifeste est un peu pénible. Est-il possible de générer ces fichiers automatiquement? Idéalement, j'aimerais simplement déclarer la dépendance de l'application sur une liste de serveurs COM (natifs et .NET), puis laisser le reste être généré automatiquement. C'est possible?

81
Wim Coenen

Il semble que la solution parfaite n'existe pas encore. Pour résumer quelques recherches:

Make My Manifest ( link )

Cet outil analyse un projet VB6 à la recherche de dépendances COM, mais il prend également en charge la déclaration manuelle des dépendances COM à liaison tardive (c'est-à-dire celles utilisées via CreateObject).

Il est intéressant de noter que cet outil place toutes les informations sur les dépendances dans le manifeste de l’application. L'application exe et ses dépendances sont décrites comme un assemblage unique composé de plusieurs fichiers. Je n'avais pas réalisé auparavant que c'était possible.

Cela ressemble à un très bon outil, mais à partir de la version 0.6.6, il présente les limitations suivantes:

  • uniquement pour les applications VB6, lance à partir du fichier de projet VB6. Dommage, car Beaucoup de ce qu’il fait n’ont vraiment rien à voir Avec VB6.
  • application de type assistant, non compatible avec un processus de construction. Ce n'est pas un gros problème si vos dépendances Ne changent pas beaucoup.
  • freeware sans source, risqué de compter dessus car il pourrait devenir un abandonware à tout moment.

Je n'ai pas vérifié s'il prend en charge les bibliothèques .NET com.

regsvr42 ( lien codeprojet )

Cet outil de ligne de commande génère des fichiers de manifeste pour les bibliothèques COM natives. Il appelle DllRegisterServer, puis espionne l'auto-enregistrement car il ajoute des informations au registre. Il peut également générer un manifeste client pour les applications.

Cet utilitaire ne prend pas en charge les bibliothèques COM .NET, car celles-ci n'exposent pas de routine DllRegisterServer.

L'utilitaire est écrit en C++. Le code source est disponible.

mt.exe

Une partie du Windows SDK (téléchargeable à partir de MSDN ), que vous possédez déjà si Visual Studio est installé. Il est documenté ici . Vous pouvez générer des fichiers de manifeste pour les bibliothèques COM natives avec ceci:

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

Vous pouvez générer des fichiers manifestes pour les bibliothèques .NET COM avec, par exemple:

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

Cependant, cet outil pose quelques problèmes:

  • Le premier extrait ne générera pas d'attributs Progid, cassant les clients Qui utilisent CreateObject avec progids.
  • Le deuxième extrait générera les éléments <runtime> et <mvid> Qui doivent être supprimés avant Les manifestes fonctionnent réellement.
  • La génération de manifestes client pour les applications N'est pas prise en charge.

Peut-être que les futures versions du SDK amélioreront cet outil, j’ai testé celui du SDK Windows 6.0a (Vista).

57
Wim Coenen

Avec la tâche MSBuild GenerateApplicationManifest , j’ai généré un manifeste sur la ligne de commande identique au manifeste généré par Visual Studio. Je soupçonne que Visual Studio utilise le GenerateApplicationManifest lors de la construction. Ci-dessous, mon script de construction qui peut être exécuté à partir de la ligne de commande en utilisant msbuild "msbuild build.xml"

Merci à Dave Templin et à son post qui m'a indiqué la tâche de GenerateApplicationManifest, ainsi que la documentation de la suite de MSDN de la tâche .

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>
24
mcdon

Make My Manifest (MMM) est un outil agréable pour ce faire. Il est également possible d'écrire un script pour traiter tous vos fichiers DLL/OCX en utilisant mt.exe afin de générer un manifeste pour chacun puis de les fusionner. MMM est généralement mieux/plus facile, car il gère également de nombreux cas spéciaux/étranges.

9
jdve

Vous pouvez utiliser Unattended Make My Manifest spin off pour générer des manifestes directement dans des versions automatisées. Il utilise un fichier de script pour ajouter des composants COM dépendants. Voici un extrait de l'exemple ini avec les commandes 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
#

Il fonctionnera sous Windows 32 ou 64 bits.

7
wqw

Pour renseigner les ProgID que le mt.exe n'inclut pas, vous pouvez appeler ProgIDFromCLSID pour les rechercher dans le registre. Cela nécessite un enregistrement COM traditionnel avant de compléter le fichier manifeste, mais par la suite, le fichier manifeste sera autonome.

Ce code C # ajoute les ProgID à toutes les classes COM dans un manifeste:

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

Le code s'appuie sur ces définitions d'interopérabilité:

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