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.
In diesem Thema wird gezeigt, wie Sie C++/WinRT--APIs nutzen, unabhängig davon, ob sie Teil von Windows sind, von einem Drittanbieter implementiert oder von Ihnen selbst implementiert werden.
Von Bedeutung
Damit die Codebeispiele in diesem Thema kurz und leicht auszuprobieren sind, können Sie sie reproduzieren, indem Sie ein neues Windows Console Application (C++/WinRT) Projekt erstellen und den Code durch Kopieren und Einfügen reproduzieren. Sie können jedoch keine beliebige benutzerdefinierte Drittanbieter-Windows-Runtime-Typen aus einer so entpackten App verwenden. Sie können nur Windows-Typen auf diese Weise nutzen.
Um benutzerdefinierte (Drittanbieter-) Windows-Runtime-Typen aus einer Konsolen-App zu nutzen, müssen Sie der App eine Paketidentität zuordnen, damit die Registrierung der verwendeten benutzerdefinierten Typen aufgelöst werden kann. Weitere Informationen finden Sie unter Windows Application Packaging Project.
Erstellen Sie alternativ ein neues Projekt mithilfe der Projektvorlagen: Blank App (C++/WinRT), Core App (C++/WinRT)oder Windows-Runtime-Komponente (C++/WinRT). Diese App-Typen verfügen bereits über eine Paketidentität.
Wenn sich die API in einem Windows-Namespace befindet
Dies ist der häufigste Fall, in dem Sie eine Windows-Runtime-API verwenden. Für jeden Typ in einem Windows-Namespace, der in den Metadaten definiert ist, definiert C++/WinRT ein C++-freundliches Äquivalent (den projizierten Typ). Ein projizierter Typ hat denselben vollqualifizierten Namen wie der Windows-Typ, ist jedoch mit C++-Syntax im C++-Namespace winrt platziert. Beispielsweise wird Windows::Foundation::Uri als winrt::Windows::Foundation::Uriin C++/WinRT projiziert.
Hier ist ein einfaches Codebeispiel. Wenn Sie die folgenden Codebeispiele direkt in die Hauptquellcodedatei eines Windows-Konsolenanwendungs- (C++/WinRT)-Projekts kopieren möchten, dann legen Sie zuerst in den Projekteigenschaften Nicht vorkompilierte Header verwenden fest.
// main.cpp
#include <winrt/Windows.Foundation.h>
using namespace winrt;
using namespace Windows::Foundation;
int main()
{
winrt::init_apartment();
Uri contosoUri{ L"http://www.contoso.com" };
Uri combinedUri = contosoUri.CombineUri(L"products");
}
Der enthaltene Header winrt/Windows.Foundation.h ist Teil des SDK, das sich im Ordner %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt\befindet. Die Header in diesem Ordner enthalten Windows-Namespacetypen, die in C++/WinRT projiziert werden. In diesem Beispiel enthält winrt/Windows.Foundation.hwinrt::Windows::Foundation::Uri. Dies ist der projizierte Typ für die Laufzeitklasse Windows::Foundation::Uri.
Tipp
Wenn Sie einen Typ aus einem Windows-Namespace verwenden möchten, schließen Sie den C++/WinRT-Header ein, der diesem Namespace entspricht. Die using namespace Direktiven sind optional, aber praktisch.
Im obigen Codebeispiel wird nach der Initialisierung von C++/WinRT ein Wert des winrt::Windows::Foundation::Uri projizierten Typs über einen seiner öffentlich dokumentierten Konstruktoren (Uri(String), in diesem Beispiel) zugewiesen. In diesem häufigsten Anwendungsfall ist das normalerweise alles, was Sie tun müssen. Sobald Sie über einen projizierten C++/WinRT-Typwert verfügen, können Sie ihn so behandeln, als wäre es eine Instanz des tatsächlichen Windows-Runtime-Typs, da er alle gleichen Member enthält.
Tatsächlich ist dieser projizierte Wert ein Stellvertreter; im Wesentlichen bedeutet es, dass er nur ein intelligenter Zeiger auf ein hinterlegtes Objekt ist. Der Konstruktor(n) des projizierten Werts ruft RoActivateInstance auf, um eine Instanz der zugrunde stehenden Windows-Runtime-Klasse (Windows.Foundation.Uri, in diesem Fall) zu erstellen und die Standardschnittstelle dieses Objekts innerhalb des neuen projizierten Werts zu speichern. Wie unten dargestellt, delegieren Ihre Aufrufe an die Mitglieder des projizierten Werts tatsächlich über den intelligenten Zeiger an das Sicherungsobjekt; An dieser Stelle treten Zustandsänderungen auf.
Wenn der contosoUri-Wert aus dem Gültigkeitsbereich fällt, wird er aufgelöst und gibt seinen Verweis auf die Standardschnittstelle frei. Wenn dieser Verweis der letzte Verweis auf das zugrunde liegende Windows-Runtime-Windows.Foundation.Uri-Objekt ist, wird auch das unterstützende Objekt zerstört.
Tipp
Ein projizierter Typ ist ein Wrapper für einen Windows Runtime-Typ, der zur Nutzung seiner APIs dient. Beispielsweise ist eine projizierte Schnittstelle ein Wrapper über eine Windows-Runtime-Schnittstelle.
C++/WinRT-Projektionsheader
Um Windows-Namespace-APIs aus C++/WinRT zu nutzen, fügen Sie Header aus dem Ordner %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt ein. Sie müssen die Kopfzeilen enthalten, die den einzelnen verwendeten Namespaces entsprechen.
Zum Beispiel befinden sich die entsprechenden C++/WinRT-Typdefinitionen für den Windows::Security::Cryptography::Certificates-Namespace in winrt/Windows.Security.Cryptography.Certificates.h. Mit diesem Header erhalten Sie Zugriff auf alle Typen im Windows::Security::Cryptography::Certificates-Namespace .
Manchmal enthält ein Namespaceheader Teile verwandter Namespaceheader, sie sollten sich jedoch nicht auf dieses Implementierungsdetails verlassen. Fügen Sie explizit die Header für die verwendeten Namespaces ein.
Die Certificate::GetCertificateBlob-Methode gibt beispielsweise eine Windows::Storage::Streams::IBuffer-Schnittstelle zurück.
Bevor Sie die Methode "Certificate::GetCertificateBlob " aufrufen, müssen Sie die winrt/Windows.Storage.Streams.h Namespaceheaderdatei einschließen, um sicherzustellen, dass Sie die zurückgegebenen Windows::Storage::Streams::IBuffer empfangen und ausführen können.
Das Vergessen, die erforderlichen Namespace-Header einzuschließen, bevor Sie Typen in diesem Namespace verwenden, ist eine häufige Ursache für Buildfehler.
Zugreifen auf Elemente über das Objekt, über eine Schnittstelle oder über die ABI
Bei der C++/WinRT-Projektion besteht die Laufzeitdarstellung einer Windows-Runtime-Klasse ausschließlich aus den zugrunde liegenden ABI-Schnittstellen. Zu Ihrer Bequemlichkeit können Sie jedoch Code für Klassen so schreiben, wie es der Autor beabsichtigt hat. Sie können z. B. die ToString--Methode eines Uri- aufrufen, als wäre dies eine Methode der Klasse. Tatsächlich ist es jedoch hinter den Kulissen eine Methode der separaten IStringable Schnittstelle.
WINRT_ASSERT ist eine Makrodefinition und expandiert zu _ASSERTE.
Uri contosoUri{ L"http://www.contoso.com" };
WINRT_ASSERT(contosoUri.ToString() == L"http://www.contoso.com/"); // QueryInterface is called at this point.
Dieser Komfort wird über eine Abfrage für die entsprechende Schnittstelle erreicht. Aber Sie haben immer die Kontrolle. Sie können sich dafür entscheiden, etwas von dieser Bequemlichkeit für ein wenig mehr Performance zu opfern, indem Sie die IStringable-Schnittstelle selbst abrufen und sie direkt verwenden. Im folgenden Codebeispiel erhalten Sie zur Laufzeit einen tatsächlichen IStringable-Schnittstellenzeiger (über eine einmalige Abfrage). Danach ist Ihr Aufruf von ToString direkt und verhindert einen weiteren Aufruf von QueryInterface-.
...
IStringable stringable = contosoUri; // One-off QueryInterface.
WINRT_ASSERT(stringable.ToString() == L"http://www.contoso.com/");
Sie können diese Technik auswählen, wenn Sie wissen, dass Sie mehrere Methoden auf derselben Schnittstelle aufrufen.
Wenn Sie übrigens auf ABI-Ebene auf Mitglieder zugreifen möchten, können Sie dies tun. Im folgenden Codebeispiel wird gezeigt, wie es funktioniert, und es gibt weitere Details sowie Beispiele für Codes in Interop zwischen C++/WinRT und dem ABI.
#include <Windows.Foundation.h>
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
using namespace winrt::Windows::Foundation;
int main()
{
winrt::init_apartment();
Uri contosoUri{ L"http://www.contoso.com" };
int port{ contosoUri.Port() }; // Access the Port "property" accessor via C++/WinRT.
winrt::com_ptr<ABI::Windows::Foundation::IUriRuntimeClass> abiUri{
contosoUri.as<ABI::Windows::Foundation::IUriRuntimeClass>() };
HRESULT hr = abiUri->get_Port(&port); // Access the get_Port ABI function.
}
Verzögerte Initialisierung
In C++/WinRT verfügt jeder projizierte Typ über einen speziellen C++/WinRT-std::nullptr_t-Konstruktor. Mit Ausnahme dieses einen führen alle projizierten Konstruktoren einschließlich des Standardkonstruktors dazu, dass ein zugrunde liegendes Windows-Runtime-Objekt erstellt wird, und geben Ihnen einen intelligenten Zeiger darauf. Daher gilt diese Regel überall dort, wo der Standardkonstruktor verwendet wird, z. B. nicht initialisierte lokale Variablen, nicht initialisierte globale Variablen und nicht initialisierte Membervariablen.
Wenn Sie andererseits eine Variable eines projizierten Typs erstellen möchten, ohne dass sie wiederum ein zugrunde stehenden Windows-Runtime-Objekt erstellt (sodass Sie diese Arbeit bis zu einem späteren Zeitpunkt verzögern können), können Sie dies tun. Deklarieren Sie die Variable oder das Feld mit diesem speziellen C++/WinRT-std::nullptr_t-Konstruktor (der die C++/WinRT-Projektion in jede Laufzeitklasse einfügt). Wir verwenden diesen speziellen Konstruktor mit m_gamerPicBuffer im folgenden Codebeispiel.
#include <winrt/Windows.Storage.Streams.h>
using namespace winrt::Windows::Storage::Streams;
#define MAX_IMAGE_SIZE 1024
struct Sample
{
void DelayedInit()
{
// Allocate the actual buffer.
m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
}
private:
Buffer m_gamerPicBuffer{ nullptr };
};
int main()
{
winrt::init_apartment();
Sample s;
// ...
s.DelayedInit();
}
Alle Konstruktoren des projizierten Typs mit Ausnahme des std::nullptr_t-Konstruktors führen dazu, dass ein zugrunde liegendes Windows-Runtime-Objekt erstellt wird. Der std::nullptr_t-Konstruktor ist im Wesentlichen ein no-op. Es erwartet, dass das projizierte Objekt zu einem späteren Zeitpunkt initialisiert wird. Unabhängig davon, ob eine Laufzeitklasse über einen Standardkonstruktor verfügt oder nicht, können Sie diese Technik für eine effiziente verzögerte Initialisierung verwenden.
Diese Überlegung wirkt sich auf andere Orte aus, an denen Sie den Standardkonstruktor aufrufen, z. B. in Vektoren und Karten. Betrachten Sie dieses Codebeispiel, für das Sie ein Leere App (C++/WinRT) Projekt benötigen.
std::map<int, TextBlock> lookup;
lookup[2] = value;
Die Aufgabe erstellt einen neuen TextBlockund überschreibt ihn dann sofort mit value. Hier ist das Mittel.
std::map<int, TextBlock> lookup;
lookup.insert_or_assign(2, value);
Siehe auch Wie sich der Standardkonstruktor auf Sammlungen auswirkt.
Initialisierung nicht versehentlich verzögern
Achten Sie darauf, dass Sie nicht aus Versehen den std::nullptr_t-Konstruktor aufrufen. Die Konfliktlösung des Compilers bevorzugt es gegenüber den Fabrik-Konstruktoren. Betrachten Sie beispielsweise diese beiden Laufzeitklassendefinitionen.
// GiftBox.idl
runtimeclass GiftBox
{
GiftBox();
}
// Gift.idl
runtimeclass Gift
{
Gift(GiftBox giftBox); // You can create a gift inside a box.
}
Angenommen, wir möchten ein Geschenk erstellen, das sich nicht in einer Box befindet (ein Geschenk, das mit einer nicht initialisierten GiftBoxerstellt wurde). Sehen wir uns zunächst den falschen Weg an, dies zu tun. Wir wissen, dass es einen Gift-Konstruktor gibt, der ein GiftBox-akzeptiert. Wenn wir jedoch versucht sind, eine NULL-GiftBox- zu übergeben (aufrufen des Gift--Konstruktors über die einheitliche Initialisierung, wie unten beschrieben), wird das gewünschte Ergebnis nicht.
// These are *not* what you intended. Doing it in one of these two ways
// actually *doesn't* create the intended backing Windows Runtime Gift object;
// only an empty smart pointer.
Gift gift{ nullptr };
auto gift{ Gift(nullptr) };
Was Sie hier bekommen, ist ein nicht initialisiertes Geschenk. Sie erhalten kein Geschenk mit einer nicht initialisierten Geschenkbox. Dies ist die richtige Vorgehensweise.
// Doing it in one of these two ways creates an initialized
// Gift with an uninitialized GiftBox.
Gift gift{ GiftBox{ nullptr } };
auto gift{ Gift(GiftBox{ nullptr }) };
Im falschen Beispiel wird das Übergeben eines nullptr-Literals zugunsten des verzögerten Initialisierungskonstruktors aufgelöst. Zur Lösung zugunsten des Factory-Konstruktors muss der Typ des Parameters ein GiftBoxsein. Sie haben weiterhin die Möglichkeit, eine explizit verzögerte Initialisierung GiftBox-zu übergeben, wie im richtigen Beispiel gezeigt.
Dieses nächste Beispiel ist auch richtig, da der Parameter den Typ GiftBox aufweist und nicht std::nullptr_t.
GiftBox giftBox{ nullptr };
Gift gift{ giftBox }; // Calls factory constructor.
Nur wenn Sie ein nullptr-literal übergeben, entsteht die Mehrdeutigkeit.
Kopieren Sie das Konstrukt nicht versehentlich.
Dieser Hinweis ähnelt demjenigen, der im Abschnitt Nicht versehentlich initialisieren oben beschrieben wird.
Zusätzlich zum Verzögerungsinitialisierungskonstruktor fügt die C++/WinRT-Projektion auch einen Kopierkonstruktor in jede Laufzeitklasse ein. Es ist ein einzelparameterbasierter Konstruktor, der denselben Typ wie das zu erstellende Objekt akzeptiert. Der resultierende intelligente Zeiger verweist auf dasselbe zugrunde liegende Windows-Runtime-Objekt, auf das durch den Konstruktorparameter verwiesen wird. Das Ergebnis sind zwei Smart-Pointer-Objekte, die auf dasselbe unterliegende Objekt zeigen.
Hier ist eine Laufzeitklassendefinition, die in den Codebeispielen verwendet wird.
// GiftBox.idl
runtimeclass GiftBox
{
GiftBox(GiftBox biggerBox); // You can place a box inside a bigger box.
}
Angenommen, wir möchten eine Geschenkbox- in einer größeren Geschenkbox-konstruieren.
GiftBox bigBox{ ... };
// These are *not* what you intended. Doing it in one of these two ways
// copies bigBox's backing-object-pointer into smallBox.
// The result is that smallBox == bigBox.
GiftBox smallBox{ bigBox };
auto smallBox{ GiftBox(bigBox) };
Die korrekte Vorgehensweise besteht darin, die Aktivierungsfabrik explizit aufzurufen.
GiftBox bigBox{ ... };
// These two ways call the activation factory explicitly.
GiftBox smallBox{
winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
auto smallBox{
winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
Wenn die API in einer Komponente für Windows-Runtime implementiert ist
Dieser Abschnitt gilt unabhängig davon, ob Sie die Komponente selbst erstellt haben oder ob sie von einem Anbieter stammt.
Hinweis
Informationen zum Installieren und Verwenden der C++/WinRT Visual Studio Extension (VSIX) und des NuGet-Pakets (die zusammen Projektvorlage und Buildunterstützung bereitstellen), finden Sie unter Visual Studio-Unterstützung für C++/WinRT.
Verweisen Sie in Ihrem Anwendungsprojekt auf die Windows-Runtime-Metadatendatei (.winmd) der Windows-Runtime-Komponente und führen Sie das Build aus. Während des Builds generiert das cppwinrt.exe-Tool eine C++-Standardbibliothek, die die API-Oberfläche für die Komponente vollständig beschreibt ( oder Projekte). Mit anderen Worten, die generierte Bibliothek enthält die projizierten Typen für die Komponente.
Ebenso wie bei einem Windows-Namespacetyp fügen Sie dann einen Header ein und erstellen den projizierten Typ über einen seiner Konstruktoren. Der Startcode Ihres Anwendungsprojekts registriert die Laufzeitklasse, und der Konstruktor des projizierten Typs ruft RoActivateInstance auf, um die Laufzeitklasse aus der referenzierten Komponente zu aktivieren.
#include <winrt/ThermometerWRC.h>
struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
ThermometerWRC::Thermometer thermometer;
...
};
Weitere Details, Code und eine exemplarische Vorgehensweise für die Verwendung von APIs, die in einer Komponente für Windows-Runtime implementiert sind, finden Sie unter Komponenten für Windows-Runtime mit C++/WinRT und Author-Ereignisse in C++/WinRT.
Wenn die API im verbrauchenden Projekt implementiert ist
Das Codebeispiel in diesem Abschnitt stammt aus dem Thema XAML-Steuerelemente; anbinden an eine C++/WinRT-Eigenschaft. In diesem Thema finden Sie weitere Details, Code und eine exemplarische Vorgehensweise zum Verwenden einer Laufzeitklasse, die in demselben Projekt implementiert ist, das sie verwendet.
Ein Typ, der von der XAML-Benutzeroberfläche verwendet wird, muss eine Laufzeitklasse sein, auch wenn er sich im selben Projekt wie der XAML-Code befindet. Für dieses Szenario generieren Sie einen projizierten Typ aus den Windows-Runtime-Metadaten der Laufzeitklasse (.winmd). Auch hier fügen Sie eine Kopfzeile ein, haben dann aber die Wahl zwischen den Methoden C++/WinRT, Version 1.0 oder Version 2.0, die Instanz der Laufzeitklasse zu erstellen. Die Version 1.0-Methode verwendet winrt::make; die Version 2.0-Methode wird als einheitliche Konstruktionbezeichnet. Sehen wir uns die einzelnen nacheinander an.
Erstellen mithilfe von winrt::make
Beginnen wir mit der Standardmethode (C++/WinRT, Version 1.0), da es ratsam ist, zumindest mit diesem Muster vertraut zu sein. Sie erstellen den projizierten Typ über den std::nullptr_t-Konstruktor. Dieser Konstruktor führt keine Initialisierung durch, daher müssen Sie der Instanz über die Winrt::make-Hilfsfunktion einen Wert zuweisen und alle erforderlichen Konstruktorargumente übergeben. Eine im selben Projekt implementierte Laufzeitklasse wie der verbrauchende Code muss nicht registriert oder über die Windows-Runtime/COM-Aktivierung instanziiert werden.
Siehe XAML-Steuerelemente und binden Sie an eine C++/WinRT-Eigenschaft, um eine vollständige exemplarische Vorgehensweise zu erhalten. In diesem Abschnitt werden Ausschnitte aus dieser Anleitung gezeigt.
// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
BookstoreViewModel MainViewModel{ get; };
}
}
// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
private:
Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
...
// MainPage.cpp
...
#include "BookstoreViewModel.h"
MainPage::MainPage()
{
m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
...
}
Einheitliche Konstruktion
Mit C++/WinRT, Version 2.0 und höher, gibt es eine optimierte Form der Konstruktion, die Ihnen als einheitliche Konstruktion bekannt ist (siehe News und Änderungen, in C++/WinRT 2.0).
Siehe XAML-Steuerelemente und binden Sie an eine C++/WinRT-Eigenschaft, um eine vollständige exemplarische Vorgehensweise zu erhalten. In diesem Abschnitt werden Ausschnitte aus dieser Anleitung gezeigt.
Um anstelle von winrt::makeeine einheitliche Konstruktion zu verwenden, benötigen Sie eine Aktivierungsfabrik. Eine gute Möglichkeit, einen zu generieren, ist das Hinzufügen eines Konstruktors zu Ihrer IDL.
// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
MainPage();
BookstoreViewModel MainViewModel{ get; };
}
}
Deklarieren und initialisieren Sie dann MainPage.hm_mainViewModel in nur einem Schritt, wie unten dargestellt.
// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
private:
Bookstore::BookstoreViewModel m_mainViewModel;
...
};
}
...
Und dann ist im MainPage--Konstruktor in MainPage.cppkein Code m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();erforderlich.
Weitere Informationen zur einheitlichen Konstruktion und zu Codebeispielen finden Sie unter Wählen Sie die einheitliche Konstruktion und den direkten Implementierungszugriff.
Instanziieren und Zurückgeben projizierter Typen und Schnittstellen
Hier ist ein Beispiel dafür, wie projizierte Typen und Schnittstellen in Ihrem nutzenden Projekt aussehen könnten. Denken Sie daran, dass ein projizierter Typ (wie in diesem Beispiel) durch ein Tool generiert wird und nicht etwas ist, das Sie selbst erstellen würden.
struct MyRuntimeClass : MyProject::IMyRuntimeClass, impl::require<MyRuntimeClass,
Windows::Foundation::IStringable, Windows::Foundation::IClosable>
MyRuntimeClass ist ein projizierter Typ; projizierte Schnittstellen umfassen IMyRuntimeClass, IStringableund IClosable. In diesem Thema werden die verschiedenen Möglichkeiten aufgezeigt, wie Sie einen projizierten Typ instanziieren können. Hier ist eine Erinnerung und Zusammenfassung, wobei MyRuntimeClass- als Beispiel verwendet wird.
// The runtime class is implemented in another compilation unit (it's either a Windows API,
// or it's implemented in a second- or third-party component).
MyProject::MyRuntimeClass myrc1;
// The runtime class is implemented in the same compilation unit.
MyProject::MyRuntimeClass myrc2{ nullptr };
myrc2 = winrt::make<MyProject::implementation::MyRuntimeClass>();
- Sie können auf die Member aller Schnittstellen eines projizierten Typs zugreifen.
- Sie können einen projizierten Typ an einen Aufrufer zurückgeben.
- Projizierte Typen und Schnittstellen werden von winrt::Windows::Foundation::IUnknownabgeleitet. Sie können also IUnknown::as für einen projizierten Typ oder eine Schnittstelle aufrufen, um andere projizierte Schnittstellen zu ermitteln, die Sie auch nutzen oder an einen Aufrufer zurückgeben können. Die als-Mitgliedsfunktion arbeitet wie QueryInterface.
void f(MyProject::MyRuntimeClass const& myrc)
{
myrc.ToString();
myrc.Close();
IClosable iclosable = myrc.as<IClosable>();
iclosable.Close();
}
Aktivierungsfabriken
Die bequeme, direkte Methode zum Erstellen eines C++/WinRT-Objekts lautet wie folgt.
using namespace winrt::Windows::Globalization::NumberFormatting;
...
CurrencyFormatter currency{ L"USD" };
Es kann jedoch vorkommen, dass Sie die Aktivierungsfactory selbst erstellen und dann bei Bedarf Objekte daraus erzeugen möchten. Hier sind einige Beispiele, die Ihnen zeigen, wie man die Funktionsvorlage winrt::get_activation_factory verwendet.
using namespace winrt::Windows::Globalization::NumberFormatting;
...
auto factory = winrt::get_activation_factory<CurrencyFormatter, ICurrencyFormatterFactory>();
CurrencyFormatter currency = factory.CreateCurrencyFormatterCode(L"USD");
using namespace winrt::Windows::Foundation;
...
auto factory = winrt::get_activation_factory<Uri, IUriRuntimeClassFactory>();
Uri uri = factory.CreateUri(L"http://www.contoso.com");
Die Klassen in den beiden obigen Beispielen sind Typen aus einem Windows-Namespace. In diesem nächsten Beispiel ist ThermometerWRC::Thermometer ein benutzerdefinierter Typ, der in einer Windows-Runtime-Komponente implementiert ist.
auto factory = winrt::get_activation_factory<ThermometerWRC::Thermometer>();
ThermometerWRC::Thermometer thermometer = factory.ActivateInstance<ThermometerWRC::Thermometer>();
Mitglied-/Typenambiguitäten
Wenn eine Memberfunktion denselben Namen wie ein Typ hat, entsteht Mehrdeutigkeit. Die Regeln für die qualifizierungsfreie Namenssuche in C++ in Mitgliedsfunktionen führen dazu, dass die Klasse durchsucht wird, bevor in Namespaces gesucht wird. Das Substitutionsversagen ist kein Fehler, weil die (SFINAE)-Regel nicht angewendet wird (sie gilt bei der Überladungsauflösung von Funktionsvorlagen). Wenn der Name innerhalb der Klasse also nicht sinnvoll ist, sucht der Compiler nicht nach einer besseren Übereinstimmung – er meldet einfach einen Fehler.
struct MyPage : Page
{
void DoWork()
{
// This doesn't compile. You get the error
// "'winrt::Windows::Foundation::IUnknown::as':
// no matching overloaded function found".
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Style>() };
}
}
Oben nimmt der Compiler an, dass Sie FrameworkElement.Style() (die in C++/WinRT eine Memberfunktion ist) als Template-Parameter an IUnknown::asübergeben. Die Lösung besteht darin, zu erzwingen, dass der Name Style als Typ "Windows::UI::Xaml::Style" interpretiert wird.
struct MyPage : Page
{
void DoWork()
{
// One option is to fully-qualify it.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Windows::UI::Xaml::Style>() };
// Another is to force it to be interpreted as a struct name.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<struct Style>() };
// If you have "using namespace Windows::UI;", then this is sufficient.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Xaml::Style>() };
// Or you can force it to be resolved in the global namespace (into which
// you imported the Windows::UI::Xaml namespace when you did
// "using namespace Windows::UI::Xaml;".
auto style = Application::Current().Resources().
Lookup(L"MyStyle").as<::Style>();
}
}
Die nicht qualifizierte Namenssuche weist eine besondere Ausnahme auf, wenn auf den Namen ::folgt. In diesem Fall werden Funktionen, Variablen und Enumerationswerte ignoriert. Dadurch können Sie Dinge wie diese tun.
struct MyPage : Page
{
void DoSomething()
{
Visibility(Visibility::Collapsed); // No ambiguity here (special exception).
}
}
Der Aufruf an Visibility() wird zum Namen der -Memberfunktion UIElement.Visibility aufgelöst. Der Parameter Visibility::Collapsed folgt jedoch dem Wort Visibility mit ::, weshalb der Methodenname ignoriert wird und der Compiler die Enum-Klasse findet.
Wichtige APIs
- QueryInterface-Funktion
- RoActivateInstance-Funktion
- Windows::Foundation::Uri-Klasse
- winrt::get_activation_factory Funktionsvorlage
- winrt::make-Funktionsvorlage
- winrt::Windows::Foundation::IUnknown-Struktur