Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
C++/WinRT puede ayudarle a crear componentes clásicos del modelo de objetos de componentes (COM) (o coclases), al igual que le ayuda a crear clases Windows Runtime. En este tema te mostramos cómo.
Comportamiento de C++/WinRT, de forma predeterminada, con respecto a las interfaces COM
La plantilla winrt::implements de C++/WinRT es la base de la que derivan las clases en tiempo de ejecución y las factorías de activación directa o indirectamente.
De forma predeterminada, winrt::implements omite silenciosamente las interfaces COM clásicas. En consecuencia, se producirá un error en las llamadas de QueryInterface (QI) para interfaces COM clásicas con E_NOINTERFACE. De forma predeterminada, winrt::implements solo admite interfaces de C++/WinRT.
- winrt::IUnknown es una interfaz de C++/WinRT, por lo que winrt::implements admite interfaces basadas en winrt::IUnknown.
- winrt::implements no admite ::IUnknown en sí mismo, de forma predeterminada.
En un momento verás cómo resolver los casos que no son compatibles de forma predeterminada. Pero en primer lugar se muestra un ejemplo de código para ilustrar lo que sucede de forma predeterminada.
// 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();
}
}
Y este es el código de cliente para consumir la clase Sample .
// Client.cpp
Sample sample; // Construct a Sample object via its projection.
// This next line doesn't compile yet.
sample.as<IInitializeWithWindow>()->Initialize(hwnd);
Habilitación de la compatibilidad clásica con COM
La buena noticia es que todo lo necesario para hacer que winrt::implements admita interfaces COM clásicas es incluir el unknwn.h archivo de encabezado antes de incluir los encabezados de C++/WinRT.
Puede hacerlo explícitamente o indirectamente mediante la inclusión de algún otro archivo de encabezado, como ole2.h. Un método recomendado es incluir el wil\cppwinrt.h archivo de encabezado, que forma parte de las bibliotecas de implementación (WIL) de Windows. El archivo de encabezado wil\cppwinrt.h no solo garantiza que unknwn.h se incluya antes de winrt/base.h, sino que también lo prepara todo para que C++/WinRT y WIL entiendan las excepciones y los códigos de error de la otra.
A continuación, puede usar as<> con las interfaces COM clásicas, y el código del ejemplo anterior se compilará.
Note
En el ejemplo anterior, incluso después de habilitar la compatibilidad con COM clásica en el cliente (el código que consume la clase), si no ha habilitado también la compatibilidad con COM clásica en el servidor (el código que implementa la clase), la llamada a como<> en el cliente se bloqueará porque se producirá un error en el QI para IInitializeWithWindow .
Una clase local (no proyectada)
Una clase local es una que se implementa y consume en la misma unidad de compilación (aplicación u otro binario); y por lo tanto, no hay ninguna proyección para ella.
Este es un ejemplo de una clase local que implementa solo interfaces COM clásicas.
struct LocalObject :
winrt::implements<LocalObject, IInitializeWithWindow>
{
...
};
Si implementa ese ejemplo, pero no habilita la compatibilidad con COM clásica, se produce un error en el código siguiente.
winrt::make<LocalObject>(); // error: ‘first_interface’: is not a member of ‘winrt::impl::interface_list<>’
De nuevo, IInitializeWithWindow no se reconoce como una interfaz COM, por lo que C++/WinRT lo omite. En el caso del ejemplo localObject , el resultado de omitir las interfaces COM significa que LocalObject no tiene ninguna interfaz. Pero cada clase COM debe implementar al menos una interfaz.
Un ejemplo sencillo de un componente COM
Este es un ejemplo sencillo de un componente COM escrito mediante C++/WinRT. Esta es una lista completa de una miniaplicación, por lo que puedes probar el código si la pegas en pch.h y main.cpp de un nuevo proyecto de aplicación de consola de 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());
}
Vea también Uso de componentes COM con C++/WinRT.
Un ejemplo más realista e interesante
El resto de este tema le guía por la creación de un proyecto de aplicación de consola mínimo que usa C++/WinRT para implementar una coclase básica (componente COM o clase COM) y un generador de clases. La aplicación de ejemplo muestra cómo enviar una notificación del sistema con un botón de respuesta y la coclase (que implementa la interfaz COM INotificationActivationCallback) permite iniciar la aplicación y recibir la devolución de llamada cuando el usuario hace clic en ese botón de la notificación del sistema.
Puede encontrar más información sobre el área de funciones de las notificaciones del sistema en Enviar una notificación local del sistema. Sin embargo, ninguno de los ejemplos de código de esa sección de la documentación usa C++/WinRT, por lo que se recomienda que prefiera el código que se muestra en este tema.
Creación de un proyecto de aplicación de consola de Windows (ToastAndCallback)
Empiece por crear un nuevo proyecto en Microsoft Visual Studio. Cree un proyecto de aplicación de consola de Windows (C++/WinRT) y asígnelo el nombre ToastAndCallback.
Abra pch.hy agregue #include <unknwn.h> antes de incluir para los encabezados de C++/WinRT. Este es el resultado; puede reemplazar el contenido de su pch.h por este listado.
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
Abra main.cpp, y elimine las directivas using que genera la plantilla del proyecto. En su lugar, inserte el código siguiente (que nos proporciona las bibliotecas, encabezados y nombres de tipo que necesitamos). Este es el resultado; puede reemplazar el contenido de su main.cpp con este listado (también hemos eliminado el código de main en el listado siguiente, porque reemplazaremos esa función más adelante).
// 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() { }
El proyecto aún no se compilará; Una vez que haya terminado de agregar código, se le pedirá que compile y ejecute.
Implementación de la coclase y el generador de clases
En C++/WinRT, se implementan coclases y generadores de clases, derivando de la estructura base winrt::implements . Inmediatamente después de las tres directivas using mostradas anteriormente (y antes de main), pegue el siguiente código para implementar su componente activador COM para notificaciones emergentes.
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;
}
};
La implementación de la coclase anterior sigue el mismo patrón que se muestra en Author API con C++/WinRT. Por lo tanto, puede usar la misma técnica para implementar interfaces COM, así como Windows Runtime interfaces. Los componentes COM y las clases Windows Runtime exponen sus características a través de interfaces. Cada interfaz COM deriva en última instancia de la interfaz IUnknown. Windows Runtime se basa en COM; una diferencia es que las interfaces de Windows Runtime derivan en última instancia de la interfaz IInspectable (e IInspectable deriva de IUnknown).
En la coclase del código anterior, implementamos el método INotificationActivationCallback::Activate, que es la función que se llama cuando el usuario hace clic en el botón de respuesta de una notificación toast. Pero antes de que se pueda llamar a esa función, es necesario crear una instancia de la coclase y ese es el trabajo de la función IClassFactory::CreateInstance .
La coclase que acabamos de implementar se conoce como activador COM para las notificaciones y tiene su identificador de clase (CLSID) en forma del callback_guid identificador (de tipo GUID) que se ve anteriormente. Usaremos ese identificador más adelante, en forma de un acceso directo del menú Inicio y una entrada del Registro de Windows. El CLSID del activador COM y la ruta de acceso a su servidor COM asociado (que es la ruta de acceso al archivo ejecutable que estamos creando aquí) son el mecanismo por el que una notificación emergente sabe de qué clase debe crear una instancia al hacer clic en su botón de devolución de llamada (tanto si se hace clic en la notificación en el Centro de actividades como si no).
Procedimientos recomendados para implementar métodos COM
Las técnicas para el control de errores y para la administración de recursos pueden ir de la mano. Es más práctico y práctico usar excepciones que códigos de error. Y si utiliza el modismo de adquisición de recursos es inicialización (RAII), puede evitar comprobar los códigos de error de forma explícita y liberar después los recursos también de forma explícita. Estas comprobaciones explícitas hacen que tu código sea más enrevesado de lo necesario y dan a los errores muchos sitios donde ocultarse. En su lugar, use RAII y produzca o capture excepciones. De este modo, las asignaciones de recursos son seguras para excepciones y el código es sencillo.
Sin embargo, no debe permitir que las excepciones escapen a las implementaciones del método COM. Puede asegurarse de ello usando el especificador noexcept en sus métodos COM. No pasa nada si se producen excepciones en cualquier punto de la cadena de llamadas de tu método, siempre que las controles antes de que tu método termine. Si usa noexcept, pero luego permite que una excepción escape al método, la aplicación finalizará.
Adición de tipos y funciones auxiliares
En este paso, agregaremos algunos tipos auxiliares y funciones de las que hace uso el resto del código. Así que, inmediatamente antes de main, agregue lo siguiente.
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;
}
Implementación de las funciones restantes y la función de punto de entrada wmain
Elimine su función main y, en su lugar, pegue este bloque de código, que incluye el código para registrar su coclase y, a continuación, mostrar una notificación toast capaz de volver a llamar a su aplicación.
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);
}
Prueba de la aplicación de ejemplo
Compile la aplicación y, a continuación, ejecútela al menos una vez como administrador para que se ejecute el código de registro y de otra configuración. Una manera de hacerlo es ejecutar Visual Studio como administrador y, a continuación, ejecutar la aplicación desde Visual Studio. Haga clic con el botón derecho en Visual Studio en la barra de tareas para mostrar la lista de accesos directos, haga clic con el botón derecho en Visual Studio en la lista de accesos directos y, a continuación, haga clic en Ejecutar como administrador. Acepte el mensaje y abra el proyecto. Al ejecutar la aplicación, se muestra un mensaje que indica si la aplicación se ejecuta como administrador o no. Si no es así, el registro y otra configuración no se ejecutarán. Ese registro y otra configuración deben ejecutarse al menos una vez para que la aplicación funcione correctamente.
Tanto si ejecuta la aplicación como administrador como si no, presione "T" para mostrar una notificación emergente. A continuación, puede hacer clic en el botón Call back ToastAndCallback ya sea directamente desde la notificación emergente que aparece o bien desde el Centro de actividades, y se iniciará la aplicación, se creará la instancia de la coclase y se ejecutará el método INotificationActivationCallback::Activate.
Servidor COM en proceso
La aplicación de ejemplo ToastAndCallback anterior funciona como un servidor COM local (o fuera de proceso). Esto se indica mediante la clave del Registro localServer32 Windows que se usa para registrar el CLSID de su coclase. Un servidor COM local hospeda sus coclass(es) dentro de un archivo binario ejecutable (un .exe).
Como alternativa (y probablemente más probable), puede elegir hospedar las coclases dentro de una biblioteca de vínculos dinámicos (a .dll). Un servidor COM en forma de DLL se conoce como un servidor COM en el proceso, y esto se indica porque los CLSID se registran mediante la clave del Registro de Windows InprocServer32.
Creación de un proyecto de biblioteca de Dynamic-Link (DLL)
Puede comenzar la tarea de crear un servidor COM en proceso mediante la creación de un nuevo proyecto en Microsoft Visual Studio. Cree un proyecto de Visual C++>Windows Desktop>Dynamic-Link Library (DLL).
Para agregar compatibilidad con C++/WinRT al nuevo proyecto, siga los pasos descritos en Modificación de un proyecto de aplicación de escritorio de Windows para agregar compatibilidad con C++/WinRT.
Implementación de las exportaciones de servidores de coclase, generador de clases y servidores en proceso
Abra dllmain.cppy agréguelo a la lista de código que se muestra a continuación.
Si ya tiene un archivo DLL que implementa las clases de Windows Runtime de C++/WinRT, ya tendrá la función DllCanUnloadNow que se muestra a continuación. Si desea agregar coclases a ese archivo DLL, puede agregar la función DllGetClassObject .
Si no tiene código existente de la Biblioteca de plantillas de C++ de Windows Runtime (WRL) con el que desee seguir siendo compatible, puede quitar las partes de WRL del código que se muestra.
// 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();
}
}
Compatibilidad con referencias débiles
Consulta también Referencias débiles en C++/WinRT.
C++/WinRT (en particular, la plantilla de estructura base winrt::implements) implementa automáticamente IWeakReferenceSource si su tipo implementa IInspectable (o cualquier interfaz que derive de IInspectable).
Esto se debe a que IWeakReferenceSource e IWeakReference están diseñados para tipos Windows Runtime. Por lo tanto, puedes activar la compatibilidad de referencia débil para tu coclase simplemente agregando winrt::Windows::Foundation::IInspectable (o una interfaz que deriva de IInspectable) a la implementación.
struct MyCoclass : winrt::implements<MyCoclass, IMyComInterface, winrt::Windows::Foundation::IInspectable>
{
// ...
};
Implementación de una interfaz COM que deriva de otra
La derivación de interfaces es una característica del COM clásico (y está ausente intencionadamente en Windows Runtime). Aquí tienes un ejemplo de cómo es la derivación de interfaz.
IFileSystemBindData2 : public IFileSystemBindData { /* ... */ };
Si va a escribir una clase que necesite implementar, por ejemplo, IFileSystemBindData e IFileSystemBindData2, el primer paso para expresarlo es declarar que solo se implementa la interfaz derivada , como esta.
// 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()
...
El siguiente paso es asegurarse de que QueryInterface se ejecute correctamente cuando se llame (directa o indirectamente) con IID_IFileSystemBindData (la interfaz base) sobre una instancia de MyFileSystemBindData. Para ello, proporcione una especialización para la plantilla de función winrt::is_guid_of.
winrt::is_guid_of es variadic y, por tanto, puede proporcionar una lista de interfaces. Aquí se muestra cómo proporcionaría una especialización para que una comprobación de IFileSystemBindData2 también incluya una prueba para 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 especialización de winrt::is_guid_of debe ser idéntica en todos los archivos del proyecto y estar visible en el punto en que la interfaz es utilizada por la plantilla winrt::implements o winrt::delegate. Normalmente, lo colocaría en un archivo de encabezado común.