Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
C++/WinRT kann Ihnen dabei helfen, klassische Komponenten des Component Object Model (COM)-Komponenten (oder coclasses) zu erstellen, genau wie sie Ihnen beim Erstellen von Windows-Runtime Klassen hilft. In diesem Thema wird gezeigt, wie es geht.
Wie C++/WinRT sich standardmäßig im Hinblick auf COM-Schnittstellen verhält
Die Winrt::implements-Vorlage von C++/WinRT ist die Basis, von der Ihre Laufzeitklassen und Aktivierungsfabriken direkt oder indirekt abgeleitet werden.
Standardmäßig ignoriert winrt::implements im Hintergrund klassische COM-Schnittstellen. Alle QueryInterface (QI)-Aufrufe für klassische COM-Schnittstellen schlagen daher mit E_NOINTERFACE fehl. Standardmäßig unterstützt winrt::implements nur C++/WinRT-Schnittstellen.
- winrt::IUnknown ist eine C++/WinRT-Schnittstelle, sodass winrt::implementswinrt::IUnknown-basierte Schnittstellen unterstützt.
- winrt::implements unterstützt standardmäßig nicht ::IUnknown selbst.
In einem Moment erfahren Sie, wie Sie die Fälle überwinden, die standardmäßig nicht unterstützt werden. Zunächst sehen Sie jedoch ein Codebeispiel, um zu veranschaulichen, was standardmäßig geschieht.
// 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();
}
}
Und hier ist Clientcode, um die Beispielklasse zu nutzen.
// Client.cpp
Sample sample; // Construct a Sample object via its projection.
// This next line doesn't compile yet.
sample.as<IInitializeWithWindow>()->Initialize(hwnd);
Aktivieren der klassischen COM-Unterstützung
Die gute Nachricht ist, dass Sie, damit winrt::implements klassische COM-Schnittstellen unterstützt, vor dem Einbinden von C++/WinRT-Headern lediglich die Headerdatei unknwn.h einbinden müssen.
Sie könnten dies explizit oder indirekt tun, indem Sie eine andere Headerdatei wie ole2.h einbinden. Eine empfohlene Methode besteht darin, die wil\cppwinrt.h Headerdatei einzuschließen, die Teil der Windows Implementierungsbibliotheken (WIL) ist. Die wil\cppwinrt.h-Headerdatei stellt nicht nur sicher, dass unknwn.h vor winrt/base.h eingebunden wird, sondern sorgt auch dafür, dass C++/WinRT und WIL die Ausnahmen und Fehlercodes des jeweils anderen verstehen.
Sie können dann as<> wie bei klassischen COM-Schnittstellen verwenden, und der Code im obigen Beispiel lässt sich kompilieren.
Note
Im obigen Beispiel wird der Aufruf von as<> im Client selbst dann abstürzen, wenn Sie die klassische COM-Unterstützung im Client (dem Code, der die Klasse verwendet) aktiviert haben, sofern Sie nicht auch die klassische COM-Unterstützung im Server (dem Code, der die Klasse implementiert) aktiviert haben, da das QI für IInitializeWithWindow fehlschlägt.
Eine lokale (nicht projizierte) Klasse
Eine lokale Klasse ist eine Klasse, die sowohl implementiert als auch in derselben Kompilierungseinheit (App oder andere Binärdatei) verwendet wird; und so gibt es keine Projektion dafür.
Hier ist ein Beispiel für eine lokale Klasse, die nur klassische COM-Schnittstellen implementiert.
struct LocalObject :
winrt::implements<LocalObject, IInitializeWithWindow>
{
...
};
Wenn Sie dieses Beispiel implementieren, aber keine klassische COM-Unterstützung aktivieren, schlägt der folgende Code fehl.
winrt::make<LocalObject>(); // error: ‘first_interface’: is not a member of ‘winrt::impl::interface_list<>’
Auch hier wird IInitializeWithWindow nicht als COM-Schnittstelle erkannt, sodass C++/WinRT sie ignoriert. Im Fall des LocalObject-Beispiels bedeutet das Ergebnis der Ignorieren von COM-Schnittstellen, dass LocalObject überhaupt keine Schnittstellen aufweist. Jede COM-Klasse muss jedoch mindestens eine Schnittstelle implementieren.
Ein einfaches Beispiel für eine COM-Komponente
Hier ist ein einfaches Beispiel für eine COM-Komponente, die mit C++/WinRT geschrieben wurde. Dies ist eine vollständige Auflistung einer Minianwendung, sodass Sie den Code ausprobieren können, wenn Sie ihn in ein pch.hmain.cpp neues Windows Console Application (C++/WinRT)-Projekt einfügen.
// 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());
}
Siehe auch Verwenden von COM-Komponenten mit C++/WinRT.
Ein realistischeres und interessanteres Beispiel
Im weiteren Verlauf dieses Themas wird erläutert, wie Sie ein minimales Konsolenanwendungsprojekt erstellen, das C++/WinRT verwendet, um eine einfache CoClass (COM-Komponente oder COM-Klasse) und eine Klassenfactory zu implementieren. Die Beispielanwendung zeigt, wie eine Toast-Benachrichtigung mit einer Callbackschaltfläche angezeigt wird, und die Coklasse (die die COM-Schnittstelle INotificationActivationCallback implementiert) ermöglicht, dass die Anwendung gestartet wird und einen Rückruf erhält, wenn der Benutzer in der Toast-Benachrichtigung auf diese Schaltfläche klickt.
Weitere Hintergrundinformationen zum Featurebereich für Popupbenachrichtigungen finden Sie unter "Senden einer lokalen Popupbenachrichtigung". Keines der Codebeispiele in diesem Abschnitt der Dokumentation verwendet C++/WinRT. Daher wird empfohlen, den in diesem Thema gezeigten Code zu bevorzugen.
Erstellen eines Windows Konsolenanwendungsprojekts (ToastAndCallback)
Beginnen Sie damit, ein neues Projekt in Microsoft Visual Studio zu erstellen. Erstellen Sie ein Windows Konsolenanwendungsprojekt (C++/WinRT), und nennen Sie es ToastAndCallback.
Öffnen Sie pch.h, und fügen Sie #include <unknwn.h> vor den #include-Anweisungen aller C++/WinRT-Header hinzu. Hier sehen Sie das Ergebnis. Sie können den Inhalt Ihres pch.h Eintrags durch diesen Eintrag ersetzen.
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
Öffnen Sie main.cpp und entfernen Sie die using-Direktiven, die von der Projektvorlage generiert werden. Ersetzen Sie sie durch den folgenden Code (der uns die benötigten Bibliotheken, Header und Typnamen bereitstellt). Hier sehen Sie das Ergebnis. Sie können den Inhalt Ihres main.cpp Eintrags durch diese Auflistung ersetzen (wir haben den Code auch aus main der nachstehenden Auflistung entfernt, da wir diese Funktion später ersetzen werden).
// 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() { }
Das Projekt wird noch nicht erstellt; Nachdem wir das Hinzufügen von Code abgeschlossen haben, werden Sie aufgefordert, Code zu erstellen und auszuführen.
Coclass und Klassenfactory implementieren
In C++/WinRT implementieren Sie Coclasses und Klassenfabriken, indem Sie die Ableitung von der winrt::implements-Basisstruktur verwenden. Fügen Sie unmittelbar nach den drei oben gezeigten using-Direktiven (und vor main) diesen Code ein, um Ihre COM-Aktivator-Komponente für Toastbenachrichtigungen zu implementieren.
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;
}
};
Die Implementierung der obigen Coclass folgt demselben Muster, das in Author-APIs mit C++/WinRT veranschaulicht wird. Sie können also dieselbe Technik verwenden, um COM-Schnittstellen sowie Windows-Runtime Schnittstellen zu implementieren. COM-Komponenten und Windows-Runtime Klassen machen ihre Features über Schnittstellen verfügbar. Jede COM-Schnittstelle wird letztendlich von der IUnknown-Schnittstellenschnittstelle abgeleitet. Die Windows-Runtime basiert auf COM – ein Unterschied besteht darin, dass Windows-Runtime-Schnittstellen letztendlich von der Schnittstelle IInspectable abgeleitet sind (und IInspectable von IUnknown abgeleitet ist).
In der CoClass im obigen Code implementieren wir die INotificationActivationCallback::Activate-Methode. Dies ist die Funktion, die aufgerufen wird, wenn der Benutzer auf die Callbackschaltfläche in einer Toastbenachrichtigung klickt. Bevor diese Funktion aufgerufen werden kann, muss jedoch eine Instanz der Coclass erstellt werden, und das ist der Auftrag der Funktion "IClassFactory::CreateInstance ".
Die soeben implementierte Coclass wird als COM-Aktivator für Benachrichtigungen bezeichnet, und sie verfügt über ihre Klassen-ID (CLSID) in Form des oben gezeigten callback_guid Bezeichners (vom Typ GUID). Wir verwenden diesen Bezeichner später in Form einer Startmenüverknüpfung und eines Windows Registrierungseintrags. Die CLSID des COM-Aktivators und der Pfad zu dem zugehörigen COM-Server (also der Pfad zu der ausführbaren Datei, die wir hier erstellen) bilden den Mechanismus, durch den eine Toastbenachrichtigung weiß, von welcher Klasse eine Instanz erstellt werden soll, wenn auf ihre Callbackschaltfläche geklickt wird (unabhängig davon, ob in der Benachrichtigung im Info-Center geklickt wird oder nicht).
Bewährte Methoden für die Implementierung von COM-Methoden
Techniken für die Fehlerbehandlung und für die Ressourcenverwaltung können hand in Hand gehen. Es ist praktischer, Ausnahmen als Fehlercodes zu verwenden. Und wenn Sie das RAII-Idiom (Resource Acquisition Is Initialization) verwenden, können Sie vermeiden, Fehlercodes explizit zu überprüfen und Ressourcen anschließend explizit freizugeben. Solche expliziten Überprüfungen machen Ihren Code unnötig komplex und bieten Fehlern jede Menge Möglichkeiten, sich zu verstecken. Verwenden Sie stattdessen RAII und lösen/fangen Sie Ausnahmen ab. Auf diese Weise sind Ihre Ressourcenzuordnungen ausnahmesicher, und Ihr Code ist einfach.
Sie dürfen jedoch nicht zulassen, dass Ausnahmen aus Ihren COM-Methodenimplementierungen nach außen dringen. Sie können das sicherstellen, indem Sie bei Ihren COM-Methoden den noexcept Spezifizierer verwenden. Es ist ok, dass Ausnahmen an einer beliebigen Stelle im Aufrufdiagramm Ihrer Methode ausgelöst werden, solange Sie sie behandeln, bevor die Methode beendet wird. Wenn Sie noexcept verwenden, dann aber zulassen, dass eine Ausnahme aus Ihrer Methode austritt, wird Ihre Anwendung beendet.
Hinzufügen von Hilfstypen und -funktionen
In diesem Schritt fügen wir einige Hilfstypen und -funktionen hinzu, die vom Rest des Codes verwendet werden. Fügen Sie daher unmittelbar vor main Folgendes hinzu.
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;
}
Implementieren Sie die verbleibenden Funktionen und die Einstiegspunktfunktion wmain.
Löschen Sie Ihre main-Funktion und fügen Sie stattdessen diesen Codeblock ein, der Code zum Registrieren Ihrer coclass enthält und anschließend eine Toastbenachrichtigung bereitstellt, die einen Rückruf an Ihre Anwendung auslösen kann.
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);
}
So testen Sie die Beispielanwendung
Erstellen Sie die Anwendung, und führen Sie sie dann mindestens einmal als Administrator aus, um die Registrierung und andere Setupcode auszuführen. Eine Möglichkeit hierzu ist das Ausführen Visual Studio als Administrator und anschließendes Ausführen der App über Visual Studio. Klicken Sie mit der rechten Maustaste auf Visual Studio in der Taskleiste, um die Sprungliste anzuzeigen, klicken Sie mit der rechten Maustaste auf Visual Studio in der Sprungliste, und klicken Sie dann auf "Als Administrator ausführen". Stimmen Sie der Eingabeaufforderung zu, und öffnen Sie das Projekt. Wenn Sie die Anwendung ausführen, wird eine Meldung angezeigt, die angibt, ob die Anwendung als Administrator ausgeführt wird. Ist das nicht der Fall, werden die Registrierung und andere Einrichtungsschritte nicht ausgeführt. Diese Registrierung und andere Setups müssen mindestens einmal ausgeführt werden, damit die Anwendung ordnungsgemäß funktioniert.
Unabhängig davon, ob Sie die Anwendung als Administrator ausführen oder nicht, drücken Sie die Taste „T“, um eine Toastbenachrichtigung anzuzeigen. Sie können dann entweder direkt in der eingeblendeten Toastbenachrichtigung oder im Info-Center auf die Schaltfläche Call back ToastAndCallback klicken, und Ihre Anwendung wird gestartet, die Coclass instanziiert und die Methode INotificationActivationCallback::Activate ausgeführt.
In-Process-COM-Server
Die obige Beispiel-App "ToastAndCallback " fungiert als lokaler (oder out-of-process)-COM-Server. Dies wird durch den Windows-Registrierungsschlüssel LocalServer32 angezeigt, den Sie zum Registrieren der CLSID der zugehörigen Co-Klasse verwenden. Ein lokaler COM-Server hostt seine Coclass(es) in einer ausführbaren Binärdatei (ein .exe).
Alternativ (und wahrscheinlich wahrscheinlicher) können Sie Ihre Coclass(es) in einer Dynamic Link Library (a .dll) hosten. Ein COM-Server in Form einer DLL wird als prozessinterner COM-Server bezeichnet und wird durch CLSIDs angegeben, die mithilfe des InprocServer32 Windows Registrierungsschlüssels registriert werden.
Erstellen eines Dynamic-Link Library (DLL)-Projekts
Sie können mit der Erstellung eines IN-Process COM-Servers beginnen, indem Sie ein neues Projekt in Microsoft Visual Studio erstellen. Erstellen Sie ein Visual C++>Windows Desktop>Dynamic-Link Library (DLL)-Projekt.
Wenn Sie dem neuen Projekt C++/WinRT-Unterstützung hinzufügen möchten, führen Sie die unter "Ändern eines Windows Desktopanwendungsprojekts" beschriebenen Schritte aus, um C++/WinRT-Unterstützung hinzuzufügen.
Implementieren Sie die Coclass-, Klassenfabrik- und In-Process-Serverexporte
Öffnen Sie dllmain.cpp, und fügen Sie den unten gezeigten Code hinzu.
Wenn Sie bereits über eine DLL verfügen, die C++/WinRT-Windows-Runtime Klassen implementiert, ist die DllCanUnloadNow-Funktion bereits unten dargestellt. Wenn Sie dieser DLL Coclasses hinzufügen möchten, können Sie die DllGetClassObject-Funktion hinzufügen.
Wenn sie nicht über Windows-Runtime C++-Vorlagenbibliothekscode (WRL) verfügen, mit dem Sie kompatibel bleiben möchten, können Sie die WRL-Teile aus dem angezeigten Code entfernen.
// 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();
}
}
Unterstützung für schwache Referenzen
Siehe auch schwache Verweise in C++/WinRT.
C++/WinRT (insbesondere die winrt::implements base struct template) implementiert IWeakReferenceSource für Sie, wenn Ihr Typ IInspectable implementiert (oder eine beliebige Schnittstelle, die von IInspectable abgeleitet wird).
Dies liegt daran, dass IWeakReferenceSource und IWeakReference für Windows-Runtime Typen entwickelt wurden. Sie können daher die Unterstützung für schwache Verweise für Ihre Coclass einfach aktivieren, indem Sie "winrt::Windows::Foundation::IInspectable" (oder eine Schnittstelle, die von IInspectable abgeleitet wird) zu Ihrer Implementierung hinzufügen.
struct MyCoclass : winrt::implements<MyCoclass, IMyComInterface, winrt::Windows::Foundation::IInspectable>
{
// ...
};
Implementieren einer COM-Schnittstelle, die von einer anderen abgeleitet wird
Die Schnittstellenableitung ist ein Merkmal des klassischen COM (und fehlt bewusst in der Windows-Runtime). Hier ist ein Beispiel dafür, wie die Schnittstellenableitung aussieht.
IFileSystemBindData2 : public IFileSystemBindData { /* ... */ };
Wenn Sie eine Klasse schreiben, die implementiert werden muss, z. B. sowohl IFileSystemBindData als auch IFileSystemBindData2, dann ist der erste Schritt beim Ausdrücken, dass Sie nur die abgeleitete Schnittstelle implementieren, wie hier gezeigt.
// 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()
...
Der nächste Schritt besteht darin, sicherzustellen, dass QueryInterface erfolgreich ist, wenn es für IID_IFileSystemBindData (die Basisschnittstelle ) für eine Instanz von MyFileSystemBindData aufgerufen wird (direkt oder indirekt). Dazu stellen Sie eine Spezialisierung für die Winrt::is_guid_of-Funktionsvorlage bereit.
winrt::is_guid_of ist variadisch, daher können Sie eine Liste von Schnittstellen übergeben. Hier erfahren Sie, wie Sie eine Spezialisierung bereitstellen, damit eine Prüfung auf IFileSystemBindData2 auch einen Test für IFileSystemBindData enthält.
// 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.
}
Die Spezialisierung von winrt::is_guid_of muss in allen Dateien des Projekts identisch sein und an der Stelle sichtbar sein, an der die Schnittstelle durch die winrt::implements- oder winrt::delegate-Vorlage verwendet wird. In der Regel fügen Sie sie in eine allgemeine Headerdatei ein.
Wichtige APIs
Zugehörige Themen
Windows developer