
Une bibliothèque de types permet de décrire l'interface de l'objet que vous voulez créer. La description en langage ODL est compilée avec MkTypLib pour générer un fichier TLB. Ce fichier est à inclure dans les références du projet VB.
La syntaxe d'un fichier ODL
Un fichier ODL a la syntaxe suivante :
[
uuid(<GUID>),
version(<version>)
]
library <Nom de la bibliothèque>
{
importlib("stdole2.tlb");
[
uuid(<GUID>),
odl
]
interface <Nom de l'interface> : stdole.IUnknown {
[<attributs>]<valeur de retour> <nom méthode>(<paramètres>);
}
}
Tout ce qui se trouve entre crochets constitue les attributs de l'entité qui suit.
Le premier <GUID> doit être un GUID unique généré avec Guidgen. La version est de la forme Major.Minor.
Le second <GUID> (celui de l'interface) doit correspondre au GUID de l'interface, si vous implémenté une interface existante ou un nouveau GUID si votre interface n'existe pas.
Idéalement le nom de l'interface commence par un I. Viennent ensuite, la liste des méthodes qui définit l'ordre dans la vtable.
<attributs> peut comprendre helpstring, vararg, propput, propputref, propget. La valeur de retour peut être soit HRESULT, soit n'importe quel type sauf un pointeur et il est préférable que ce ne soit pas une structure. Le nom de la méthode doit être unique dans l'interface (sauf pour les propput,propputref et propget).
<paramètre> à la syntaxe suivante : [<marshaling>]<type de donnée> <nom de paramètre>.
<marshaling> peut être soit [in], soit [in,out], soit [out,retval] et peut inclure defaultvalue(<valeur>).
Intégrer une description du contenu
Pour fournir une aide dans l'Explorateur d'objet de VB, vous pouvez rajouter entre n'importe quel [] l'attribut helpstring(« Description ») sauf dans les paramètres.
Bien que VB ne puisse pas définir d'alias, il sait très bien les utiliser sauf pour les Enums. Par exemple, on peut définir un alias de long (ou même unsigned long, VB ne le gère pas directement mais peut le gérer à travers un alias. Pour les opérations, ils sont traités comme des signés). Un alias se définit dans un fichier ODL par :
Typedef [public] <type de base> <nom de l'alias>
Les constantes se définissent obligatoirement dans un module. Les constantes se déclarent comme suit :
[
dllname(« n'importe quoi »)
]
module <nom du module>
{
const <type> <nom constante> = <valeur> ;
}
« n'importe quoi » c'est une chaîne quelconque puisque l'attribut dllname est obligatoire pour un module.
<type> et <valeur> ne peuvent être que des type simples : unsigned char, short, long, float (Single en VB), double, LPWSTR, LPSTR, BSTR et c'est tout...
Les chaînes sont définies comme en C : on peut inclure les séquences d'échappement telles que \n (nouvelle ligne), \t (tabulation), \r (retour à la ligne), \\ (\)...
|
Nom en ODL |
Nom en VB |
Taille en octets |
|
unsigned char |
Byte |
1 |
|
Short |
Integer |
2 |
|
Long |
Long |
4 |
|
Float |
Single |
4 |
|
Double |
Double |
8 |
|
currency |
Currency |
8 |
|
DATE |
Date |
8 |
|
BSTR |
String |
4 + 2 * nombre de caractères + 2 |
IDispatch* |
Object |
4 |
|
boolean |
Boolean |
2 |
|
VARIANT |
Variant |
16 au moins |
|
SAFEARRAY(<type>)* |
Tableau en paramètres |
4 |
|
Struct |
Type/End Type |
Somme de la taille des members |
|
SAFEARRAY(<type>) |
Tableau de taille variable |
24 |
|
<type> <nom>[<taille>] |
Tableau fixe |
Sizeof(<type>) * <taille> |
|
Enum |
Enum |
4 |
|
LPSTR |
Pas d'équivalent |
Nombre de caractères + 1 (ANSI) |
|
LPWSTR |
Pas d'équivalent |
2 * Nombre de caractères + 2 (UNICODE) |
Dans le langage ODL, une structure se définit comme suit :
typedef struct {
<type> <nom> ;
...
} <nom structure> ;
Dans une structure, on ne peut inclure que les types suivants :
Les types simples : long, float, DATE, ...
Les types plus complexes :
Les tableaux : SAFEARRAY(<type>)
Les Variant : VARIANT
Les chaines : BSTR mais pas LPSTR, LPWSTR
Les tableaux de taille fixe : <type> <nom>[<taille>]
Les objets : <interface>*
A noter que l'on ne peut pas définir un typedef struct qui porte le nom de GUID et surement d'autres.
A noter aussi que l'on peut régler l'alignement des données afin d'obtenir un alignement différents du 4 octets de VB. Sous mktyplib, cela se fait sur la ligne de commande /align <valeur>.
Un module permet aussi de déclarer des fonctions de DLLs.
La syntaxe est la suivante :
[
dllname(« <nom de la dll> »)
]
module <Nom du module>
{
[entry(<index ou nom>)] <type> <nom de fonction>(<paramètres>) ;
...
}
<index ou nom> est soit le nom de la fonction, soit sont index. <nom de la dll> est le nom de la dll (avec ou sans chemin).
A noter, une restriction dans les attributs des paramètres : on ne peut pas utiliser retval. Nous sommes donc limité à [in] et [in,out].
Tout ce qui va être définit ici doit être remplacé pour être utilisé par VB :
Les paramètres « tableau par valeur ». VB ne sait gérer que les tableaux passés par référence (SAFEARRAY(<type>)* en ODL).
Les paramètres « structures par valeur ». VB ne sait gérer les structures passés par référence (<structure>* en ODL). Il faut déclarer autant de paramètre de type Long que de champs dans la structure et ainsi remplir la pile avec les champs de la structure
Les pointeurs dans les structures. Il suffit de les remplacer par des long.
Les types non signés. VB ne gère comme type non signé que Byte qui est un unsigned char. VB ne sait gérer ni char (ne pas confondre avec char*) ni int. Il suffit de remplacer tous les unsigned long ou short par long et short et, char par unsigned char.
Les chaines de caractères de taille fixe dans une structure. Il faut le remplacer par un tableau de short <nom>[<taille>].
Les unions.
Les paramètres de type [out] <type>*. Il peut être remplacé par [in,out]<type>* si l'on passe toujours une variable ou par [in] long si l'on est amené à passer un NULL (0). On peut enfin le transformer en [out,retval] si c'est le dernier paramètre. Il ne peut y avoir qu'un seul [out,retval]. Il devient alors la valeur de retour de la fonction. Voir plus loin pour les valeurs de retour.
Pour trouver une interface déjà définie, vous pouvez utiliser l'utilitaire OleView.exe et récupérer son IID (GUID).
Vérifié que vous N'avez PAS les attributs oleautomation et dual dans les attributs des interfaces. Ajoutez l'attribut odl.
Ajouter en tête toutes les fonctions des interfaces dont l'interface hérite sauf IUnknown. L'interface doit dériver de IUnknown. Sa déclaration doit être : interface <nom de l'interface> : stdole.IUnknown.
Remplacer tout ce que VB ne supporte pas par les équivalents (voir section « Ce que VB ne sait pas gérer dans les typelibs »)
Changer tout ce qu'il faut pour que VB arrête de faire le difficile
Tous les types de bases : Byte, Integer, Long, Single, Double, Boolean, Currency, Date, Enum sont des types qui peuvent être passé directement par valeur. Pour ces types, si le paramètre est défini :
[in] <type> , c'est un ByVal As <type>
[in,out] <type>*, c'est un ByRef As <type>
<type> est à choisir parmi un nom d'enum, unsigned char, short, long, float, double, boolean, currency, DATE.
Il n'y pas d'autres combinaisons. A noter que pour ces types, on peut définir une valeur par défaut, qui rend le paramètre optionnel, avec l'attribut defaultvalue(<valeur>).
Les types String (BSTR, LPSTR, LPWSTR), Object (IDispatch*) et autres types objets (toutes les interfaces) sont, par définition, des types pointeurs. Pour ces types, on définit :
[in] BSTR, [in] LPSTR, [in] LPWSTR, c'est un ByVal As String
[in,out] BSTR*, [in,out] LPSTR*, [in,out] LPWSTR*, c'est un ByRef As String
[in] IDispatch*, c'est un ByVal As Object
[in,out] IDispatch**, c'est un ByRef As Object
A noter que ByRef As Object (ou As <interface>) n'est nécessaire que lorsque l'on compte modifier la référence d'objet, ou en renvoyer une. Pour un simple passage de paramètre à des fins d'utilisation, on optera toujours pour un ByVal
Les types tableaux sont eux aussi des pointeurs :
[in] SAFEARRAY(<type>)* et [in,out] SAFEARRAY(<type>)* sont équivalent puisque l'on ne peut pas passer de tableau par valeur.
<type> est n'importe quel type, simple, structure ou tableau.
Le dernier paramètre de la liste peut être attribué avec [out,retval] s'il est du type ByRef c'est à dire pointeur (long*, boolean*, ..., BSTR*, IDispatch**, <interface>**, ...). Il ne peut y en avoir qu'un seul.
Pour déclarer que la liste des paramètres n'est pas connue à partir d'un certain paramètre, on utilise ParamArray suivi d'un nom de paramètre de type tableau de Variant (sous VB). En langage ODL, il faut ajouter vararg dans les attributs de la méthode (et non dans les attributs du paramètre). Il s'en suit une définition de méthode comme suit :
[vararg] <type> <nom>(<liste de paramètres connus>,SAFEARRAY(VARIANT)* <nom arg>) ;
Pour déclarer une propriété en lecture-écriture, il faut deux fonctions : une pour lire et une pour écrire. Nous distinguerons deux cas :
Les propriétés pour types normaux
Les propriétés pour types objets
Les propriétés normales : Property Get / Property Let (propget / propput)
Une propriété de ce type aura deux entrées dans la vtable. En langage ODL, elle auront le prototype suivant :
Pour la lecture, Property Get :
[propget] HRESULT <nom propriété>([out,retval] <type>* <nom>) ;
Pour l'écriture, Property Let :
[propput] HRESULT <nom propriété>([in] <type> <nom>);
Les propriétés objets : Property Get / Property Set (propget /propputref)
Une propriété de ce type aura deux entrées dans la vtable. En langage ODL, elle auront le prototype suivant :
Pour la lecture, Property Get :
[propget] HRESULT <nom propriété>([out,retval] <interface>** <nom>) ;
Pour l'écriture, Property Let :
[propputref] HRESULT <nom propriété>([in] <interface>* <nom>);
On peut aussi avoir des propriétés en lecture-seule ou écriture-seule en supprimant une des deux fonctions.