Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
C++/WinRT peut vous aider à créer des composants COM (ou coclasses) classiques, tout comme il vous aide à créer des classes Windows Runtime. Cette rubrique vous montre comment procéder.
Comportement de C++/WinRT, par défaut, par rapport aux interfaces COM
Le modèle winrt ::implements de C++/WinRT est la base à partir de laquelle vos classes d’exécution et fabriques d’activation dérivent directement ou indirectement.
Par défaut, winrt::implements ignore en silence les interfaces COM classiques. Les appels QueryInterface (QI) pour les interfaces COM classiques échouent par conséquent avec E_NOINTERFACE. Par défaut, winrt ::implements prend uniquement en charge les interfaces C++/WinRT.
- winrt ::IUnknown est une interface C++/WinRT, donc winrt ::implements prend en charge les interfaces basées sur winrt ::IUnknown.
- winrt ::implements ne prend pas en charge ::IUnknown lui-même, par défaut.
Dans un moment, vous verrez comment surmonter les cas qui ne sont pas pris en charge par défaut. Mais voici d’abord un exemple de code pour illustrer ce qui se passe par défaut.
// Sample.idl
namespace MyProject
{
runtimeclass Sample
{
Sample();
void DoWork();
}
}
// Sample.h
#include "pch.h"
#include <shobjidl.h> // Needed only for this file.
namespace winrt::MyProject::implementation
{
struct Sample : implements<Sample, IInitializeWithWindow>
{
IFACEMETHOD(Initialize)(HWND hwnd);
void DoWork();
}
}
Et voici le code client pour consommer la classe Sample .
// Client.cpp
Sample sample; // Construct a Sample object via its projection.
// This next line doesn't compile yet.
sample.as<IInitializeWithWindow>()->Initialize(hwnd);
Activation de la prise en charge COM classique
La bonne nouvelle, c’est qu’il suffit, pour que winrt::implements prenne en charge les interfaces COM classiques, d’inclure le fichier d’en-tête unknwn.h avant d’inclure un quelconque en-tête C++/WinRT.
Vous pouvez le faire explicitement, ou indirectement en incluant un autre fichier d’en-tête tel que ole2.h. Une méthode recommandée consiste à inclure le wil\cppwinrt.h fichier d’en-tête, qui fait partie des bibliothèques d’implémentation Windows (WIL). Le wil\cppwinrt.h fichier d’en-tête s’assure non seulement qu’il unknwn.h est inclus avant winrt/base.h, il configure également des éléments afin que C++/WinRT et WIL comprennent les exceptions et les codes d’erreur des uns et des autres.
Vous pouvez ensuite utiliser as<> pour les interfaces COM classiques, et le code de l’exemple ci-dessus se compilera.
Note
Dans l’exemple ci-dessus, même après avoir activé la prise en charge COM classique dans le client (le code consommant la classe), si vous n’avez pas également activé la prise en charge COM classique dans le serveur (le code implémentant la classe), l’appel à tel que<> dans le client se bloque, car le QI pour IInitializeWithWindow échoue.
Classe locale (non projetée)
Une classe locale est une classe qui est implémentée et consommée dans la même unité de compilation (application ou autre binaire) ; et donc il n’y a pas de projection pour elle.
Voici un exemple de classe locale qui implémente uniquement des interfaces COM classiques.
struct LocalObject :
winrt::implements<LocalObject, IInitializeWithWindow>
{
...
};
Si vous implémentez cet exemple, mais que vous n’activez pas la prise en charge COM classique, le code suivant échoue.
winrt::make<LocalObject>(); // error: ‘first_interface’: is not a member of ‘winrt::impl::interface_list<>’
Là encore, IInitializeWithWindow n’est pas reconnu comme une interface COM, donc C++/WinRT l’ignore. Dans le cas de l’exemple LocalObject , le résultat de l’ignorance des interfaces COM signifie que LocalObject n’a pas d’interfaces du tout. Toutefois, chaque classe COM doit implémenter au moins une interface.
Exemple simple d’un composant COM
Voici un exemple simple d’un composant COM écrit à l’aide de C++/WinRT. Il s’agit du code complet d’une mini-application ; vous pouvez donc l’essayer en le collant dans pch.h et main.cpp d’un nouveau projet Application console Windows (C++/WinRT).
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
// main.cpp : Defines the entry point for the console application.
#include "pch.h"
struct __declspec(uuid("ddc36e02-18ac-47c4-ae17-d420eece2281")) IMyComInterface : ::IUnknown
{
virtual HRESULT __stdcall Call() = 0;
};
using namespace winrt;
using namespace Windows::Foundation;
int main()
{
winrt::init_apartment();
struct MyCoclass : winrt::implements<MyCoclass, IPersist, IStringable, IMyComInterface>
{
HRESULT __stdcall Call() noexcept override
{
return S_OK;
}
HRESULT __stdcall GetClassID(CLSID* id) noexcept override
{
*id = IID_IPersist; // Doesn't matter what we return, for this example.
return S_OK;
}
winrt::hstring ToString()
{
return L"MyCoclass as a string";
}
};
auto mycoclass_instance{ winrt::make<MyCoclass>() };
CLSID id{};
winrt::check_hresult(mycoclass_instance->GetClassID(&id));
winrt::check_hresult(mycoclass_instance.as<IMyComInterface>()->Call());
}
Consultez également Consommer des composants COM avec C++/WinRT.
Un exemple plus réaliste et intéressant
Le reste de cette rubrique décrit la création d’un projet d’application console minimal qui utilise C++/WinRT pour implémenter une coclasse de base (composant COM ou classe COM) et une fabrique de classes. L’exemple d’application montre comment afficher une notification toast comportant un bouton de rappel, et la coclasse (qui implémente l’interface COM INotificationActivationCallback) permet à l’application d’être lancée et de recevoir un rappel lorsque l’utilisateur clique sur ce bouton dans la notification toast.
Vous trouverez plus d’informations générales sur la fonctionnalité de notifications toast dans Envoyer une notification toast locale. Toutefois, aucune des exemples de code de cette section de la documentation n’utilise C++/WinRT. Nous vous recommandons donc de préférer le code présenté dans cette rubrique.
Créer un projet d’application console Windows (ToastAndCallback)
Commencez par créer un projet dans Microsoft Visual Studio. Créez un projet d’application console Windows (C++/WinRT) et nommez-le ToastAndCallback.
Ouvrez pch.het ajoutez #include <unknwn.h> avant les éléments inclus pour tous les en-têtes C++/WinRT. Voici le résultat ; vous pouvez remplacer le contenu de votre pch.h liste par cette liste.
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
Ouvrez main.cppet supprimez les directives using générées par le modèle de projet. À leur place, insérez le code suivant (qui nous donne les libs, les en-têtes et les noms de type dont nous avons besoin). Voici le résultat ; vous pouvez remplacer le contenu de votre main.cpp liste par cette liste (nous avons également supprimé le code de main la liste ci-dessous, car nous remplacerons cette fonction ultérieurement).
// main.cpp : Defines the entry point for the console application.
#include "pch.h"
#pragma comment(lib, "advapi32")
#pragma comment(lib, "ole32")
#pragma comment(lib, "shell32")
#include <iomanip>
#include <iostream>
#include <notificationactivationcallback.h>
#include <propkey.h>
#include <propvarutil.h>
#include <shlobj.h>
#include <winrt/Windows.UI.Notifications.h>
#include <winrt/Windows.Data.Xml.Dom.h>
using namespace winrt;
using namespace Windows::Data::Xml::Dom;
using namespace Windows::UI::Notifications;
int main() { }
Le projet ne sera pas encore généré ; Une fois que nous avons terminé d’ajouter du code, vous serez invité à générer et à exécuter.
Implémenter la coclasse et la fabrique de classes
Dans C++/WinRT, vous implémentez des coclasses et des fabriques de classes en dérivant de la structure de base winrt::implements. Immédiatement après les trois directives d’utilisation indiquées ci-dessus (et avant main), collez ce code pour implémenter votre composant d’activateur COM de notification toast.
static constexpr GUID callback_guid // BAF2FA85-E121-4CC9-A942-CE335B6F917F
{
0xBAF2FA85, 0xE121, 0x4CC9, {0xA9, 0x42, 0xCE, 0x33, 0x5B, 0x6F, 0x91, 0x7F}
};
std::wstring const this_app_name{ L"ToastAndCallback" };
struct callback : winrt::implements<callback, INotificationActivationCallback>
{
HRESULT __stdcall Activate(
LPCWSTR app,
LPCWSTR args,
[[maybe_unused]] NOTIFICATION_USER_INPUT_DATA const* data,
[[maybe_unused]] ULONG count) noexcept final
{
try
{
std::wcout << this_app_name << L" has been called back from a notification." << std::endl;
std::wcout << L"Value of the 'app' parameter is '" << app << L"'." << std::endl;
std::wcout << L"Value of the 'args' parameter is '" << args << L"'." << std::endl;
return S_OK;
}
catch (...)
{
return winrt::to_hresult();
}
}
};
struct callback_factory : implements<callback_factory, IClassFactory>
{
HRESULT __stdcall CreateInstance(
IUnknown* outer,
GUID const& iid,
void** result) noexcept final
{
*result = nullptr;
if (outer)
{
return CLASS_E_NOAGGREGATION;
}
return make<callback>()->QueryInterface(iid, result);
}
HRESULT __stdcall LockServer(BOOL) noexcept final
{
return S_OK;
}
};
L’implémentation de la coclasse ci-dessus suit le même modèle que celui illustré dans les API Author avec C++/WinRT. Vous pouvez donc utiliser la même technique pour implémenter des interfaces COM ainsi que des interfaces Windows Runtime. Les composants COM et les classes Windows Runtime exposent leurs fonctionnalités via des interfaces. Chaque interface COM dérive finalement de l’interface iUnknown . Le Windows Runtime est basé sur COM , une distinction étant que Windows Runtime interfaces dérivent finalement de l’interface IInspectable (et IInspectable dérive de IUnknown).
Dans la coclasse dans le code ci-dessus, nous implémentons la méthode INotificationActivationCallback ::Activate , qui est la fonction appelée lorsque l’utilisateur clique sur le bouton de rappel sur une notification toast. Mais avant que cette fonction puisse être appelée, une instance de la coclasse doit être créée, et c’est le travail de la fonction IClassFactory ::CreateInstance .
La coclasse que nous venons d’implémenter est appelée activateur COM pour les notifications, et elle a son ID de classe (CLSID) sous la forme de l’identificateur callback_guid (de type GUID) que vous voyez ci-dessus. Nous allons utiliser cet identificateur ultérieurement, sous la forme d'un raccourci de menu Démarrer et d'une entrée de Registre Windows. L’activateur COM CLSID et le chemin d’accès à son serveur COM associé (c’est-à-dire le chemin d’accès à l’exécutable que nous créons ici) constituent le mécanisme qui permet à une notification toast de savoir de quelle classe créer une instance lorsque l’on clique sur son bouton de callback (que l’on clique ou non sur la notification dans le Centre de notifications).
Meilleures pratiques pour l’implémentation de méthodes COM
Les techniques de gestion des erreurs et de gestion des ressources peuvent aller de pair. Il est plus pratique et pratique d’utiliser des exceptions que des codes d’erreur. Et si vous employez l’idiome RAII (« l’acquisition de ressource est initialisation »), vous pouvez éviter d’avoir à vérifier explicitement les codes d’erreur et à libérer explicitement les ressources. Ces vérifications explicites rendent votre code plus complexe que nécessaire, et il donne aux bogues beaucoup d’endroits à masquer. Utilisez plutôt RAII, et levez/interceptez des exceptions. De cette façon, vos allocations de ressources sont sécurisées par des exceptions et votre code est simple.
Toutefois, vous ne devez pas autoriser les exceptions à échapper à vos implémentations de méthode COM. Vous pouvez vous en assurer en utilisant le spécificateur noexcept sur vos méthodes COM. Il est possible que les exceptions soient levées n’importe où dans le graphique d’appel de votre méthode, tant que vous les gérez avant de quitter votre méthode. Si vous utilisez noexcept, mais que vous autorisez une exception pour échapper à votre méthode, votre application se termine.
Ajouter des types d’assistance et des fonctions
Dans cette étape, nous allons ajouter des types d’assistance et des fonctions dont le reste du code utilise. Par conséquent, juste avant main, ajoutez ce qui suit.
struct prop_variant : PROPVARIANT
{
prop_variant() noexcept : PROPVARIANT{}
{
}
~prop_variant() noexcept
{
clear();
}
void clear() noexcept
{
WINRT_VERIFY_(S_OK, ::PropVariantClear(this));
}
};
struct registry_traits
{
using type = HKEY;
static void close(type value) noexcept
{
WINRT_VERIFY_(ERROR_SUCCESS, ::RegCloseKey(value));
}
static constexpr type invalid() noexcept
{
return nullptr;
}
};
using registry_key = winrt::handle_type<registry_traits>;
std::wstring get_module_path()
{
std::wstring path(100, L'?');
uint32_t path_size{};
DWORD actual_size{};
do
{
path_size = static_cast<uint32_t>(path.size());
actual_size = ::GetModuleFileName(nullptr, path.data(), path_size);
if (actual_size + 1 > path_size)
{
path.resize(path_size * 2, L'?');
}
} while (actual_size + 1 > path_size);
path.resize(actual_size);
return path;
}
std::wstring get_shortcut_path()
{
std::wstring format{ LR"(%ProgramData%\Microsoft\Windows\Start Menu\Programs\)" };
format += (this_app_name + L".lnk");
auto required{ ::ExpandEnvironmentStrings(format.c_str(), nullptr, 0) };
std::wstring path(required - 1, L'?');
::ExpandEnvironmentStrings(format.c_str(), path.data(), required);
return path;
}
Implémenter les fonctions restantes et la fonction de point d’entrée wmain
Supprimez votre fonction main, puis collez à la place ce bloc de code, qui comprend le code permettant d’enregistrer votre coclasse, puis d’envoyer une notification toast pouvant rappeler votre application.
void register_callback()
{
DWORD registration{};
winrt::check_hresult(::CoRegisterClassObject(
callback_guid,
make<callback_factory>().get(),
CLSCTX_LOCAL_SERVER,
REGCLS_SINGLEUSE,
®istration));
}
void create_shortcut()
{
auto link{ winrt::create_instance<IShellLink>(CLSID_ShellLink) };
std::wstring module_path{ get_module_path() };
winrt::check_hresult(link->SetPath(module_path.c_str()));
auto store = link.as<IPropertyStore>();
prop_variant value;
winrt::check_hresult(::InitPropVariantFromString(this_app_name.c_str(), &value));
winrt::check_hresult(store->SetValue(PKEY_AppUserModel_ID, value));
value.clear();
winrt::check_hresult(::InitPropVariantFromCLSID(callback_guid, &value));
winrt::check_hresult(store->SetValue(PKEY_AppUserModel_ToastActivatorCLSID, value));
auto file{ store.as<IPersistFile>() };
std::wstring shortcut_path{ get_shortcut_path() };
winrt::check_hresult(file->Save(shortcut_path.c_str(), TRUE));
std::wcout << L"In " << shortcut_path << L", created a shortcut to " << module_path << std::endl;
}
void update_registry()
{
std::wstring key_path{ LR"(SOFTWARE\Classes\CLSID\{????????-????-????-????-????????????})" };
::StringFromGUID2(callback_guid, key_path.data() + 23, 39);
key_path += LR"(\LocalServer32)";
registry_key key;
winrt::check_win32(::RegCreateKeyEx(
HKEY_CURRENT_USER,
key_path.c_str(),
0,
nullptr,
0,
KEY_WRITE,
nullptr,
key.put(),
nullptr));
::RegDeleteValue(key.get(), nullptr);
std::wstring path{ get_module_path() };
winrt::check_win32(::RegSetValueEx(
key.get(),
nullptr,
0,
REG_SZ,
reinterpret_cast<BYTE const*>(path.c_str()),
static_cast<uint32_t>((path.size() + 1) * sizeof(wchar_t))));
std::wcout << L"In " << key_path << L", registered local server at " << path << std::endl;
}
void create_toast()
{
XmlDocument xml;
std::wstring toastPayload
{
LR"(
<toast>
<visual>
<binding template='ToastGeneric'>
<text>)"
};
toastPayload += this_app_name;
toastPayload += LR"(
</text>
</binding>
</visual>
<actions>
<action content='Call back )";
toastPayload += this_app_name;
toastPayload += LR"(
' arguments='the_args' activationKind='Foreground' />
</actions>
</toast>)";
xml.LoadXml(toastPayload);
ToastNotification toast{ xml };
ToastNotifier notifier{ ToastNotificationManager::CreateToastNotifier(this_app_name) };
notifier.Show(toast);
::Sleep(50); // Give the callback chance to display.
}
void LaunchedNormally(HANDLE, INPUT_RECORD &, DWORD &);
void LaunchedFromNotification(HANDLE, INPUT_RECORD &, DWORD &);
int wmain(int argc, wchar_t * argv[], wchar_t * /* envp */[])
{
winrt::init_apartment();
register_callback();
HANDLE consoleHandle{ ::GetStdHandle(STD_INPUT_HANDLE) };
INPUT_RECORD buffer{};
DWORD events{};
::FlushConsoleInputBuffer(consoleHandle);
if (argc == 1)
{
LaunchedNormally(consoleHandle, buffer, events);
}
else if (argc == 2 && wcscmp(argv[1], L"-Embedding") == 0)
{
LaunchedFromNotification(consoleHandle, buffer, events);
}
}
void LaunchedNormally(HANDLE consoleHandle, INPUT_RECORD & buffer, DWORD & events)
{
try
{
bool runningAsAdmin{ ::IsUserAnAdmin() == TRUE };
std::wcout << this_app_name << L" is running" << (runningAsAdmin ? L" (administrator)." : L" (NOT as administrator).") << std::endl;
if (runningAsAdmin)
{
create_shortcut();
update_registry();
}
std::wcout << std::endl << L"Press 'T' to display a toast notification (press any other key to exit)." << std::endl;
::ReadConsoleInput(consoleHandle, &buffer, 1, &events);
if (towupper(buffer.Event.KeyEvent.uChar.UnicodeChar) == L'T')
{
create_toast();
}
}
catch (winrt::hresult_error const& e)
{
std::wcout << L"Error: " << e.message().c_str() << L" (" << std::hex << std::showbase << std::setw(8) << static_cast<uint32_t>(e.code()) << L")" << std::endl;
}
}
void LaunchedFromNotification(HANDLE consoleHandle, INPUT_RECORD & buffer, DWORD & events)
{
::Sleep(50); // Give the callback chance to display its message.
std::wcout << std::endl << L"Press any key to exit." << std::endl;
::ReadConsoleInput(consoleHandle, &buffer, 1, &events);
}
Comment tester l’exemple d’application
Compilez l’application, puis exécutez-la au moins une fois en tant qu’administrateur afin de déclencher l’exécution du code d’enregistrement et des autres tâches d’initialisation. Pour ce faire, exécutez Visual Studio en tant qu’administrateur, puis exécutez l’application à partir de Visual Studio. Cliquez avec le bouton droit sur Visual Studio dans la barre des tâches pour afficher la liste de raccourcis, cliquez avec le bouton droit sur Visual Studio dans la liste de raccourcis, puis cliquez sur Exécuter en tant qu’administrateur. Acceptez l’invite, puis ouvrez le projet. Lorsque vous exécutez l’application, un message s’affiche indiquant si l’application s’exécute en tant qu’administrateur ou non. Si ce n’est pas le cas, l’inscription et d’autres configurations ne s’exécuteront pas. Cette inscription et d’autres configurations doivent s’exécuter au moins une fois pour que l’application fonctionne correctement.
Que vous exécutiez ou non l’application en tant qu’administrateur, appuyez sur « T » pour afficher un toast. Vous pouvez ensuite cliquer sur le bouton Rappeler ToastAndCallback directement à partir de la notification toast qui s’affiche ou à partir du Centre de notifications, et votre application est lancée, la coclasse instanciée et la méthode INotificationActivationCallback ::Activate exécutée.
Serveur COM dans le processus
L’exemple d’application ToastAndCallback ci-dessus fonctionne comme un serveur COM local (ou hors processus). Cela est indiqué par la clé de Registre Windows LocalServer32 que vous utilisez pour inscrire le CLSID de sa coclasse. Un serveur COM local héberge sa coclasse(es) à l’intérieur d’un fichier binaire exécutable (un .exe).
Vous pouvez également choisir d’héberger votre coclasse(es) à l’intérieur d’une bibliothèque de liens dynamiques (a .dll). Un serveur COM sous la forme d’une DLL est appelé serveur COM in-process, et cela est indiqué par l’enregistrement des CLSID à l’aide de la clé de Registre Windows InprocServer32.
Créer un projet de bibliothèque de Dynamic-Link (DLL)
Vous pouvez commencer la tâche de créer un serveur COM in-process en créant un projet dans Microsoft Visual Studio. Créez un projet Visual C++>Bureau Windows>bibliothèque de liens dynamiques (DLL).
Pour ajouter la prise en charge C++/WinRT au nouveau projet, suivez les étapes décrites dans Modifier un projet d’application de bureau Windows pour ajouter la prise en charge de C++/WinRT.
Implémenter la coclasse, la fabrique de classes et les exportations de serveur in-proc
Ouvrez dllmain.cpp, puis ajoutez-y la liste de codes indiquée ci-dessous.
Si vous avez déjà une DLL qui implémente des classes C++/WinRT Windows Runtime, vous disposez déjà de la fonction DllCanUnloadNow indiquée ci-dessous. Si vous souhaitez ajouter des coclasses à cette DLL, vous pouvez ajouter la fonction DllGetClassObject .
Si vous n’avez pas de code Windows Runtime C++ Template Library (WRL) existant avec lequel vous souhaitez continuer à être compatible, vous pouvez alors supprimer les parties WRL du code présenté.
// dllmain.cpp
struct MyCoclass : winrt::implements<MyCoclass, IPersist>
{
HRESULT STDMETHODCALLTYPE GetClassID(CLSID* id) noexcept override
{
*id = IID_IPersist; // Doesn't matter what we return, for this example.
return S_OK;
}
};
struct __declspec(uuid("85d6672d-0606-4389-a50a-356ce7bded09"))
MyCoclassFactory : winrt::implements<MyCoclassFactory, IClassFactory>
{
HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) noexcept override
{
try
{
return winrt::make<MyCoclass>()->QueryInterface(riid, ppvObject);
}
catch (...)
{
return winrt::to_hresult();
}
}
HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) noexcept override
{
// ...
return S_OK;
}
// ...
};
HRESULT __stdcall DllCanUnloadNow()
{
#ifdef _WRL_MODULE_H_
if (!::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().Terminate())
{
return S_FALSE;
}
#endif
if (winrt::get_module_lock())
{
return S_FALSE;
}
winrt::clear_factory_cache();
return S_OK;
}
HRESULT __stdcall DllGetClassObject(GUID const& clsid, GUID const& iid, void** result)
{
try
{
*result = nullptr;
if (clsid == __uuidof(MyCoclassFactory))
{
return winrt::make<MyCoclassFactory>()->QueryInterface(iid, result);
}
#ifdef _WRL_MODULE_H_
return ::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().GetClassObject(clsid, iid, result);
#else
return winrt::hresult_class_not_available().to_abi();
#endif
}
catch (...)
{
return winrt::to_hresult();
}
}
Prise en charge des références faibles
Consultez également les références faibles en C++/WinRT.
C++/WinRT (plus précisément, le modèle de structure de base winrt::implements) implémente IWeakReferenceSource pour vous si votre type implémente IInspectable (ou toute interface dérivant de IInspectable).
Cela est dû au fait que IWeakReferenceSource et IWeakReference sont conçus pour les types Windows Runtime. Ainsi, vous pouvez activer la prise en charge des références faibles pour votre coclass simplement en ajoutant winrt::Windows::Foundation::IInspectable (ou une interface qui dérive de IInspectable) dans votre implémentation.
struct MyCoclass : winrt::implements<MyCoclass, IMyComInterface, winrt::Windows::Foundation::IInspectable>
{
// ...
};
Implémenter une interface COM qui dérive d’une autre
La dérivation de l’interface est une fonctionnalité com classique (et il arrive qu’elle soit absente, intentionnellement, de la Windows Runtime). Voici un exemple de dérivation de l’interface.
IFileSystemBindData2 : public IFileSystemBindData { /* ... */ };
Si vous écrivez une classe qui doit implémenter, par exemple, à la fois IFileSystemBindData et IFileSystemBindData2, la première étape pour exprimer cela consiste à déclarer que vous implémentez uniquement l’interface dérivée, comme ceci.
// pch.h
#pragma once
#include <Shobjidl.h>
...
// main.cpp
...
struct MyFileSystemBindData :
implements<MyFileSystemBindData,
IFileSystemBindData2>
{
// IFileSystemBindData
IFACEMETHOD(SetFindData)(const WIN32_FIND_DATAW* pfd) override { /* ... */ return S_OK; };
IFACEMETHOD(GetFindData)(WIN32_FIND_DATAW* pfd) override { /* ... */ return S_OK; };
// IFileSystemBindData2
IFACEMETHOD(SetFileID)(LARGE_INTEGER liFileID) override { /* ... */ return S_OK; };
IFACEMETHOD(GetFileID)(LARGE_INTEGER* pliFileID) override { /* ... */ return S_OK; };
IFACEMETHOD(SetJunctionCLSID)(REFCLSID clsid) override { /* ... */ return S_OK; };
IFACEMETHOD(GetJunctionCLSID)(CLSID* pclsid) override { /* ... */ return S_OK; };
};
...
int main()
...
L’étape suivante consiste à s’assurer que QueryInterface réussit lorsqu’il est appelé (directement ou indirectement) pour IID_IFileSystemBindData (l’interface de base ) sur une instance de MyFileSystemBindData. Pour ce faire, vous devez fournir une spécialisation pour le modèle de fonction winrt::is_guid_of.
winrt::is_guid_of est variadique, et vous pouvez donc lui passer une liste d’interfaces. Voici comment fournir une spécialisation afin qu’une vérification de IFileSystemBindData2 inclut également un test pour IFileSystemBindData.
// pch.h
...
namespace winrt
{
template<>
inline bool is_guid_of<IFileSystemBindData2>(guid const& id) noexcept
{
return is_guid_of<IFileSystemBindData2, IFileSystemBindData>(id);
}
}
// main.cpp
...
int main()
{
...
auto mfsbd{ winrt::make<MyFileSystemBindData>() };
auto a{ mfsbd.as<IFileSystemBindData2>() }; // Would succeed even without the **is_guid_of** specialization.
auto b{ mfsbd.as<IFileSystemBindData>() }; // Needs the **is_guid_of** specialization in order to succeed.
}
La spécialisation de winrt::is_guid_of doit être identique dans tous les fichiers du projet et visible à l’endroit où l’interface est utilisée par le modèle winrt::implements ou winrt::delegate. En règle générale, vous l’avez placé dans un fichier d’en-tête commun.
API importantes
Rubriques connexes
Windows developer