Qu’est-ce qu’une interface COM ?
Si vous connaissez C# ou Java, les interfaces doivent être un concept familier. Une interface définit un ensemble de méthodes qu’un objet peut prendre en charge, sans rien dicter sur l’implémentation. L’interface marque une limite claire entre le code qui appelle une méthode et le code qui implémente la méthode. En termes informatiques, l’appelant est dissocié de l’implémentation.
En C++, l’équivalent le plus proche d’une interface est une classe virtuelle pure, c’est-à-dire une classe qui contient uniquement des méthodes virtuelles pures et aucun autre membre. Voici un exemple hypothétique d’interface :
// The following is not actual COM.
// Pseudo-C++:
interface IDrawable
{
void Draw();
};
L’idée de cet exemple est qu’un ensemble d’objets dans une bibliothèque graphique est dessinable. L’interface IDrawable
définit les opérations que tout objet dessinable doit prendre en charge. (Par convention, les noms d’interface commencent par « I ».) Dans cet exemple, l’interface IDrawable
définit une seule opération : Draw
.
Toutes les interfaces étant abstraites, un programme n’a pas pu créer une instance d’un IDrawable
objet en tant que tel. Par exemple, le code suivant ne sera pas compilé.
IDrawable draw;
draw.Draw();
Au lieu de cela, la bibliothèque graphique fournit des objets qui implémentent l’interface IDrawable
. Par exemple, la bibliothèque peut fournir un objet shape pour dessiner des formes et un objet bitmap pour dessiner des images. En C++, cette opération s’effectue en héritant d’une classe de base abstraite commune :
class Shape : public IDrawable
{
public:
virtual void Draw(); // Override Draw and provide implementation.
};
class Bitmap : public IDrawable
{
public:
virtual void Draw(); // Override Draw and provide implementation.
};
Les Shape
classes et Bitmap
définissent deux types distincts d’objet dessinable. Chaque classe hérite de IDrawable
et fournit sa propre implémentation de la Draw
méthode . Naturellement, les deux implémentations peuvent différer considérablement. Par exemple, la Shape::Draw
méthode peut rastériser un ensemble de lignes, tandis qu’elle Bitmap::Draw
blit un tableau de pixels.
Un programme utilisant cette bibliothèque graphique manipulerait Shape
les objets et Bitmap
par le biais IDrawable
de pointeurs, plutôt que d’utiliser Shape
des pointeurs ou Bitmap
directement.
IDrawable *pDrawable = CreateTriangleShape();
if (pDrawable)
{
pDrawable->Draw();
}
Voici un exemple qui effectue une boucle sur un tableau de IDrawable
pointeurs. Le tableau peut contenir un assortiment hétérogène de formes, bitmaps et autres objets graphiques, tant que chaque objet du tableau hérite IDrawable
de .
void DrawSomeShapes(IDrawable **drawableArray, size_t count)
{
for (size_t i = 0; i < count; i++)
{
drawableArray[i]->Draw();
}
}
Un point clé à propos de COM est que le code appelant ne voit jamais le type de la classe dérivée. En d’autres termes, vous ne déclareriez jamais une variable de type Shape
ou Bitmap
dans votre code. Toutes les opérations sur les formes et les bitmaps sont effectuées à l’aide IDrawable
de pointeurs. De cette façon, COM maintient une séparation stricte entre l’interface et l’implémentation. Les détails d’implémentation des Shape
classes et peuvent Bitmap
changer, par exemple pour corriger des bogues ou ajouter de nouvelles fonctionnalités, sans aucune modification du code appelant.
Dans une implémentation C++, les interfaces sont déclarées à l’aide d’une classe ou d’une structure.
Notes
Les exemples de code de cette rubrique sont destinés à transmettre des concepts généraux, et non des pratiques réelles. La définition de nouvelles interfaces COM dépasse le cadre de cette série, mais vous ne définiriez pas d’interface directement dans un fichier d’en-tête. Au lieu de cela, une interface COM est définie à l’aide d’un langage appelé IDL (Interface Definition Language). Le fichier IDL est traité par un compilateur IDL, qui génère un fichier d’en-tête C++.
class IDrawable
{
public:
virtual void Draw() = 0;
};
Lorsque vous utilisez COM, il est important de se rappeler que les interfaces ne sont pas des objets. Il s’agit de collections de méthodes que les objets doivent implémenter. Plusieurs objets peuvent implémenter la même interface, comme le montrent les Shape
exemples et Bitmap
. En outre, un objet peut implémenter plusieurs interfaces. Par exemple, la bibliothèque graphique peut définir une interface nommée ISerializable
qui prend en charge l’enregistrement et le chargement d’objets graphiques. Considérez maintenant les déclarations de classe suivantes :
// An interface for serialization.
class ISerializable
{
public:
virtual void Load(PCWSTR filename) = 0; // Load from file.
virtual void Save(PCWSTR filename) = 0; // Save to file.
};
// Declarations of drawable object types.
class Shape : public IDrawable
{
...
};
class Bitmap : public IDrawable, public ISerializable
{
...
};
Dans cet exemple, la Bitmap
classe implémente ISerializable
. Le programme peut utiliser cette méthode pour enregistrer ou charger la bitmap. Toutefois, la classe n’implémente Shape
ISerializable
pas , elle n’expose donc pas cette fonctionnalité. Le diagramme suivant montre les relations d’héritage dans cet exemple.
Cette section a examiné la base conceptuelle des interfaces, mais jusqu’à présent, nous n’avons pas vu de code COM réel. Nous allons commencer par la première chose que toute application COM doit faire : initialiser la bibliothèque COM.