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. Anhand der Beispielanwendung wird verdeutlicht, wie du eine Popupbenachrichtigung mit einer Rückrufschaltfläche bereitstellst. Mit der Co-Klasse (über die die COM-Schnittstelle INotificationActivationCallback implementiert wird) kann die Anwendung gestartet und zurückgerufen werden, wenn der Benutzer in der Popupbenachrichtigung 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.
Öffne pch.h, und füge #include <unknwn.h> vor den Include-Elementen für C++/WinRT-Header hinzu. Hier ist das Ergebnis angegeben. Du kannst den Inhalt deiner Datei pch.h durch diese Auflistung ersetzen.
// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
Öffne main.cpp, und entferne 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.
Implementieren der Co-Klasse und Klassenfactory
In C++/WinRT implementieren Sie Coclasses und Klassenfabriken, indem Sie die Ableitung von der winrt::implements-Basisstruktur verwenden. Füge direkt nach den drei oben angegebenen using-Direktiven (und vor main) diesen Code ein, um die COM-Aktivatorkomponente deiner Popupbenachrichtigung 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 zum zugeordneten COM-Server (der Pfad zur ausführbaren Datei, die wir hier erstellen) stellen den Mechanismus dar, mit dem eine Popupbenachrichtigung die Information erhält, von welcher Klasse beim Klicken auf die Rückrufschaltfläche eine Instanz erstellt werden muss (ob im Info-Center auf die Benachrichtigung 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. Falls du die Programmiertechnik „Ressourcenbelegung ist Initialisierung“ (Resource Acquisition Is Initialization, RAII) verwendest, kannst du das explizite Überprüfen auf Fehlercodes und das anschließende Freigeben von Ressourcen vermeiden. Solche expliziten Überprüfungen machen Ihren Code unnötig komplex und bieten Fehlern jede Menge Möglichkeiten, sich zu verstecken. Verwende stattdessen RAII und Throw/Catch-Ausnahmen (Auslösen/Abfangen). 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ösche deine Funktion main, und füge stattdessen diese Codeauflistung ein, die Code zum Registrieren deiner Co-Klasse enthält. Anschließend wird ein Popup bereitgestellt, mit dem für deine Anwendung ein Rückruf durchgeführt werden 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.
Drücke unabhängig davon, ob du die Anwendung als Administrator ausführst, die Taste „T“, damit ein Popup angezeigt wird. Du kannst dann entweder direkt in der angezeigten Popupbenachrichtigung oder im Info-Center auf die Schaltfläche Call back ToastAndCallback (Rückruf ToastAndCallback) klicken. Deine Anwendung wird gestartet, die Co-Klasse wird instanziiert, und die INotificationActivationCallback::Activate-Methode wird 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 der Co-Klasse, Klassenfactory 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 eine Variadic-Funktion, sodass Sie eine Liste von Schnittstellen zur Verfügung stellen können. 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