Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit onderwerp wordt uitgelegd hoe u C++/WinRT-API's implementeert met behulp van de basisstruct winrt::implements, hetzij rechtstreeks, hetzij onrechtstreeks. Synoniemen voor auteur in deze context zijn produceren of implementeren. In dit onderwerp worden de volgende scenario's behandeld voor het implementeren van API's op een C++/WinRT-type, in deze volgorde.
Note
In dit onderwerp wordt aandacht besteed aan het onderwerp van Windows Runtime onderdelen, maar alleen in de context van C++/WinRT. Als u inhoud zoekt over Windows Runtime onderdelen die betrekking hebben op alle Windows Runtime talen, raadpleegt u Windows Runtime onderdelen.
- U ontwerpt geen Windows Runtime-klasse (runtimeklasse); u wilt alleen een of meer Windows Runtime interfaces implementeren voor lokaal verbruik in uw app. In dit geval leidt u rechtstreeks af van winrt::implements en implementeert u functies.
- U ontwerpt een runtimeklasse. Mogelijk ontwerpt u een onderdeel dat moet worden gebruikt vanuit een app. Of wellicht ontwikkelt u een type dat vanuit de XAML-gebruikersinterface (UI) wordt gebruikt, en in dat geval implementeert én gebruikt u een runtime-klasse binnen dezelfde compilatie-eenheid. In deze gevallen kunt u met de hulpprogramma's klassen genereren die zijn afgeleid van winrt::implements.
In beide gevallen wordt het type dat uw C++/WinRT-API's implementeert het implementatietype genoemd.
Belangrijk
Het is belangrijk om het concept van een implementatietype te onderscheiden van dat van een projected type. Het verwachte type wordt beschreven in Consume-API's met C++/WinRT.
Als u geen runtimeklasse schrijft
Het eenvoudigste scenario is wanneer uw type een Windows Runtime-interface implementeert en u dat type binnen dezelfde app gebruikt. In dat geval hoeft uw type geen runtimeklasse te zijn; gewoon een gewone C++-klasse. U kunt bijvoorbeeld een WinUI 3-bureaublad-app schrijven op basis van Microsoft::UI::Xaml::Application.
Als naar uw type wordt verwezen door de XAML-gebruikersinterface , moet het een runtimeklasse zijn, ook al bevindt het zich in hetzelfde project als de XAML. Zie voor dat geval de sectie Als u een runtimeklasse ontwerpt waarnaar moet worden verwezen in uw XAML-gebruikersinterface.
Note
Zie Visual Studio support for C++/WinRT voor informatie over het installeren en gebruiken van de Visual Studio-extensie (VSIX) en het NuGet-pakket voor C++/WinRT, die samen projectsjablonen en ondersteuning voor builds bieden.
In Visual Studio illustreert de projectsjabloon Blank App, Packaged (WinUI 3 in Desktop) voor C++ het WinUI 3-toepassingspatroon. De app-klasse is afgeleid van Microsoft::UI::Xaml::Application en het toegangspunt roept de beginmethode aan.
#include "App.xaml.h"
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
winrt::init_apartment();
::winrt::Microsoft::UI::Xaml::Application::Start(
[](auto&&) { ::winrt::make<App>(); });
}
De app-klasse maakt een Microsoft::UI::Xaml::Window en activeert deze in OnLaunched.
// App.xaml.h
struct App : AppT<App>
{
App();
void OnLaunched(Microsoft::UI::Xaml::LaunchActivatedEventArgs const&);
private:
winrt::Microsoft::UI::Xaml::Window window{ nullptr };
};
// App.xaml.cpp
void App::OnLaunched(LaunchActivatedEventArgs const&)
{
window = make<MainWindow>();
window.Activate();
}
C++/WinRT heeft de basissjabloon winrt::implementeert om eenvoudig een interface (of meerdere) te implementeren zonder gebruik te maken van COM-stijl programmeren. U leidt uw type simpelweg af van implements en implementeert vervolgens de functies van de interface. Hier volgt een voorbeeld waarmee een aangepaste interface wordt geïmplementeerd.
struct MyType : implements<MyType, IStringable>
{
hstring ToString()
{
return L"MyType";
}
};
Als u een runtimeklasse ontwerpt in een Windows Runtime-onderdeel
Als uw type is verpakt in een Windows Runtime-onderdeel voor verbruik vanuit een ander binair (het andere binaire bestand is meestal een toepassing), moet uw type een runtimeklasse zijn. U declareert een runtimeklasse in een Microsoft IDL-bestand (Interface Definition Language) (.idl) (zie Factoring Runtime-klassen in Midl-bestanden (.idl)).
Elk IDL-bestand resulteert in een .winmd bestand en Visual Studio alle bestanden samenvoegt in één bestand met dezelfde naam als uw hoofdnaamruimte. Dit uiteindelijke .winmd bestand is het bestand waarnaar de consumenten van uw onderdeel verwijzen.
Hier volgt een voorbeeld van het declareren van een runtimeklasse in een IDL-bestand.
// MyRuntimeClass.idl
namespace MyProject
{
runtimeclass MyRuntimeClass
{
// Declaring a constructor (or constructors) in the IDL causes the runtime class to be
// activatable from outside the compilation unit.
MyRuntimeClass();
String Name;
}
}
Deze IDL declareert een Windows Runtime-klasse (runtime). Een runtimeklasse is een type dat kan worden geactiveerd en gebruikt via moderne COM-interfaces, meestal over uitvoerbare grenzen. Wanneer u een IDL-bestand aan uw project toevoegt en bouwt, genereert de C++/WinRT-hulpprogrammaketen (midl.exe en cppwinrt.exe) een implementatietype voor u. Voor een voorbeeld van de IDL-bestandswerkstroom in de praktijk, zie XAML-besturingselementen; binden aan een C++/WinRT-eigenschap.
Met behulp van de bovenstaande voorbeeld-IDL is het implementatietype een C++-struct-stub met de naam winrt::MyProject::implementation::MyRuntimeClass in broncodebestanden met de naam \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h en MyRuntimeClass.cpp.
Het implementatietype ziet er als volgt uit.
// MyRuntimeClass.h
...
namespace winrt::MyProject::implementation
{
struct MyRuntimeClass : MyRuntimeClassT<MyRuntimeClass>
{
MyRuntimeClass() = default;
winrt::hstring Name();
void Name(winrt::hstring const& value);
};
}
// winrt::MyProject::factory_implementation::MyRuntimeClass is here, too.
U ziet dat het F-gebonden polymorfismepatroon wordt gebruikt (MyRuntimeClass gebruikt zichzelf als sjabloonargument voor de basis, MyRuntimeClassT). Dit wordt ook wel het nieuwsgierig terugkerende sjabloonpatroon (CRTP) genoemd. Als u de overnameketen naar boven volgt, komt u MyRuntimeClass_base tegen.
U kunt de implementatie van eenvoudige eigenschappen vereenvoudigen met behulp van Windows Implementation Libraries (WIL). U doet dit als volgt:
// MyRuntimeClass.h
...
namespace winrt::MyProject::implementation
{
struct MyRuntimeClass : MyRuntimeClassT<MyRuntimeClass>
{
MyRuntimeClass() = default;
wil::single_threaded_rw_property<winrt::hstring> Name;
};
}
template <typename D, typename... I>
struct MyRuntimeClass_base : implements<D, MyProject::IMyRuntimeClass, I...>
Dus in dit scenario is de winrt::implements-basissjabloonstruct opnieuw de basis van de overervingshiërarchie.
Zie Windows Runtime onderdelen met C++/WinRT en Author-gebeurtenissen in C++/WinRT voor meer informatie, code en een overzicht van het ontwerpen van API's in een Windows Runtime-onderdeel.
Als u een runtime-klasse maakt waarnaar wordt verwezen in uw XAML-gebruikersinterface
Als naar uw type wordt verwezen door uw XAML-gebruikersinterface, moet het een runtimeklasse zijn, ook al bevindt het zich in hetzelfde project als de XAML. Hoewel ze doorgaans worden geactiveerd binnen uitvoerbare grenzen, kan in plaats daarvan een runtimeklasse worden gebruikt in de compilatie-eenheid waarmee deze wordt geïmplementeerd.
In dit scenario ontwerpt en gebruikt u de API's. De procedure voor het implementeren van uw runtimeklasse is in wezen hetzelfde als voor een Windows Runtime-onderdeel. Zie de vorige sectie: als u een runtimeklasse in een Windows Runtime-onderdeel ontwerpt. Het enige detail dat verschilt, is dat de C++/WinRT-toolchain vanuit de IDL niet alleen een implementatietype genereert, maar ook een geprojecteerd type. Het is belangrijk om te waarderen dat alleen 'MyRuntimeClass' in dit scenario dubbelzinnig kan zijn; er zijn verschillende entiteiten met die naam, van verschillende soorten.
- MyRuntimeClass is de naam van een runtimeklasse. Maar dit is echt een abstractie: gedeclareerd in IDL en geïmplementeerd in een programmeertaal.
-
MyRuntimeClass is de naam van de C++-struct winrt::MyProject::implementation::MyRuntimeClass, de C++/WinRT-implementatie van de runtimeklasse. Zoals we hebben gezien, als er afzonderlijke implementatie- en verbruiksprojecten zijn, bestaat deze struct alleen in het implementatieproject. Dit is het implementatietype, of de implementatie. Dit type wordt gegenereerd (door het
cppwinrt.exehulpprogramma) in de bestanden\MyProject\MyProject\Generated Files\sources\MyRuntimeClass.henMyRuntimeClass.cpp. -
MyRuntimeClass is de naam van het verwachte type in de vorm van de C++-struct winrt::MyProject::MyRuntimeClass. Als er afzonderlijke implementatie- en verbruiksprojecten zijn, bestaat deze struct alleen in het verbruikende project. Dit is het geprojecteerde type, of de projectie. Dit type wordt gegenereerd (door
cppwinrt.exe) in het bestand\MyProject\MyProject\Generated Files\winrt\impl\MyProject.2.h.
Dit zijn de onderdelen van het geprojecteerde type die relevant zijn voor dit onderwerp.
// MyProject.2.h
...
namespace winrt::MyProject
{
struct MyRuntimeClass : MyProject::IMyRuntimeClass
{
MyRuntimeClass(std::nullptr_t) noexcept {}
MyRuntimeClass();
};
}
Zie XAML-besturingselementen; binding aan een C++/WinRT-eigenschap voor een stapsgewijs voorbeeld van het implementeren van de INotifyPropertyChanged-interface in een runtimeklasse.
De procedure voor het gebruik van uw runtimeklasse in dit scenario wordt beschreven in Api's verbruiken met C++/WinRT.
Runtimeklassen onderbrengen in MIDL-bestanden (.idl)
De Visual Studio project- en itemsjablonen produceren een afzonderlijk IDL-bestand voor elke runtimeklasse. Dit geeft een logische correspondentie tussen een IDL-bestand en de gegenereerde broncodebestanden.
Als u echter alle runtimeklassen van uw project samenvoegt in één IDL-bestand, kan dat de buildtijd aanzienlijk verbeteren. Als u anders complexe (of circulaire) import afhankelijkheden tussen deze afhankelijkheden zou hebben, is het mogelijk dat consolideren daadwerkelijk nodig is. En misschien vindt u het gemakkelijker om uw runtimeklassen te ontwerpen en te controleren als ze bij elkaar zijn.
Constructoren van runtimeklassen
Hier zijn enkele punten die we kunnen meenemen uit de vermeldingen die we hierboven hebben bekeken.
- Elke constructor die u in uw IDL declareert, zorgt ervoor dat er een constructor wordt gegenereerd op zowel uw implementatietype als op het verwachte type. IdL-gedeclareerde constructors worden gebruikt om de runtimeklasse uit een andere compilatie-eenheid te gebruiken.
- Of u nu idL-gedeclareerde constructor(s) hebt of niet, een overbelasting van de constructor die std::nullptr_t gebruikt, wordt gegenereerd op uw projecttype. Het aanroepen van de std::nullptr_t constructor is de eerste van twee stappen bij het gebruik van de runtimeklasse van dezelfde compilatie-eenheid. Zie API's gebruiken met C++/WinRT voor meer informatie en een codevoorbeeld.
- Als u de runtimeklasse uit dezelfde compilatie-eenheid gebruikt, kunt u ook niet-standaardconstructors rechtstreeks implementeren op het implementatietype (dat zich nog wel in
MyRuntimeClass.hbevindt).
Note
Als u verwacht dat uw runtimeklasse wordt gebruikt vanuit een andere compilatie-eenheid (wat gebruikelijk is), neemt u constructor(s) op in uw IDL (ten minste een standaardconstructor). Hierdoor krijgt u ook een factory-implementatie naast uw implementatietype.
Als u uw runtimeklasse alleen binnen dezelfde compilatie-eenheid wilt schrijven en gebruiken, declareert u geen constructor(s) in uw IDL. U hebt geen factory-implementatie nodig en er wordt geen implementatie gegenereerd. De standaardconstructor van uw implementatietype wordt verwijderd, maar u kunt deze eenvoudig bewerken en in plaats daarvan standaard instellen.
Als u uw runtimeklasse alleen binnen dezelfde compilatie-eenheid wilt maken en gebruiken en u constructorparameters nodig hebt, moet u de constructor(s) maken die u rechtstreeks voor uw implementatietype nodig hebt.
Runtimeklassemethoden, eigenschappen en gebeurtenissen
We hebben gezien dat de werkstroom IDL moet gebruiken om uw runtimeklasse en de bijbehorende leden te declareren, waarna met de hulpprogramma's prototypen en stub-implementaties voor u worden gegenereerd. Wat betreft de automatisch gegenereerde prototypen voor de leden van uw runtime-klasse, kunt u ze bewerken zodat ze verschillende typen doorgeven van de typen die u in uw IDL declareert. U kunt dit echter alleen doen zolang het type dat u in IDL declareert, kan worden doorgestuurd naar het type dat u declareert in de geïmplementeerde versie.
Hieronder vindt u enkele voorbeelden.
- U kunt parametertypen ontspannen. Als uw methode bijvoorbeeld in IDL een SomeClass gebruikt, kunt u ervoor kiezen om dit te wijzigen in IInspectable in uw implementatie. Dit werkt omdat een SomeClass kan worden doorgestuurd naar IInspectable (het omgekeerde zou natuurlijk niet werken).
- U kunt een kopieerbare parameter per waarde accepteren in plaats van per verwijzing. Wijzig bijvoorbeeld
SomeClass const&inSomeClass. Dat is nodig wanneer u moet voorkomen dat u een verwijzing opneemt in een coroutine (zie Parameteroverdracht). - U kunt de retourwaarde versoepelen. U kunt bijvoorbeeld void veranderen in winrt::fire_and_forget.
De laatste twee zijn erg handig wanneer u een asynchrone gebeurtenis-handler schrijft.
Implementatietypen en interfaces instantiëren en teruggeven
Voor deze sectie nemen we een voorbeeld van een implementatietype met de naam MyType, waarmee de IStringable - en IClosable-interfaces worden geïmplementeerd.
U kunt MyType rechtstreeks afleiden uit winrt::implements (dit is geen runtimeklasse).
#include <winrt/Windows.Foundation.h>
using namespace winrt;
using namespace Windows::Foundation;
struct MyType : implements<MyType, IStringable, IClosable>
{
winrt::hstring ToString(){ ... }
void Close(){}
};
U kunt het ook genereren op basis van IDL (het is een runtime-klasse).
// MyType.idl
namespace MyProject
{
runtimeclass MyType: Windows.Foundation.IStringable, Windows.Foundation.IClosable
{
MyType();
}
}
U kunt uw implementatietype niet rechtstreeks toewijzen.
MyType myimpl; // error C2259: 'MyType': cannot instantiate abstract class
Maar u kunt van MyType naar een IStringable - of IClosable-object gaan dat u kunt gebruiken of retourneren als onderdeel van uw projectie door de winrt::make-functiesjabloon aan te roepen. make retourneert de standaardinterface van het implementatietype.
IStringable istringable = winrt::make<MyType>();
Note
Als u echter naar uw type verwijst vanuit uw XAML-gebruikersinterface, zijn er zowel een implementatietype als een geprojecteerd type in hetzelfde project. In dat geval retourneert make een exemplaar van het geprojecteerde type. Zie XAML-besturingselementen voor een codevoorbeeld van dat scenario; bind aan een C++/WinRT-eigenschap.
We kunnen (in het bovenstaande codevoorbeeld) alleen gebruiken istringable om de leden van de IStringable-interface aan te roepen. Maar een C++/WinRT-interface (een projectinterface) is afgeleid van winrt::Windows::Foundation::IUnknown. U kunt dus IUnknown::as (of IUnknown::try_as) aanroepen om te zoeken naar andere projecttypen of interfaces, die u ook kunt gebruiken of retourneren.
Hint
Een scenario waarin u as of try_as niet moet aanroepen, is klasseafleiding tijdens runtime ("composable classes"). Wanneer een implementatietype een andere klasse samenstelt, gebruikt u niet as of try_as om een ongecontroleerde of gecontroleerde QueryInterface uit te voeren op de klasse die wordt samengesteld. In plaats daarvan benadert u het (this->) m_inner-gegevenslid en roept u daarop as of try_as aan. Zie De afleiding van runtimeklassen in dit onderwerp voor meer informatie.
istringable.ToString();
IClosable iclosable = istringable.as<IClosable>();
iclosable.Close();
Als u toegang nodig hebt tot alle leden van de implementatie en later een interface naar een aanroeper wilt retourneren, gebruikt u de functiesjabloon winrt::make_self . make_self geeft een winrt::com_ptr terug die het implementatietype omhult. U hebt toegang tot de leden van al zijn interfaces (met behulp van de pijloperator), u kunt deze ongewijzigd aan de aanroeper retourneren, of u kunt er as op aanroepen en het resulterende interfaceobject aan de aanroeper retourneren.
winrt::com_ptr<MyType> myimpl = winrt::make_self<MyType>();
myimpl->ToString();
myimpl->Close();
IClosable iclosable = myimpl.as<IClosable>();
iclosable.Close();
De klasse MyType maakt geen deel uit van de projectie; het is de implementatie. Maar op deze manier kunt u de implementatiemethoden rechtstreeks aanroepen, zonder de overhead van een virtuele functie-aanroep. In het bovenstaande voorbeeld wordt de niet-virtuele methode rechtstreeks aangeroepen, ook al gebruikt MyType::ToString dezelfde handtekening als de geprojecteerde methode op IStringable, zonder de binaire interface van de toepassing (ABI) te overschrijden. De com_ptr houdt gewoon een aanwijzer op de MyType-struct , zodat u ook toegang hebt tot alle andere interne gegevens van MyType via de myimpl variabele en de pijloperator.
In het geval dat u een interfaceobject hebt en u weet dat het een interface voor uw implementatie is, kunt u teruggaan naar de implementatie met behulp van de functiesjabloon winrt::get_self . Nogmaals, het is een techniek die virtuele functie-aanroepen vermijdt en u rechtstreeks toegang geeft tot de implementatie.
Note
Als u de Windows SDK-versie 10.0.17763.0 (Windows 10, versie 1809) of hoger niet hebt geïnstalleerd, moet u winrt::from_abi aanroepen in plaats van winrt::get_self.
Dit is een voorbeeld. Er is nog een voorbeeld in de aangepaste besturingselementklasse BgLabelControl implementeren.
void ImplFromIClosable(IClosable const& from)
{
MyType* myimpl = winrt::get_self<MyType>(from);
myimpl->ToString();
myimpl->Close();
}
Maar alleen het oorspronkelijke interfaceobject behoudt een verwijzing. Als u eraan wilt vasthouden, dan kunt u com_ptr::copy_from aanroepen.
winrt::com_ptr<MyType> impl;
impl.copy_from(winrt::get_self<MyType>(from));
// com_ptr::copy_from ensures that AddRef is called.
Het implementatietype zelf erft niet over van winrt::Windows::Foundation::IUnknown, dus het heeft geen as-functie. Toch kunt u, zoals u kunt zien in de functie ImplFromIClosable hierboven, de leden van al zijn interfaces benaderen. Maar als u dat doet, retourneer dan niet de ruwe instantie van het implementatietype aan de aanroeper. Gebruik in plaats daarvan een van de technieken die al zijn getoond, en retourneer een geprojecteerde interface of een com_ptr.
Als u een exemplaar van uw implementatietype hebt en u deze moet doorgeven aan een functie die het bijbehorende projecttype verwacht, kunt u dit doen, zoals wordt weergegeven in het onderstaande codevoorbeeld. Er bestaat een conversieoperator op uw implementatietype (mits het implementatietype is gegenereerd door het cppwinrt.exe hulpprogramma) waardoor dit mogelijk is. U kunt een waarde van het implementatietype rechtstreeks doorgeven aan een methode die een waarde van het bijbehorende verwachte type verwacht. In een memberfunctie van een implementatietype kunt u *this doorgeven aan een methode die een waarde van het bijbehorende geprojecteerde type verwacht.
// MyClass.idl
import "MyOtherClass.idl";
namespace MyProject
{
runtimeclass MyClass
{
MyClass();
void MemberFunction(MyOtherClass oc);
}
}
// MyClass.h
...
namespace winrt::MyProject::implementation
{
struct MyClass : MyClassT<MyClass>
{
MyClass() = default;
void MemberFunction(MyProject::MyOtherClass const& oc) { oc.DoWork(*this); }
};
}
...
// MyOtherClass.idl
import "MyClass.idl";
namespace MyProject
{
runtimeclass MyOtherClass
{
MyOtherClass();
void DoWork(MyClass c);
}
}
// MyOtherClass.h
...
namespace winrt::MyProject::implementation
{
struct MyOtherClass : MyOtherClassT<MyOtherClass>
{
MyOtherClass() = default;
void DoWork(MyProject::MyClass const& c){ /* ... */ }
};
}
...
//main.cpp
#include "pch.h"
#include <winrt/base.h>
#include "MyClass.h"
#include "MyOtherClass.h"
using namespace winrt;
// MyProject::MyClass is the projected type; the implementation type would be MyProject::implementation::MyClass.
void FreeFunction(MyProject::MyOtherClass const& oc)
{
auto defaultInterface = winrt::make<MyProject::implementation::MyClass>();
MyProject::implementation::MyClass* myimpl = winrt::get_self<MyProject::implementation::MyClass>(defaultInterface);
oc.DoWork(*myimpl);
}
...
Afleiding van runtime-klasse
U kunt een runtimeklasse maken die is afgeleid van een andere runtimeklasse, mits de basisklasse wordt gedeclareerd als 'niet-verzegeld'. De term van de Windows Runtime voor klasseovererving is "composable classes". De code voor het implementeren van een afgeleide klasse is afhankelijk van of de basisklasse wordt geleverd door een ander onderdeel of door hetzelfde onderdeel. Gelukkig hoeft u deze regels niet te leren. U kunt alleen de voorbeeld-implementaties kopiëren uit de sources uitvoermap die door de cppwinrt.exe compiler wordt geproduceerd.
Bekijk dit voorbeeld.
// MyProject.idl
namespace MyProject
{
[default_interface]
runtimeclass MyButton : Microsoft.UI.Xaml.Controls.Button
{
MyButton();
}
unsealed runtimeclass MyBase
{
MyBase();
overridable Int32 MethodOverride();
}
[default_interface]
runtimeclass MyDerived : MyBase
{
MyDerived();
}
}
In het bovenstaande voorbeeld is MyButton afgeleid van het besturingselement XAML-knop , dat wordt geleverd door een ander onderdeel. In dat geval lijkt de implementatie op de implementatie van een niet-samenstelbare klasse:
namespace winrt::MyProject::implementation
{
struct MyButton : MyButtonT<MyButton>
{
};
}
namespace winrt::MyProject::factory_implementation
{
struct MyButton : MyButtonT<MyButton, implementation::MyButton>
{
};
}
Aan de andere kant wordt MyDerived in het bovenstaande voorbeeld afgeleid van een andere klasse in hetzelfde onderdeel. In dit geval is voor de implementatie een extra sjabloonparameter vereist waarmee de implementatieklasse voor de basisklasse wordt opgegeven.
namespace winrt::MyProject::implementation
{
struct MyDerived : MyDerivedT<MyDerived, implementation::MyBase>
{ // ^^^^^^^^^^^^^^^^^^^^^^
};
}
namespace winrt::MyProject::factory_implementation
{
struct MyDerived : MyDerivedT<MyDerived, implementation::MyDerived>
{
};
}
In beide gevallen kan uw implementatie een methode aanroepen vanuit de basisklasse door deze te kwalificeren met de base_type typealias:
namespace winrt::MyProject::implementation
{
struct MyButton : MyButtonT<MyButton>
{
void OnApplyTemplate()
{
// Call base class method
base_type::OnApplyTemplate();
// Do more work after the base class method is done
DoAdditionalWork();
}
};
struct MyDerived : MyDerivedT<MyDerived, implementation::MyBase>
{
int MethodOverride()
{
// Return double what the base class returns
return 2 * base_type::MethodOverride();
}
};
}
Hint
Wanneer een implementatietype een andere klasse samenstelt, gebruikt u niet as of try_as om een ongecontroleerde of gecontroleerde QueryInterface uit te voeren op de klasse die wordt samengesteld. In plaats daarvan benadert u het (this->) m_inner-gegevenslid en roept u daarop as of try_as aan.
Afgeleid van een type dat een niet-standaardconstructor heeft
ToggleButtonAutomationPeer::ToggleButtonAutomationPeer(ToggleButton) is een voorbeeld van een niet-standaardconstructor. Er is geen standaardconstructor. Als u een ToggleButtonAutomationPeer wilt maken, moet u een eigenaar doorgeven. Als u zich dus afleidt van ToggleButtonAutomationPeer, moet u een constructor implementeren die een owner accepteert en deze doorgeeft aan de basisklasse. Laten we eens kijken hoe dat eruitziet in de praktijk.
// MySpecializedToggleButton.idl
namespace MyNamespace
{
runtimeclass MySpecializedToggleButton :
Microsoft.UI.Xaml.Controls.Primitives.ToggleButton
{
...
};
}
// MySpecializedToggleButtonAutomationPeer.idl
namespace MyNamespace
{
runtimeclass MySpecializedToggleButtonAutomationPeer :
Microsoft.UI.Xaml.Automation.Peers.ToggleButtonAutomationPeer
{
MySpecializedToggleButtonAutomationPeer(MySpecializedToggleButton owner);
};
}
De gegenereerde constructor voor uw implementatietype ziet er als volgt uit.
// MySpecializedToggleButtonAutomationPeer.cpp
...
MySpecializedToggleButtonAutomationPeer::MySpecializedToggleButtonAutomationPeer
(MyNamespace::MySpecializedToggleButton const& owner)
{
...
}
...
Het enige ontbrekende onderdeel is dat u die constructorparameter moet doorgeven aan de basisklasse. Herinner je je het F-gebonden polymorfismepatroon dat we hierboven hebben genoemd? Zodra u bekend bent met de details van dat patroon zoals gebruikt door C++/WinRT, kunt u achterhalen wat uw basisklasse wordt genoemd (of u kunt gewoon kijken in het headerbestand van uw implementatieklasse). Zo roept u in dit geval de constructor van de basisklasse aan.
// MySpecializedToggleButtonAutomationPeer.cpp
...
MySpecializedToggleButtonAutomationPeer::MySpecializedToggleButtonAutomationPeer
(MyNamespace::MySpecializedToggleButton const& owner) :
MySpecializedToggleButtonAutomationPeerT<MySpecializedToggleButtonAutomationPeer>(owner)
{
...
}
...
De basisklasseconstructor verwacht een ToggleButton. En MySpecializedToggleButtonis eenToggleButton.
Totdat u de hierboven beschreven bewerking uitvoert (om die constructorparameter door te geven aan de basisklasse), markeert de compiler uw constructor en wijst erop dat er geen geschikte standaardconstructor beschikbaar is voor een type genaamd (in dit geval) MySpecializedToggleButtonAutomationPeer_base<MySpecializedToggleButtonAutomationPeer>. Dat is eigenlijk de basisklasse van de basklasse van uw implementatietype.
Naamruimten: geprojecteerde typen, implementatietypen en factories
Zoals u eerder in dit onderwerp hebt gezien, bestaat er een C++/WinRT-runtimeklasse in de vorm van meer dan één C++-klasse in meer dan één naamruimte. De naam MyRuntimeClass heeft dus één betekenis in de winrt::MyProject-naamruimte en een andere betekenis in de winrt::MyProject::implementation-naamruimte . Houd er rekening mee welke naamruimte u momenteel in context hebt en gebruik vervolgens voorvoegsels voor naamruimten als u een naam uit een andere naamruimte nodig hebt. Laten we de betreffende naamruimten nader bekijken.
- winrt::MyProject. Deze naamruimte bevat geprojecteerde typen. Een object van een projected type is een proxy; het is in feite een slimme aanwijzer naar een backing-object, waarbij dat backing-object hier in uw project kan worden geïmplementeerd of in een andere compilatie-eenheid kan worden geïmplementeerd.
- winrt::MyProject::implementation. Deze naamruimte bevat implementatietypen. Een object van een implementatietype is geen aanwijzer; het is een waarde: een volledig C++-stackobject. Bouw niet rechtstreeks een implementatietype; Roep in plaats daarvan winrt::make aan, waarbij u uw implementatietype doorgeeft als de sjabloonparameter. We hebben voorbeelden van winrt::make in action eerder in dit onderwerp weergegeven en er is nog een voorbeeld in XAML-besturingselementen; binden aan een C++/WinRT-eigenschap. Zie ook Directe toewijzingen diagnosticeren.
- winrt::MyProject::factory_implementation. Deze naamruimte bevat fabrieken. Een object in deze naamruimte ondersteunt IActivationFactory.
In deze tabel ziet u de minimale naamruimtekwalificatie die u in verschillende contexten moet gebruiken.
| De naamruimte die zich in context bevindt | Het verwachte type opgeven | Het implementatietype opgeven |
|---|---|---|
| winrt::MyProject | MyRuntimeClass |
implementation::MyRuntimeClass |
| winrt::MyProject::implementation | MyProject::MyRuntimeClass |
MyRuntimeClass |
Belangrijk
Wanneer u een projecttype uit uw implementatie wilt retourneren, moet u ervoor zorgen dat u het implementatietype niet instantiëren door te schrijven MyRuntimeClass myRuntimeClass;. De juiste technieken en code voor dat scenario worden eerder in dit onderwerp weergegeven in de sectie Instantiëren en retourneren van implementatietypen en interfaces.
Het probleem met MyRuntimeClass myRuntimeClass; in dat scenario is dat er een winrt::MyProject::implementation::MyRuntimeClass-object op de stack wordt gemaakt. Dat object (van het implementatietype) gedraagt zich op een of andere manier als het verwachte type. U kunt methoden hierop op dezelfde manier aanroepen; en het converteert zelfs naar een projectgeprojecteerd type. Maar het object wordt, volgens de gebruikelijke C++-regels, vernietigd wanneer de scope eindigt. Dus als u een geprojecteerd type (een smart pointer) naar dat object hebt teruggegeven, dan is die pointer nu dangling.
Dit type beschadiging van het geheugen is moeilijk te diagnosticeren. Dus voor debug-builds helpt een C++/WinRT-assertie u deze fout op te sporen met een stack-detector. Maar koroutines worden op de heap gealloceerd, dus krijg je geen hulp bij deze fout als je die in een koroutine maakt. Zie Diagnose van directe toewijzingen voor meer informatie.
Projecttypen en implementatietypen gebruiken met verschillende C++/WinRT-functies
Hier volgen verschillende plaatsen waar een C++/WinRT-functies een type verwachten en welk type het verwacht (projecttype, implementatietype of beide).
| Feature | Accepteert | Notes |
|---|---|---|
T (een slimme aanwijzer vertegenwoordigen) |
Geprojecteerd | Zie de waarschuwing in Naamruimten: geprojecteerde typen, implementatietypen en factories over het per ongeluk gebruiken van het implementatietype. |
agile_ref<T> |
Both | Als u het implementatietype gebruikt, moet het constructorargument zijn com_ptr<T>. |
com_ptr<T> |
Implementation | Bij gebruik van het geprojecteerde type ontstaat de fout: 'Release' is not a member of 'T'. |
default_interface<T> |
Both | Als u het implementatietype gebruikt, wordt de eerste geïmplementeerde interface geretourneerd. |
get_self<T> |
Implementation | Bij gebruik van het geprojecteerde type ontstaat de fout: '_abi_TrustLevel': is not a member of 'T'. |
guid_of<T>() |
Both | Retourneert de GUID van de standaardinterface. |
IWinRTTemplateInterface<T> |
Geprojecteerd | Het compileren met het implementatietype werkt, maar dat is een vergissing—zie de waarschuwing in Naamruimten: geprojecteerde typen, implementatietypen en fabrieken. |
make<T> |
Implementation | Het gebruik van het geprojecteerde type leidt tot de fout: 'implements_type': is not a member of any direct or indirect base class of 'T' |
make_agile(T const&) |
Both | Als u het implementatietype gebruikt, moet het argument zijn com_ptr<T>. |
make_self<T> |
Implementation | Als u het geprojecteerde type gebruikt, ontstaat de fout: 'Release': is not a member of any direct or indirect base class of 'T' |
name_of<T> |
Geprojecteerd | Als u het implementatietype gebruikt, krijgt u de tekenreeks-GUID van de standaardinterface. |
weak_ref<T> |
Both | Als u het implementatietype gebruikt, moet het constructorargument zijn com_ptr<T>. |
Aanmelden voor uniforme bouw en directe implementatietoegang
In deze sectie wordt een C++/WinRT 2.0-functie beschreven die is aangemeld, hoewel deze standaard is ingeschakeld voor nieuwe projecten. Voor een bestaand project moet u zich aanmelden door het cppwinrt.exe hulpprogramma te configureren. Stel in Visual Studio de projecteigenschap Common Properties>C++/WinRT>Optimized in op Ja. Dat heeft het effect van het toevoegen <CppWinRTOptimized>true</CppWinRTOptimized> aan uw projectbestand. Het heeft hetzelfde effect als het toevoegen van de switch bij het aanroepen cppwinrt.exe vanaf de opdrachtregel.
De -opt[imize] optie maakt mogelijk wat vaak uniforme opbouw wordt genoemd. Met uniforme (of geünificeerde) constructie gebruikt u de C++/WinRT-taalprojectie zelf om uw implementatietypen efficiënt en zonder problemen met de loader te maken en te gebruiken (typen die door uw onderdeel worden geïmplementeerd, voor gebruik door toepassingen).
Voordat u de functie beschrijft, laten we eerst de situatie zien zonder uniforme constructie. Ter illustratie beginnen we met dit voorbeeld Windows Runtime klasse.
// MyClass.idl
namespace MyProject
{
runtimeclass MyClass
{
MyClass();
void Method();
static void StaticMethod();
}
}
Als C++-ontwikkelaar die bekend is met het gebruik van de C++/WinRT-bibliotheek, kunt u de klasse als volgt gebruiken.
using namespace winrt::MyProject;
MyClass c;
c.Method();
MyClass::StaticMethod();
En dat zou perfect redelijk zijn, mits de weergegeven verbruikscode zich niet in hetzelfde onderdeel bevindt dat deze klasse implementeert. Als taalprojectie beschermt C++/WinRT u als ontwikkelaar van de ABI (de binaire interface van de COM-toepassing die de Windows Runtime definieert). C++/WinRT roept de implementatie niet rechtstreeks aan; het verloopt via de ABI.
Daarom roept de C++/WinRT-projectie op de coderegel waarop u een MyClass-object construeert (MyClass c;) RoGetActivationFactory aan om de klasse of activeringsfabriek op te halen, en gebruikt vervolgens die fabriek om het object te maken. De laatste regel gebruikt eveneens de factory om iets te creëren wat op een aanroep van een statische methode lijkt. Dit alles vereist dat uw klasse wordt geregistreerd en dat uw module het DllGetActivationFactory-toegangspunt implementeert. C++/WinRT heeft een zeer snelle factory-cache, dus dit veroorzaakt geen problemen voor een toepassing die uw component gebruikt. Het probleem is dat u binnen uw onderdeel net iets hebt gedaan dat een beetje problematisch is.
Ten eerste, ongeacht hoe snel de C++/WinRT-factorycache is, zal het aanroepen via RoGetActivationFactory (of zelfs volgende aanroepen via de factorycache) altijd langzamer zijn dan rechtstreeks in de implementatie aanroepen. Een aanroep van RoGetActivationFactory gevolgd door IActivationFactory::ActivateInstance gevolgd door QueryInterface is duidelijk niet zo efficiënt als het gebruik van een C++ new -expressie voor een lokaal gedefinieerd type. Als gevolg hiervan zijn doorgewinterde C++/WinRT-ontwikkelaars gewend om de winrt::make of winrt::make_self helperfuncties te gebruiken bij het maken van objecten binnen een onderdeel.
// MyClass c;
MyProject::MyClass c{ winrt::make<implementation::MyClass>() };
Maar zoals je kunt zien, is dat lang niet zo handig of beknopt. U moet een helperfunctie gebruiken om het object te maken en u moet ook onderscheid maken tussen het implementatietype en het verwachte type.
Ten tweede betekent het gebruik van de projectie om de klasse te maken dat de activeringsfactory in de cache wordt opgeslagen. Normaal gesproken is dat wat u wilt, maar als de factory zich in dezelfde module (DLL) bevindt die de aanroep uitvoert, hebt u de DLL in feite vastgepind en voorkomt u dat deze ooit nog wordt ontladen. In veel gevallen maakt dat niet uit; maar sommige systeemonderdelen moeten het lossen ondersteunen.
Hier komt de term uniforme constructie binnen. Ongeacht of de code voor het maken van code zich in een project bevindt dat alleen de klasse verbruikt, of dat deze zich in het project bevindt dat de klasse daadwerkelijk implementeert , kunt u vrijelijk dezelfde syntaxis gebruiken om het object te maken.
// MyProject::MyClass c{ winrt::make<implementation::MyClass>() };
MyClass c;
Wanneer u uw onderdeelproject bouwt met de schakeloptie -opt[imize], wordt de oproep via de taalprojectie gecompileerd naar dezelfde efficiënte oproep naar de functie winrt::make, die rechtstreeks het implementatietype maakt. Dat maakt uw syntaxis eenvoudig en voorspelbaar, voorkomt prestatieverlies door via de factory aan te roepen en voorkomt daarbij dat de component wordt vastgezet. Naast onderdeelprojecten is dit ook handig voor XAML-toepassingen. Door RoGetActivationFactory te omzeilen voor klassen die in dezelfde applicatie zijn geïmplementeerd, kunt u deze op alle dezelfde manieren instantiëren als wanneer ze zich buiten uw onderdeel bevinden (zonder dat ze hoeven te worden geregistreerd).
Uniforme opbouw geldt voor elke aanroep die achter de schermen door de factory wordt afgehandeld. Praktisch betekent dit dat de optimalisatie zowel constructors als statische leden bedient. Hier is dat oorspronkelijke voorbeeld opnieuw.
MyClass c;
c.Method();
MyClass::StaticMethod();
Zonder -opt[imize], de eerste en laatste instructies vereisen aanroepen via het factory-object.
Met-opt[imize], geen van beiden doet dat. En deze aanroepen worden rechtstreeks gecompileerd op basis van de implementatie en hebben zelfs de mogelijkheid om inline te worden geplaatst. Dat sluit aan bij de andere term die vaak wordt gebruikt wanneer men spreekt over -opt[imize], namelijk rechtstreekse toegang tot implementatie.
Taalprojecties zijn handig, maar wanneer u rechtstreeks toegang hebt tot de implementatie, kunt en moet u hiervan profiteren om de meest efficiënte code te produceren. C++/WinRT kan dat voor u doen, zonder dat u de veiligheid en productiviteit van de projectie hoeft te verlaten.
Dit is een ingrijpende wijziging, omdat de component moet meewerken om de taalprojectie rechtstreeks toegang te geven tot de implementatietypen ervan. Omdat C++/WinRT een bibliotheek met alleen headers is, kunt u binnen kijken en zien wat er aan de hand is. Zonder -opt[imize]worden de MyClass-constructor en staticMethod-lid gedefinieerd door de projectie zoals deze.
namespace winrt::MyProject
{
inline MyClass::MyClass() :
MyClass(impl::call_factory<MyClass>([](auto&& f){
return f.template ActivateInstance<MyClass>(); }))
{
}
inline void MyClass::StaticMethod()
{
impl::call_factory<MyClass, MyProject::IClassStatics>([&](auto&& f) {
return f.StaticMethod(); });
}
}
Het is niet nodig om al het bovenstaande te volgen; het doel is om aan te geven dat beide aanroepen betrekking hebben op een aanroep naar een functie met de naam call_factory. Dat is de aanwijzing dat deze aanroepen te maken hebben met de factory-cache en de implementatie niet rechtstreeks benaderen.
Met-opt[imize] zijn diezelfde functies helemaal niet gedefinieerd. In plaats daarvan worden ze in de projectie gedeclareerd en wordt de invulling ervan aan de component overgelaten.
Het onderdeel kan vervolgens definities opgeven die rechtstreeks in de implementatie worden aangeroepen. Nu zijn we aangekomen bij de belangrijke wijziging. Deze definities worden voor u gegenereerd wanneer u zowel -component als -opt[imize] gebruikt, en ze verschijnen in een bestand met de naam Type.g.cpp, waarbij Type de naam is van de runtime-klasse die wordt geïmplementeerd. Daarom kunt u tegen verschillende linkerfouten aanlopen wanneer u -opt[imize] voor het eerst inschakelt in een bestaand project. U moet dat gegenereerde bestand opnemen in uw implementatie om dingen op te steken.
In ons voorbeeld kan MyClass.h er als volgt uitzien (ongeacht of -opt[imize] wordt gebruikt).
// MyClass.h
#pragma once
#include "MyClass.g.h"
namespace winrt::MyProject::implementation
{
struct MyClass : ClassT<MyClass>
{
MyClass() = default;
static void StaticMethod();
void Method();
};
}
namespace winrt::MyProject::factory_implementation
{
struct MyClass : ClassT<MyClass, implementation::MyClass>
{
};
}
Uw MyClass.cpp is waar alles samenkomt.
#include "pch.h"
#include "MyClass.h"
#include "MyClass.g.cpp" // !!It's important that you add this line!!
namespace winrt::MyProject::implementation
{
void MyClass::StaticMethod()
{
}
void MyClass::Method()
{
}
}
Dus om uniforme opbouw in een bestaand project te gebruiken, moet u het .cpp-bestand van elke implementatie bewerken, zodat u #include <Sub/Namespace/Type.g.cpp> na het opnemen van (en de definitie van) de implementatieklasse. Dit bestand bevat de definities van de functies die de projectie niet gedefinieerd heeft. Hier ziet u hoe deze definities eruitzien in het MyClass.g.cpp bestand.
namespace winrt::MyProject
{
MyClass::MyClass() :
MyClass(make<MyProject::implementation::MyClass>())
{
}
void MyClass::StaticMethod()
{
return MyProject::implementation::MyClass::StaticMethod();
}
}
En daarmee is de projectie netjes voltooid, met efficiënte aanroepen rechtstreeks naar de implementatie, zonder aanroepen naar de fabriekscache en terwijl de linker tevreden wordt gesteld.
Het laatste wat -opt[imize] voor u doet, is de implementatie van het module.g.cpp van uw project wijzigen (het bestand dat u helpt bij het implementeren van de exports DllGetActivationFactory en DllCanUnloadNow van uw DLL) op zo'n manier dat incrementele builds doorgaans veel sneller verlopen doordat de sterke typekoppeling die door C++/WinRT 1.0 vereist was, wordt weggenomen. Dit wordt vaak factories met type-erasure genoemd. Zonder -opt[imize]begint het module.g.cpp bestand dat voor uw onderdeel is gegenereerd door de definities van al uw implementatieklassen op te slaan, zoals in MyClass.hdit voorbeeld. Vervolgens wordt de implementatiefactory voor elke klasse als volgt rechtstreeks aangemaakt.
if (requal(name, L"MyProject.MyClass"))
{
return winrt::detach_abi(winrt::make<winrt::MyProject::factory_implementation::MyClass>());
}
Nogmaals, u hoeft niet alle details te volgen. Wat handig is om te zien, is dat hiervoor de volledige definitie is vereist voor alle klassen die door uw onderdeel worden geïmplementeerd. Dit kan een dramatisch effect hebben op uw binnenste lus, omdat elke wijziging in één implementatie ertoe zal leiden module.g.cpp dat deze opnieuw wordt gecompileert. Met -opt[imize], dit is niet langer het geval. In plaats daarvan gebeuren er twee dingen met het gegenereerde module.g.cpp bestand. De eerste is dat deze geen implementatieklassen meer bevat. In dit voorbeeld zal dit MyClass.h helemaal niet bevatten. In plaats daarvan maakt het de implementatiefabrieken aan zonder enige kennis van hun implementatie.
void* winrt_make_MyProject_MyClass();
if (requal(name, L"MyProject.MyClass"))
{
return winrt_make_MyProject_MyClass();
}
Het is uiteraard niet nodig om hun definities op te nemen en het is aan de linker om de definitie van de winrt_make_Component_Class functie op te lossen. Natuurlijk hoeft u hier niet over na te denken, omdat het MyClass.g.cpp bestand dat voor u wordt gegenereerd (en die u eerder hebt opgenomen om uniforme constructie te ondersteunen) deze functie ook definieert. Hier volgt het volledige MyClass.g.cpp bestand dat voor dit voorbeeld wordt gegenereerd.
void* winrt_make_MyProject_MyClass()
{
return winrt::detach_abi(winrt::make<winrt::MyProject::factory_implementation::MyClass>());
}
namespace winrt::MyProject
{
MyClass::MyClass() :
MyClass(make<MyProject::implementation::MyClass>())
{
}
void MyClass::StaticMethod()
{
return MyProject::implementation::MyClass::StaticMethod();
}
}
Zoals u ziet, maakt de winrt_make_MyProject_MyClass functie rechtstreeks de fabriek van uw implementatie. Dit alles betekent dat u een bepaalde implementatie zonder problemen kunt wijzigen en dat module.g.cpp helemaal niet opnieuw hoeft te worden gecompileerd. Alleen wanneer u Windows Runtime-klassen toevoegt of verwijdert, wordt module.g.cpp bijgewerkt en moet het opnieuw worden gecompileerd.
Virtuele methoden van basisklasse overschrijven
Uw afgeleide klasse kan problemen hebben met virtuele methoden als zowel de basis- als de afgeleide klasse app-gedefinieerde klassen zijn, maar de virtuele methode is gedefinieerd in een grootouder Windows Runtime klasse. In de praktijk gebeurt dit als u afgeleid bent van XAML-klassen. De rest van deze sectie gaat verder vanuit het voorbeeld in afgeleide klassen.
namespace winrt::MyNamespace::implementation
{
struct BasePage : BasePageT<BasePage>
{
void OnNavigatedFrom(Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& e);
};
struct DerivedPage : DerivedPageT<DerivedPage>
{
void OnNavigatedFrom(Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& e);
};
}
De hiërarchie is Microsoft::UI::Xaml::Controls::Page<- BasePage<- DerivedPage. De methode BasePage::OnNavigatedFrom overschrijft Page::OnNavigatedFrom, maar DerivedPage::OnNavigatedFrom overschrijft BasePage::OnNavigatedFrom niet.
Hier hergebruikt DerivedPage de vtable IPageOverrides van BasePage. Dit betekent dat de methode IPageOverrides::OnNavigatedFrom niet kan worden overschreven. Een mogelijke oplossing vereist dat BasePage zelf een sjabloonklasse is en dat de implementatie volledig in een headerbestand staat, maar dat maakt het onacceptabel ingewikkeld.
Declareer als tijdelijke oplossing de methode OnNavigatedFrom als expliciet virtueel in de basisklasse. Op die manier roept de vtable-vermelding voor DerivedPage::IPageOverrides::OnNavigatedFromBasePage::IPageOverrides::OnNavigatedFrom aan, roept de producer BasePage::OnNavigatedFrom aan, die (omdat deze virtueel is) uiteindelijk DerivedPage::OnNavigatedFrom aanroept.
namespace winrt::MyNamespace::implementation
{
struct BasePage : BasePageT<BasePage>
{
// Note the `virtual` keyword here.
virtual void OnNavigatedFrom(Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& e);
};
struct DerivedPage : DerivedPageT<DerivedPage>
{
void OnNavigatedFrom(Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& e);
};
}
Hiervoor moeten alle leden van de klassehiërarchie akkoord gaan met de retourwaarde en parametertypen van de methode OnNavigatedFrom . Als ze het niet eens zijn, moet u de bovenstaande versie gebruiken als de virtuele methode en de alternatieven verpakken.
Note
Uw IDL hoeft de overschreven methode niet te declareren. Zie Overschrijfbare methoden implementeren voor meer informatie.
Belangrijke API's
- winrt::com_ptr struct-sjabloon
- winrt::com_ptr::copy_from, functie
- winrt::from_abi functiesjabloon
- winrt::get_self functiesjabloon
- winrt::implementeert de structsjabloon
- winrt::functiesjabloon maken
- winrt::make_self functiesjabloon
- winrt::Windows::Foundation::IUnknown::as function
- winrt::Windows::Foundation::IUnknown::try_as, functie
Verwante onderwerpen
Windows developer