Génération et Installation de Typelib avec WiX
Après avoir demandé ce que Visual Studio fait pour enregistrer une bibliothèque COM , il est devenu clair que VS a fait deux choses pour l'enregistrement COM:
- enregistré la bibliothèque COM
- crée et enregistre une bibliothèque de types
Visual Studio semble faire cet enregistrement en utilisant regasm.EXE. Pour la première partie (l'enregistrement COM direct) en utilisant tallow
ou heat
(WiX 2.0 ou Wix 3.0) semble obtenir toutes les informations d'enregistrement COM de base correct.
Cependant, ce que suif / heat ne semble pas faire est de configurer une installation de bibliothèque de types. Il serait possible de créer une action personnalisée pour le faire avec un programme D'installation WiX et regasm.exe, mais invoquer des actions personnalisées ne sont pas les meilleures pratiques en ce qui concerne les installateurs basés sur Microsoft installer.
Après d'autres recherches, il semble qu'un msi ait la capacité de générer la bibliothèque de types lors de l'installation. En fait, WiX semble avoir un soutien direct pour cela! Dans un élément de fichier, vous pouvez ajouter un élémentTypelib . En fait, un article ici sur wix a un exemple de remplissage de L'élément TypeLib avec interface éléments.
Il semble qu'il y ait au moins deux attributs requis pour un élément D'Interface:
- Id
- Nom
Larry Osterman parle des autres parties de l'interface qui doivent être enregistrées pour un TypeLib en général, et cette entrée D'Interface semble prendre en charge le pièces individuelles. Larry dit que nous devons spécifier le ProxyStubClassId32 comme "{00020424-0000-0000-C000-000000000046}", afin que nous puissions facilement ajouter cela.
Où aller à partir de là et quoi remplir pour les différents éléments D'Interface m'a perplexe. Je suis allé de l'avant et ajouté L'élément TypeLib à mon fichier wix, et il compile avec succès. Je suis un peu désemparé quant à la façon de configurer les éléments D'Interface. Que devons-nous faire pour remplir correctement L'élément TypeLib, et quelles applications ou outils puis-je utiliser pour l'obtenir?
La réponse ci-dessous par wcoenen semble prometteuse...Je vais donner un coup de feu.
Mise à jour: posté ma solution finale ci-dessous comme une réponse.
3 réponses
Voici la façon dont l'homme paresseux résout ce problème: utilisez heat
de Wix 3.0.
Si vous avez une bibliothèque de types générée automatiquement et installée via regasm, heat
peut prendre la .tlb comme argument dans
heat file c:\my\path\to\my.tlb -out tlb.wxs
Il va générer tous les éléments typelib et interface dont vous avez besoin pour vous inscrire. Cela ne résoudra pas le problème de devoir les connaître à l'avance, et cela ne résoudra pas le problème des GUID qui changent lorsque la version de l'assembly change (même si l'interface ne le fait pas - ce qui est la seule fois où vous êtes censé le changer) mais il vous y arriver à mi-chemin.
L'astuce suivante peut vous aider à récolter les modifications du registre et à les transformer en un fichier wxs, y compris l'élément typelib que vous recherchez.
-
Tout d'abord, ramenez votre registre dans un état où la bibliothèque de types n'a pas été enregistrée:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /tlb /u mylib.dll
-
Exportez cet état propre du registre vers hklm-before.reg:
c:\WINDOWS\system32\reg.exe export HKLM hklm-before.reg
-
Enregistrez à nouveau la bibliothèque de types:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /tlb mylib.dll
-
Exporter le nouvel état du registre à hklm-après.reg:
c:\WINDOWS\system32\reg.exe export HKLM hklm-after.reg
Maintenant, nous avons deux fichiers texte, hklm avant.reg et hklm-après.reg. Créer un diff.fichier reg {[29] } qui ne contient que les différences pertinentes entre ceux-ci. Vous pouvez trouver les différences facilement avec un outil de comparaison. J'aime utiliser l'outil diff inclus dans TortoiseSVN puisque je l'utilise déjà tous les jours. (WinDiff ne semble pas bien fonctionner dans ce cas en raison de problèmes d'encodage de texte.)
-
, Nous pouvons maintenant convertir diff.reg dans un .wxs par appeler
heat.exe
avec la commandereg
. (Nécessite Wix 3.5 ou plus récent.)heat reg diff.reg -out typelib.wxs
Il ressemble à enregistrer une bibliothèque de types, la meilleure façon serait de générer votre propre fichier IDL ou ODL, qui contiendra vos GUID. Les Typelibs générés directement à partir de L'Assembly sont [i]dépendants[/i] des numéros de version de l'assembly : les GUID sont générés en fonction de ces informations, même si l'interface n'a pas changé. Visual Studio utilise regasm pour enregistrer et générer le typelib. En dessous de cela, il utilise RegisterTypeLib, un appel win32. Utilisation de l'élément typelib semble faire quelque chose de similaire. Rien de bon.
Cependant! Créer la bibliothèque de types à la main est douloureux. Il est possible d'obtenir ces GUID d'une autre manière: les extraire du typelib et créer les éléments vous-même.
Larry Osterman a les informations nécessaires: certaines clés de registre doivent être définies. Vous pouvez le faire avec la table de registre (et dans Wix3, cela signifie les éléments RegistryValue.) L'astuce ici est d'obtenir les GUID: tout ancien GUID ne fonctionnera pas. Normalement, obtenir les GUID est simplement une question de regarder dans L'IDL pour votre bibliothèque (vous avez écrit votre propre IDL, Non? :) ).
Si vous n'avez pas écrit un fichier IDL ou ODL à compiler dans un typelib, ils existent toujours, dans le fichier. Microsoft fournit plusieurs outils pratiques: LoadTypeLibEx et L'interface ITypeLib. Avec ces interfaces, vous pouvez parcourir la bibliothèque de types et obtenir toutes sortes d'informations. Comment parcourons-nous la bibliothèque?
J'ai simplement regardé comment Regasm il l'a fait! Un rapide dissassembler plus tard, et nous trouvons que regasm est écrit en C# aussi. La gloire de la journée. J'ai commencé un projet, et avec quelques instructions en utilisant et un PInvoke plus tard, nous avons:
using System.Runtime.InteropServices; // for struct marshaling
using System.Runtime.InteropServices.ComTypes; // for the ITypeLib + related types
// TYPELIBATTR lives in two places: Interop and ComTypes, but the one
// in Interop is deprecated.
using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR;
/// <summary>
/// The registry kind enumeration for LoadTypeLibEx. This must be made
/// here, since it doesn't exist anywhere else in C# afaik. This is found
/// here: http://msdn.microsoft.com/en-us/library/ms221159.aspx
/// </summary>
enum REGKIND
{
REGKIND_DEFAULT,
REGKIND_REGISTER,
REGKIND_NONE
}
// and this is how we get the library.
[DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
private static extern void LoadTypeLibEx(string strTypeLibName, REGKIND regKind, out ITypeLib TypeLib);
Ouf! Une fois que nous avons cela, nous devons naviguer dans la structure. Ceci interagit avec des ressources non gérées, alors préparez-vous à être Marshal
ing des choses autour.
ITypeLib lib = null;
LoadTypeLibEx(Value, REGKIND.REGKIND_NONE, out lib);
IntPtr libInfoPtr = IntPtr.Zero;
lib.GetLibAttr(out libInfoPtr);
TYPELIBATTR libInfo =
(TYPELIBATTR) Marshal.PtrToStructure(libInfoPtr, typeof(TYPELIBATTR));
int typeCount = lib.GetTypeInfoCount();
for (int i = 0; i < typeCount; ++i)
{
ITypeInfo info;
lib.GetTypeInfo(i, out info);
IntPtr typeDescrPtr = IntPtr.Zero;
info.GetTypeAttr(out typeDescrPtr);
TYPELIBATTR type =
(TYPELIBATTR)Marshal.PtrToStructure(typeDescrPtr, typeof(TYPELIBATTR));
// get GUID, other info from the specific type
}
lib.ReleaseTLibAttr(libInfoPtr);
libInfoPtr = IntPtr.Zero;
Ouf. Donc, vous devez écrire du code pour extraire les informations. Une fois que vous faites, vous devez remplir cette information dans Les entrées Registy, comme spécifié par Larry Osterman .
Bien sûr, vous pouvez éviter cette étape en écrivant simplement votre propre fichier IDL pour commencer. Le choix dans la douleur: c'est à vous!