Question A propos du post "COM Interop ou la galère ..."

Plus d'informations
il y a 16 ans 40 minutes #6403 par Laurent Dardenne
Quelques remarques à propos de ce post .
>>Alors qu’en VBScript on pouvait le faire très simplement !
Oui c'est son point fort, car il est orienté COM. Il ne voit que des objets, là ou PowerShell voit des objets COM et des objets dotnet

>>Nous pouvons constater que le type de l’objet retourné est de type __ComObject...
La différence entre les deux exemples se situe dans la précision d'un GUID d'interface pour le premier, GUID que l'on retrouve dans la base de registre
[code:1]
HKEY_CLASSES_ROOT\Interface\{D30C1661-CDAF-11D0-8A3E-00C04FC9E26E}
[/code:1]
Le nom de la valeur par défaut est un nom d'interface, IWebBrowser2 .
>>En effet, il se trouve que cet objet COM (mais ce n’est malheureusement pas un cas isolé) n’implémente pas de bibliothèque de type, c'est-à-dire qu’il n’expose ni propriétés, ni méthodes.
Ce n'est pas tout à fait ça, c'est la librairie de type (tlb) qui décrit un objet COM.

>>De plus il ne possède pas non plus DE « wrapper » qui permet à cet objet d’être accessible facilement depuis un langage .NET tel que PowerShell. N’étant pas développeur, je vais m’arrêter là pour les explications techniques sur le sujet. Que faire donc ?
A partir du moment où on dispose d'une tlb on peut construire ce wrapper, de plus PowerShell n'est pas en cause dans ce cas.
Si tu utilises Visual Studio, celui-ci crée en interne ce Wrapper, je dirais donc que c'est Powershell qui ne dispose pas de la capacité de créer ce wrapper à partir d'un fichier .tlb.

Comment savoir si une tlb existe pour un objet COM (une interface) ?
En consultant la doc de l'interface IADsADSystemInfo , on peut voir en fin de page que le champ DLL indique \"Activeds.dll\".
A partir de là on recherche une librairie de type nommée \"Activeds.tlb\". Et ça match dans [code:1]\"C:\WINDOWS\system32\activeds.tlb\"[/code:1]

>>Et bien il va falloir ruser et essayer de passer outre les mécanismes d’abstraction de .NET pour accéder directement à notre objet COM.
L'abstraction est une notion de conception, peut être voulais-tu parler, dans le contexte de PowerShell, d'adaptation ?

>>plus exactement System.Type, ce qui est notre cas.
Sous PS il me semble que cela sera tjr le cas, puisque tout est adapté, voir aussi .

On peut, moyennant quelques étapes de développement, créer ce wrapper tant regretté. Il nous faut déjà connaitre le chemin du sdk 2.0 qui propose qq utilitaires :
[code:1]
$SDKFolder=(Get-item \"HKLM:\SOFTWARE\Microsoft\Microsoft SDKs\.NETFramework\v2.0\"|Get-ItemProperty).InstallationFolder
cd c:\temp
[/code:1]
Une fois ceci fait, on doit créer un fichier de signature afin d'autoriser l'insertion d'un assembly dans le GAC, à moins que votre organisation vous en propose un :
[code:1]
#Création d'une signature
&\"$SDKFolder\bin\sn.exe\" -k signature.snk
[/code:1]
Je n'ai pas testé, mais il me semble que l'on peut s'affranchir de cette étape, encore faut-il savoir ce que l'on fait.
Ensuite on crée un fichier interop, notre wrapper, en utilisant l'outil TlbImp (tlb import) :
[code:1]
#Création de l'assembly à partir d'une librairie de type (COM)
&\"$SDKFolder\bin\TlbImp\" C:\WINDOWS\system32\activeds.tlb /out:C:\temp\Interop.activeds.dll /namespace:\"Interop.activeds\" /primary /keyfile:C:\temp\signature.snk /asmversion:1.0.0
[/code:1]
En ligne de commande vous noterez quelques avertissements, là où Visual Studio n'en remonte aucun.
Ceci fait on peut copier le fichier dans le GAC (Global Assembly Cache) ou le charger directement :
[code:1]
$AdsAssembly=[Reflection.Assembly]::LoadFrom(\"$pwd\Interop.activeds.dll\"«»)
[/code:1]
Reste à tester l'usage de notre Wrapper :
[code:1]
$Path= new-object Interop.ActiveDs.IADsPathname
#New-Object : Constructeur introuvable. Impossible de trouver un constructeur approprié pour le type Interop.ActiveDs.IADsPathname.
[/code:1]
Cela ne fonctionne pas car on continue de référencer le nom de l'interface COM, alors que le wrapper nous propose des classes:
[code:1]
$Classes=$AdsAssembly.GetExportedTypes()|? {$_.name -match \"class$\"}
$Classes
[/code:1]
Dans la liste on trouve une classe PathnameClass en lieu et place de IADsPathname :
[code:1]
$Path= new-object Interop.ActiveDs.PathNameClass
$Path|gm
# TypeName: Interop.activeds.PathnameClass
#
# Name MemberType Definition
# ----

# AddLeafElement Method System.Void AddLeafElement(string bstrLeafElement)
# CopyPath Method System.Object CopyPath()
# CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
# Equals Method bool Equals(System.Object obj)
# GetElement Method string GetElement(int lnElementIndex)
# GetEscapedElement Method string GetEscapedElement(int lnReserved, string bstrInStr)
# ...
[/code:1]
Reste à confirmer le résultat de la méthode GetEscapedElement:
[code:1]
$CN = 'cn=jim/morisson=test'
$Path.GetEscapedElement(0,$CN)
#cn=jim\/morisson\=test
[/code:1]
Cela me semble correspondre.
Une fois notre wrapper construit plus besoin d'utiliser InvokeMember qu'on utilisera seulement s'il n'existe aucune TLB ou si son import n'est pas possible.
Notre classe .NET est en fait une coclass , une classe Composants, qui contient toutes les interfaces supportées par un objet :
[code:1]
$CoClasse=$Classes[0]
$CoClasse.GetInterfaces()
[/code:1]

coclass: A component object (an association between a class identifier (CLSID) and a set of named implementations of IUnknown) that is defined using the coclass keyword.

On peut s'arrêter là. PowerShell, c'est comment déjà ? Ah oui facile :P
Mais pas COM :sick:

ps:
Reste à demander à MS un cmdlet spécialisé pour cette opération...<br><br>Message édité par: Laurent Dardenne, à: 21/03/10 10:35

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 11 mois #6404 par Arnaud Petitjean
Salut Laurent,

Merci pour toutes ces précisions techniques. Je référencerais ton post à mon billet pour que les lecteurs aient une explication plus poussée du sujet.

On peut s'arrêter là. PowerShell, c'est comment déjà ? Ah oui facile
Mais pas COM

ps:
Reste à demander à MS un cmdlet spécialisé pour cette opération...


Tu as très bien résumé la situation :)

Ce serait vraiment bien que MS nous mette ça dans la v3, je leur proposerait sur Connect.

Bon week-end,

Arnaud

MVP PowerShell et créateur de ce magnifique forum :-)
Auteur de 6 livres PowerShell aux éditions ENI
Fondateur de la société Start-Scripting
Besoin d'une formation PowerShell ?

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 11 mois #6405 par Laurent Dardenne
Arnaud écrit:

Ce serait vraiment bien que MS nous mette ça dans la v3, je leur proposerait sur Connect.

Bonne idée, je ne l'ai pas fait, car j'ai un soucis d'indentifiant sur MSConnect.

Sinon il reste un petit point à éclaircir sur l'instruction :
[code:1]
$objIE = New-Object -ComObject InternetExplorer.Application.1
[/code:1]
Internet Explorer étant lié aux composants du shell Windows, il est difficile de mettre en évidence la dll interop utilisée.
Si on utilise une application Office on voit que l'appel à New-Object -ComObject charge implicitement un fichier interop :
[code:1]
$before=[AppDomain]::CurrentDomain.GetAssemblies()
$ExcelIE = New-Object -ComObject Excel.Application
$After=[AppDomain]::CurrentDomain.GetAssemblies()
Compare-object $before $after -property location
# location SideIndicator
#

# C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.Excel\1... =&gt;
[/code:1]
On comprend mieux pourquoi, avec une même instruction, dans certains cas cela fonctionne et pas dans d'autres.

Pour terminer certaines coclasses liées à IE sont dans les fichiers suivants :
[code:1]
cd \&quot;$env:«»ProgramFiles\Microsoft.NET\Primary Interop Assemblies\&quot;
&amp;\&quot;$SDKFolder\bin\ildasm.exe\&quot; .\Microsoft.mshtml.dll
#C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies\interop.shdocvw.dll
[/code:1]
Celles-ci devant être chargées explicitement, voir ce post .
Arnaud écrit:

Bon week-end,

Merci, à toi aussi.

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.066 secondes
Propulsé par Kunena