88 votes

Générer des fichiers manifestes pour les COM sans enregistrement

J'ai quelques applications (certaines natives, d'autres .NET) qui utilisent des fichiers manifestes afin qu'elles puissent être déployé de manière totalement isolée sans nécessiter d'enregistrement COM global. Par exemple, la dépendance vis-à-vis du 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 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 gestion manuelle de ces fichiers manifestes est un peu pénible. Existe-t-il un moyen de générer ces fichiers automatiquement ? Idéalement, je voudrais simplement déclarer la dépendance de l'application à une liste de serveurs COM (natifs et .NET) et laisser le reste être généré automatiquement. Est-ce possible ?

0 votes

+1 aussi : Retagged regfreecom car ce tag est plus commun pour les COM sans registre.

0 votes

Puis-je utiliser une version supérieure de mstscax.dll dans mon propre dossier d'installation par le biais d'un fichier manifeste ?

0 votes

@acewind oui. (Vous voudrez peut-être poster une nouvelle question avec plus de détails).

65voto

Wim Coenen Points 41940

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

Faire mon manifeste ( lien )

Cet outil analyse un projet VB6 pour rechercher les dépendances COM, mais il prend également en charge la déclaration manuelle des dépendances COM liées tardivement (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 cela était possible.

Cela semble être 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. Dommage, car une grande partie de ce qu'il fait n'a vraiment rien à faire avec VB6.
  • application de type assistant, qui ne pas adaptée pour être intégrée dans un processus. Ce n'est pas un gros problème si vos dépendances ne changent pas beaucoup.
  • freeware sans source, il est risqué de s'y fier car il peut devenir un abandonware à tout moment.

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

regsvr42 ( lien vers le codeprojet )

Cet outil en ligne de commande génère des fichiers manifestes pour les bibliothèques COM natives. Il invoque DllRegisterServer et espionne ensuite l'auto-enregistrement en ajoutant 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

Fait partie du SDK de Windows (peut être téléchargé à partir de MSDN ), que vous avez déjà si vous avez installé Visual Studio. Il est documenté ici . Vous pouvez générer des fichiers manifestes pour les bibliothèques COM natives avec lui comme 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 COM de .NET comme suit :

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

Toutefois, cet outil présente quelques problèmes :

  • Le premier extrait ne générera pas les attributs attributs progid, cassant ainsi les clients qui utilisent CreateObject avec des progids.
  • Le deuxième extrait va générer <runtime> y <mvid> éléments qui doivent être enlevés avant que les manifestes fonctionnent réellement.
  • La génération de manifestes clients 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).

1 votes

Je pense que vous avez oublié une option : mazecomputer.com mais je n'y connais rien, le site web ne le décrit pas.

0 votes

MMM redirigera également les DLL non COM (standard). Je ne suis pas sûr que les autres outils le fassent.

0 votes

Juste une note pour les nerveux : la source de MMM a été publiée. En revanche, il semble que ce soit parce que l'auteur a décidé d'arrêter d'y travailler. C'est tout de même un signe positif.

33voto

mcdon Points 1690

Avec la tâche MSBuild Générer un Manifeste d'application J'ai généré un manifeste à la ligne de commande identique au manifeste que Visual Studio génère. Je soupçonne Visual Studio d'utiliser le Générer un Manifeste d'application pendant la construction. Voici mon script de build qui peut être exécuté depuis la ligne de commande en utilisant msbuild "msbuild build.xml".

Merci à Dave Templin et à son qui m'a orienté vers la tâche GenerateApplicationManifest. et le site MSDN 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>

2 votes

Je pense que cela devrait vraiment être marqué comme la réponse à cette question. Je l'utilise maintenant pour automatiser toute la génération de nos manifestes. Merci @mcdon, vous m'avez épargné beaucoup de travail.

0 votes

Je suis d'accord pour dire que c'est la meilleure solution pour construire avec Visual Studio. Elle n'est probablement pas mieux notée uniquement parce qu'elle a été postée beaucoup plus tard que les autres réponses.

0 votes

Comment ajouter ceci dans un csproj ?

9voto

jdve Points 129

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

3 votes

Je suis un peu nerveux à propos de ce truc MMM ; c'est juste un blog, un freeware mais aucun code source disponible, seulement un lien vers un "exe auto-extractible" et je vois des commentaires sur l'utilitaire qui fait planter XP. mmm...

0 votes

Ces "plantages" étaient dus à la mort de l'utilitaire MMM lui-même. Cela a été corrigé dans la version 0.6.5 mais vous voudrez quand même la 0.6.6, car bien qu'elle soit encore une bêta, elle n'expire plus. Vous pouvez toujours utiliser MT.EXE à la place, comme déjà suggéré.

0 votes

Mt.exe ne génère pas de progid's lorsque je l'utilise sur des serveurs com natifs comme dbgrid32.ocx.

8voto

wqw Points 6624

Vous pouvez utiliser Faire mon manifeste sans surveillance spin off pour générer des manifestes directement dans les constructions automatisées. Il utilise un fichier script pour ajouter les 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 fonctionne sous Windows 32 ou 64 bits.

0 votes

+1 Intéressant, d'autant plus que le code source est disponible. Je suis un peu confus par la similitude des noms, apparemment "make my manifest" et "unattended make my manifest" sont des outils différents par des auteurs différents.

2 votes

Note - en 2017 (8 ans après...), ce projet est toujours actif avec des mises à jour de maintenance occasionnelles. github.com/wqweto/UMMM/commits/master . Il fonctionne bien et je l'utilise régulièrement.

0voto

Edward Brey Points 8771

Pour remplir les ProgIDs 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 remplir le fichier manifeste, mais par la suite, le fichier manifeste se suffira à lui-même.

Ce code C# ajoute les ProgIDs à 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;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X