Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
C++/WinRT vám může pomoct vytvářet klasické komponenty modelu COM (Component Object Model) (nebo coclasses), stejně jako vám to pomůže vytvářet prostředí Windows Runtime třídy. V tomto tématu se dozvíte, jak na to.
Jak se C++/WinRT standardně chová ve vztahu k rozhraním COM
Šablona winrt::implements jazyka C++/WinRT je základem, z níž vaše běhové třídy a aktivační továrny přímo či nepřímo vycházejí.
Ve výchozím nastavení winrt::implements tiše ignoruje klasická rozhraní COM. Jakákoli volání QueryInterface (QI) pro klasická rozhraní COM v důsledku toho selžou s chybou E_NOINTERFACE. Ve výchozím nastavení winrt::implementuje pouze rozhraní C++/WinRT.
- winrt::IUnknown je rozhraní C++/WinRT, takže winrt::implements podporuje rozhraní založená na winrt::IUnknown.
- winrt::implements ve výchozím nastavení nepodporuje ::IUnknown .
Za chvíli se dozvíte, jak překonat případy, které nejsou ve výchozím nastavení podporované. Nejprve je ale tady příklad kódu, který ilustruje, co se ve výchozím nastavení stane.
// 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();
}
}
A zde je klientský kód pro použití třídy Sample.
// Client.cpp
Sample sample; // Construct a Sample object via its projection.
// This next line doesn't compile yet.
sample.as<IInitializeWithWindow>()->Initialize(hwnd);
Povolení podpory klasického modelu COM
Dobrou zprávou je, že k tomu, aby winrt::implements podporoval klasická rozhraní COM, stačí před zahrnutím jakýchkoli hlaviček C++/WinRT zahrnout hlavičkový soubor unknwn.h.
Můžete to udělat explicitně nebo nepřímo zahrnutím některého jiného hlavičkového souboru, například ole2.h. Jedna z doporučených metod je zahrnout soubor záhlaví wil\cppwinrt.h, který je součástí Windows Implementation Libraries (WIL). Hlavičkový wil\cppwinrt.h soubor nejen zajistí, že unknwn.h je součástí winrt/base.hdříve, ale také nastaví věci tak, aby C++/WinRT a WIL porozuměly výjimkám a kódům chyb.
Pak můžete použít as<> stejně jako u klasických rozhraní COM a kód ve výše uvedeném příkladu se zkompiluje.
Note
Ve výše uvedeném příkladu platí, že i když v klientovi (kódu využívajícím třídu) povolíte podporu klasického modelu COM, pokud ji nepovolíte také na serveru (v kódu implementujícím třídu), volání as<> v klientovi způsobí pád, protože QI pro IInitializeWithWindow selže.
Místní (neprojektovaná) třída
Místní třída je třída, která je implementována a spotřebována ve stejné kompilační jednotce (aplikace nebo jiný binární soubor); a proto pro ni není žádná projekce.
Zde je příklad lokální třídy, která implementuje pouze klasická rozhraní COM.
struct LocalObject :
winrt::implements<LocalObject, IInitializeWithWindow>
{
...
};
Pokud tento příklad implementujete, ale nepovolíte klasickou podporu modelu COM, následující kód selže.
winrt::make<LocalObject>(); // error: ‘first_interface’: is not a member of ‘winrt::impl::interface_list<>’
IInitializeWithWindow se znovu nerozpozná jako rozhraní MODELU COM, takže C++/WinRT ho ignoruje. V případě příkladu LocalObject výsledek ignorování rozhraní COM znamená, že LocalObject nemá vůbec žádná rozhraní. Každá třída modelu COM však musí implementovat alespoň jedno rozhraní.
Jednoduchý příklad komponenty MODELU COM
Tady je jednoduchý příklad komponenty COM napsané pomocí C++/WinRT. Toto je úplný výpis miniaplikace, takže si můžete kód vyzkoušet, když ho vložíte do pch.h a main.cpp v novém projektu Aplikace konzoly 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());
}
Viz také Použití komponent modelu COM pomocí C++/WinRT.
Realističtější a zajímavější příklad
Zbývající část tohoto tématu vás provede vytvořením minimálního projektu konzolové aplikace, který používá C++/WinRT k implementaci základní třídy coclass (komponenty MODELU COM nebo třídy MODELU COM) a objektu pro vytváření tříd. Ukázková aplikace ukazuje, jak odeslat toastové oznámení s tlačítkem pro zpětné volání, a coclass (který implementuje rozhraní COM INotificationActivationCallback) umožňuje spuštění aplikace a vyvolání zpětného volání, když uživatel klikne na toto tlačítko v toastovém oznámení.
Další informace o oblasti funkcí oznámení toast najdete v článku Odeslání místního oznámení toast. Žádný z příkladů kódu v této části dokumentace ale nepoužívá C++/WinRT, proto doporučujeme, abyste raději kód zobrazený v tomto tématu.
Vytvoření projektu konzolové aplikace Windows (ToastAndCallback)
Začněte vytvořením nového project v Microsoft Visual Studio. Vytvořte projekt konzolové aplikace Windows (C++/WinRT) a pojmenujte ho ToastAndCallback.
Otevřete pch.ha přidejte #include <unknwn.h> před zahrnutí všech hlaviček C++/WinRT. Zde je výsledek; můžete obsah svého pch.h nahradit tímto výpisem.
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
Otevřete main.cpp a odstraňte direktivy `using`, které vygeneruje šablona projektu. Na jejich místo vložte následující kód (který nám dává názvy libů, záhlaví a typů, které potřebujeme). Tady je výsledek; obsah tohoto main.cpp výpisu můžete nahradit (kód jsme také odebrali z main následujícího výpisu, protože tuto funkci později nahradíme).
// 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() { }
Projekt se ještě nevytvoře; po přidání kódu se zobrazí výzva k sestavení a spuštění.
Implementace třídy coclass a objektu pro vytváření tříd
V C++/WinRT implementujete ko-třídy a továrny tříd tak, že je odvodíte od základní struktury winrt::implements. Ihned po třech direktivách using uvedených výše (a před main) vložte tento kód, který implementuje komponentu aktivátoru COM oznámení informační zprávy.
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;
}
};
Implementace výše uvedené třídy coclass se řídí stejným vzorem, který je ukázán v rozhraních AUTHOR API s C++/WinRT. Stejnou techniku tedy můžete použít k implementaci rozhraní COM i rozhraní prostředí Windows Runtime. Komponenty modelu COM a třídy prostředí Windows Runtime zpřístupňují své funkce prostřednictvím rozhraní. Každé rozhraní COM je odvozeno z rozhraní IUnknown interface. prostředí Windows Runtime je založen na modelu COM – jedním rozdílem je, že prostředí Windows Runtime rozhraní nakonec pochází z IInspectable rozhraní (a IInspectable je odvozen od IUnknown).
V coclass ve výše uvedeném kódu implementujeme metodu INotificationActivationCallback::Activate, což je funkce, která se zavolá, když uživatel klikne na tlačítko pro zpětné volání v toastovém oznámení. Před zavoláním této funkce je však potřeba vytvořit instanci třídy coclass a to je úloha funkce IClassFactory::CreateInstance .
Třída coclass, kterou jsme právě implementovali, se označuje jako aktivátor COM pro oznámení a má id třídy (CLSID) ve formě identifikátoru callback_guid ( identifikátoru GUID typu), který vidíte výše. Tento identifikátor použijeme později ve formě zástupce nabídky Start a položky registru Windows. Aktivátor COM CLSID a cesta k přidruženému serveru COM (což je cesta ke spustitelnému souboru, který zde vytváříme) je mechanismus, pomocí kterého informační zpráva ví, jakou třídu má vytvořit instanci, když se na tlačítko zpětného volání klikne (jestli se oznámení klikne v Centru akcí nebo ne).
Osvědčené postupy pro implementaci metod COM
Techniky pro zpracování chyb a správu prostředků mohou jít ruku v ruce. Je pohodlnější a praktičtější používat výjimky než kódy chyb. A pokud použijete idiom resource acquisition is initialization (RAII), můžete se vyhnout tomu, že budete muset explicitně kontrolovat chybové kódy a následně explicitně uvolňovat prostředky. Takové explicitní kontroly dělají váš kód zbytečně složitějším a dávají chybám spoustu míst, kde se mohou skrýt. Místo toho používejte RAII a vyhazujte a zachytávejte výjimky. Díky tomu jsou vaše alokace prostředků odolné vůči výjimkám a váš kód je jednoduchý.
Nesmíte však povolit výjimky, které uniknou implementacím vaší metody COM. Toho můžete dosáhnout použitím specifikátoru noexcept u metod COM. Je v pořádku, aby byly výjimky vyvolány kdekoli v grafu volání vaší metody, pokud je zpracujete dříve, než se vaše metoda ukončí. Pokud použijete noexcept, ale pak povolíte výjimku utéct vaší metody, aplikace se ukončí.
Přidání pomocných typů a funkcí
V tomto kroku přidáme některé pomocné typy a funkce, které zbytek kódu využívá. Takže bezprostředně před main, přidejte následující.
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;
}
Implementujte zbývající funkce a funkci vstupního bodu wmain
Smažte funkci main a místo ní vložte tuto ukázku kódu, která obsahuje kód pro registraci vaší třídy COM (coclass) a následné zobrazení oznámení toast, které může zpětně volat vaši aplikaci.
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);
}
Testování ukázkové aplikace
Zkompilujte aplikaci a pak ji alespoň jednou spusťte jako správce, aby se provedl registrační a další inicializační kód. Jedním ze způsobů, jak to udělat, je spustit Visual Studio jako správce a pak aplikaci spustit z Visual Studio. Klikněte pravým tlačítkem myši na Visual Studio na hlavním panelu úloh, aby se zobrazil seznam pro přechod, klikněte pravým tlačítkem myši na Visual Studio v seznamu pro přechod a potom klikněte na Spustit jako správce. Odsouhlaste výzvu a otevřete projekt. Při spuštění aplikace se zobrazí zpráva, která označuje, jestli je aplikace spuštěná jako správce. Pokud tomu tak není, registrace a jiná instalace se nespustí. Aby aplikace správně fungovala, musí se tato registrace a další instalace spustit alespoň jednou.
Ať už aplikaci spouštíte jako správce, nebo ne, stisknutím klávesy 'T' zobrazíte oznámení. Potom můžete kliknout na tlačítko Call back ToastAndCallback buď přímo z informačního oznámení, které se zobrazí, nebo z Centra akcí, a vaše aplikace se spustí, vytvoří instanci třídy coclass a INotificationActivationCallback::Activate metoda se spustí.
Server COM v procesu
Ukázková aplikace ToastAndCallback výše funguje jako místní (nebo mimo proces) server COM. To je indikováno klíčem registru LocalServer32 Windows, který používáte k registraci CLSID jeho třídy coclass. Místní server COM hostuje svou spolutřídu nebo spolutřídy ve spustitelném binárním souboru (.exe).
Případně (a pravděpodobněji) se můžete rozhodnout hostovat své coclass(y) v knihovně DLL (v .dll). Server COM ve formě knihovny DLL se označuje jako procesový server COM a je označen identifikátory CLSI, které jsou registrovány pomocí klíče registru InprocServer32 Windows.
Vytvoření projektu knihovny Dynamic-Link (DLL)
Úkol vytvoření procesového serveru COM můžete zahájit vytvořením nového projektu v Microsoft Visual Studio. Vytvořte projekt knihovny DLL (Visual C++>Windows Desktop>Dynamic-Link Library).
Pokud chcete do nového projektu přidat podporu C++/WinRT, postupujte podle kroků popsaných v části Úprava projektu desktopové aplikace Windows a přidejte podporu C++/WinRT.
Implementujte coclass, továrnu tříd a exporty serveru in-proc
Otevřete dllmain.cppa přidejte do něj výpis kódu uvedený níže.
Pokud již máte knihovnu DLL, která implementuje C++/WinRT třídy prostředí Windows Runtime, budete již mít funkci DllCanUnloadNow zobrazenou níže. Pokud chcete do této knihovny DLL přidat třídy coclass, můžete přidat funkci DllGetClassObject .
Pokud nemáte existující prostředí Windows Runtime kód knihovny šablon jazyka C++, se kterým chcete zůstat kompatibilní, můžete odebrat části knihovny WRL ze zobrazeného kódu.
// 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();
}
}
Podpora slabých odkazů
Viz také slabé reference v C++/WinRT.
C++/WinRT (konkrétně winrt::implements base struct template) implementuje IWeakReferenceSource pro vás, pokud váš typ implementuje IInspectable (nebo jakékoli rozhraní odvozené od IInspectable).
Důvodem je to, že IWeakReferenceSource a IWeakReference jsou určeny pro typy prostředí Windows Runtime. Proto můžete zapnout slabou referenční podporu pro vaši třídu coclass jednoduše přidáním winrt::Windows::Foundation::IInspectable (nebo rozhraní, které je odvozeno od IInspectable) k vaší implementaci.
struct MyCoclass : winrt::implements<MyCoclass, IMyComInterface, winrt::Windows::Foundation::IInspectable>
{
// ...
};
Implementujte rozhraní COM, které je odvozeno od jiného.
Odvození rozhraní je vlastností klasického COM (a záměrně chybí ve prostředí Windows Runtime). Tady je příklad, jak vypadá odvození rozhraní.
IFileSystemBindData2 : public IFileSystemBindData { /* ... */ };
Pokud píšete třídu, která potřebuje implementovat například jak IFileSystemBindData, tak IFileSystemBindData2, pak prvním krokem k tomu je deklarovat, že implementujete pouze odvozené rozhraní, takto.
// 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()
...
Dalším krokem je zajistit, že QueryInterface proběhne úspěšně, když je volána (přímo nebo nepřímo) pro IID_IFileSystemBindData ( základní rozhraní) proti instanci MyFileSystemBindData. Provedete to tak, že poskytnete specializaci pro šablonu funkce winrt::is_guid_of .
winrt::is_guid_of je variadická, takže jí můžete předat seznam rozhraní. Tady je postup, jak byste zadali specializaci, aby kontrola IFileSystemBindData2 obsahovala také test pro 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.
}
Specializace winrt::is_guid_of musí být identická ve všech souborech projektu a viditelná v místě, kde rozhraní používá šablona winrt::implements nebo winrt::delegate. Obvykle byste ho umístili do společného souboru hlaviček.
Důležitá rozhraní API
Příbuzná témata
Windows developer