Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
C++/WinRT помогает создавать классические компоненты объектной модели компонентов (COM) (или соклассы), так же, как это помогает создавать классы среды выполнения Windows. В этом разделе показано, как это сделать.
Поведение C++/WinRT по умолчанию относительно com-интерфейсов
C++/WinRT::winrt::implements шаблон является основой, от которой непосредственно или опосредованно наследуются ваши классы времени выполнения и фабрики активации.
По умолчанию winrt::реализует молчаливо игнорирует классические COM-интерфейсы. Все вызовы QueryInterface (QI) для классических COM-интерфейсов завершатся ошибкой с E_NOINTERFACE. По умолчанию winrt::реализует поддерживает только интерфейсы C++/WinRT.
- winrt::IUnknown является интерфейсом C++/WinRT, поэтому winrt::implements поддерживает winrt::IUnknown-основанные интерфейсы.
- winrt::implements по умолчанию не поддерживает ::IUnknown.
Скоро вы увидите, как преодолеть случаи, которые не поддерживаются по умолчанию. Но сначала ниже приведен пример кода для иллюстрации того, что происходит по умолчанию.
// 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();
}
}
И вот код клиента для использования класса Sample.
// Client.cpp
Sample sample; // Construct a Sample object via its projection.
// This next line doesn't compile yet.
sample.as<IInitializeWithWindow>()->Initialize(hwnd);
Включение классической поддержки COM
Хорошая новость в том, что для обеспечения поддержки классических COM-интерфейсов с помощью winrt::implements достаточно включить файл заголовков unknwn.h
перед подключением заголовков C++/WinRT.
Это можно сделать явно или косвенно, включив другой файл заголовка, например ole2.h
. Одним из рекомендуемых методов является включение файла заголовка wil\cppwinrt.h
, который является частью библиотек реализации Windows (WIL) . Файл заголовка wil\cppwinrt.h
не только гарантирует, что unknwn.h
включен до winrt/base.h
, но также настраивает взаимодействие, чтобы C++/WinRT и WIL разбирались в исключениях и кодах ошибок друг друга.
Затем вы можете использовать как<> для классических COM-интерфейсов, и код в приведенном выше примере скомпилируется.
Замечание
В приведенном выше примере даже после включения классической поддержки COM в клиенте (код, использующем класс), если вы также не включили классическую поддержку COM на сервере (код, реализующий класс), вызов как<> в клиенте завершится сбоем, так как QI для IInitializeWithWindow завершится ошибкой.
Локальный (непроектируемый) класс
Локальный класс — это класс, который реализуется и используется в одной единице компиляции (приложение или другой двоичный файл); и поэтому для него нет проекции.
Ниже приведен пример локального класса, реализующего только классические интерфейсы COM .
struct LocalObject :
winrt::implements<LocalObject, IInitializeWithWindow>
{
...
};
Если вы реализуете этот пример, но не включаете классическую поддержку COM, то следующий код завершается сбоем.
winrt::make<LocalObject>(); // error: ‘first_interface’: is not a member of ‘winrt::impl::interface_list<>’
Опять же, IInitializeWithWindow не распознается как COM-интерфейс, поэтому C++/WinRT игнорирует его. В примере LocalObject результат игнорирования COM-интерфейсов состоит в том, что LocalObject вообще не имеет интерфейсов. Но каждый класс COM должен реализовать по крайней мере один интерфейс.
Простой пример com-компонента
Ниже приведен простой пример com-компонента, написанного с помощью C++/WinRT. Это полный список мини-приложения, поэтому вы можете попробовать код, если вы вставьте его в pch.h
и main.cpp
нового консольного приложения 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());
}
См. также Потребление COM-компонентов с C++/WinRT.
Более реалистичный и интересный пример
Оставшаяся часть этого раздела описывает создание минимального проекта консольного приложения, использующего C++/WinRT для реализации базового coclass (COM-компонента или класса COM) и фабрики классов. В примере приложения показано, как доставить сообщение-тост с кнопкой обратного вызова, а сокласс (который реализует интерфейс INotificationActivationCallback COM) позволяет запускать приложение и вызывать его, когда пользователь нажимает на эту кнопку в сообщении-тосте.
Дополнительные сведения о области функции всплывающего уведомления можно найти на странице Отправить локальное всплывающее уведомление. Ни один из примеров кода в этом разделе документации не использует C++/WinRT, поэтому рекомендуется использовать код, показанный в этом разделе.
Создание проекта консольного приложения Windows (ToastAndCallback)
Начните с создания проекта в Microsoft Visual Studio. Создайте проект консольного приложения Windows
Откройте pch.h
и добавьте #include <unknwn.h>
перед инструкциями для любых заголовков C++/WinRT. Вот результат; содержимое вашего pch.h
вы можете заменить на этот список.
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
Откройте main.cpp
и удалите директивы using, создаваемые шаблоном проекта. На их место вставьте следующий код (который предоставляет нам нужные библиотеки, заголовочные файлы и имена типов). Вот результат; вы можете заменить содержимое main.cpp
этим описанием (мы также удалили код из main
в приведенном ниже списке, так как мы заменим эту функцию позже).
// 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() { }
Проект еще не будет построен; После завершения добавления кода вам будет предложено создать и запустить.
Реализовать coclass и фабрику классов
В C++/WinRT вы реализуете кослассы и фабрики классов, производные от базовой структуры winrt::implements. Сразу после трех приведенных выше директив using (и до main
) вставьте этот код, чтобы реализовать компонент-активатор COM для уведомления 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;
}
};
Реализация вышеуказанного кокласса следует тому же шаблону, который демонстрируется в API Author с помощью C++/WinRT. Таким образом, можно использовать тот же метод для реализации com-интерфейсов, а также интерфейсов среды выполнения Windows. Компоненты COM и классы среды выполнения Windows предоставляют свои функции через интерфейсы. Каждый интерфейс COM в конечном счете производен от интерфейса IUnknown . Среда выполнения Windows основана на COM — одно из различий заключается в том, что интерфейсы среды выполнения Windows в конечном счете происходят от интерфейса IInspectable (и IInspectable происходит от IUnknown).
В коклассе в приведенном выше коде мы реализуем метод INotificationActivationCallback::Activate, который вызывается при нажатии пользователем кнопки для активации на всплывающем уведомлении. Но перед вызовом этой функции необходимо создать экземпляр сокласса, и это задание функции IClassFactory::CreateInstance.
Coclass, нами реализованный, называется COM-активатором для уведомлений, с идентификатором класса (CLSID) в виде идентификатора callback_guid
(типа GUID), который вы видите выше. Позже мы будем использовать этот идентификатор в виде контекстного меню "Пуск" и записи реестра Windows. Активатор COM CLSID и путь к связанному COM-серверу (который является путем к исполняемому файлу, который мы создаем здесь) — это механизм, который позволяет всплывающему уведомлению определить, какой класс необходимо создать при нажатии на кнопку обратного вызова, независимо от того, нажато ли уведомление в Центре уведомлений или нет.
Рекомендации по реализации методов COM
Методы обработки ошибок и управления ресурсами могут тесно взаимосвязываться. Это удобнее и удобно использовать исключения, чем коды ошибок. И если вы используете идиому ресурс-инициализация-приобретение (RAII), то можно избежать явной проверки кодов ошибок и явного освобождения ресурсов. Такие явные проверки делают код более свертанным, чем необходимо, и он дает ошибкам множество мест для скрытия. Вместо этого используйте RAII и исключения для перехвата. Таким образом, выделение ресурсов устойчиво к исключениям, и код прост.
Однако вы не должны позволять исключениям выходить за пределы реализаций ваших методов COM. Вы можете убедиться в этом, используя спецификатор noexcept
в ваших методах COM. Допустимо, чтобы исключения возникали в любом месте графа вызовов вашего метода, если вы их обработаете до завершения метода. Если вы используете noexcept
, но позволяете исключению покинуть ваш метод, то ваше приложение завершит работу.
Добавление вспомогательных типов и функций
На этом шаге мы добавим некоторые вспомогательные типы и функции, которые использует остальная часть кода. Таким образом, сразу перед main
добавьте следующее.
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;
}
Реализуйте оставшиеся функции и функцию точки входа wmain
Удалите свою функцию main
и замените ее этим кодом, который включает код для регистрации вашего coclass, а также для доставки уведомления toast, способного вызывать ваше приложение.
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);
}
Тестирование примера приложения
Создайте приложение, а затем запустите его по крайней мере один раз от имени администратора, чтобы вызвать регистрацию и другую настройку, выполнить код. Один из способов сделать это — запустить Visual Studio от имени администратора, а затем запустить приложение из Visual Studio. Щелкните Правой кнопкой мыши Visual Studio на панели задач, чтобы отобразить список переходов, щелкните правой кнопкой мыши Visual Studio в списке переходов и щелкните Запустить от имени администратора. Примите запрос, а затем откройте проект. При запуске приложения отображается сообщение, указывающее, работает ли приложение как администратор. Если это не так, регистрация и другая настройка не будут выполняться. Эта регистрация и другая настройка должна выполняться по крайней мере один раз, чтобы приложение работало правильно.
Независимо от того, запущено ли приложение в качестве администратора, нажмите клавишу T, чтобы отобразить тост-уведомление. Затем можно нажать кнопку Call back ToastAndCallback либо непосредственно из всплывающего уведомления, которое появится, либо из Центра уведомлений, и приложение будет запущено, coclass будет создан, и метод INotificationActivationCallback::Activate выполнен.
Внутрипроцессный сервер COM
В примере приложения ToastAndCallback выше приложение функционирует в качестве локального (или внепроцессного) COM-сервера. Это обозначено разделом реестра Windows LocalServer32, который используется для регистрации CLSID его coclass. Локальный COM-сервер размещает свои coclass в исполняемом двоичном файле (.exe
).
Кроме того, (и, возможно, более вероятно), вы можете разместить свой coclass(es) в библиотеке динамических ссылок (.dll
). COM-сервер в виде библиотеки DLL называется внутрипроцессным COM-сервером, и это обозначается CLSID, зарегистрированными с помощью ключа реестра Windows InprocServer32.
Создание проекта библиотеки Dynamic-Link (DLL)
Вы можете начать задачу создания внутрипроцессного COM-сервера, создав проект в Microsoft Visual Studio. Создайте проект
Чтобы добавить поддержку C++/WinRT в новый проект, выполните действия, описанные в разделе Изменение проекта классического приложения Windows, чтобы добавить поддержку C++/WinRT.
Реализуйте экспорт coclass, фабрики классов и внутрипроцессного сервера
Откройте dllmain.cpp
и добавьте в него список кода, показанный ниже.
Если у вас уже есть библиотека DLL, реализующая классы среды выполнения Windows C++/WinRT, вы уже будете иметь функцию DllCanUnloadNow, показанную ниже. Если вы хотите добавить коклассы в эту библиотеку DLL, можно добавить функцию DllGetClassObject.
Если у вас нет библиотеки шаблонов среды выполнения Windows C++ (WRL) код, с которым вы хотите оставаться совместимым, можно удалить части WRL из показанного кода.
// 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();
}
}
Поддержка слабых ссылок
См. также в разделе о слабых ссылках в C++/WinRT.
C++/WinRT (в частности, winrt::реализует шаблон базовой структуры) реализует IWeakReferenceSource, если тип реализует IInspectable (или любой интерфейс, производный от IInspectable).
Это связано с тем, что IWeakReferenceSource и IWeakReference предназначены для типов среды выполнения Windows. Таким образом, вы можете включить слабую поддержку ссылок для совместного класса, просто добавив winrt::Windows::Foundation::IInspectable (или интерфейс, производный от IInspectable) в реализацию.
struct MyCoclass : winrt::implements<MyCoclass, IMyComInterface, winrt::Windows::Foundation::IInspectable>
{
// ...
};
Реализация COM-интерфейса, наследуемого от другого
Производность интерфейса — это функция классического COM (и она отсутствует намеренно в среде выполнения Windows). Ниже приведен пример того, как выглядит производность интерфейса.
IFileSystemBindData2 : public IFileSystemBindData { /* ... */ };
Если вы пишете класс, которому нужно реализовать, например, как IFileSystemBindData, так и IFileSystemBindData2, то первый шаг в выражении этого — объявить, что реализуется только производный интерфейс .
// 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()
...
Следующий шаг — убедиться, что QueryInterface успешно выполняется при вызове (прямо или косвенно) для IID_IFileSystemBindData (базового интерфейса) для экземпляра MyFileSystemBindData. Для этого необходимо предоставить специализацию для шаблона функции winrt::is_guid_of.
winrt::is_guid_of является вариативным, поэтому вы можете предоставить ему список интерфейсов. Вот как вы можете задать специализацию так, чтобы проверка для IFileSystemBindData2 также провела тест для 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.
}
Специализация winrt::is_guid_of должна быть идентична во всех файлах проекта и видима в момент, когда интерфейс используется winrt::реализует или winrt::delegate шаблоном. Как правило, вы помещаете его в общий файл заголовка.