Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
C++/WinRT- kunt u helpen bij het maken van klassieke COM-onderdelen (Component Object Model) of coklassen, net zoals u hiermee Windows Runtime-klassen kunt maken. In dit onderwerp wordt uitgelegd hoe u dit doet.
Hoe C++/WinRT zich standaard gedraagt met betrekking tot COM-interfaces
C++/WinRT's winrt::implements sjabloon is de basis waaruit uw runtimeklassen en activeringsfaciliteiten direct of indirect voortkomen.
Standaard negeert winrt::implementeert stilzwijgend klassieke COM-interfaces. Eventuele QueryInterface (QI)-aanroepen voor klassieke COM-interfaces mislukken bijgevolg met E_NOINTERFACE. Standaard ondersteunt winrt::implements alleen C++/WinRT-interfaces.
- winrt::IUnknown is een C++/WinRT-interface, dus winrt::implementeert ondersteunt winrt::IUnknown-gebaseerde interfaces.
- winrt::implementeert biedt zelf standaard geen ondersteuning voor ::IUnknown.
Op een moment ziet u hoe u de gevallen kunt overwinnen die niet standaard worden ondersteund. Maar eerst volgt een codevoorbeeld om te illustreren wat er standaard gebeurt.
// 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();
}
}
En hier ziet u de clientcode om de klasse Sample te gebruiken.
// Client.cpp
Sample sample; // Construct a Sample object via its projection.
// This next line doesn't compile yet.
sample.as<IInitializeWithWindow>()->Initialize(hwnd);
Klassieke COM-ondersteuning inschakelen
Het goede nieuws is dat het enige wat nodig is om winrt::implements klassieke COM-interfaces te laten ondersteunen, is het unknwn.h-headerbestand op te nemen voordat u enige C++/WinRT-headers opneemt.
U kunt dit expliciet of indirect doen door een ander headerbestand zoals ole2.hop te slaan. Een aanbevolen methode is om het wil\cppwinrt.h headerbestand op te nemen, dat deel uitmaakt van de Windows Implementation Libraries (WIL). Het wil\cppwinrt.h headerbestand zorgt er niet alleen voor dat unknwn.h wordt opgenomen vóór winrt/base.h, maar stelt ook dingen in zodat C++/WinRT en WIL elkaars uitzonderingen en foutcodes begrijpen.
U kunt vervolgens als<> gebruiken voor klassieke COM-interfaces, en de code in het bovenstaande voorbeeld zal compileren.
Opmerking
In het bovenstaande voorbeeld, zelfs na het inschakelen van klassieke COM-ondersteuning in de client (de code die de klasse gebruikt), als u geen klassieke COM-ondersteuning op de server hebt ingeschakeld (de code die de klasse implementeert), loopt de aanroep naar als<> in de client vast omdat de QI voor IInitializeWithWindow- mislukt.
Een lokale (niet-geprojecteerde) klasse
Een lokale klasse is een klasse die zowel wordt geïmplementeerd als gebruikt in dezelfde compilatie-eenheid (app of een ander binair bestand); en er is dus geen projectie voor.
Hier volgt een voorbeeld van een lokale klasse die alleen klassieke COM-interfaces implementeert.
struct LocalObject :
winrt::implements<LocalObject, IInitializeWithWindow>
{
...
};
Als u dat voorbeeld implementeert, maar u geen klassieke COM-ondersteuning inschakelt, mislukt de volgende code.
winrt::make<LocalObject>(); // error: ‘first_interface’: is not a member of ‘winrt::impl::interface_list<>’
Nogmaals, IInitializeWithWindow wordt niet herkend als com-interface, dus C++/WinRT negeert het. In het geval van het LocalObject voorbeeld betekent het resultaat van het negeren van COM-interfaces dat LocalObject helemaal geen interfaces heeft. Maar elke COM-klasse moet ten minste één interface implementeren.
Een eenvoudig voorbeeld van een COM-onderdeel
Hier volgt een eenvoudig voorbeeld van een COM-onderdeel dat is geschreven met C++/WinRT. Dit is een volledige weergave van een minitoepassing, zodat u de code kunt uitproberen als u deze plakt in de pch.h en main.cpp van een nieuw Windows Console-toepassing (C++/WinRT) project.
// 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());
}
Zie ook COM-onderdelen gebruiken met C++/WinRT.
Een realistischer en interessanter voorbeeld
In de rest van dit onderwerp wordt uitgelegd hoe u een minimaal consoletoepassingsproject maakt dat gebruikmaakt van C++/WinRT om een standaard coklasse (COM-onderdeel of COM-klasse) en klassefactory te implementeren. In de voorbeeldtoepassing laat zien hoe u een pop-upmelding met een callback-knop erop kunt leveren en hoe de coklasse (die de INotificationActivationCallback COM-interface implementeert) de applicatie kan starten en teruggeroepen kan worden wanneer de gebruiker op die knop in de pop-up klikt.
Meer achtergrondinformatie over het gebied van de toastmeldingsfunctie vindt u op Een lokale toastmelding verzenden. Geen van de codevoorbeelden in die sectie van de documentatie maken echter gebruik van C++/WinRT, dus we raden u aan de voorkeur te geven aan de code die in dit onderwerp wordt weergegeven.
Een Windows-consoletoepassingsproject maken (ToastAndCallback)
Begin met het maken van een nieuw project in Microsoft Visual Studio. Maak een Windows-consoletoepassing (C++/WinRT) project en noem het ToastAndCallback-.
Open pch.hen voeg #include <unknwn.h> toe vóór de insluiting voor C++/WinRT-headers. Hier is het resultaat; U kunt de inhoud van uw pch.h vervangen door deze lijst.
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
Open main.cppen verwijder de using-directieven die door de projectsjabloon worden gegenereerd. Voeg in hun plaats de volgende code in (waarmee we de bibliotheken, headers en typenamen krijgen die we nodig hebben). Dit is het resultaat. u kunt de inhoud van uw main.cpp vervangen door deze vermelding (de code is ook verwijderd uit main in de onderstaande lijst, omdat we deze functie later vervangen).
// 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() { }
Het project wordt nog niet gebouwd; Nadat we klaar zijn met het toevoegen van code, wordt u gevraagd om te bouwen en uit te voeren.
De coklasse en klassefabriek implementeren
In C++/WinRT implementeert u coclasses en class factories door af te leiden van de winrt::implements basisstruct. Plak direct na de drie hierboven weergegeven gebruiksrichtlijnen (en vóór main), deze code om uw COM-activatoronderdeel voor pop-upmeldingen te implementeren.
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;
}
};
De implementatie van de bovenstaande coklasse volgt hetzelfde patroon dat wordt gedemonstreerd in Author API's met C++/WinRT. U kunt dus dezelfde techniek gebruiken om COM-interfaces en Windows Runtime-interfaces te implementeren. COM-onderdelen en Windows Runtime-klassen stellen hun functies beschikbaar via interfaces. Elke COM-interface is uiteindelijk afgeleid van de IUnknown interface interface. De Windows Runtime is gebaseerd op COM, één onderscheid is dat Windows Runtime-interfaces uiteindelijk zijn afgeleid van de IInspectable-interface (en IInspectable is afgeleid van IUnknown).
In de coklasse in de bovenstaande code implementeren we de methode INotificationActivationCallback::Activate. Dit is de functie die wordt aangeroepen wanneer de gebruiker op de terugroepknop op een toastmelding klikt. Maar voordat deze functie kan worden aangeroepen, moet een exemplaar van de coklasse worden gemaakt en dat is de taak van de IClassFactory::CreateInstance functie.
De coklasse die we zojuist hebben geïmplementeerd, staat bekend als de COM-activator voor meldingen en heeft de klasse-id (CLSID) in de vorm van de callback_guid-id (van het type GUID) die u hierboven ziet. Deze id wordt later gebruikt in de vorm van een snelkoppeling in het startmenu en een Windows-registervermelding. De COM-activator CLSID en het pad naar de bijbehorende COM-server (dit is het pad naar het uitvoerbare bestand dat we hier aan het bouwen zijn) vormen het mechanisme waarmee een melding weet welke klasse moet worden aangemaakt wanneer de callback-knop wordt geklikt, ongeacht of de melding wordt geselecteerd in het Actiecentrum of niet.
Aanbevolen procedures voor het implementeren van COM-methoden
Technieken voor foutafhandeling en voor resourcebeheer kunnen hand in hand gaan. Het is handiger en praktisch om uitzonderingen te gebruiken dan foutcodes. En als u het idioom 'resource-acquisitie-is-initialisatie' (RAII) toepast, dan kunt u voorkomen dat u expliciet foutcodes moet controleren en vervolgens expliciet resources moet vrijgeven. Dergelijke expliciete controles zorgen ervoor dat uw code ingewikkelder is dan nodig is, en het biedt bugs voldoende plekken om te verbergen. Gebruik in plaats daarvan RAII en throw/catch-uitzonderingen. Op die manier zijn uw resourcetoewijzingen uitzonderingsveilig en is uw code eenvoudig.
U mag echter niet toestaan dat er uitzonderingen ontsnappen uit uw COM-methode-implementaties. U kunt ervoor zorgen dat u de noexcept-aanduiding gebruikt voor uw COM-methoden. Het is prima dat uitzonderingen overal in de aanroepketen van uw methode worden gegooid, zolang u ze afhandelt voordat uw methode eindigt. Als u noexceptgebruikt, maar vervolgens een uitzondering toestaat om te ontsnappen aan uw methode, wordt uw toepassing beëindigd.
Helpertypen en -functies toevoegen
In deze stap voegen we enkele helpertypen en functies toe waarvan de rest van de code gebruikmaakt. Voeg dus direct voor mainhet volgende toe.
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;
}
Implementeer de resterende functies en de ingangspuntfunctie wmain
Verwijder uw main-functie en plak deze codevermelding, die code bevat om uw coclass te registreren en vervolgens een waarschuwing te leveren waarmee uw toepassing kan worden aangeroepen.
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);
}
De voorbeeldtoepassing testen
Bouw de toepassing en voer deze ten minste één keer uit als beheerder om ervoor te zorgen dat de registratie en andere installatiecode worden uitgevoerd. Een manier om dat te doen, is om Visual Studio als beheerder uit te voeren en vervolgens de app uit te voeren vanuit Visual Studio. Klik met de rechtermuisknop op Visual Studio op de taakbalk om de spronglijst weer te geven, klik met de rechtermuisknop op Visual Studio in de spronglijst en klik vervolgens op Als administrator uitvoeren. Ga akkoord met de prompt en open het project. Wanneer u de toepassing uitvoert, wordt er een bericht weergegeven dat aangeeft of de toepassing wordt uitgevoerd als beheerder. Als dat niet het geval is, worden de registratie en andere instellingen niet uitgevoerd. Deze registratie en andere installatie moeten ten minste één keer worden uitgevoerd om de toepassing correct te laten werken.
Of u de toepassing als beheerder uitvoert of niet, druk op 'T' om een melding weer te geven. U kunt dan op de knop Call back ToastAndCallback klikken, hetzij rechtstreeks vanuit de pop-upmelding die verschijnt, of vanuit het Actiecentrum. Uw applicatie wordt dan gestart, de co-klasse geïnstantieerd en de INotificationActivationCallback::Activate methode uitgevoerd.
In-process COM-server
De ToastAndCallback voorbeeld-app hierboven fungeert als een lokale (of out-of-process) COM-server. Dit wordt aangegeven door de LocalServer32 Windows-registersleutel die u gebruikt om de CLSID van de bijbehorende coklasse te registreren. Een lokale COM-server host de coklasse(n) in een uitvoerbaar binair bestand (een .exe).
U kunt er ook voor kiezen om uw coclass(en) in een dynamic-link-bibliotheek (een .dll) te hosten. Een COM-server in de vorm van een DLL wordt een in-process COM-server genoemd en wordt aangegeven door CLSID's die worden geregistreerd met behulp van de InprocServer32 Windows-registersleutel.
Een DLL-project (Dynamic-Link Library) maken
U kunt beginnen met het maken van een in-process COM-server door een nieuw project te maken in Microsoft Visual Studio. Maak een Visual C++>Windows Desktop>Dynamic-Link Library (DLL) project.
Als u C++/WinRT-ondersteuning wilt toevoegen aan het nieuwe project, volgt u de stappen die worden beschreven in Een Windows-bureaubladtoepassingsproject wijzigen om C++/WinRT-ondersteuning toe te voegen.
De coclass-, klassefactory- en in-proc-serverexports implementeren
Open dllmain.cppen voeg er de onderstaande codevermelding aan toe.
Als u al een DLL hebt die C++/WinRT Windows Runtime-klassen implementeert, beschikt u al over de dllCanUnloadNow functie die hieronder wordt weergegeven. Als u coklassen wilt toevoegen aan dat DLL-bestand, kunt u de dllGetClassObject functie toevoegen.
Als u geen bestaande Windows Runtime C++-sjabloonbibliotheek (WRL) hebt code waarmee u compatibel wilt blijven, kunt u de WRL-onderdelen verwijderen uit de weergegeven code.
// 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();
}
}
Ondersteuning voor zwakke verwijzingen
Zie ook Zwakke verwijzingen in C++/WinRT.
C++/WinRT (met name de winrt::implementeert basisstructsjabloon) implementeert IWeakReferenceSource- als uw type IInspectable- (of een interface die is afgeleid van IInspectable-) implementeert.
Dit komt doordat IWeakReferenceSource en IWeakReference zijn ontworpen voor Windows Runtime-typen. U kunt dus zwakke referentieondersteuning voor uw coklasse inschakelen door winrt::Windows::Foundation::IInspectable (of een interface die is afgeleid van IInspectable) toe te voegen aan uw implementatie.
struct MyCoclass : winrt::implements<MyCoclass, IMyComInterface, winrt::Windows::Foundation::IInspectable>
{
// ...
};
Een COM-interface implementeren die is afgeleid van een andere
Interface-afleiding is een functie van klassieke COM (en deze is toevallig afwezig, opzettelijk, uit de Windows Runtime). Hier volgt een voorbeeld van hoe interface-afleiding eruitziet.
IFileSystemBindData2 : public IFileSystemBindData { /* ... */ };
Als u een klasse schrijft die bijvoorbeeld zowel IFileSystemBindData- als IFileSystemBindData2-moet implementeren, is de eerste stap om te verklaren dat u alleen de afgeleide-interface implementeert, zoals deze.
// 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()
...
De volgende stap is ervoor te zorgen dat QueryInterface slaagt wanneer deze (direct of indirect) wordt aangeroepen voor IID_IFileSystemBindData (de basisinterface) op een exemplaar van MyFileSystemBindData. U doet dit door een specialisatie op te geven voor de winrt::is_guid_of functiesjabloon.
winrt::is_guid_of is variadic, en daarom kunt u het een lijst met interfaces geven. U kunt als volgt een specialisatie opgeven, zodat een controle op IFileSystemBindData2- ook een test bevat voor 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.
}
De specialisatie van winrt::is_guid_of moet identiek zijn voor alle bestanden in het project en zichtbaar zijn op het moment dat de interface wordt gebruikt door de winrt::implements of winrt::delegate sjabloon. Normaal gesproken plaatst u het in een gemeenschappelijk headerbestand.