1. Introduction

La recherche de la portabilité maximum des scripts écrits en VBScript ou JScript conduit naturellement à s'affranchir des contraintes d'inscription des composants ActiveX dans la base de registre. S'il existe une solution officielle depuis Windows XP SP2 au moyen des SxS Assemblies (voir à ce sujet cet article), une autre voie a été explorée depuis longtemps par des concepteurs soucieux de rendre portables leurs projets. Cette voie est celle de l'émulation de la fonction CoCreateInstance de l'API COM.

Il existe déjà un wrapper DLL dénommé DirectCOM mais dont les conventions d'appel de paramètres ne sont pas compatibles avec DynamicWapperX. RegFreeX a donc été écrit pour y remédier mais également pour repousser certaines limitations constatées dans les wrappers existants.

En effet, comme il s'agit d'une DLL standard, VBScript/JScript devront utiliser le composant DynamicWrapperX pour appeler ses fonctions. Ce composant et sa documentation sont disponibles ici.

2. Présentation de RegFreeX

Ce wrapper se présente sous la forme d'une bibliothèque DLL classique qui exporte trois fonctions.

2-1. CreateObjectEx

La principale, CreateObjectEx, a pour effet d'instancier une variable objet à partir d'une classe exposée par le composant. À la différence d'une fonction CreateObject classique, l'objet instancié n'est pas renvoyé par la fonction mais par un paramètre en sortie. La valeur renvoyée renseigne sur le statut de l'opération (succès ou codes d'erreur).

2-2. CallByNameEx

La deuxième fonction CallByNameEx est une particularité de RegFreeX et nécessite quelques explications.

Lorsqu'un objet est instancié en liaison différée comme en l'espèce, l'appel d'une méthode - ou d'une propriété ce qui revient au même - conduit le code, chargé de retrouver l'ID de celle-ci, à appeler l'interface IProvideClassInfo pour déterminer l'interface par défaut de la classe. Quand IProvideClassInfo n'est pas supportée par le composant (elle est facultative), le code jette un œil dans la base de registre pour trouver l'information. Si le composant n'est pas enregistré, l'appel échouera.

Volontiers cachotier, Microsoft n'a pas jugé utile d'attirer l'attention des concepteurs sur cette utilisation particulière de l'interface et il est fréquent que des composants ActiveX non-visuels - notamment ceux écrits en C++ - omettent de l'exposer ce qui les rend inutilisables en mode non-enregistré.

Il existe toutefois un "plan B" qui consiste à coder entièrement l'appel de méthode afin d'éviter tout accès au registre. C'est le rôle de la fonction CallByNameEx.

Dans le but d'identifier les composants "incomplets", la fonction CreateObjectEx renvoie un code d'instanciation spécifique (voir l'exemple infra).

2-3. CArray

La dernière fonction, CArray, est purement utilitaire et pallie l'impossibilité de transmettre directement à DynamicWrapperX une variable tableau. Il était donc nécessaire de définir une fonction capable de renvoyer l'adresse de cette variable afin de permettre ce transfert par référence.

3. Référence

Tous les paramètres sont obligatoires et sont mentionnés en gras.

3-1. CreateObjectEx(ClassName, FileName, ErrorName, Obj)

Instancie une variable objet à partir d'une classe exposée par un composant ActiveX.
ClassName : nom de la classe à instancier ;
FileName : nom du fichier du composant ActiveX ;
ErrorName : chaine décrivant la nature de l'erreur - vide si pas d'erreur ;
Obj : instance de l'objet IDispatch renvoyé ;
Valeur retournée :
-2 : objet instancié mais nécessite la fonction CallByNameEx (pas de IProvideClassInfo) ;
-1 : objet instancié et utilisable directement ;
 0 : fichier DLL absent ;
 1 : fonction 'DllGetClassObject' introuvable ;
 2 : ressource typelib introuvable ;
 3 : nom de classe inconnu ;
 4 : pas d'interface par défaut.

3-2. CallByNameEx(Obj, Name, iCallType, pArgs, ReturnValue)

Appel d'une méthode ou gestion d'une propriété d'une instance de classe issue d'un composant dépourvu d'interface IProvideClassInfo.
Obj : objet IDispatch dont la méthode ou la propriété doit être appelée ;
Name : nom méthode ou propriété ;
iCallType : définit le type d'appel :
   1 : méthode ;
   2 : lecture valeur propriété ;
   3 : écriture valeur propriété ;
   4 : écriture valeur propriété objet ;
pArgs : pointeur du tableau variant des paramètres éventuels de la méthode ou de la propriété à modifier ;
ReturnValue : valeur retournée par cet appel - le type dépend de la méthode ou de la propriété ;
Valeur retournée :
-1 : pas d'erreur OK ;
 0 : interface ITypeInfo non trouvée - l'objet n'a pas été instancié par CreateObjectEx ;
 1 : méthode ou propriété inconnue ;
 2 : mauvais type de paramètre ;
 3 : mauvais nombre de paramètres ou variable tableau des paramètres absente.
 n : une valeur quelconque d'erreur dépendant de la méthode/propriété appelée (généralement de la forme h800x000x).

3-3. CArray(NewArray, iBound)

Crée une variable tableau de dimension donnée et renvoie son pointeur
(contourne une limitation de DynamicWrapperX qui n'accepte pas les paramètres tableau).
NewArray : variable tableau à créer ;
iBound : nombre d'items du tableau ;
Valeur retournée :
adresse de la variable tableau créée.

4. Exemple de script - TestADO.vbs

Le composant MS ActiveX Data Objects ne peut être géré qu'au moyen de la fonction CallByNameEx. L'exemple qui suit ouvre un fichier texte dénommé "rfc6365.txt", y ajoute en dernière ligne la phrase "Cette ligne a été ajoutée par omen999" et sauvegarde le fichier sous le nom "rfc6365_.txt".

TestADO.vbs
Sélectionnez

'**************************************************************************
'* outil de test RegFreeX wrapper avec MS ActiveX Data Objects (ADO)
'* msado15.dll doit être désenregistré de la base de registre avant le test...
'* DynamicWrapperX requis - http://www.script-coding.com/dynwrapx_eng.html
'* novembre 2011  omen999 - http://omen999.developpez.com
'**************************************************************************
Option Explicit

Const _
vbMethod=1,_
vbGet=2, _
vbSet=4, _
vbLet=8

Dim oWrap 
Dim oShell
Dim sErr
Dim oStream
Dim iRep
Dim vResult
Dim aArgs
Dim pArgs
Dim vSize

'initiation DynamicWrapperX
On Error Resume Next
Set oWrap = CreateObject("DynamicWrapperX")
If oWrap Is Nothing Then
  Set oShell = CreateObject("WScript.Shell")
  oShell.Run "wscript.exe " & Chr(34) & WScript.ScriptFullName & Chr(34)
  WScript.Quit
End If
On Error GoTo 0
oWrap.Register "RegFreeX.dll","CreateObjectEx","i=wwPP","r=l"
oWrap.Register "RegFreeX.dll","CallByNameEx","i=pwupP","r=l"
oWrap.Register "RegFreeX.dll","CArray","i=Pl","r=p"

'instanciation objet Stream
iRep=oWrap.CreateObjectEx("Stream","msado15.dll",sErr,oStream)
If iRep > -1 Then 
  MsgBox sErr,16,"Erreur RegFreeX"
ElseIf iRep = -2 Then ' le composant ne supporte pas IProvideClassInfo
  iRep = oWrap.CallByNameEx(oStream,"Open",vbMethod,0,vResult)
  'création de la variable tableau paramètres avec un seul item 
  pArgs=oWrap.CArray(aArgs,1)
  'définition du charset souhaité
  aArgs(0)="Windows-1252" 
  iRep = oWrap.CallByNameEx(oStream,"Charset",vbSet,pArgs,vResult)
  aArgs(0)="rfc6365.txt"
  iRep = oWrap.CallByNameEx(oStream,"LoadFromFile",vbMethod,pArgs,vResult)
  iRep = oWrap.CallByNameEx(oStream,"Size",vbGet,0,vSize)
  aArgs(0)=vSize
  'le curseur est placé à la fin du stream
  iRep = oWrap.CallByNameEx(oStream,"Position",vbSet,pArgs,vResult)
  aArgs(0)="Cette ligne a été ajoutée par omen999"
  iRep = oWrap.CallByNameEx(oStream,"WriteText",vbMethod,pArgs,vResult)
  aArgs(0)="rfc6365_.txt"
  iRep = oWrap.CallByNameEx(oStream,"SaveToFile",vbMethod,pArgs,vResult)
  If iRep = -1 Then 
    MsgBox "Fichier généré avec succès",64,"Test RegFreeX"
  Else
    If iRep < -1 Then iRep = Hex(iRep)
    MsgBox "Erreur code : " & iRep,16,"Erreur RegFreeX"   
  End If
  iRep = oWrap.CallByNameEx(oStream,"Close",vbMethod,0,vResult)
End If

5. Liens

6. Remerciements

Je tiens à remercier bbil et Claude LELOUP pour leur relecture attentive et leurs observations.