I. Introduction▲
DynamicWrapperX est un composant ActiveX (serveur COM) écrit dans le but d'étendre et d'améliorer les possibilités du composant initial Dynawrap. Il permet aux langages JScript et VBScript d'appeler les fonctions exportées par les fichiers bibliothèques DLL, en particulier les fonctions de l'API Windows comme s'il s'agissait des méthodes d'un objet. Depuis la version 2, vous pouvez également appeler une fonction à partir de son adresse en mémoire ou directement son code machine représenté sous la forme d'une chaîne hexadécimale. Ce composant n'est pas une adaptation du code original de Dynawrap et a été écrit en partant de zéro en assembleur GoAsm. La version 2 a été testée sous Windows XP SP3, Windows 7 SP1 et Windows 8.1.
- possibilité d'inscrire le composant dans la base de registres uniquement pour l'utilisateur courant. Cela peut être utile si l'utilisateur n'a pas les droits d'administrateur ;
- ajout de nouveaux types de paramètres d'entrée et de valeurs de retour ;
- ajout des paramètres de sortie ;
- gestion identique des chaînes de caractères dans JScript et VBScript ;
- support des fonctions de rappel (callback) utilisées notamment par les fonctions d'énumération de l'API Windows comme EnumWindows (voir les exemples ci-dessous) ;
- suppression d'un bug sous Win98. Il n'est plus nécessaire d'instancier un objet pour chaque fonction enregistrée ;
- ajout de méthodes utiles de gestion de variables : NumGet(), NumPut(), StrPtr(), StrGet(), Space().
- Windows 98 n'est plus officiellement supporté (cette version n'a pas été testée dans cet environnement) ;
- le composant est désormais disponible en deux variantes pour chaque plate-forme : x86 et x64 ;
- son CLSID a été modifié ;
- lors de l'inscription dans la base de registre, la présence de la version 1 est vérifiée. Les conséquences seront différentes selon le résultat de cette vérification (voir enregistrement du composant ci-dessous) ;
- nouveaux types de paramètres et de valeurs de retour : m (Int64), q (UInt64) et v (pointeur vers une structure VARIANT) ;
- le nombre de fonctions de rappel est désormais illimité ;
- la méthode Register() est maintenant capable d'enregistrer une fonction par son numéro ordinal ou sous un nom différent (alias) ;
- le paramètre flag f est réintroduit ;
- nouvelles méthodes : RegisterAddr(), RegisterCode(), StrPut(), ObjPtr(), ObjGet(), TabPtr(), VarPtr(), MemAlloc(), MemFree(), Bitness(), Version() ;
- pour les méthodes StrPtr(), StrGet() et StrPut() vous pouvez spécifier la page de code de la chaîne au lieu de son type.
- les valeurs numériques de type « f » (float) sont désormais correctement définies dans les variables de la version 64 bits (valeurs retournées par les fonctions et celles lues dans la mémoire par la fonction NumGet()) ;
- la fonction ObjGet() levait une exception si elle était appelée sans que la valeur retournée fût assignée à une variable.
- nouvelles méthodes : LastError(), MemZero(), MemCopy(), MemRead() et MemWrite() ;
- un paramètre optionnel Offset a été ajouté aux fonctions StrGet() et StrPut() ;
- les noms de page de code comme « utf-8 » peuvent désormais être utilisés dans les fonctions StrPtr(), StrGet() et StrPut() ;
- le paramètre optionnel Offset des fonctions NumGet() et NumPut() peut être omis de la liste des paramètres.
- aucunes, le composant n'est plus compressé avec MPress afin d'éviter les faux positifs signalés par certains programmes de protection…
- la méthode LastError() qui levait une exception lorsqu'elle était appelée avec le paramètre Flag = 1 alors qu'il n'existait pas de description de l'erreur, a été corrigée.
- la méthode RegisterCallBack() accepte désormais le type de paramètre « f » pour pouvoir gérer les bibliothèques respectant la convention d'appel cdecl.
Bien que le composant DynamicWrapperX ait été testé soigneusement, l'auteur ne peut garantir un fonctionnement sans défaut. L'usage se fait aux risques et périls de son utilisateur.
II. Enregistrement du composant▲
Pour inscrire le composant, deux options sont possibles :
regsvr32.exe <chemin-du-composant>\dynwrapx.dll
regsvr32.exe /i <chemin-du-composant>\dynwrapx.dll
Si dynwrapx.dll réside dans le répertoire System32, System, Windows, dans le répertoire courant, ou dans un de ceux qui sont énumérés dans la variable d'environnement PATH, le chemin peut être omis. Si le composant est enregistré pour l'utilisateur courant, il ne sera disponible que pour cet utilisateur. En outre, à partir de Windows Vista, le composant ne sera pas non plus disponible pour les applications s'exécutant avec des droits d'administrateur.
Sur un système 64 bits, les deux variantes x86 et x64 devraient être enregistrées parallèlement afin que les applications 32 bits et 64 bits puissent utiliser chacune leur composant respectif.
Au cours du processus d'enregistrement, la version 2 vérifiera la présence de la version 1. Si c'est le cas, la version 2 s'installera en parallèle avec la version 1 et ne sera disponible que par son nom de version dénommé « DynamicWrapperX.2 ». Pour remplacer la version 1 par la version 2, vous devez désinscrire le premier. Dans ce cas, la version 2 sera également disponible par son nom de classe générique « DynamicWrapperX ».
Pour désinscrire le composant, procéder comme suit :
regsvr32.exe /u <chemin-du-composant>\dynwrapx.dll
regsvr32.exe /u /i <chemin-du-composant>\dynwrapx.dll
III. Liste des méthodes exposées - (les méthodes de la version 2 sont affichées en rouge)▲
- Register(DllName, FuncName[, i = paramTypes][, r = RetValType][, f = Flag])
- référence une fonction de la DLL comme une méthode de l'objet ; - RegisterAddr(Addr, FuncName[, i = paramTypes][, r = RetValType][, f = Flag])
- référence une adresse mémoire comme une méthode de l'objet ; - RegisterCode(HexStr[, FuncName][, i = paramTypes][, r = RetValType][, f = Flag])
- référence du code binaire défini sous la forme d'une chaîne hexadécimale comme une méthode de l'objet ; - RegisterCallback(FuncRef[, i = paramTypes][, r = RetValType][, f = Flag])
- référence une fonction du script comme fonction de rappel (callback) ; - NumGet(Address[, Offset][, Type])
- lit une valeur numérique en mémoire à l'adresse donnée Address + Offset ; - NumPut(Var, Address[, Offset][, Type])
- écrit en mémoire le contenu d'une variable numérique Var à l'adresse donnée Address + Offset ; - StrPtr(Var[, Type/Codepage])
- renvoie le pointeur vers la chaîne Var ; - StrGet(Address[, Type/Codepage])
- renvoie une chaîne de caractères à partir d'une adresse donnée Address ; - StrPut(Str, Address[, Type/Codepage])
- écrit une chaîne de caractères dans la mémoire à partir de l'adresse spécifiée Address ; - Space(Count[, Char])
- renvoie un pointeur vers une chaîne de Count caractères Char ; - ObjPtr(Object)
- renvoie un pointeur de l'objet ; - ObjGet(ObjPtr)
- récupère un objet à partir de son pointeur ; - ArrPtr(Array)
- renvoie un pointeur vers la structure SAFEARRAY du tableau spécifié ; - VarPtr(Variable)
- renvoie un pointeur vers la structure VARIANT d'une variable de script ; - MemAlloc(Bytes[, ZeroMem])
- alloue Bytes octets de la mémoire ; - MemFree(MemPtr)
- libère la mémoire précédemment allouée ; - Bitness()
- retourne la largeur de bits (32 ou 64) de l'objet DynamicWrapperX ; - Version([Field])
- retourne la(es) valeur(s) spécifiée(s) des quatre parties du numéro de version de DynamicWrapperX ; - LastError([Flag])
- renvoie le code d'erreur obtenu auprès de la fonction de l'API GetLastError immédiatement après le retour de la fonction initiale ; - MemZero(Address, Bytes)
- remplit une zone mémoire à partir de l'adresse spécifiée Address avec Bytes octets de zéros binaires ; - MemCopy(SrcAddr, DestAddr, Bytes)
- copie le contenu de Bytes octets d'une zone mémoire d'adresse initiale SrcAddr vers une adresse DestAddr ; - MemRead(Address, Bytes[, BytesPerGroup][, GroupsPerLine])
- lit le contenu d'une zone mémoire sous la forme d'une chaîne hexadécimale avec des options de format ; - MemWrite(HexStr, DestAddr[, Bytes])
- écrit une chaîne hexadécimale en mémoire après conversion sous forme binaire.
IV. Référence▲
IV-1. Register(DllName, FuncName[, i = paramTypes][, r = RetValType][, f = Flag])▲
Cette méthode référence les fonctions exportées par la bibliothèque DllName comme une méthode de l'objet. Après cela, vous pouvez appeler la fonction de la même façon qu'une méthode de l'objet, c'est-à-dire par son nom précédé d'un point. Le premier paramètre DllName est le nom de la DLL, le deuxième FuncName est le nom de la fonction qui doit correspondre à une fonction existante de la DLL. Les trois autres paramètres sont facultatifs et dépendent de la fonction concernée. Leur ordre est libre car ce sont des arguments nommés. Le préfixe i indique le type des paramètres de la fonction, r spécifie le type de la valeur de retour et f définit les indicateurs qui caractérisent le mode d'appel de la fonction.
DWX =
new ActiveXObject
(
"
DynamicWrapperX
"
);
//
instancie
l'objet
DWX.Register
(
"
user32.dll
"
,
"
MessageBoxW
"
,
"
i=hwwu
"
,
"
r=l
"
);
//
référence
une
fonction
de
la
dll
res =
DWX.MessageBoxW
(
0
,
"
Bonjour,
monde!
"
,
"
Test
"
,
4
);
//
appel
de
la
fonction
Set
DWX =
CreateObject
(
"
DynamicWrapperX
"
) '
instancie
l'objet
DWX.
Register
"
user32.dll
"
, "
MessageBoxW
"
, "
i=hwwu
"
, "
r=l
"
'
référence
une
fonction
de
la
dll
res =
DWX.
MessageBoxW
(
0
,"
Bonjour,
monde!
"
, "
Test
"
, 4
) '
appel
de
la
fonction
Le nom de la bibliothèque DllName sans indication de répertoire signifie une recherche par nom, en premier lieu dans la mémoire du processus parmi les bibliothèques déjà chargées - par ordre de leur chargement - puis sous Win XP l'ordre de recherche sur le disque sera le suivant :
- Répertoire de l'application (dans ce cas, soit wscript.exe ou cscript.exe) ;
- Répertoire courant ;
- System32 ;
- System ;
- Windows ;
- Répertoires de la variable d'environnement PATH.
Si la bibliothèque est un fichier comportant l'extension « .dll », cette extension peut être omise. Ainsi dans l'exemple ci-dessus, on pourrait simplement écrire « user32 ». Si la bibliothèque est un fichier sans extension, il faudra ajouter un point à la fin du nom, par exemple : « mylib. ».
Si la fonction a été référencée par son numéro ordinal, ce nombre est placé après le nom de la bibliothèque séparé par des deux-points. Le même procédé s'applique au référencement sous un nom différent. Celui-ci peut être utile si le nom de la fonction contient des caractères illégaux pour les noms de méthode.
Conservez à l'esprit que les nombres ordinaux des fonctions peuvent varier selon les différentes versions d'une bibliothèque et entre les variantes x86 et x64 d'une même version.
Le nombre ordinal peut être décimal ou hexadécimal avec le préfixe « 0x ». Si le nom de la bibliothèque n'a pas d'extension, « .dll » est ajouté par défaut.
DWX.Register
(
"
user32:110
"
,
"
MethodName
"
,
"
i=hwwu
"
,
"
r=l
"
);
//
référence
par
ordinal
DWX.Register
(
"
user32:BadName
"
,
"
GoodName
"
,
"
i=hwwu
"
,
"
r=l
"
);
//
référence
sous
un
nom
différent
DWX.
Register
"
user32:110
"
, "
MethodName
"
, "
i=hwwu
"
, "
r=l
"
'
référence
par
ordinal
DWX.
Register
"
user32:BadName
"
, "
GoodName
"
, "
i=hwwu
"
, "
r=l
"
'
référence
sous
un
nom
différent
Le nom de la fonction FuncName peut présenter des variantes selon que vous souhaitez la version Unicode ou son équivalent ANSI. Cela ne concerne normalement que les fonctions utilisant des paramètres de type chaîne. Par exemple, la fonction décrite dans la documentation telle que MessageBox, est effectivement présente dans user32.dll sous deux formes : MessageBoxW (pour les chaînes de caractères Unicode) et MessageBoxA (pour les chaînes de caractères classiques ANSI). Conformément à l'usage, le mode de recherche classique des fonctions a été maintenu : par exemple, si vous définissez le nom MessageBox et que cette fonction n'est pas trouvée dans user32.dll, la recherche se fera également pour la fonction MessageBoxA. Les noms de fonctions Unicode imposent en revanche de préciser le suffixe W.
Les paramètres peuvent être omis quand les fonctions ne réclament aucun paramètre sans qu'il soit nécessaire de conserver la virgule.
La valeur de retour peut également être omise, quelle que soit la valeur renvoyée.
DWX =
new ActiveXObject
(
"
DynamicWrapperX
"
);
DWX.Register
(
"
kernel32
"
,
"
GetCommandLine
"
,
"
r=s
"
);
//
cette
fonction
n'a
pas
de
paramètres
CmdLine =
DWX.GetCommandLine
(
);
//
ligne
de
commande
de
démarrage
du
process
WScript.Echo
(
CmdLine);
Set
DWX =
CreateObject
(
"
DynamicWrapperX
"
)
DWX.
Register
"
kernel32
"
, "
Beep
"
, "
i=uu
"
'
Beep
retourne
une
valeur
inutile
DWX.
Beep
800
, 1000
'
le
haut-parleur
du
PC
émet
un
son
(bip)
Le paramètre indicateur n'a actuellement qu'une seule valeur possible « t » qui signifie que la convention d'appel de la fonction est « thiscall ». Il est uniquement utilisé par la version 32 bits de DynamicWrapperX et est ignoré par la version 64 bits.
DWX.Register
(
"
SomeLib
"
,
"
SomeFunc
"
,
"
i=hwwu
"
,
"
r=l
"
,
"
f=t
"
);
//
appel
de
la
fonction
en
mode
"thiscall"
DWX.
Register
"
SomeLib
"
, "
SomeFunc
"
, "
i=hwwu
"
, "
r=l
"
, "
f=t
"
'
appel
de
la
fonction
en
mode
"thiscall"
IV-2. RegisterAddr(Addr, FuncName[, i = paramTypes][, r = RetValType][, f = Flag])▲
Cette méthode référence une adresse mémoire comme une méthode de l'objet. Les autres paramètres sont identiques à ceux de la fonction Register().
DWX.RegisterAddr
(
Address,
"
FuncName
"
,
"
i=hwwu
"
,
"
r=l
"
);
DWX.
RegisterAddr
Address, "
FuncName
"
, "
i=hwwu
"
, "
r=l
"
IV-3. RegisterCode(HexStr[, FuncName][, i = paramTypes][, r = RetValType][, f = Flag])▲
Cette méthode référence du code binaire défini sous la forme d'une chaîne hexadécimale comme une méthode de l'objet.
Le premier paramètre de cette méthode conserve le code machine d'une fonction sous la forme d'une chaîne hexadécimale. La mémoire nécessaire est allouée et le code y est écrit sous forme binaire. Après cela, vous pouvez l'appeler par le nom spécifié dans le deuxième paramètre. Cette méthode renvoie l'adresse du code. Le code machine est différent selon les plates-formes x86 et x64, d'où la nécessité de vérifier le nombre de bits du processus - via la vérification du nombre de bits de l'objet DynamicWrapperX - afin de déterminer quelle version du code utiliser. Une application possible pour ce type de fonction est de permettre le traitement de grandes quantités de données, où le code machine peut s'exécuter des dizaines voire des centaines de fois plus rapidement que le code de script.
Si vous voulez utiliser le code par son adresse, sans pour autant créer une nouvelle méthode, vous pouvez omettre tous les arguments sauf le premier. Mais gardez à l'esprit que la mémoire allouée pour le code est toujours affectée à l'objet et sera libérée avec lui.
La chaîne hexadécimale peut être continue ou divisée par des espaces, des tabulations ou des retours à la ligne :
DWX =
new ActiveXObject
(
"
DynamicWrapperX
"
);
if
(
DWX.
Bitness =
=
32
) {
Code =
"
8B442404F76C2408C3
"
//
la
fonction
multiplie
ses
arguments
et
retourne
le
résultat
}
else {
Code =
"
4889C8
48F7EA
C3
"
//
mov
rax,rcx
;
imul
RDX;
ret
}
CodeAddr =
DWX.RegisterCode
(
Code,
"
Multiply
"
,
"
i=ll
"
,
"
r=l
"
);
WScript.Echo
(
DWX.Multiply
(
5
,
4
));
Set
DWX =
CreateObject
(
"
DynamicWrapperX
"
)
If
DWX.
Bitness
=
32
Then
Code =
"
8B442404F76C2408C3
"
'
la
fonction
multiplie
ses
arguments
et
retourne
le
résultat
Else
Code =
"
4889C8
48F7EA
C3
"
'
mov
rax,rcx;
imul
rdx;
ret
End
If
CodeAddr =
DWX.
RegisterCode
(
Code, "
Multiply
"
, "
i=ll
"
, "
r=l
"
)
WScript.
Echo
(
DWX.
Multiply
(
5
,4
))
Vous pouvez également insérer des commentaires directement dans la chaîne hexadécimale en les entourant de parenthèses :
Code = "4889C8 (mov rax,rcx) 48F7EA(imul rdx) C3(ret)"
Si la chaîne hexadécimale est multiligne, vous pouvez signaler les commentaires avec un point-virgule :
Code =
"
4889C8
;
mov
rax,rcx
\n
"
+
"
48F7EA
;
imul
rdx
\ n
"
+
"
C3
;
ret
"
Code =
"
4889C8
;
mov
rax,rcx
"
&
vbCrLf
&
_
"
48F7EA
;
imul
rdx
"
&
vbCrLf
&
_
"
C3
;
ret
"
Mais notez que dans ce cas, chaque ligne - sauf la dernière - doit avoir un ou plusieurs caractères de saut de ligne à la fin car ils sont utilisés comme fin de marqueur de commentaire.
V. Types de paramètres d'entrée et de valeurs de retour :▲
- m - entier signé 64 bits - INT64, LONGLONG, etc. ;
- q - entier non signé 64 bits - UINT64, ULONGLONG, etc. ;
- l - entier signé 32 bits - LONG, INT, LPARAM, LRESULT, etc.
compris entre : -2 147 483 648 … 2 147 483 647 ; - u - entier non signé 32 bits - ULONG, UINT, DWORD, WPARAM, etc.
compris entre : 0 … 4 294 967 295 ; - h - valeur handle - HANDLE, HWND, HMODULE, HINSTANCE, HICON, etc.
compris entre : -2 147 483 648 … 4 294 967 295 ; - p - valeur identique à 'u' pour les nombres mais peut également pointer vers une interface IDispatch* ou une chaîne ;
- n - entier signé 16 bits - SHORT
compris entre : -32 768 … 32 767 ; - t - entier non signé 16 bits - USHORT, WORD, WCHAR, OLECHAR, etc.
compris entre : 0 … 65 535 ; - c - entier signé 8 bits - CHAR
compris entre : -128 … 127 ; - b - entier non signé 8 bits - UCHAR, BYTE
compris entre : 0 … 255 ; - f - réel virgule flottante simple précision (32 bits) - FLOAT ;
- d - réel virgule flottante double précision (64 bits) - DOUBLE ;
- w - chaîne Unicode - BSTR, LPWSTR, LPOLESTR, OLECHAR*, WCHAR*, etc. ;
- s - chaîne codage ANSI / Windows (code page par défaut) - LPSTR, LPCSTR, char*, etc. ;
- z - chaîne codage OEM / DOS (code page par défaut) - LPSTR, LPCSTR, char*, etc. ;
- v - pointeur vers une structure de type VARIANT.
Outre les handles et les pointeurs, il existe d'autres types qui adaptent leur largeur de bits en fonction du nombre de bits du processus. Par exemple : LPARAM, WPARAM et SIZE_T. Ils doivent être définis et renvoyés comme type h ou p de telle sorte que votre code puisse fonctionner correctement, quel que soit le nombre de bits de l'interpréteur de script.
Les types de variable correspondant à m et q seraient VT_I8 et VT_UI8, mais ils ne sont pas pris en charge par JScript et VBScript, ce qui limite ce que vous pouvez faire avec des entiers 64 bits dans les scripts. Tant que la valeur retournée par la fonction le permet, DynamicWrapperX le convertit en VT_I4 (entier 32 bits signé) ou VT_R8 (nombre double précision en virgule flottante). Dans la mesure où la mantisse du VT_R8 a seulement 53 bits, elle ne peut représenter tous les nombres dans l'intervalle de l'entier 64 bits. Dans ce cas, VT_I8 ou VT_UI8 est retourné. Tout ce que vous pouvez faire avec ces types est de les transmettre comme paramètres à une autre méthode ou de les afficher via WScript.Echo ou MsgBox. Aucun calcul sur ces données n'est possible.
Quand un grand nombre entier est retourné en tant que VT_R8 et que vous voulez afficher sa valeur dans une boîte de message, cet affichage peut ne pas être correct parce que le nombre de chiffres après la virgule dans la représentation de chaîne d'un nombre à virgule flottante est limité. En conséquence, si le nombre est 9 223 372 036 854 775 808 vous verrez dans la boîte de message la chaîne « 9,22337203685478E+18 » au lieu de « 9,223372036854775808E+18 ». Toutefois, la valeur numérique réelle de la variable n'est pas arrondie et conserve sa précision.
Si la valeur d'un entier 64 bits ne peut être représentée par un type numérique disponible, vous pouvez le spécifier sous forme de chaîne décimale ou hexadécimale (avec un préfixe 0x).
DWX.Register("lib.dll", "func", "i=m")
DWX.func("0xFFFFFFFFFFFFFFFF")
DWX.func("-0x7FFFFFFFFFFFFFFF")
DWX.func("18446744073709551615")
DWX.func("-9223372036854775807")
VI. Type de paramètres de sortie :▲
- M - pointeur 64 bits vers son adresse en mémoire - LONGLONG*, PLONGLONG, etc. ;
- Q - comme ci-dessus mais valeur non signée - ULONGLONG*, PULONGLONG, etc. ;
- L - pointeur 32 bits vers son adresse en mémoire - LONG*, LPLONG, etc. ;
- H - idem - HANDLE*, PHANDLE, LPHANDLE, etc. ;
- U - idem - ULONG*, LPDWORD, etc. ;
- P - idem ;
- N - idem - SHORT* ;
- T - idem - USHORT*, LPWORD, WCHAR*, OLECHAR*, etc. ;
- C - idem - CHAR*, etc. ;
- B - idem - UCHAR*, LPBYTE, etc. ;
- F - idem - FLOAT*, PFLOAT ;
- D - idem - DOUBLE*, PDOUBLE ;
- W - pointeur vers chaîne Unicode ;
- S - pointeur vers chaîne ANSI ;
- Z - pointeur vers chaîne OEM/DOS.
Les types de sortie ci-dessus peuvent être utilisés dans les langages de script qui passent les arguments des méthodes par référence comme VBScript. Dans ce cas DynamicWrapperX peut obtenir un pointeur vers la valeur de l'argument qui est ensuite passé à la fonction enregistrée, laquelle peut alors modifier la valeur à cette adresse. Si les arguments sont passés par valeur comme dans JScript, ils sont préalablement copiés et il n'y a donc pas moyen de retrouver et de modifier l'argument original. Dans ce cas, vous pouvez allouer en premier lieu un tampon en mémoire au moyen de MemAlloc() puis transmettre le pointeur retourné par cette fonction (en tant que type p), et après l'appel de la fonction lire la valeur qu'elle a écrite dans le tampon avec NumGet().
Certains moteurs de script copient également les chaînes avant de les passer à des méthodes. Dans ce cas, l'utilisation de types de sortie pour les chaînes n'a pas de sens non plus. La solution est similaire à celle qui a été décrite ci-dessus : un tampon en mémoire passé sous la forme d'un pointeur de type p et une lecture de la chaîne qui en résulte via la fonction StrGet().
VII. À propos des chaînes de caractères▲
JScript et VBScript utilisent des chaînes au format BSTR. Il s'agit d'une chaîne de caractères Unicode, chaque caractère étant codé sur 2 octets. La chaîne est terminée par un caractère spécial constitué de 2 octets nuls. En outre, le premier caractère est précédé par une valeur numérique codée sur quatre octets contenant la longueur de la chaîne en octets (à l'exclusion du caractère terminal). Le pointeur des variables chaînes est l'adresse du premier caractère effectif de la chaîne (c'est-à-dire sans prendre en compte l'en-tête de quatre octets).
- Paramètres d'entrée chaîne : w, s, z. Pour les types s et z la chaîne est copiée et convertie en fonction de l'encodage. La fonction API reçoit alors un pointeur vers cette copie. Après l'exécution de la fonction, cette copie est supprimée. Dans le cas du type w c'est le pointeur vers la chaîne originale qui est transmis.
- Paramètres de sortie chaîne : W, S, Z. Dans ce cas et pour les trois types, un pointeur vers la chaîne originale est passé, mais S et Z sont d'abord convertis - sur place - selon la page code correspondante. Au retour de la fonction, le contenu des chaînes définies par S et Z est reconverti en Unicode avec réajustement de sa taille, celle de la variable W est simplement recalculée. La longueur (en octets) de la chaîne est enregistrée dans l'en-tête. Cette dernière opération est nécessaire pour éviter les bogues, par exemple lors de la concaténation avec d'autres chaînes. Si la chaîne est destinée à recevoir des données de la fonction appelée, veillez à lui donner une taille suffisante.
- Pointeur : p. C'est la façon la plus simple. Le pointeur de la chaîne originale est transmis sans conversion. En conséquence, si la fonction écrit dans cette chaîne, les données seront conservées sous leur forme d'origine sans conversion ou ajustement de la longueur. La chaîne devient une simple mémoire tampon pour stocker les données. En apparence, cette option ressemble à l'option w mais ce n'est pas exact. La différence tient dans le fait que l'option p accepte non seulement les variables chaînes, mais aussi les variables numériques.
Retourner une chaîne avec p revient à récupérer une variable numérique contenant un pointeur vers la chaîne. Retourner une chaîne avec w, s ou z revient à récupérer une variable chaîne contenant un pointeur vers une copie de la chaîne retournée par la fonction API. Les chaînes retournées avec les paramètres s et z sont copiées avec conversion préalable. Les chaînes originales ne sont pas libérées car il n'est pas certain que cette libération soit sûre.
Si les éventuelles fuites mémoire vous préoccupent, privilégiez l'utilisation du paramètre p autant que possible pour le retour des chaînes.
Les fonctions API qui réclament des paramètres chaînes existent généralement en deux versions - par exemple, MessageBoxA et MessageBoxW. Il semble plus logique d'utiliser dans les scripts les versions Unicode - celles dont le nom se termine par W - puisqu'on évite alors tout travail de conversion à partir et vers Unicode.
L'utilisation de chaînes comme mémoire tampon peut ne pas être une technique fiable selon la façon dont le moteur de script traite les chaînes en interne. En conséquence, l'utilisation de la fonction MemAlloc() au lieu de Space() est recommandée.
VIII. RegisterCallback(FuncRef [, i = ParamTypes][, r = RetValType][, f = Flag])▲
Cette méthode permet de référencer un pointeur vers une fonction du script en vue de son exploitation par certaines fonctions de l'API. Par exemple, la fonction API EnumWindows utilise une fonction de rappel (callback). Elle énumère les fenêtres existantes, chaque fenêtre étant traitée par une fonction dont l'adresse est transmise en paramètre. Après chaque appel, la fonction principale attend la valeur renvoyée par la fonction callback. Si 1, l'énumération se poursuit, si 0 elle s'arrête.
En JScript et VBScript, une fonction de script n'est pas directement disponible sous forme de pointeur car ces fonctions sont des objets et leurs valeurs pointent vers une interface de type IDispatch. Aussi, la référence à une fonction de script est initialement transmise à la méthode RegisterCallback(). La fonction API qui utilise une fonction callback obtiendra alors un pointeur vers une procédure interne de dynwrapx.dll qui renverra l'appel à la fonction du script et lira la valeur renvoyée.
Avec JScript, le nom de la fonction - sans parenthèses - servira directement de référence alors qu'avec VbScript, vous devrez utiliser la fonction GetRef(). Outre la référence à la fonction, vous aurez à préciser les éventuels paramètres d'entrée ainsi que la valeur de retour exactement comme pour la méthode Register() (les paramètres en minuscules sont seuls utilisables). Toutefois, le paramètre indicateur « f » qui est optionnel pour la méthode Register() est ici obligatoire si la bibliothèque concernée par la fonction de rappel utilise la convention d'appel cdecl et ceci afin de laisser à la bibliothèque seule le soin de rétablir la pile. Ce paramètre prend alors la valeur c, exemple : « f=c ».
DWX =
new ActiveXObject
(
"
DynamicWrapperX
"
);
DWX.Register
(
"
user32
"
,
"
EnumWindows
"
,
"
i=ph
"
);
DWX.Register
(
"
user32
"
,
"
GetWindowTextW
"
,
"
i=hpl
"
);
//
variante
Unicode
//
DWX.Register("user32",
"GetWindowText",
"i=hpl");
//
variante
ANSI
pCbkFunc =
DWX.RegisterCallback
(
CbkEnumWin,
"
i=hh
"
,
"
r=l
"
);
//
enregistrement
CbkEnumWin
//
comme
procédure
de
rappel
//
et
obtention
de
son
pointeur
n=
0
,
m=
0
,
WinList=
"
"
;
Buf =
DWX.MemAlloc
(
256
);
//
buffer
de
réception
pour
les
titres
de
fenêtres
(une
chaîne
de
sortie)
DWX.EnumWindows
(
pCbkFunc,
0
);
//
appel
d'EnumWindows
et
transmission
du
pointeur
//
pour
la
procédure
de
rappel
DWX.MemFree
(
Buf);
WScript.Echo
(
"
Windows
in
total
:
"
+
m +
"
\ nWith
a
title
:
"
+
n +
"
\n\n
"
+
WinList);
//
la
procédure
de
rappel
(callback)
function CbkEnumWin
(
hwnd,
lparam)
{
var Title;
DWX.GetWindowTextW
(
hwnd,
Buf,
128
);
//
Unicode
Title =
DWX.StrGet
(
Buf,
"
w
"
);
//
DWX.GetWindowText(hwnd,
Buf,
256);
//
ANSI
//
Title
=
DWX.StrGet(Buf,
"s");
if
(
Title.
length >
0
) {
//
si
le
titre
existe,
on
l'ajoute
à
la
liste
WinList +
=
hwnd +
"
\ t
"
+
Title +
"
\ n
"
;
+
+
n;
}
+
+
m;
return 1
;
//
une
valeur
nulle
arrêtera
l'énumération
}
Set
DWX =
CreateObject
(
"
DynamicWrapperX
"
)
DWX.
Register
"
user32
"
, "
EnumWindows
"
, "
i=ph
"
DWX.
Register
"
user32
"
, "
GetWindowTextW
"
, "
i=hpl
"
'
variante
Unicode
'
DWX.Register
"user32","GetWindowText","i=hpl"
'
variante
ANSI
Set
Ref =
GetRef
(
"
CbkEnumWin
"
) '
référence
de
la
fonction.
pCbkFunc =
DWX.
RegisterCallback
(
Ref, "
i=hh
"
, "
r=l
"
) '
enregistrement
CbkEnumWin
'
comme
procédure
de
rappel
'
et
obtention
de
son
pointeur
n =
0
: m =
0
: WinList =
"
"
Buf =
DWX.
MemAlloc
(
256
) '
buffer
de
réception
pour
les
titres
de
fenêtres
(une
chaîne
de
sortie)
DWX.
EnumWindows
pCbkFunc, 0
'
appel
d'EnumWindows
et
transmission
du
pointeur
'
pour
la
procédure
de
rappel
DWX.
MemFree
Buf
WScript.
Echo
"
Windows
in
total
:
"
&
m &
vbCrLf
&
"
With
a
title
:
"
&
n &
_
vbCrLf
&
vbCrLf
&
WinList
'
la
procédure
de
rappel
(callback)
Function
CbkEnumWin
(
hwnd, lparam)
DWX.
GetWindowTextW
hwnd , Buf , 128
'
Unicode
Title =
DWX.
StrGet
(
Buf, "
w
"
)
'
DWX.GetWindowText
hwnd,
Buf,
256
'
ANSI
'
Title
=
DWX.StrGet(Buf,
"s")
If
Len
(
Titre) >
0
Then
'
si
le
titre
existe,
on
l'ajoute
à
la
liste
WinList =
WinList &
hwnd &
vbTab
&
Titre &
vbCrLf
n =
n +
1
End
If
m =
m +
1
CbkEnumWin =
1
'
une
valeur
nulle
arrêtera
l'énumération
End
Function
IX. Autres méthodes (gestion des variables)▲
Les paramètres entre crochets sont facultatifs mais vous ne pouvez pas les omettre si le suivant est présent à l'exception du paramètre Offset des méthodes NumGet() et NumPut().
IX-1. NumGet(Address [, Offset] [, Type])▲
Lit le contenu de la mémoire et l'affecte à une variable numérique.
Address - adresse de base.
[Offset] - décalage (en octets), positif ou négatif : utile dans les cycles de lecture/écriture de
nombres consécutifs, par défaut 0.
[Type] - le type de données à lire : même syntaxe que pour la méthode Register(), mais uniquement
les lettres minuscules, par défaut l. Seules les lettres minuscules peuvent être utilisées. Le nombre est
retourné dans la valeur renvoyée par la méthode.
IX-2. NumPut(Var, Address [, Offset] [, Type])▲
Écrit une variable numérique dans la mémoire.
Var - soit une valeur littérale numérique ou la variable contenant ladite valeur. Pour le reste, syntaxe
identique à NumGet().
Renvoie l'adresse disponible immédiatement après la valeur écrite.
Dans les deux dernières méthodes, Address peut être soit une valeur numérique soit une chaîne. Dans ce dernier cas, le pointeur de chaîne sert d'adresse de base. Cela vous permet d'utiliser la chaîne comme un tampon pour les données à placer à cet endroit comme des tableaux ou des données structurées, etc.
IX-3. StrPtr(Var[, Type/Codepage])▲
Retourne un pointeur vers une chaîne de caractères.
Var - nom de la variable chaîne.
[Type/Codepage] - les valeurs
possibles : w (par défaut), s, z, une page de code par exemple « cp65001 » (UTF-8) ou « cp1201 » (UTF-16 Big Endian) ou
enfin un nom de page de code comme « UTF-8 » ou « UTF-16 BE ». Si le nom n'est pas reconnu par la méthode, une recherche sera effectuée dans la
clé de registre « HKEY_CLASSES_ROOT\MIME\Database\Charset » de telle sorte que vous puissiez choisir comme paramètre un nom parmi les sous-clés.
En cas de valeur s ou z, la chaîne est préalablement convertie au même emplacement. Si la chaîne résultante ne rentre pas dans
la mémoire tampon d'origine, elle sera tronquée. Dans la mesure où la chaîne d'origine est en Unicode où chaque caractère prend deux octets, l'espace sera
suffisant dans la majorité des cas. Certains textes Est asiatiques sont peut-être plus longs en mode ANSI ou UTF-8 qu'en UTF-16 mais c'est une pure
supputation personnelle.
(1)
L'intérêt ou non d'obtenir un tel pointeur va dépendre de la façon dont le moteur de script transmet les chaînes à une méthode. S'il s'agit d'une transmission par copie, vous n'obtiendrez qu'un pointeur inutile vers une copie temporaire de cette chaîne.
IX-4. StrGet(Address[, Offset][, Type/Codepage])▲
Retourne une chaîne lue à partir d'une adresse augmentée de l'Offset éventuel et renvoie une copie de celle-ci.
Address peut être soit sous
la forme numérique soit une variable chaîne.
[Type/Codepage] - les valeurs
possibles : w (par défaut), s, z ou le codage de la chaîne de source. En cas de
valeur s ou z, une conversion au format Unicode est opérée. Pour plus de détails, voir la fonction StrPtr() ci-dessus.
Offset peut être une valeur négative ; zéro par défaut.
IX-5. StrPut(String, Address[, Offset][, Type/Codepage])▲
Écrit une chaîne String dans la mémoire à l'adresse spécifiée Address augmentée de l'Offset éventuel.
[Type/Codepage] - le type ou le codage de la chaîne de destination. Pour plus de détails, voir StrPtr(). La méthode
renvoie l'adresse immédiatement après le caractère nul final. Si le paramètre Address est égal à zéro, la méthode retourne
la taille de la mémoire tampon requise pour la chaîne de destination, y compris le caractère nul final.
Offset peut être une valeur négative ; zéro par défaut.
IX-6. Space(Count [, Char])▲
Retourne une variable chaîne contenant un pointeur vers une chaîne BSTR de longueur donnée.
Count - le nombre de caractères Unicode (2 bits) de la chaîne.
[Char] - le
caractère qui remplira la chaîne. Par défaut il s'agit du caractère espace comme dans la fonction
VBScript Space(). Pour remplir la ligne de zéros binaires, définissez Char comme une chaîne vide ("").
Cette méthode a été initialement conçue pour créer des chaînes à usage de tampon de mémoire (pour les nombres, les structures ou les chaînes renvoyées par des fonctions). Cependant, la pratique a montré que cette technique est souvent dangereuse. Vous ne savez jamais comment le moteur de script traitera la mémoire allouée à une chaîne. En d'autres termes, vous n'avez pas le plein contrôle sur ce tampon et vos données qui y sont stockées peuvent être perdues ou corrompues. Pour cette raison, la méthode MemAlloc() doit être privilégiée pour allouer de la mémoire (voir ci-dessous).
IX-7. ObjPtr(Object)▲
Renvoie un pointeur vers un objet Object.
IX-8. TabPtr(Array)▲
Renvoie un pointeur vers la structure SAFEARRAY du tableau Array spécifié. Ne fonctionne pas pour les tableaux JScript parce qu'ils sont en réalité des objets.
IX-9. VarPtr(Var)▲
Dans les langages de script qui passent les arguments par référence aux méthodes, comme VBScript, cette méthode peut récupérer un pointeur vers la structure VARIANT de la variable Var.
IX-10. MemAlloc(Bytes [, ZeroMem])▲
Alloue de la mémoire et renvoie un pointeur vers le début du tampon créé.
[ZeroMem] - si 1, la mémoire sera remplie avec des zéros binaires.
IX-11. MemFree(MemPtr)▲
Libère la mémoire précédemment allouée.
MemPtr - pointeur renvoyé par un appel précédent à MemAlloc().
IX-12. Bitness()▲
Récupère la largeur de bits de DynamicWrapperX (et donc la largeur de bits du processus qui l'utilise). Retourne la valeur 32 ou 64.
IX-13. Version([Field])▲
Renvoie le(s) champ(s) spécifié(s) des quatre sections du numéro de version de DynamicWrapperX.
[Field] - choisir une valeur appropriée
selon le tableau ci-dessous. Si Field est omis, sa valeur par défaut est zéro.
- 0 - Chaîne de version : "2.5.7.10" ;
- 1 - Numéro majeur de version : 2 ;
- 2 - Numéro mineur de version : 5 ;
- 3 - Numéro de build : 7 ;
- 4 - Version de révision : 10 (0xA) ;
- 5 - Numéro majeur + mineur : 0x20005 ;
- 6 - Build + version de révision : 0x7000A ;
- 7 - Numéro de version complet : 0x200050007000A.
Chaque champ numérique est un entier 16 bits qui occupe deux octets.
IX-14. LastError([Flag])▲
Si la fonction qui vient d'être appelée a été référencée avec le flag « l » (précédente erreur), la méthode renvoie le code d'erreur obtenu auprès de la fonction de l'API GetLastError immédiatement après le retour de la fonction initiale.
Si Flag = 1, la méthode retourne le texte décrivant la nature de l'erreur. Avant l'appel de la fonction référencée, le code d'erreur est toujours mis à zéro par la fonction SetLastError(0). Il vous appartient de vérifier dans la documentation que ce code d'erreur est bien géré par la fonction référencée afin que le test d'erreur ait un sens.
Les quatre méthodes suivantes, introduites par la version 2.1.0.0, autorisent la gestion directe de la mémoire et sont surtout destinées aux utilisateurs avancés désireux de combiner le Vbscript/JScript avec le langage assembleur ; à utiliser avec les précautions d'usage.
IX-15. MemZero(Address,Bytes)▲
Remplit une zone mémoire à partir de l'adresse spécifiée Address avec Bytes octets de zéros binaires.
Pas de valeur retournée.
IX-16. MemCopy(SrcAddr, DestAddr, Bytes)▲
Copie le contenu de Bytes octets d'une zone mémoire d'adresse initiale SrcAddr vers une adresse DestAddr.
Les zones peuvent se chevaucher.
La valeur retournée sera l'adresse située immédiatement après la dernière valeur écrite.
IX-17. MemRead(Address, Bytes[, BytesPerGroup][, GroupsPerLine])▲
Lit le contenu d'une zone mémoire sous la forme d'une chaîne hexadécimale avec des options de format.
- Address - adresse initiale de la zone ;
- Bytes - taille de la zone en octets ;
- BytesPerGroup - nombre d'octets de chaque groupe ;
- GroupsPerLine - nombre de groupes par ligne.
Les groupes seront séparés par un espace et les lignes par un saut de ligne.
IX-18. MemWrite(HexStr, DestAddr[, Bytes])▲
Écrit une chaîne hexadécimale en mémoire après conversion sous forme binaire.
- HexStr - la chaîne hexadécimale ;
- DestAddr - adresse de base ;
- Bytes - le nombre d'octets à écrire. S'il est omis ou nul, la chaîne entière est écrite.
Dans une chaîne de ce type, chaque octet est représenté par deux caractères. La chaîne peut contenir
des espaces, tabulations, sauts de ligne et commentaires acceptés par la fonction RegisterCode().
La valeur retournée sera l'adresse située immédiatement après la dernière valeur écrite.
Si DestAddress est nul, la méthode retourne la taille en octets occupée par la représentation binaire de la chaîne hexadécimale. Ce peut être utile si la chaîne complète contient des espaces ou commentaires qui ne seront pas convertis.
X. Exemples d'utilisation▲
DWX =
new ActiveXObject
(
"
DynamicWrapperX
"
);
str =
"
Bonjour
le
monde!
"
;
//
lecture
de
la
mémoire
et
des
codes
d'une
chaîne
codes =
"
"
;
for
(
i=
0
;
i <
str.
length;
+
+
i)
codes +
=
DWX.NumGet
(
str,
i*
2
,
"
t
"
) +
"
"
;
//
i
multiplié
par
2
car
la
chaîne
est
Unicode
et
//
l'adresse
en
octets
WScript.Echo
(
"
code
des
caractères
:\n
"
+
codes);
//
lecture/écriture,
la
chaîne
est
enregistrée
dans
l'ordre
inverse
de
ses
caractères
len =
str.
length;
buf =
DWX.MemAlloc
(
len*
2
+
2
);
//
tampon
pour
l'écriture,
chaque
caractère
Unicode
=
2
octets
DWX.NumPut
(
0
,
buf,
len*
2
,
"
t
"
);
//
écrit
2
octets
nuls
à
la
fin
(caractère
terminal)
for
(
i=
0
,
j=
len-
1
;
i <
len;
+
+
i,
-
-
j) {
code =
DWX.NumGet
(
str,
i*
2
,
"
t
"
);
DWX.NumPut
(
code,
buf,
j*
2
,
"
t
"
);
}
str1 =
DWX.StrGet
(
buf);
DWX.MemFree
(
buf);
WScript.Echo
(
"
chaîne
inversée
:\n
"
+
str1);
//
Manipulation
de
chaînes
ptr =
DWX.StrPtr
(
str);
//
place
le
pointeur
de
chaîne
dans
une
variable
numérique
//
la
chaîne
reste
en
Unicode
WScript.Echo
(
"
Addresse
de
la
chaîne
:
"
+
ptr);
ptr =
DWX.StrPtr
(
str,
"
z
"
);
//
récupère
le
pointeur
de
la
même
chaîne
//
préalablement
convertie
en
OEM/DOS
str1 =
DWX.StrGet
(
ptr,
"
z
"
);
//
lecture
de
la
chaîne
reconvertie
en
Unicode
WScript.Echo
(
"
Chaîne
reconstituée
:\n
"
+
str1);
//
Création
d'une
copie
UTF-8
d'une
chaîne
bufsize =
DWX.StrPut
(
str1,
0
,
"
cp65001
"
);
buf =
DWX.MemAlloc
(
bufsize);
DWX.StrPut
(
str1,
buf,
"
cp65001
"
);
//
lecture
et
retour
à
l'Unicode
str2 =
DWX.StrGet
(
buf,
"
cp65001
"
);
DWX.MemFree
(
buf);
WScript.Echo
(
"
Converti
depuis
l'UTF-8
:\n
"
+
str2);
Set
DWX =
CreateObject
(
"
DynamicWrapperX
"
)
str =
"
Bonjour
le
monde
!
C'est
moi.
"
'
lecture
de
la
mémoire
et
des
codes
d'une
chaîne
strlen =
Len
(
str)
codes =
"
"
For
i =
0
To
strlen-
1
codes =
codes &
DWX.
NumGet
(
str, i*
2
, "
t
"
) &
"
"
'
i
multiplié
par
2
car
la
chaîne
est
Unicode
et
Next
'
l'adresse
en
octets
WScript.
Echo
"
code
des
caractères
:
"
&
vbCrLf
&
codes
'
lecture/écriture,
la
chaîne
est
enregistrée
dans
l'ordre
inverse
de
ses
caractères
buf =
DWX.
MemAlloc
(
strlen *
2
+
2
) '
tampon
pour
l'écriture,
chaque
caractère
Unicode
=
2
octets
DWX.
NumPut
0
, buf, strlen *
2
, "
t
"
'
écrit
2
octets
nuls
à
la
fin
(caractère
terminal)
j =
strlen-
1
'
strlen-1
est
la
position
du
dernier
caractère
For
i=
0
To
strlen-
1
code =
DWX.
NumGet
(
str, i*
2
, "
t
"
) '
lecture
de
la
gauche
vers
la
droite
(le
décalage
augmente)
DWX.
NumPut
code, buf, j*
2
, "
t
"
'
écriture
de
la
droite
vers
la
gauche
(le
décalage
diminue)
j =
j-
1
Next
str1 =
DWX.
StrGet
(
buf)
DWX.
MemFree
buf
WScript.
Echo
"
Chaîne
inversée
:
"
&
vbCrLf
&
str1
'
Manipulation
de
chaînes
ptr =
DWX.
StrPtr
(
str) '
place
le
pointeur
de
chaîne
dans
une
variable
numérique
'
la
chaîne
reste
en
Unicode
WScript.
Echo
"
Addresse
de
la
chaîne
:
"
&
ptr
ptr =
DWX.
StrPtr
(
str, "
z
"
) '
récupère
le
pointeur
de
la
même
chaîne
'
préalablement
convertie
en
OEM/DOS
str1 =
DWX.
StrGet
(
ptr, "
z
"
) '
lecture
de
la
chaîne
reconvertie
en
Unicode
WScript.
Echo
"
Chaîne
reconstituée
:
"
&
vbCrLf
&
str1
'
création
d'une
copie
UTF-8
d'une
chaîne
bufsize =
DWX.
StrPut
(
str1, 0
, "
cp65001
"
)
buf =
DWX.
MemAlloc
(
bufsize)
DWX.
StrPut
str1, buf, "
cp65001
"
'
lecture
et
retour
à
l'Unicode
str2 =
DWX.
StrGet
(
buf, "
cp65001
"
)
DWX.
MemFree
buf
WScript.
Echo
"
Converti
depuis
l'UTF-8:
"
&
vbCrLf
&
str2
XI. Liens et téléchargements▲
Le composant (x86 et x64) peut être téléchargé ici : Dynwrapx2.2.0.0.zip (version 2.2.0.0 du 03 .08.2018).
Vous pouvez également poser vos questions et laisser vos commentaires et suggestions sur le forum russe :
http://forum.script-coding.com/viewtopic.php?id=7328
ou en français ici : forum Developpez.