Win32::API - Utilitaire d'importation d'API Win32


NOM

Win32::API - Utilitaire d'importation d'API Win32

Retour en haut de la page


SYNOPSIS

  use Win32::API;
  $function = new Win32::API(
      $library, $functionname, \@argumenttypes, $returntype,
  );
  $return = $function->Call(@arguments);

Retour en haut de la page


RÉSUMÉ

Avec ce module, vous pouvez importer et appeler des fonctions quelconques des bibliothèques à liens dynamiques Win32 (DLL), sans devoir écrire une extension XS. Notez cependant que ce module ne peut pas tout faire (les paramètres d'entrée et de sortie sont limités aux cas les plus simples) et, de toute manière, une extension XS régulière est toujours plus sûre et plus rapide.

La version courante de Win32::API est disponible sur mon site web :

  http://dada.perl.it/

Elle est aussi disponible sur votre miroir CPAN le plus proche (mais prévoir quelques jours pour que la dernière version soit déployée autour du monde) accessible à:

  http://www.perl.com/CPAN/authors/Aldo_Calpini/

Un court exemple de la manière d'utiliser ce module (il obtient juste le PID du processus courant comme, par exemple, la variable interne $$ de Perl) :

    use Win32::API;
    $GetPID = new Win32::API("kernel32", "GetCurrentProcessId", '', 'N');
    $PID = $GetPID->Call();

Les possibilités sont presque infinies (mais toutes ne sont pas bonnes :-). Amusez-vous.

Retour en haut de la page


CRÉDITS

Tous les crédits vont à Andrea Frosini pour l'astuce élégante en assembleur qui fait fonctionner la chose. Un grand merci aussi à Gurusamy Sarathy pour son inestimable aide dans le développement XS, et à toute la communauté Perl pour être ce qu'il est.

Retour en haut de la page


DESCRIPTION

Pour utiliser ce module, placez la ligne suivante au début de votre script :

    use Win32::API;

Vous pouvez utiliser maintenant la fonction new() du module Win32::API pour créer un nouvel objet API (voyez IMPORTER UNE FONCTION) et alors invoquer la méthode Call() sur cet objet pour exécuter un appel à l'API importé (voyez APPELER UNE FONCTION IMPORTÉE).

IMPORTER UNE FONCTION

Vous pouvez importer une fonction d'une bibliothèque à lien dynamique (DLL) 32 bits avec la fonction new(). Cela crée un objet Perl qui contient la référence à cette fonction que vous pouvez appeler plus tard avec Call(). Vous devez lui passer 4 paramètres :

  1. Le nom de la bibliothèque dont vous voulez importer une fonction.

  2. Le nom de la fonction (comme exportée par la bibliothèque)

  3. Le nombre et les types des arguments que la fonction attend en entrée.

  4. Le type de la valeur retournée par la fonction.

Pour expliquer le mieux possible leur signification, supposons que nous voulons importer et appeler l'API Win32 GetTempPath(). Cette fonction est définie en C par :

    DWORD WINAPI GetTempPathA( DWORD nBufferLength, LPSTR lpBuffer );

Cela est documenté dans le Win32 SDK Reference; vous pouvez le chercher sur le site WWW de Microsoft, ou dans la documentation de votre compilateur C, si vous en avez un.

  1. Le premier paramètre est le nom du fichier bibliothèque qui exporte cette fonction; notre fonction réside dans le fichier système KERNEL32.DLL. Quand on spécifie ce nom comme paramètre, l'extension .DLL est implicite, et si aucun chemin n'est donné, le fichier est cherché dans les répertoires, suivants :
    1. Le répertoire dans lequel l'application est chargée.

    2. Le répertoire courant.

    3. Le répertoire système Windows (par ex. c:\windows\system ou system32).

    4. Le répertoire Windows (par ex. c:\windows).

    5. Les répertoires qui sont listés dans la variable d'environnement PATH.

    Vous n'avez donc pas à écrire C:\windows\system\kernel32.dll; kernel32 est suffisant.

        $GetTempPath = new Win32::API('kernel32', ...
  2. Maintenant pour le deuxième paramètre : le nom de la fonction. Il doit être écrit exactement comme il est exporté par la bibliothèque (la casse des caractères est importante ici). Si vous utilisez Windows 95 ou NT 4.0, vous pouvez utiliser la commande Quick View sur le fichier DLL pour voir les fonctions qu'elle exporte. Souvenez-vous que vous ne pouvez importer que les fonctions des DLL 32 bits : dans Quick View, les caractéristiques du fichier devraient rapporter quelque part «32 bit word machine»; empiriquement, quand vous voyez que tous les noms de fonctions exportées sont en majuscules, la DLL est une bibliothèque 16 bits et vous ne pouvez pas l'utiliser. Si leur capitalisation semble correcte, alors c'est probablement une DLL 32 bits.

    Notez aussi que beaucoup d'APIs Win32 sont exportés deux fois, avec l'addition d'un A ou d'un W à la fin de leur nom, pour - respectivement - la version ASCII et la version Unicode. Quand un nom de fonction n'est pas trouvé, Win32::API ajoute un A au nom et essaie encore une fois; si l'extension est construite sur un system Unicode, alors il essaiera avec un W à la place. Donc notre nom de fonction sera:

        $GetTempPath = new Win32::API('kernel32', 'GetTempPath', ...

    Dans notre cas GetTempPath est chargé en fait comme comme GetTempPathA.

  3. Le troisième paramètre, la liste des paramètres d'entrée, spécifie combien d'arguments la fonction a besoin, ainsi que leurs types. Il peut être passé comme simple chaîne dans laquelle chaque caractère représente un paramètre, ou comme une référence à une liste. Les formes suivantes sont valides:
        "abcd"
        [a, b, c, d]
        \@LIST

    Mais celles-ci ne le sont pas :

        (a, b, c, d)
        @LIST

    Le nombre de caractères de la chaîne, ou d'éléments dans la liste, spécifie le nombre de paramètres, et chaque caractère ou élément de liste spécifie le type d'un argument; les types admis sont:

    I: la valeur est un entier (integer)
    N: la valeur est un entier long
    F: la valeur est un nombre à virgule flottante (float)
    D: la valeur est un nombre en double précision (double)
    P: la valeur est un pointeur (vers une chaîne, une structure, ...etc.)

    Notre fonction a besoin de deux paramètres: un nombre (DWORD) et un pointeur vers une chaîne (LPSTR):

        $GetTempPath = new Win32::API('kernel32', 'GetTempPath', 'NP', ...
  4. Le quatrième et dernier paramètre est le type de la valeur retournée par la fonction. Ce peut être un des types vus au-dessus, plus un autre type nommé V (pour void), utilisé pour les fonctions qui ne retournent pas de valeur. Dans notre exemple, la valeur retournée par GetTempPath() est un DWORD, donc notre type du retour sera N :
        $GetTempPath = new Win32::API('kernel32', 'GetTempPath', 'NP', 'N');

    Maintenant la ligne est complète, et l'API GetTempPath() est prêt à être utilisé dans Perl. Avant de l'appeler, vous devriez tester que $GetTempPath est defined, sinon la fonction ou la bibliothèque ne pourraient pas être chargées; dans ce cas, $! contiendrait le message d'erreur rapporté par Windows. Notre définition, avec détection d'erreur ajoutée, devrait ressembler à ceci :

        $GetTempPath = new Win32::API('kernel32', 'GetTempPath', 'NP', 'N');
        if(not defined $GetTempPath) {
            die "Can't import API GetTempPath: $!\n";
        }

APPELER UNE FONCTION IMPORTÉE

Pour effectivement faire un appel à une fonction importée, vous devez utiliser la méthode Call() sur l'objet Win32::API que vous avez créé. En continuant l'exemple du paragraphe précédent, l'API GetTempPath() peut être appelé en utilisant la méthode:

    $GetTempPath->Call(...

Bien sûr, les paramètres doivent être passés comme défini dans la phase d'import. En particulier, si le nombre de paramètres ne correspond pas (dans l'exemple, si GetTempPath() est appelé avec plus ou avec moins de deux paramètres), Perl va émettre un message d'erreur avec croak et terminer l'exécution du script avec un die.

Les deux paramètres demandés ici sont la longueur du tampon qui contiendra le chemin temporaire retourné, et un pointeur sur le tampon lui-même. Pour les paramètres numériques, vous pouvez utiliser ou une expression constante ou une variable, alors que pour les pointeurs vous devez utiliser un nom de variable (pas de référence Perl, seulement un nom de variable ordinaire). Notez aussi que la mémoire doit être allouée avant d'appeler la fonction, exactement comme en C. Par exemple, pour passer un tampon de 80 caractères à GetTempPath(), il faut l'initialiser auparavant avec:

    $lpBuffer = " " x 80;

Cela alloue une chaîne de 80 caractères. Si vous ne le faites pas, vous obtiendrez probablement une erreur Runtime exception, et généralement rien ne marchera. L'appel devrait inclure par conséquent :

    $lpBuffer = " " x 80;
    $GetTempPath->Call(80, $lpBuffer);

Et le résultat sera entreposé dans la variable $lpBuffer. Notez que vous n'avez pas besoin de passer une référence à la variable (c-à-d. vous n'avez pas besoin de \$lpBuffer), même si sa valeur est fixée par la fonction.

Un petit problème ici, c'est que Perl ne retaille pas la variable, donc $lpBuffer contiendra encore en retour 80 caractères; les caractères en excès seront des espaces, parce que nous avons dit " " x 80.

Dans le cas présent, nous sommes assez chanceux, car la valeur retournée par la fonction GetTempPath() est la longueur de la chaîne, donc, pour obtenir le chemin temporaire réel, nous pouvons écrire:

    $lpBuffer = " " x 80;
    $return = $GetTempPath->Call(80, $lpBuffer);
    $TempPath = substr($lpBuffer, 0, $return);

Si vous ne savez pas la longueur de la chaîne, vous pouvez la couper au premier caractère \0 (zéro ASCII) qui est le délimiteur de chaîne en C:

    $TempPath = ((split(/\0/, $lpBuffer))[0];
    # or
    $lpBuffer =~ s/\0.*$//;

Une autre note: pour passer un pointeur vers une structure en C, vous devez empaqueter les éléments exigés dans une variable avec pack(). Et, bien sûr, pour accéder aux valeurs entreposées dans une structure, vous devez utiliser unpack() comme il se doit. Un court exemple pour montrer comment cela marche : la structure POINT est définie en C par :

    typedef struct {
        LONG  x;
        LONG  y;
    } POINT;

Donc, pour appeler une fonction qui utilise une structure POINT vous avez besoin des lignes suivantes:

    $GetCursorPos = new Win32::API('user32', 'GetCursorPos', 'P', 'V');
    $lpPoint = pack('LL', 0, 0);        # stocke deux LONGs
    $GetCursorPos->Call($lpPoint);
    ($x, $y) = unpack('LL', $lpPoint);  # obtient les valeurs actuelles

Le reste est laissé en exercice au lecteur...

Retour en haut de la page


AUTEUR

Aldo Calpini ( dada@perl.it ).

Retour en haut de la page


VERSION FRANÇAISE

Cette traduction française correspond à la version anglaise distribuée avec perl 5.6.1. Pour en savoir plus concernant ces traductions, consultez http://www.enstimac.fr/Perl/ .

Retour en haut de la page


TRADUCTION EN FRANÇAIS

Jean-Louis Morel <jl_morel@bribes.org>

Retour en haut de la page

 Win32::API - Utilitaire d'importation d'API Win32