Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
C++/WinRT pode ajudar-te a criar componentes clássicos do Component Object Model (COM) (ou coclasses), tal como te ajuda a criar classes Windows Runtime. Este tópico mostra como.
Como o C++/WinRT se comporta, por defeito, em relação às interfaces COM
O template winrt: :implements do C++/WinRT é a base a partir da qual as tuas classes de runtime e fábricas de ativação derivam direta ou indiretamente.
Por defeito, o winrt::implements ignora silenciosamente as interfaces clássicas do COM. Qualquer chamada QueryInterface (QI) para interfaces COM clássicas falhará, consequentemente, com E_NOINTERFACE. Por defeito, o winrt::implements suporta apenas interfaces C++/WinRT.
- winrt::IUnknown é uma interface C++/WinRT, por isso o winrt::implements suporta interfaces baseadas em winrt::IUnknown.
- winrt::implements não suporta o próprio ::IUnknown, por predefinição.
Já de seguida vais ver como contornar os casos que não são suportados por defeito. Mas primeiro, aqui está um exemplo de código para ilustrar o que acontece por defeito.
// 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();
}
}
E aqui está o código do cliente para consumir a classe de exemplo .
// Client.cpp
Sample sample; // Construct a Sample object via its projection.
// This next line doesn't compile yet.
sample.as<IInitializeWithWindow>()->Initialize(hwnd);
Ativação do suporte COM clássico
A boa notícia é que tudo o que é preciso para que o winrt::implements suporte interfaces COM clássicas é incluir o unknwn.h ficheiro de cabeçalho antes de incluir qualquer cabeçalho C++/WinRT.
Pode fazer isso explicitamente, ou indiretamente, incluindo outro ficheiro de cabeçalho como ole2.h. Um método recomendado é incluir o wil\cppwinrt.h ficheiro cabeçalho, que faz parte das Bibliotecas de Implementação do Windows (WIL). O ficheiro de cabeçalho wil\cppwinrt.h não só garante que unknwn.h é incluído antes de winrt/base.h, como também faz com que o C++/WinRT e o WIL compreendam mutuamente as exceções e os códigos de erro.
Podes então usar as<> para interfaces COM clássicas, e o código do exemplo acima será compilado.
Note
No exemplo acima, mesmo depois de ativar o suporte classic COM no cliente (o código que consome a classe), se não tiver também ativado o suporte classic COM no servidor (o código que implementa a classe), então a chamada ao AS<> no cliente vai crashar porque o QI do IInitializeWithWindow falha.
Uma classe local (não projetada)
Uma classe local é aquela que é tanto implementada como consumida na mesma unidade de compilação (app, ou outro binário); E por isso não há projeção para isso.
Aqui está um exemplo de uma classe local que implementa apenas interfaces COM clássicas.
struct LocalObject :
winrt::implements<LocalObject, IInitializeWithWindow>
{
...
};
Se implementares esse exemplo, mas não ativares o suporte clássico COM, então o código seguinte falha.
winrt::make<LocalObject>(); // error: ‘first_interface’: is not a member of ‘winrt::impl::interface_list<>’
Mais uma vez, o IInitializeWithWindow não é reconhecido como interface COM, por isso o C++/WinRT ignora-o. No caso do exemplo do LocalObjecto , o resultado de ignorar interfaces COM significa que o LocalObjeto não tem interfaces nenhumas. Mas cada classe COM deve implementar pelo menos uma interface.
Um exemplo simples de um componente COM
Aqui está um exemplo simples de um componente COM escrito usando C++/WinRT. Esta é uma listagem completa de uma miniaplicação, pelo que pode testar o código se o colar em pch.h e main.cpp de um novo projeto Aplicação de Consola do 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());
}
Veja também Consumo de componentes COM com C++/WinRT.
Um exemplo mais realista e interessante
O restante deste tópico percorre a criação de um projeto de aplicação de consola minimalista que use C++/WinRT para implementar uma coclasse básica (componente COM, ou classe COM) e uma fábrica de classes. A aplicação de exemplo mostra como entregar uma notificação de toast com um botão de callback, e a coclasse (que implementa a interface INotificationActivationCallback COM) permite que a aplicação seja lançada e chamada de volta quando o utilizador clica nesse botão no toast.
Pode encontrar mais informações sobre a funcionalidade de notificações do tipo toast em Enviar uma notificação local do tipo toast. Nenhum dos exemplos de código nessa secção da documentação usa C++/WinRT, por isso recomendamos que prefira o código apresentado neste tópico.
Criar um projeto de Aplicação para a Consola do Windows (ToastAndCallback)
Comece criando um novo projeto no Microsoft Visual Studio. Crie um projeto de Aplicação de Consola Windows (C++/WinRT) e chame-lhe ToastAndCallback.
Abra pch.h, e adicione #include <unknwn.h> antes dos includes para quaisquer cabeçalhos C++/WinRT. Aqui está o resultado; Pode substituir o conteúdo do seu pch.h por esta lista.
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
Abre main.cpp, e remove as diretivas de uso que o modelo do projeto gera. Em vez disso, insira o seguinte código (que nos fornece as bibliotecas, os cabeçalhos e os nomes de tipos de que precisamos). Aqui está o resultado; Pode substituir o conteúdo do seu main.cpp por esta listagem (também removemos o código da main lista abaixo, porque iremos substituir essa função mais tarde).
// 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() { }
O projeto ainda não vai ser construído; Depois de terminarmos de adicionar código, ser-se-á solicitado a compilar e executar.
Implementar a co-classe e a classe de fábrica
Em C++/WinRT, implementa coclasses e fábricas de classes derivando da estrutura base winrt::implements. Imediatamente após as três diretivas de uso mostradas acima (e antes main), cole este código para implementar o seu componente ativador COM de notificação 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;
}
};
A implementação da coclasse acima segue o mesmo padrão demonstrado nas APIs de Autor com C++/WinRT. Assim, pode usar a mesma técnica para implementar interfaces COM, bem como interfaces de Windows Runtime. Os componentes COM e as classes Windows Runtime expõem as suas funcionalidades através de interfaces. Cada interface COM deriva, em última instância, da interface IUnknown. O Windows Runtime baseia-se no COM — sendo uma das diferenças que as interfaces do Windows Runtime derivam, em última instância, da interface IInspectable (e IInspectable deriva de IUnknown).
Na coclasse no código acima, implementamos o método INotificationActivationCallback::Activate, que é a função invocada quando o utilizador clica no botão de retorno de chamada numa notificação do tipo toast. Mas antes de essa função poder ser chamada, é necessário criar uma instância da coclasse, e esse é o trabalho da função IClassFactory::CreateInstance .
A coclasse que acabámos de implementar é conhecida como ativador COM para notificações, e tem o seu id de classe (CLSID) na forma do callback_guid identificador (do tipo GUID) que vê acima. Vamos usar esse identificador mais tarde, sob a forma de um atalho no menu Iniciar e uma entrada no Registo do Windows. O CLSID do ativador COM e o caminho para o respetivo servidor COM associado (ou seja, o caminho para o executável que estamos aqui a compilar) constituem o mecanismo pelo qual uma notificação de alerta sabe de que classe deve criar uma instância quando se clica no respetivo botão de retorno (quer se clique na notificação no Centro de Ações, quer não).
Boas práticas para implementar métodos COM
Técnicas para o tratamento de erros e para a gestão de recursos podem andar de mãos dadas. É mais conveniente e prático usar exceções do que códigos de erro. E, se utilizar o padrão RAII (aquisição de recursos na inicialização), então pode evitar verificar explicitamente os códigos de erro e libertar explicitamente os recursos. Verificações tão explícitas tornam o seu código mais complicado do que o necessário e dão muitos lugares para se esconder aos bugs. Em vez disso, utiliza o RAII e lança/captura exceções. Assim, as tuas alocações de recursos são seguras contra exceções e o teu código é simples.
No entanto, não deve permitir que exceções se propaguem para fora das implementações dos seus métodos COM. Pode garantir isso usando o noexcept especificador nos seus métodos COM. É aceitável que sejam lançadas exceções em qualquer ponto do grafo de chamadas do teu método, desde que as trates antes de o teu método terminar. Se usar noexcept, mas depois permitir que uma exceção se propague para fora do seu método, a sua aplicação terminará.
Adicionar tipos e funções de auxiliares
Neste passo, vamos adicionar alguns tipos e funções auxiliares que o resto do código utiliza. Portanto, imediatamente antes main, adicione o seguinte.
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;
}
Implementar as funções restantes e a função de entrada wmain
Elimine a sua função main e, no seu lugar, cole esta listagem de código, que inclui o código para registar a sua coclass e, em seguida, apresentar uma notificação toast capaz de invocar novamente a sua aplicação.
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);
}
Como testar a aplicação de exemplo
Constrói a aplicação e depois executa-a pelo menos uma vez como administrador para fazer com que o código de registo e outras configurações corram. Uma forma de o fazer é correr o Visual Studio como administrador e depois executar a aplicação a partir do Visual Studio. Clique com o botão direito no Visual Studio na barra de tarefas para mostrar a lista de saltos, clique com o botão direito no Visual Studio na lista de saltos e depois clique em Executar como administrador. Concorda com o prompt e depois abre o projeto. Quando executa a aplicação, é exibida uma mensagem indicando se a aplicação está a correr como administrador. Se isso não acontecer, o registo e a restante configuração não serão executados. Esse registo e outra configuração têm de ser executados pelo menos uma vez para que a aplicação funcione corretamente.
Quer estejas ou não a usar a aplicação como administrador, carrega em 'T' para fazer aparecer um brinde. Pode então clicar no botão Chamar de volta ToastAndCallback , seja diretamente a partir da notificação de toast que aparece, ou do Centro de Ação, e a sua aplicação será lançada, a coclasse instanciada e o método INotificationActivationCallback::Activate executado.
Servidor COM em processo
A aplicação de exemplo ToastAndCallback acima funciona como um servidor COM local (ou fora de processo). Isto é indicado pela chave do Registo do Windows LocalServer32 que utiliza para registar o CLSID da sua coclasse. Um servidor COM local aloja a(s) sua(s) coclasse(s) dentro de um binário executável (um .exe).
Alternativamente (e possivelmente mais provável), pode optar por hospedar a(s) sua(s) coclasse(s) dentro de uma biblioteca de ligação dinâmica (a .dll). Um servidor COM sob a forma de DLL é conhecido como servidor COM em processo, e é indicado pelo facto de CLSIDs serem registados através da chave do Registo do Windows InprocServer32.
Criar um projeto de Biblioteca Dynamic-Link (DLL)
Pode começar a tarefa de criar um servidor COM em processo criando um novo projeto em Microsoft Visual Studio. Crie um projeto Visual C++>Windows Desktop>Dynamic-Link Library (DLL).
Para adicionar suporte a C++/WinRT ao novo projeto, siga os passos descritos em Modificar um projeto de aplicação Windows Desktop para adicionar suporte a C++/WinRT.
Implementar a coclasse, a fábrica de classes e as exportações de servidores em processo
Abra dllmain.cpp, e adicione a lista de códigos mostrada abaixo.
Se já tens uma DLL que implementa classes Windows Runtime C++/WinRT, então já terás a função DllCanUnloadNow mostrada abaixo. Se quiseres adicionar coclasses a essa DLL, podes adicionar a função DllGetClassObject .
Se não tiveres código existente da Windows Runtime C++ Template Library (WRL) com o qual queiras ser compatível, podes remover as partes WRL do código mostrado.
// 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();
}
}
Suporte para referências fracas
Veja também Referências fracas em C++/WinRT.
C++/WinRT (especificamente, o modelo de estrutura base winrt::implements) implementa IWeakReferenceSource por si, se o seu tipo implementar IInspectable (ou qualquer interface que deriva de IInspectable).
Isto deve-se ao facto de o IWeakReferenceSource e o IWeakReference serem concebidos para tipos de Windows Runtime. Assim, podes ativar o suporte fraco de referência para a tua coclasse simplesmente adicionando winrt::Windows::Foundation::IInspectable (ou uma interface que derive de IInspectable) à tua implementação.
struct MyCoclass : winrt::implements<MyCoclass, IMyComInterface, winrt::Windows::Foundation::IInspectable>
{
// ...
};
Implementar uma interface COM que derive de outra
A derivação de interfaces é uma funcionalidade do COM clássico (e por acaso está ausente, intencionalmente, do Windows Runtime). Eis um exemplo de como se apresenta a derivação de interfaces.
IFileSystemBindData2 : public IFileSystemBindData { /* ... */ };
Se estiveres a escrever uma classe que precisa de implementar, por exemplo, tanto IFileSystemBindData como IFileSystemBindData2, então o primeiro passo para expressar isso é declarar que implementas apenas a interface derivada , assim.
// 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()
...
O passo seguinte é garantir que QueryInterface é bem-sucedido quando é chamado (direta ou indiretamente) para IID_IFileSystemBindData (a interface base) numa instância de MyFileSystemBindData. Faz isso fornecendo uma especialização para o modelo de função winrt::is_guid_of.
winrt::is_guid_of é variádica, pelo que lhe pode passar uma lista de interfaces. Aqui está como se fornece uma especialização para que uma verificação para IFileSystemBindData2 inclua também um teste 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.
}
A especialização de winrt::is_guid_of deve ser idêntica em todos os ficheiros do projeto, e visível no ponto em que a interface é utilizada pelo modelo winrt::implements ou winrt::delegate. Normalmente, coloca-se num ficheiro de cabeçalho comum.
APIs importantes
Tópicos relacionados
Windows developer