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.
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 nouvelles de la version 2 sont affichées en rouge)▲
- Register(DllName, FuncName[, i = paramTypes][, r = RetValType][, f = Flags])
- référence une fonction de la DLL comme une méthode de l'objet ; - RegisterAddr(Addr, FuncName[, i = paramTypes][, r = RetValType][, f = Flags])
- référence une adresse mémoire comme une méthode de l'objet ; - RegisterCode(HexStr[, FuncName][, i = paramTypes][, r = RetValType][, f = Flags])
- 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])
- 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.
IV. Référence▲
IV-1. Register(DllName, FuncName[, i = paramTypes][, r = RetValType][, f = Flags])▲
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 comme 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 = Flags])▲
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 = Flags])▲
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é 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. A 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])▲
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).
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)
fonction 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)
Fonction 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.
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 ou une page de codes,
par exemple « cp65001 » (UTF-8) ou « cp1201 » (UTF-16 Big Endian). 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.
IX-4. StrGet(Address [, Type/Codepage])▲
Retourne une chaîne lue à partir d'une adresse 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.
IX-5. StrPut(String, Address [, Type/Codepage])▲
Écrit une chaîne Stringdans la mémoire à l'adresse spécifiée Address.
[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.
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.
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) avec sa documentation en anglais et en russe peut être téléchargé ici : Dynwrapx2.0.0.0.zip (version 2.0.0.0 du 19.10.2013).
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.
XII. Remerciements▲
Je tiens à remercier bbil pour sa relecture attentive et ses observations.