Question

J'ai des applications (certaines natives, certaines .NET) qui utilisent des fichiers manifestes de sorte qu'elles puissent être déployé en isolation complète , sans nécessiter d’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 de manifeste est un peu compliquée. Est-il possible de générer ces fichiers automatiquement? Idéalement, j'aimerais simplement déclarer la dépendance de l'application à une liste de serveurs COM (natifs et .NET), puis laisser le reste être généré automatiquement. Est-ce possible?

Était-ce utile?

La solution

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

Créer mon manifeste ( lien )

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

Fait intéressant, 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, démarre à partir du fichier de projet VB6. Honte, parce que beaucoup de ce qu'il fait n'a vraiment rien à faire avec VB6.
  • application de style assistant, non approprié pour intégrer dans une construction processus. Ce n'est pas un gros problème si votre les dépendances ne changent pas beaucoup.
  • gratuiciel sans source, il est risqué d’y avoir recours 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 codeproject )

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 dans le 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 kit de développement logiciel Windows (peut être téléchargée à partir de displaylang = fr "rel =" noreferrer "> 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 ce format:

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

Vous pouvez générer des fichiers de manifeste pour les bibliothèques COM .NET avec ce format:

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

Cependant, cet outil présente des problèmes:

  • Le premier extrait ne générera pas attributs progressifs, rupture des clients qui utilisent CreateObject avec progids.
  • Le deuxième extrait générera <runtime> et <mvid> éléments qui doivent être retirés avant les manifestes fonctionnent réellement.
  • Génération de manifestes client pour 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).

Autres conseils

Avec la tâche MSBuild, GenerateApplicationManifest , j'ai généré un manifeste à la commande. ligne identique au manifeste généré par Visual Studio. J'imagine que Visual Studio utilise le GenerateApplicationManifest lors de la génération. Vous trouverez ci-dessous mon script de génération qui peut être exécuté à partir de la ligne de commande à l'aide de msbuild & "; Msbuild Build.xml &";

.

Merci à Dave Templin et à son message qui m'a indiqué la tâche GenerateApplicationManifest , ainsi que celle de MSDN < a href = "http://msdn.microsoft.com/en-us/library/6wc2ccdc.aspx" rel = "noreferrer"> documentation supplémentaire 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>

Créer mon manifeste (MMM) est un bon outil pour ce faire. Il est également possible d'écrire un script pour traiter tous vos fichiers DLL / OCX à l'aide de mt.exe. pour générer un manifeste pour chacun, puis les fusionner tous ensemble. MMM est généralement mieux / plus facile, car il gère également de nombreux cas spéciaux / étranges.

Vous pouvez utiliser Créer un manifeste sans surveillance 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.

Pour renseigner les ID de programme que 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, ce fichier sera autonome.

Ce code C # ajoute les ProgID à toutes les classes COM d'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 repose 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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top