Les appels dAPIs Quand on commence àfaire de la programmation proche du système, donc avancée,on a souvent besoin dAPI : fonctions proposées par lesystème dexploitation. Ces fonctions sont stockéesdans de bibliothèque de liens : les DLLs.
Il en existe troiscatégories principales :
User32.dll : toutes le fonctions en rapport avec les fenêtres
Gdi32.dll : tout ce qui est en rapport avec les images
Kernel32.dll : tout ce qui a rapport avec les mécanismes internes : informations système, système de fichier, mutes, sémaphores.
I.Déclaration
Les déclarationsdAPIs se font comme suit :
{Private|Public}Declare [{Ansi|Unicode}] {Function|Sub} nom_fonction [Aliasvrai_nom_fonction] Lib nom_dll.dll (Param1 As Type1,Param2 As Type2,
ParamN As TypeN) [As TypeRetour]
{Private|Public} :portée de la déclaration (privée oupublique)
{Ansi|Unicode} :indique si les chaines sont traitées comme ANSI (1 octet) ouUNICODE (2 octets)
{Function|Sub} :fonction ou procédure, une API Windows est rarement uneprocédure (sauf CopyMemory)
[Aliasvrai_nom_fonction] : permet de renommer la fonction sisont vrai nom est, par exemple, trop long ou trop compliqué (_fct@8) ou pour importer des fonctions exportées par Index (dans cecas lalias sera #index).
Penser à regarder si uneclasse faisant la même chose que lAPI nexisterait pas
1.Les paramètres
Il existe deux typesde paramètres : les paramètres par valeur et lesparamètres par référence (pointeur).
Un paramètrepar référence indique que lon passe ladresse dela variable à la fonction pour pouvoir modifier la variable àlintérieur. Le nom dun tel paramètre est précédéde ByRef.
Un paramètrepar valeur indique que lon passe le contenu de la variable àla fonction. On ne peut donc pas modifier la variable àlintérieur de la fonction. Le nom dun tel paramètreest précédé de ByVal ou rien (ByVal par défaut).
2.Le type de retour
Si cest une Sub,alors il ny a pas de type de retour.
Sinon, cest presquetoujours un Integer. Tout type de retour de plus de 4 octetsdevrait être passé par référence enparamètre.
3.Et si ma fonction nest pas dansune liste
Alors la cest unpeut plus compliqué. Mais si vous êtes là, cestque vous avez le prototype C de votre fonction.
Pour que VB puisse utiliser unefonction C comme une API, il faut :
II. Traduction des types
Lattribut MarshalAs
Cette attribut seplace avant le paramètre : <MarshalAs(
)>{ByRef|ByVal} nom As Type.
Par exemple :
PublicSub M1 (<MarshalAs(UnmanagedType.LPWStr)> msg As String)
On peut aussilappliquer à un member de structure :
Par exemple :
<MarshalAs(UnmanagedType.LPWStr)> Public msg As String
Pour utilisercorrectement lattribut MarshalAs, il faut ajouter la clause, endébut de classe :
ImportSystem.Runtime.InteropServices
Les types standards
Voici un tableau detypes courants et de leurs traductions VB6 :
| Type en C | Type en VB |
| Char | ByVal Byte (mais considéré non signé) |
| Short | ByVal Short |
| Int | ByVal Integer |
| Long | ByVal Integer |
| unsigned char | ByVal Byte |
| unsigned short | ByVal Short (mais considéré signé) |
| unsigned int | ByVal Integer (mais considéré signé) |
| unsigned long | ByVal Integer (mais considéré signé) |
| BOOL | ByVal <MarshalAs(UnmanagedType.Bool)> param As Boolean |
| bool | (<MarshalAs(UnmanagedType.I1)> param As Boolean |
| Float | ByVal Single |
| Double | ByVal Double |
| ULONGLONG, LONGLONG | ByVal Long |
| char* | ByVal <MarshalAs(UnmanagedType.LPStr) param As String |
| Short* | ByRef Short |
| int* | ByRef Integer |
| long* | ByRef Integer |
| unsigned char* | ByRef Byte |
| unsigned short* | ByRef Integer (mais considéré signé) |
| unsigned int* | ByRef Long (mais considéré signé) |
| unsigned long* | ByRef Long (mais considéré signé) |
| BOOL* | ByRef <MarshalAs(UnmanagedType.Bool)> param As Boolean |
| float* | ByRef Single |
| double* | ByRef Double |
| ULONGLONG*, LONGLONG* | ByRef Long |
| BSTR | ByVal <MarshalAs(UnmanagedType.BStr)>param As String |
| BSTR* | ByRef <MarshalAs(UnmanagedType.BStr)>param As String |
| TCHAR* | ByVal <MarshalAs(UnmanagedType.LPTStr)>param As String |
| IUnknown* | ByVal <MarshalAs(UnmanagedType.IUnknown)> param As Object |
| IUnknown** | ByRef <MarshalAs(UnmanagedType.IUnknown)> param As Object |
| Interface* | ByVal {Object|Interface} |
| Interface** | ByRef {Object|Interface} |
Structure | ByVal Structure |
| Structure* | ByRef Structure (avec Attributs éventuellement pour les tableaux) |
Tableau C | Voir plus bas |
SAFEARRAY(type)* | ByRef <MarshalAs(UnmanagedType.SafeArray,SafeArraySubType:=Type) param As Type |
Currency de COM | <MarshalAs(UnmanagedType.Currency)> param As Decimal |
Type param[taille] | <MarshalAs(UnmanagedType.LPArray, SizeConst=taille)> param() As Type |
char param[taille] | <MarshalAs(UnmanagedType.LPArray, SizeConst=taille)> param() As String |
Hxxx | Integer (représente un handle) |
Pour les autres types, il estnécessaire de rajouter des attributs aux paramètres
Le type Any de VB6
<MarshalAs(UnmanagedType.AsAny)>param As Object
Mais il estpréférable de déclarer plusieurs fonctionsDeclare avec les différents types possibles pour le paramètre.
Les types pointeurs
Il existe un typepointeur en VB.Net : System.IntPtr.
Note : Lesfonctions VarPtr, ObjPtr et StrPtr nexistent plus et jedéconseille d'essayer de les réécrire car lesversions possibles ne marchent pas aussi bien.
Voir Travailleravec les pointeurs
Les types Structures
Attribut StructLayout
On peut changer letype de chaînes de caractères, lalignement et lataille de la structure. :
<StructLayout(LayoutKind.Sequential,CharSet :=charset,Pack :=alignement,Size:=taille)>
Private Structure nom
.
End Structure
charset (facultatif): CharSet.Unicode ou CharSet.Ansi
alignement (facultatif): aligner tous les 1, 2, 4, 8, 16, 32, 64 ou 128 octets
taille (trèsfacultatif): taille absolue de la structure en octet
Note sur le contenudes structures :
Letype de charset (char/ANSI ou wchar/UNICODE) est défini danslattribut StructLayout de la structure.
Les chaines char*, WCHAR*, TCHAR* sont remplacées par <MarshalAs(UnmanagedType.LPStr)>membre As String, <MarshalAs(UnmanagedType.LPWStr)>membre As String et <MarshalAs(UnmanagedType.LPTStr)>membre As String.
Les types pointeurs sont System.IntPtr. Voir classe Marshal plus bas
Les tableaux de taille fixe type nom[taille] se traduisent par <MarshalAs(UnmanagedType.ByValArray, SizeConst:=taille)>param() As Type
Pour les autres types :
| Type C | Type VB |
| char/BYTE | Byte (interprété non signé) |
| short/USHORT/WORD | Short |
| int, long/DWORD/ULONG | Integer |
| ULONGLONG | |
| Float | Single |
| double | Double |
| BOOL/BOOLEAN | Integer |
| bool | Byte |
Passer un tableau type C : de typesimples ou de structures
Pour passer un tableaude type C, on a UNE SEULE solution :
<MarshalAs(UnmanagedType.LPArray)>ByRef Type_des_cases_du_tableau : dans ce cas, on passe la première case du tableau (alloué), en général, il y a un paramètre pour donner la taille du tableau que lon passe
Parexemple :
Dimt(10) As Long
Res= Fct(t(0),10)
Passer un tampon chaîne type C
Pour passer un bufferchaîne C : char/wchar* buffer (int taille, suit engénéral), on utilise un ByVal String. Il fautimpérativement remplir la chaîne avec des caractèresavant de la passer à la fonction.
Parexemple :
Dim sas New String(20)
res =Fct(s,20)
On peut aussi utiliserla classe StringBuilder de la même façon.
Les pointeurs de fonctions de rappel(callback)
On peut utiliser letype « Delegate ». Pour cela :
On écrit le prototype Delegate de la fonction callback : {Private|Public|Protected} Delegate Function nomDelegate(params) As retour
Ecrire la déclaration de la fonction qui prend en paramètre le pointeur de fonction avec comme type pour ce paramètre pointeur de fonction nomDelegate.
Créer une fonctions ayant le même prototype que le delegate sans Delegate :
{Private|Public|Protected}Function nom(params) As retour
...
EndFunction
Note: nomDelegate n'a rien d'obligatoire : vous pouvez donner unautre nom au type delegate. Vous pouvez aussi avoir plusieursfonctions du même prototype que le delegate.
Travail avec les pointeurs :lobjet Marshal
Le type IntPtr
Un IntPtr se construitavec un Integer ou avec un Long. On peut le convertir en Integer etLong. Pour initialiser le pointeur à NULL, on écrit :ptr = IntPtr.Zero.
Lecture et écriture direct dans lesdonnées dun pointeur
Type peutprendre différentes valeurs : Byte, Int16, Int32, Int64et IntPtr.
Pour lire directementen mémoire, on utilise les fonctions Marshal.ReadType.Il en existe deux (en réalité trois) versions :
Marshal.ReadType(ByValpointeur As IntPtr) As Type
Marshal.ReadType(ByValpointeur As IntPtr, ByVal offsetAs Integer) As Type
Lapremière version renvoie la donnée pointée parpointeur. On lutilise pour les données seules.
Laseconde version renvoie la donnée pointée parpointeur[offset]. On lutilise pour lesdonnées type tableau C.
Exemple :
Dim ptr As IntPtr =adresse trouvée quelque part
Dimb As Byte = Marshal.ReadByte(ptr)
Pour écriredirectement en mémoire, on utilise les fonctionsMarshal.WriteType. Il en existe deux (en réalitétrois) versions :
Marshal.WriteType(ByValpointeur As IntPtr, ByVal valeur AsType) As Type
Marshal.WriteType(ByValpointeur As IntPtr, ByVal offsetAs Integer, ByVal valeur As Type)As Type
Lapremière version écrit valeur dans la zonepointée par pointeur. On lutilise pour les donnéesseules.
Laseconde version écrit valeur dans la zone pointéepar pointeur[offset]. On lutilise pour lesdonnées type tableau C.
Exemple :
Dim ptr As IntPtr =adresse trouvée quelque part
Dimb As Byte = Marshal.WriteByte(ptr,19)
Copier les donnéesd'un pointeur vers un objet VB
Pour les types simples et les tableaux detypes simples
La fonction Copypermet de copier des tableaux
OverloadsPublic Shared Sub Copy(ByVal zone_source As IntPtr,ByVal tableau_destination() As Type,ByVal index_début_copie As Integer, ByValnb_case_copie As Integer)
OverloadsPublic Shared Sub Copy(ByVal tableau_source() As Type,ByVal index_début_copie As Integer, ByValdestination As IntPtr, ByVal nb_case_copieAs Integer)
Pourla fonction Copy, Typepeut prendre les valeurs suivantes : Byte, Char, Double,Short, Integer, Long, Single.
Les chaînes de caractères
Les fonctionsPtrToStringAnsi, PtrToStringBSTR, PtrToStringUni permettent de copierune chaîne de caractères respectivement : ANSI (1octet), BSTR (2 octets), UNICODE (2 octets).
Il existe deuxversions de ces fonctions :
prenant en paramètre un pointeur de type IntPtr et renvoyant un objet String contenant la chaîne entière
prenant en paramètre un pointeur de type IntPtr et le nombre de caractère à copier (Integer) et renvoyant un objet String contenant la chaîne copiée
Les structures
La fonctionPtrToStructure permet de copier une structure unique. Il en existedeux versions :
Cetteversion permet de copier une structure pointée par ptrdans un objet structure existant
Exemple :
Dim ptrAs IntPtr = une adresse quelconque
Dims As New MaStructure
Marshal.PtrToStructure(ptr,s)
FunctionPtrToStructure(ByVal ptr As IntPtr, ByValstructureType As Type) As Object
Cetteversion alloue un nouvel objet structure de type structureTypeà partir des données ptr.
Exemple :
Dim ptrAs IntPtr = une adresse quelconque
Dims As MaStructure = CType(Marshal.PtrToStructure(ptr,GetType(MaStructure)), MaStructure)
Les tableaux de structures
On peut faire uneboucle en incrémentant le pointeur de la taille d'unestructure : ptr = New IntPtr(ptr.ToInt32 + taille).
Transformer un objet VB enpointeur
VarPtr,StrPtr et ObjPtr : GCHandle
Note : ces fonctions sont àutiliser en dernier recourt : penser aux fonctions de l'objet Marshalavant de faire un CopyMemory VarPtr. L'objet Marshal donne du codeplus sûr.
VarPtr : déconseillée
La classeGCHandle permet de réécrire ces fonctions trèsutiles de VB6.
PublicFunction VarPtr(ByVal obj As Object) As IntPtr
Dim g AsGCHandle = GCHandle.Alloc(obj,GCHandleType.Pinned)
Dim ptrAs IntPtr = g.AddrOfPinnedObject()
g.Free()
Returnptr
EndFunction
Unefois que lon a le pointeur vers la variable destination etle pointeur source (ou inversement), on peut faire une copiede mémoire avec CopyMemory comme avec VB6.
ATTENTION : tous les types (etsurtout les structures avec ByvalArray) ne sont pas Pinnable. Il sepeut donc que cette fonction échoue.
Pouravoir les mêmes fonctionnalités :
Dim ptrAs IntPtr = Marshal.AllocHGlobal(taille)
'onappelle la fonction avec un paramètre Byval IntPtr
Dimattr As type =CType(Marshal.PtrToStructure(ptr, GetType(type)),type)
Marshal.FreeHGlobal(ptr)
On peutaussi convertir un GCHandle (créé avec GCHandle.Alloc)en IntPtr et inversement avec GCHandle.op_Explicit.
StrPtr
Note :il est préférable d'utiliser les attributs deparamètres, Ansi ou Unicode avec le Declare et Byval String.
Il existe les fonctions StringToHGlobalAnsi etStringToHGlobalUni. Elles permettent respectivement de renvoyerun pointeur vers une chaîne ANSI et UNICODE en mémoire.Elle prennent en paramètre un objet de type String :
Function StringToHGlobalType(ByVal chaine As String) As IntPtr
Il est nécessaire de libérer la zone pointée(précédente) par le pointeur renvoyé afin de nepas faire de fuite mémoire :
Sub FreeHGlobal(ByVal pointeur_vers_chaine As IntPtr)
ObjPtr
En avez vous vraiment besoin....
Les« autres » méthodes de lobjet Marshal
| Marshaling avancé | GetManagedThunkForUnmanagedMethodPtr, GetUnmanagedThunkForManagedMethodPtr, NumParamBytes |
| Fonction de bibliothèque COM | BindToMoniker, GetActiveObject |
| Utilitaires COM | ChangeWrapperHandleStrength, CreateWrapperOfType, GetComObjectData, GetComSlotForMethodInfo, GetEndComSlot, GetMethodInfoForComSlot, GetStartComSlot, ReleaseComObject, SetComObjectData |
| Transformation des données | Managées en non managées : Copy, GetComInterfaceForObject, GetIDispatchForObject, GetIUnknownForObject, StringToBSTR, StringToCoTaskMemAnsi, StringToCoTaskMemAuto, StringToCoTaskMemUni, StringToHGlobalAnsi, StringToHGlobalAuto, StringToHGlobalUni, StructureToPtr, UnsafeAddrOfPinnedArrayElement Non managées en managées : Copy, GetObjectForIUnknown, GetObjectForNativeVariant, GetObjectsForNativeVariants, GetTypedObjectForIUnknown, GetTypeForITypeInfo, PtrToStringAnsi, PtrToStringAuto, PtrToStringBSTR, PtrToStringUni Propriétés : SystemDefaultCharSize, SystemMaxDBCSCharSize |
| Lecture et écriture directes | ReadByte, ReadInt16, ReadInt32, ReadInt64, ReadIntPtr, WriteByte, WriteInt16, WriteInt32, WriteInt64, WriteIntPtr |
| Gestion des erreurs | COM : GetHRForException, ThrowExceptionForHR Win32 : GetLastWin32Error, GetExceptionCode, GetExceptionPointers Les deux : GetHRForLastWin32Error |
| Utilitaires d'hébergement | GetThreadFromFiberCookie |
| Iunknown | AddRef, QueryInterface, Release |
| Gestion de la mémoire | COM : AllocCoTaskMem, ReAllocCoTaskMem, FreeCoTaskMem, FreeBSTR Win32 : AllocHGlobal, ReAllocHGlobal, FreeHGlobal Les deux : DestroyStructure |
| Utilitaires d'appel de plate-forme | Prelink, PrelinkAll, GetHINSTANCE |
| Examen de la structure | OffsetOf, SizeOf |
| Informations de type | GenerateGuidForType, GenerateProgIdForType, GetTypeInfoName, GetTypeLibGuid, GetTypeLibGuidForAssembly, GetTypeLibLcid, GetTypeLibName, IsComObject, IsTypeVisibleFromCom |