Delen via


API's maken met C++/WinRT

In dit onderwerp wordt uitgelegd hoe je C++/WinRT API's maakt met de winrt::implements basisstructuur, zowel direct als indirect. 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.

Opmerking

In dit onderwerp wordt aandacht besteed aan het onderwerp van Windows Runtime-onderdelen, maar alleen in de context van C++/WinRT. Als u op zoek bent naar inhoud over Windows Runtime-onderdelen die betrekking hebben op alle Windows Runtime-talen, raadpleegt u Windows Runtime-onderdelen.

  • U geen een Windows Runtime-klasse (runtimeklasse) te ontwerpen; u alleen een of meer Windows Runtime-interfaces wilt implementeren voor lokaal verbruik in uw app. U leidt rechtstreeks af van winrt::implements in dit geval en implementeert functies.
  • U bent een runtimeklasse aan het schrijven. Mogelijk ontwerpt u een onderdeel dat moet worden gebruikt vanuit een app. Of u bent misschien bezig een type te maken dat moet worden gebruikt vanuit de XAML-gebruikersinterface (UI), en dan implementeert en gebruikt u een runtime class binnen hetzelfde compilatie-eenheid. In deze gevallen kunt u met de hulpprogramma's klassen genereren die zijn afgeleid van winrt::implementeert.

In beide gevallen wordt het type dat uw C++/WinRT-API's implementeert het implementatietypegenoemd.

Belangrijk

Het is belangrijk om het concept van een implementatietype te onderscheiden van dat van een projected type. Het projecttype wordt beschreven in API's gebruiken met C++/WinRT.

Als u niet een runtimeklasse creëert

Het eenvoudigste scenario is waar 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 app schrijven op basis van CoreApplication-.

Als naar uw type wordt verwezen door de XAML-gebruikersinterface, een runtimeklasse moet zijn, ook al bevindt het zich in hetzelfde project als de XAML. Zie voor dat geval de sectie Als u een runtimeklasse ontwerpt waarnaar wordt verwezen in uw XAML UI-.

Opmerking

Zie voor informatie over het installeren en gebruiken van de C++/WinRT Visual Studio-extensie (VSIX) en het NuGet-pakket (dat samen projectsjabloon en build-ondersteuning biedt), Visual Studio-ondersteuning voor C++/WinRT-.

In Visual Studio illustreert de Visual C++>Windows Universal>Core App (C++/WinRT) projectsjabloon het patroon CoreApplication. Het patroon begint met het doorgeven van een implementatie van Windows::ApplicationModel::Core::IFrameworkViewSource aan CoreApplication::Run.

using namespace Windows::ApplicationModel::Core;
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    IFrameworkViewSource source = ...
    CoreApplication::Run(source);
}

CoreApplication de interface gebruikt om de eerste weergave van de app te maken. Conceptueel ziet IFrameworkViewSource er als volgt uit.

struct IFrameworkViewSource : IInspectable
{
    IFrameworkView CreateView();
};

Conceptueel nogmaals, de implementatie van CoreApplication::Run doet dit.

void Run(IFrameworkViewSource viewSource) const
{
    IFrameworkView view = viewSource.CreateView();
    ...
}

U implementeert dus als ontwikkelaar de IFrameworkViewSource interface. C++/WinRT heeft de basisstructsjabloon winrt::implementeert om het eenvoudig te maken een interface (of meerdere) te implementeren zonder gebruik te maken van COM-stijl programmeren. U leidt uw type eenvoudig af van , implementeerten implementeert vervolgens de functies van de interface. Dit doet u als volgt.

// App.cpp
...
struct App : implements<App, IFrameworkViewSource>
{
    IFrameworkView CreateView()
    {
        return ...
    }
}
...

Dat is geregeld IFrameworkViewSource. De volgende stap is het retourneren van een object dat de IFrameworkView interface implementeert. U kunt ervoor kiezen om die interface ook te implementeren op App-. Dit volgende codevoorbeeld vertegenwoordigt een minimale app waarmee ten minste een venster op het bureaublad kan worden geopend.

// App.cpp
...
struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    IFrameworkView CreateView()
    {
        return *this;
    }

    void Initialize(CoreApplicationView const &) {}

    void Load(hstring const&) {}

    void Run()
    {
        CoreWindow window = CoreWindow::GetForCurrentThread();
        window.Activate();

        CoreDispatcher dispatcher = window.Dispatcher();
        dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
    }

    void SetWindow(CoreWindow const & window)
    {
        // Prepare app visuals here
    }

    void Uninitialize() {}
};
...

Omdat uw App type eenIFrameworkViewSource-is, kunt u er slechts een doorgeven aan uitvoeren.

using namespace Windows::ApplicationModel::Core;
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    CoreApplication::Run(winrt::make<App>());
}

Als u een runtimeklasse maakt in een Windows Runtime-onderdeel

Als uw type is verpakt in een Windows Runtime-onderdeel voor verbruik vanuit een ander binair onderdeel (het andere binaire bestand is meestal een toepassing), moet uw type een runtimeklasse zijn. U declareert een runtimeklasse in een IdL-bestand (Microsoft Interface Definition Language) (.idl) (zie Factoring Runtime-klassen in Midl-bestanden (.idl)).

Elk IDL-bestand resulteert in een .winmd-bestand en Visual Studio voegt alle bestanden samen in één bestand met dezelfde naam als uw hoofdnaamruimte. Dit laatste .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;
    }
}

Met deze IDL wordt een Windows Runtime-klasse gedeclareerd. 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. Als u wilt zien hoe de werkstroom van het IDL-bestand in actie is, zie XAML-besturingselementen; koppel 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 merkwaardig terugkerende sjabloonpatroon (CRTP) genoemd. Als u de overnameketen naar boven volgt, komt u MyRuntimeClass_basetegen.

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;
    };
}

Zie Eenvoudige eigenschappen.

template <typename D, typename... I>
struct MyRuntimeClass_base : implements<D, MyProject::IMyRuntimeClass, I...>

In dit scenario is de basis van de erfenishiërarchie dus weer de winrt::implements basisstructuursjabloon.

Zie voor meer informatie, code en een overzicht van het ontwerpen van API's in een Windows Runtime-onderdeel Windows Runtime-onderdelen met C++/WinRT- en Auteursgebeurtenissen in C++/WinRT-.

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 bent u zowel bezig met het ontwikkelen van als het gebruiken van de API's voor. De procedure voor het implementeren van uw runtimeklasse is in wezen hetzelfde als die voor een Windows Runtime-onderdeel. Zie dus de vorige sectie:Als u een runtimeklasse maakt in een Windows Runtime-onderdeel. Het enige detail dat verschilt, is dat de C++/WinRT-toolchain, vanuit de IDL, niet alleen een implementatietype genereert, maar ook een projectietype. Het is belangrijk om te weten dat het zeggen van 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 implementatietypeof de implementatie. Dit type wordt gegenereerd (door het hulpprogramma cppwinrt.exe) in de bestanden \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h en MyRuntimeClass.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 projecttypeof de projectie. Dit type wordt gegenereerd (door cppwinrt.exe) in het bestand \MyProject\MyProject\Generated Files\winrt\impl\MyProject.2.h.

Geprojecteerde type en implementatietype

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 voor een voorbeeld van het implementeren van de INotifyPropertyChanged interface op een runtimeklasse; binden aan een C++/WinRT-eigenschap.

De procedure voor het gebruik van uw runtimeklasse in dit scenario wordt beschreven in API's gebruiken met C++/WinRT.

Runtimeklassen factoreren 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 kringvormige) import afhankelijkheden hebt, 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.

Constructors voor runtime-klassen

Hier volgen enkele punten die we kunnen meenemen uit het overzicht dat we hierboven hebben gezien.

  • 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 consumeren.
  • Of u nu IDL-gedeclareerde constructor(s) hebt of niet, een constructoroverload die std::nullptr_t aanneemt, wordt gegenereerd op uw geprojecteerde type. Het aanroepen van de std::nullptr_t constructor is de eerste van twee stappen in het gebruikmaken van de runtimeklasse uit dezelfde compilatie-eenheid. Zie API's gebruiken met C++/WinRT-voor meer informatie en een codevoorbeeld.
  • Als u de runtimeklasse van dezelfde compilatie-eenheid gebruikt, kunt u ook niet-standaardconstructors rechtstreeks implementeren op het implementatietype (wat zich in MyRuntimeClass.hbevindt).

Opmerking

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 die automatisch gegenereerde prototypen voor de leden van uw runtimeklasse, 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 in IDL bijvoorbeeld uw methode een SomeClass-vereist, kunt u ervoor kiezen om dit in uw implementatie te wijzigen naar IInspectable. Dit werkt omdat elke 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& in SomeClass. Dat is nodig wanneer u moet voorkomen dat u een verwijzing vastlegt in een coroutine (zie Parameter-passing).
  • U kunt de restricties op de retourwaarde versoepelen. U kunt bijvoorbeeld void wijzigen in winrt::fire_and_forget.

De laatste twee zijn erg handig wanneer u een asynchrone gebeurtenis-handler schrijft.

Implementatietypen en interfaces instantiëren en retourneren

Voor deze sectie gaan we een voorbeeld nemen van een implementatietype met de naam MyType, waarmee de IStringable en IClosable interfaces worden geïmplementeerd.

U kunt MyType rechtstreeks afleiden uit winrt::implementeert (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

U kunt echter 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. zorgt ervoor dat de standaardinterface van het implementatietype retourneert.

IStringable istringable = winrt::make<MyType>();

Opmerking

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 met een exemplaar van het verwachte type. Zie XAML-besturingselementen voor een codevoorbeeld van dat scenario; binden 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 IUnknown::as (of IUnknown::try_as) erop aanroepen om te zoeken naar andere projecttypen of interfaces, die u ook kunt gebruiken of retourneren.

Aanbeveling

Een scenario waarin u geenas of try_as moet roepen is klassereductie bij runtime ("composable classes"). Wanneer een implementatietype een andere klasse samenstelt, gebruik dan niet as of try_as om een ongecontroleerde of gecontroleerde QueryInterface uit te voeren van de klasse die wordt samengesteld. In plaats daarvan opent u het this->-gegevenslid en roept u daarop m_inner 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 retourneert een winrt::com_ptr het implementatietype verpakken. U hebt toegang tot de leden van al zijn interfaces (met behulp van de pijloperator), u kunt het retourneren naar een beller as-is, of u kunt aanroepen als en het resulterende interfaceobject aan een beller 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. Hoewel in het bovenstaande voorbeeld MyType::ToString dezelfde handtekening gebruikt als de geprojecteerde methode op IStringable, wordt de niet-virtuele methode rechtstreeks aangeroepen zonder de binaire interface van de toepassing (ABI) te overschrijden. De com_ptr bevat gewoon een aanwijzer naar de MyType struct, zodat u ook toegang hebt tot alle andere interne details 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 winrt::get_self functiesjabloon. Nogmaals, het is een techniek die virtuele functie-aanroepen vermijdt en waardoor u rechtstreeks toegang krijgt tot de implementatie.

Opmerking

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.

Hier volgt een voorbeeld. Er is nog een voorbeeld in Implementeer de BgLabelControl aangepaste besturingsklasse.

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 wilt vasthouden, kunt u com_ptr::copy_fromaanroepen.

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 is niet afgeleid van winrt::Windows::Foundation::IUnknown, dus het heeft geen als functie. Zoals u kunt zien in de functie ImplFromIClosable hierboven, heeft u toegang tot de leden van al zijn interfaces. Geef in dat geval de instantie van het onbewerkte implementatietype niet terug aan de aanroeper. Gebruik in plaats daarvan een van de technieken die al worden weergegeven 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 hulpprogramma cppwinrt.exe) dat dit mogelijk maakt. U kunt een waarde van het implementatietype rechtstreeks doorgeven aan een methode die een waarde van het bijbehorende verwachte type verwacht. Vanuit een lidfunctie van het implementatietype kunt u *this doorgeven aan een methode die een waarde van het bijbehorende projecttype 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 Windows Runtime voor klasse-erfing is 'samenstelbare klassen'. 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 : Windows.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();
        }
    };
}

Aanbeveling

Wanneer een implementatietype een andere klasse samenstelt, gebruik dan niet as of try_as om een ongecontroleerde of gecontroleerde QueryInterface uit te voeren van de klasse die wordt samengesteld. In plaats daarvan opent u het this->-gegevenslid en roept u daarop m_inner 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, dus als u een ToggleButtonAutomationPeerwilt maken, moet u een eigenaardoorgeven. Als u dus afgeleid bent van ToggleButtonAutomationPeer, moet u een constructor opgeven die een eigenaar accepteert en deze doorgeeft aan de basisklasse. Laten we eens kijken hoe dat eruitziet in de praktijk.

// MySpecializedToggleButton.idl
namespace MyNamespace
{
    runtimeclass MySpecializedToggleButton :
        Windows.UI.Xaml.Controls.Primitives.ToggleButton
    {
        ...
    };
}
// MySpecializedToggleButtonAutomationPeer.idl
namespace MyNamespace
{
    runtimeclass MySpecializedToggleButtonAutomationPeer :
        Windows.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). In dit geval roept u de basisklasseconstructor aan.

// MySpecializedToggleButtonAutomationPeer.cpp
...
MySpecializedToggleButtonAutomationPeer::MySpecializedToggleButtonAutomationPeer
    (MyNamespace::MySpecializedToggleButton const& owner) : 
    MySpecializedToggleButtonAutomationPeerT<MySpecializedToggleButtonAutomationPeer>(owner)
{
    ...
}
...

De constructor van de basisklasse 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: projectietypen, implementatietypen en fabrieken

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::implementatie. 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::makeaan, waarbij u uw implementatietype doorgeeft als de sjabloonparameter. We hebben eerder in dit onderwerp al voorbeelden van winrt::make in actie getoond, en er is nog een voorbeeld te vinden in XAML-besturingselementen; binden aan een C++/WinRT-eigenschap. Zie ook Diagnostisering van directe toewijzingen.
  • 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 MyRuntimeClass myRuntimeClass;te schrijven. De juiste technieken en code voor dat scenario worden eerder in dit onderwerp weergegeven in de sectie Implementatietypen en interfaces instantiëren en retourneren.

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 vernietigd volgens de normale C++-regels wanneer het bereik wordt afgesloten. Dus als u een geprojecteerd type (een slimme aanwijzer) naar dat object hebt geretourneerd, wordt die aanwijzer nu gehaakt.

Dit type beschadiging van het geheugen is moeilijk te diagnosticeren. Voor debug-builds helpt een C++/WinRT-assertie u deze fout op te vangen met behulp van een stack-detector. Maar koroutines worden toegewezen aan de heap, dus je krijgt geen hulp bij deze fout als je het binnen een koroutine maakt. Voor meer informatie, zie Diagnostiek van directe toewijzingen.

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).

Eigenschap Accepteert Opmerkingen
T (een slimme aanwijzer) Geprojecteerd Let op de waarschuwing in naamruimten: geprojecteerde typen, implementatietypen en factories over het per ongeluk gebruiken van het implementatietype.
agile_ref<T> Beide Als u het implementatietype gebruikt, moet het constructorargument com_ptr<T>zijn.
com_ptr<T> Implementatie Als u het geprojecteerde type gebruikt, wordt de fout gegenereerd: 'Release' is not a member of 'T'.
default_interface<T> Beide Als u het implementatietype gebruikt, wordt de eerste geïmplementeerde interface geretourneerd.
get_self<T> Implementatie Als u het geprojecteerde type gebruikt, wordt de fout gegenereerd: '_abi_TrustLevel': is not a member of 'T'.
guid_of<T>() Beide Retourneert de GUID van de standaardinterface.
IWinRTTemplateInterface<T>
Geprojecteerd Het gebruik van het implementatietype compileert, maar het is een vergissing. Bekijk de waarschuwing bij naamruimten: geprojecteerde typen, implementatietypen en factory's.
make<T> Implementatie Bij het gebruik van het geprojecteerde type leidt dit tot de fout: 'implements_type': is not a member of any direct or indirect base class of 'T'
make_agile(T const&amp;) Beide Als u het implementatietype gebruikt, moet het argument com_ptr<T>zijn.
make_self<T> Implementatie Bij het gebruik van het geprojecteerde type leidt dit tot 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> Beide Als u het implementatietype gebruikt, moet het constructorargument com_ptr<T>zijn.

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 hulpprogramma cppwinrt.exe te configureren. Stel in Visual Studio de projecteigenschap Algemene eigenschappen>C++/WinRT>Geoptimaliseerde in op Ja. Dit heeft het effect van het toevoegen van <CppWinRTOptimized>true</CppWinRTOptimized> aan uw projectbestand. Het heeft hetzelfde effect als het toevoegen van de optie bij het aanroepen van cppwinrt.exe vanaf de opdrachtregel.

De -opt[imize] schakelaar maakt wat vaak uniforme constructiewordt genoemd mogelijk. Met uniforme (of geïntegreerde) constructie gebruikt u de C++/WinRT-taalprojectie zelf om uw implementatietypen (typen geïmplementeerd door uw component, voor gebruik door toepassingen) efficiënt en zonder problemen met laadprogramma's te maken en te gebruiken.

Voordat we het kenmerk beschrijven, laten we eerst situatie zien zonder uniforme constructie. Ter illustratie beginnen we met deze 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 direct aan; het verloopt via de ABI.

Daarom, op de regel code waar u een MyClass-object (MyClass c;) maakt, roept de C++/WinRT-projectie RoGetActivationFactory aan om de klasse of activeringsfactory op te halen en gebruikt vervolgens die fabriek om het object te maken. De laatste regel maakt ook gebruik van de fabriek om wat een statische methode-aanroep lijkt te zijn, te creëren. Dit alles vereist dat uw klasse is geregistreerd en dat uw module het DllGetActivationFactory- invoerpunt implementeert. C++/WinRT heeft een zeer snelle factorycache, dus veroorzaakt geen van dit een probleem voor een toepassing die uw onderdeel gebruikt. Het probleem is dat u binnen uw onderdeel net iets hebt gedaan dat een beetje problematisch is.

Ten eerste, hoe snel de C++/WinRT-factorycache ook is, het aanroepen via RoGetActivationFactory (of zelfs latere aanroepen via de factorycache) is altijd langzamer dan het rechtstreeks aanroepen van de implementatie. 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 noch concies. 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 fabriek zich in dezelfde module (DLL) bevindt die de aanroep doet, hebt u de DLL effectief vastgepinnd en voorkomen dat het ooit kan worden ontladen. In veel gevallen maakt dat niet uit; maar sommige systeemonderdelen moeten het lossen ondersteunen.

Dit is waar de term uniforme constructie binnenkomt. 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 daadwerkelijk het implementeren van de klasse, kunt u vrijelijk dezelfde syntaxis gebruiken om het object te maken.

// MyProject::MyClass c{ winrt::make<implementation::MyClass>() };
MyClass c;

Wanneer u uw componentproject bouwt met de -opt[imize]-switch, wordt de aanroep via de taalprojectie gecompileerd naar dezelfde efficiënte aanroep van de winrt::make functie, die direct het implementatietype aanmaakt. nl-NL: Dit maakt uw syntaxis eenvoudig en voorspelbaar, het voorkomt prestatieverlies door aanroepen via de fabriek en voorkomt het vastpinnen van de component in het proces. Naast onderdeelprojecten is dit ook handig voor XAML-toepassingen. Door RoGetActivationFactory- te omzeilen voor klassen die in dezelfde toepassing zijn geïmplementeerd, kunt u ze aanmaken zonder dat ze hoeven te worden geregistreerd, op dezelfde manieren als u dat zou kunnen doen als ze buiten uw component waren.

Uniforme constructie is van toepassing op elke aanroep die door de fabriek achter de schermen wordt bediend. 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]zijn voor de eerste en laatste instructies aanroepen via het factory-object vereist. Meten-opt[imize]doet geen van beiden het. En deze aanroepen worden rechtstreeks gecompileerd op basis van de implementatie en hebben zelfs de mogelijkheid om inline te kunnen worden uitgevoerd. Die spreekt met de andere term die vaak wordt gebruikt bij het praten over -opt[imize], namelijk directe implementatie toegang.

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 brekende wijziging omdat de component moet samenwerken om de taalprojectie directe toegang te geven tot de implementatietypen. 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 het StaticMethod lid door de projectie als volgt gedefinieerd.

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 uw aanwijzing dat deze oproepen te maken hebben met de factorycache en dat ze niet rechtstreeks toegang hebben tot de implementatie. Met-opt[imize]worden dezelfde functies helemaal niet gedefinieerd. In plaats daarvan worden ze door de projectie gedeclareerd en worden de definities 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 worden weergegeven in een bestand met de naam Type.g.cpp, waarbij Type de naam is van de runtimeklasse die wordt geïmplementeerd. Daarom kunt u verschillende linkerfouten tegenkomen wanneer u -opt[imize] voor het eerst inschakelt bij 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()
    {
    }
}

Als u dus uniforme constructie in een bestaand project wilt gebruiken, moet u het .cpp-bestand van elke implementatie bewerken, zodat u #include <Sub/Namespace/Type.g.cpp> kunt uitvoeren na de invoeging (en 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 dat voltooit de projectie met efficiënte aanroepen rechtstreeks in de implementatie, het vermijden van aanroepen naar de fabriekscache en het tevredenstellen van de linker.

Het laatste wat -opt[imize] voor u doet, is de implementatie van het module.g.cpp van uw project zo wijzigen dat incrementele builds meestal veel sneller zijn. Dit bestand helpt u bij het implementeren van de exports DllGetActivationFactory en DllCanUnloadNow, waarbij de sterke typekoppeling die vereist was voor C++/WinRT 1.0 wordt geëlimineerd. Dit wordt vaak type-uitgewiste fabriekengenoemd. Zonder -opt[imize]begint het module.g.cpp-bestand dat voor uw onderdeel wordt gegenereerd door de definities van al uw implementatieklassen( de MyClass.h) op te geven in dit voorbeeld. Vervolgens maakt het rechtstreeks de implementatiefactory voor elke klasse.

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 leidt dat module.g.cpp opnieuw wordt gecompileert. Met -opt[imize]is dit niet meer 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 bevat het helemaal geen MyClass.h. In plaats daarvan worden de implementatiefuncties gecreëerd zonder enige kennis van hun uitvoering.

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 dat u eerder hebt opgenomen om uniforme constructie te ondersteunen) ook deze functie 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 kunt zien, maakt de winrt_make_MyProject_MyClass functie rechtstreeks de fabriek van uw implementatie. Dit betekent allemaal dat u elke implementatie gelukkig kunt wijzigen en dat de module.g.cpp helemaal niet opnieuw hoeft te worden gecompileerd. Alleen wanneer u Windows Runtime-klassen toevoegt of verwijdert, zal module.g.cpp worden 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 uit het voorbeeld in Afgeleide klassen.

namespace winrt::MyNamespace::implementation
{
    struct BasePage : BasePageT<BasePage>
    {
        void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e);
    };

    struct DerivedPage : DerivedPageT<DerivedPage>
    {
        void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e);
    };
}

De hiërarchie is Windows::UI::Xaml::Controls::Page<- BasePage<- DerivedPage. De methode BasePage::OnNavigatedFrom overschrijft Page::OnNavigatedFrom, maar DerivedPage::OnNavigatedFrom overschrijft BasePage::OnNavigatedFromniet.

Hier hergebruikt DerivedPage de IPageOverrides vtable van BasePage, wat betekent dat de methode IPageOverrides::OnNavigatedFrom niet wordt overschreven. Een mogelijke oplossing vereist BasePage zelf een sjabloonklasse te zijn en de implementatie volledig in een headerbestand te hebben, maar dat maakt dingen onaanvaardbaar ingewikkeld.

Declareer als tijdelijke oplossing de methode OnNavigatedFrom expliciet virtueel in de basisklasse. Op die manier, wanneer de vtable-vermelding voor DerivedPage::IPageOverrides::OnNavigatedFromBasePage::IPageOverrides::OnNavigatedFromaanroept, roept de producer BasePage::OnNavigatedFromaan, die (vanwege zijn virtuele aard) uiteindelijk DerivedPage::OnNavigatedFromaanroept.

namespace winrt::MyNamespace::implementation
{
    struct BasePage : BasePageT<BasePage>
    {
        // Note the `virtual` keyword here.
        virtual void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e);
    };

    struct DerivedPage : DerivedPageT<DerivedPage>
    {
        void OnNavigatedFrom(Windows::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.

Opmerking

Uw IDL hoeft de overschreven methode niet te declareren. Zie Overschrijfbare methoden implementerenvoor meer informatie.

Belangrijke API's