Creare API con C++/WinRTAuthor APIs with C++/WinRT

Questo argomento descrive come creare API C++/WinRT usando lo struct di base winrt::implements, direttamente o indirettamente.This topic shows how to author C++/WinRT APIs by using the winrt::implements base struct, either directly or indirectly. In questo contesto, creare è un sinonimo di produrre o implementare.Synonyms for author in this context are produce, or implement. Questo argomento illustra gli scenari seguenti per l'implementazione di API su un tipo C++/WinRT, in questo ordine.This topic covers the following scenarios for implementing APIs on a C++/WinRT type, in this order.

Nota

Include informazioni sui componenti Windows Runtime, ma solo nel contesto di C++/WinRT.This topic touches on the subject of Windows Runtime components, but only in the context of C++/WinRT. Per informazioni sui componenti Windows Runtime relative a tutti i linguaggi di Windows Runtime, vedi Componenti Windows Runtime.If you're looking for content about Windows Runtime components that covers all Windows Runtime languages, then see Windows Runtime components.

  • Non stai creando una classe Windows Runtime (classe di runtime). Vuoi semplicemente implementare una o più interfacce di Windows Runtime per l'utilizzo in locale nell'app.You're not authoring a Windows Runtime class (runtime class); you just want to implement one or more Windows Runtime interfaces for local consumption within your app. In questo caso, esegui la derivazione direttamente da winrt::implements e implementi le funzioni.You derive directly from winrt::implements in this case, and implement functions.
  • Stai creando una classe di runtime.You are authoring a runtime class. Potresti creare un componente che verrà utilizzato da un'appYou might be authoring a component to be consumed from an app. oppure potresti creare un tipo che verrà utilizzato da un'interfaccia utente XAML. In tal caso, implementi e utilizzi una classe di runtime all'interno della stessa unità di compilazione.Or you might be authoring a type to be consumed from XAML user interface (UI), and in that case you're both implementing and consuming a runtime class within the same compilation unit. In questi casi, consenti agli strumenti di generare le classi derivate da winrt::implements.In these cases, you let the tools generate classes for you that derive from winrt::implements.

In entrambi i casi, il tipo che implementa le API C++/WinRT viene denominato tipo di implementazione.In both cases, the type that implements your C++/WinRT APIs is called the implementation type.

Importante

È importante distinguere il concetto di tipo di implementazione rispetto a quello di tipo proiettato.It's important to distinguish the concept of an implementation type from that of a projected type. Il tipo proiettato è descritto in Utilizzare API con C++/WinRT.The projected type is described in Consume APIs with C++/WinRT.

Se non stai creando una classe di runtimeIf you're not authoring a runtime class

Lo scenario più semplice è rappresentato dall'implementazione di un'interfaccia di Windows Runtime per utilizzo locale.The simplest scenario is where you're implementing a Windows Runtime interface for local consumption. Non è necessaria una classe di runtime, ma solo una classe C++ ordinaria.You don't need a runtime class; just an ordinary C++ class. Ad esempio, potrebbe essere il caso della scrittura di un'app basata su CoreApplication.For example, you might be writing an app based around CoreApplication.

Nota

Per informazioni sull'installazione e sull'uso dell'Estensione C++/WinRT per Visual Studio (VSIX) e del pacchetto NuGet, che insieme forniscono il modello di progetto e il supporto della compilazione, vedi Supporto di Visual Studio per C++/WinRT.For info about installing and using the C++/WinRT Visual Studio Extension (VSIX) and the NuGet package (which together provide project template and build support), see Visual Studio support for C++/WinRT.

In Visual Studio il modello di progetto di Visual C++ > Windows Universal > App Core (C++/WinRT) illustra il modello CoreApplication.In Visual Studio, the Visual C++ > Windows Universal > Core App (C++/WinRT) project template illustrates the CoreApplication pattern. Il modello inizia con il passaggio di un'implementazione di Windows::ApplicationModel::Core::IFrameworkViewSource a CoreApplication::Run.The pattern begins with passing an implementation of Windows::ApplicationModel::Core::IFrameworkViewSource to CoreApplication::Run.

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

CoreApplication usa l'interfaccia per creare la prima visualizzazione dell'app.CoreApplication uses the interface to create the app's first view. Dal punto di vista concettuale, IFrameworkViewSource avrà un aspetto simile al seguente.Conceptually, IFrameworkViewSource looks like this.

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

Sempre dal punto di vista concettuale, l'implementazione di CoreApplication::Run esegue questa operazione.Again conceptually, the implementation of CoreApplication::Run does this.

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

In qualità di sviluppatore, dovrai pertanto implementare l'interfaccia IFrameworkViewSource.So you, as the developer, implement the IFrameworkViewSource interface. C++/WinRT dispone del modello struct di base winrt::implements che semplifica l'implementazione di una o più interfacce, senza ricorrere alla programmazione di tipo COM.C++/WinRT has the base struct template winrt::implements to make it easy to implement an interface (or several) without resorting to COM-style programming. Dovrai semplicemente derivare il tipo da implements e quindi implementare le funzioni dell'interfaccia.You just derive your type from implements, and then implement the interface's functions. Ecco come.Here's how.

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

Viene preso in considerazione IFrameworkViewSource.That's taken care of IFrameworkViewSource. Il passaggio successivo consiste nel restituire un oggetto che implementa l'interfaccia IFrameworkView.The next step is to return an object that implements the IFrameworkView interface. Puoi anche scegliere di implementare l'interfaccia su App.You can choose to implement that interface on App, too. Il prossimo esempio di codice rappresenta un'app minima che renderà una finestra operativa almeno sul desktop.This next code example represents a minimal app that will at least get a window up and running on the desktop.

// 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() {}
};
...

Poiché il tipo App è un oggetto IFrameworkViewSource, potrai passarne uno solo a Run.Since your App type is an IFrameworkViewSource, you can just pass one to Run.

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

Se stai creando una classe di runtime in un componente Windows RuntimeIf you're authoring a runtime class in a Windows Runtime component

Un tipo incluso in un componente Windows Runtime per l'utilizzo in un'applicazione deve essere necessariamente una classe di runtime.If your type is packaged in a Windows Runtime component for consumption from an application, then it needs to be a runtime class. Una classe di runtime viene dichiarata in un file di Microsoft Interface Definition Language (IDL) con estensione idl. Vedi Factoring delle classi di runtime nei file Midl (.idl).You declare a runtime class in a Microsoft Interface Definition Language (IDL) (.idl) file (see Factoring runtime classes into Midl files (.idl)).

Per ogni file IDL viene generato un file con estensione .winmd e Visual Studio unisce tutti i file risultanti in un singolo file con lo stesso nome dello spazio dei nomi radice.Each IDL file results in a .winmd file, and Visual Studio merges all of those into a single file with the same name as your root namespace. Tale file .winmd finale sarà quello a cui faranno riferimento i consumer del componente.That final .winmd file will be the one that the consumers of your component will reference.

Di seguito è riportato un esempio di dichiarazione di una classe di runtime in un file IDL.Here's an example of declaring a runtime class in an IDL file.

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

Questo file con estensione idl dichiara una classe Windows Runtime (runtime).This IDL declares a Windows Runtime (runtime) class. Una classe di runtime è un tipo che può essere attivato e utilizzato mediante le interfacce COM moderne, in genere tra limiti eseguibili.A runtime class is a type that can be activated and consumed via modern COM interfaces, typically across executable boundaries. Quando aggiungi un file con estensione idl al progetto ed esegui la compilazione, la toolchain C++/WinRT (midl.exe e cppwinrt.exe) genera automaticamente un tipo di implementazione.When you add an IDL file to your project, and build, the C++/WinRT toolchain (midl.exe and cppwinrt.exe) generate an implementation type for you. Per un esempio del flusso di lavoro effettivo per il file con estensione idl, vedi Controlli XAML, binding a una proprietà C++/WinRT.For an example of the IDL file workflow in action, see XAML controls; bind to a C++/WinRT property.

Se usi il file con estensione idl di esempio precedente, il tipo di implementazione è uno stub di struct C++ denominato winrt::MyProject::implementation::MyRuntimeClass nei file del codice sorgente denominati \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h e MyRuntimeClass.cpp.Using the example IDL above, the implementation type is a C++ struct stub named winrt::MyProject::implementation::MyRuntimeClass in source code files named \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h and MyRuntimeClass.cpp.

Il tipo di implementazione ha un aspetto simile al seguente.The implementation type looks like this.

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

Tieni presente il modello di polimorfismo F-bound in uso (MyRuntimeClass usa se stesso come argomento di modello per la base, MyRuntimeClassT),Note the F-bound polymorphism pattern being used (MyRuntimeClass uses itself as a template argument to its base, MyRuntimeClassT). noto anche con il nome CRTP (Curiously Recurring Template Pattern).This is also called the curiously recurring template pattern (CRTP). Seguendo la catena di ereditarietà verso l'alto, si troverà MyRuntimeClass_base.If you follow the inheritance chain upwards, you'll come across MyRuntimeClass_base.

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

In questo scenario, alla radice della gerarchia di ereditarietà, è di nuovo presente il modello di struct di base winrt::implements.So, in this scenario, at the root of the inheritance hierarchy is the winrt::implements base struct template once again.

Per altre informazioni, codice e una procedura dettagliata sulla creazione di API in un componente Windows Runtime, vedere Componenti Windows Runtime con C++/WinRT e Creare eventi in CC++/WinRT.For more details, code, and a walkthrough of authoring APIs in a Windows Runtime component, see Windows Runtime components with C++/WinRT and Author events in C++/WinRT.

Se stai creando una classe di runtime a cui fare riferimento nell'interfaccia utente XAMLIf you're authoring a runtime class to be referenced in your XAML UI

Se al tipo viene fatto riferimento nell'interfaccia utente XAML, deve essere una classe di runtime, anche se si trova nello stesso progetto del codice XAML.If your type is referenced by your XAML UI, then it needs to be a runtime class, even though it's in the same project as the XAML. Sebbene l'attivazione avvenga in genere tra limiti eseguibili, una classe di runtime può essere invece usata nell'unità di compilazione che la implementa.Although they are typically activated across executable boundaries, a runtime class can instead be used within the compilation unit that implements it.

Questo scenario prevede la creazione e l'utilizzo delle API.In this scenario, you're both authoring and consuming the APIs. La procedura per l'implementazione di una classe di runtime equivale in pratica a quella per l'implementazione di un componente Windows Runtime.The procedure for implementing your runtime class is essentially the same as that for a Windows Runtime component. Vedi quindi la sezione precedente: Se stai creando una classe di runtime in un componente Windows Runtime.So, see the previous section—If you're authoring a runtime class in a Windows Runtime component. L'unica differenza è data dal fatto che dal file con estensione idl la toolchain C++/WinRT non genera solo un tipo di implementazione, ma anche un tipo proiettato.The only detail that differs is that, from the IDL, the C++/WinRT toolchain generates not only an implementation type but also a projected type. È anche importante notare che la dicitura "MyRuntimeClass" può risultare ambigua in questo scenario, in quanto con questo nome esistono diverse entità, di tipi diversi.It's important to appreciate that saying only "MyRuntimeClass" in this scenario may be ambiguous; there are several entities with that name, of different kinds.

  • MyRuntimeClass è il nome di una classe di runtime.MyRuntimeClass is the name of a runtime class. È tuttavia un'astrazione: dichiarata in IDL e implementata in un linguaggio di programmazione.But this is really an abstraction: declared in IDL, and implemented in some programming language.
  • MyRuntimeClass è il nome dello struct C++ winrt::MyProject::implementation::MyRuntimeClass, che rappresenta l'implementazione C++/WinRT della classe di runtime.MyRuntimeClass is the name of the C++ struct winrt::MyProject::implementation::MyRuntimeClass, which is the C++/WinRT implementation of the runtime class. Come abbiamo visto, se esistono progetti di implementazione e utilizzo separati, questo struct è presente solo nel progetto di implementazione.As we've seen, if there are separate implementing and consuming projects, then this struct exists only in the implementing project. Si tratta del tipo di implementazione o dell'implementazione.This is the implementation type, or the implementation. Questo tipo viene generato (dallo strumento cppwinrt.exe) nei file \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h e MyRuntimeClass.cpp.This type is generated (by the cppwinrt.exe tool) in the files \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h and MyRuntimeClass.cpp.
  • MyRuntimeClass è il nome del tipo proiettato nel formato dello struct C++ winrt::MyProject::MyRuntimeClass.MyRuntimeClass is the name of the projected type in the form of the C++ struct winrt::MyProject::MyRuntimeClass. Se esistono progetti di implementazione e utilizzo separati, questo struct è presente solo nel progetto di utilizzo.If there are separate implementing and consuming projects, then this struct exists only in the consuming project. Si tratta del tipo proiettato o della proiezione.This is the projected type, or the projection. Questo tipo viene generato (da cppwinrt.exe) nel file \MyProject\MyProject\Generated Files\winrt\impl\MyProject.2.h.This type is generated (by cppwinrt.exe) in the file \MyProject\MyProject\Generated Files\winrt\impl\MyProject.2.h.

Tipo proiettato e tipo di implementazione

Di seguito sono riportate le parti del tipo proiettato rilevanti per questo argomento.Here are the parts of the projected type that are relevant to this topic.

// MyProject.2.h
...
namespace winrt::MyProject
{
    struct MyRuntimeClass : MyProject::IMyRuntimeClass
    {
        MyRuntimeClass(std::nullptr_t) noexcept {}
        MyRuntimeClass();
    };
}

Per una procedura dettagliata di esempio per l'implementazione dell'interfaccia INotifyPropertyChanged in una classe di runtime, vedi Controlli XAML, binding a una proprietà C++/WinRT.For an example walkthrough of implementing the INotifyPropertyChanged interface on a runtime class, see XAML controls; bind to a C++/WinRT property.

La procedura per l'utilizzo della classe di runtime in questo scenario è descritta in Utilizzare API con C++/WinRT.The procedure for consuming your runtime class in this scenario is described in Consume APIs with C++/WinRT.

Factoring delle classi di runtime nei file Midl (.idl)Factoring runtime classes into Midl files (.idl)

I modelli di progetto ed elemento di Visual Studio generano un file IDL separato per ogni classe di runtime.The Visual Studio project and item templates produce a separate IDL file for each runtime class. Ciò consente di ottenere una corrispondenza logica tra un file IDL e i relativi file di codice sorgente generati.That gives a logical correspondence between an IDL file and its generated source code files.

Se tuttavia tutte le classi di runtime del progetto vengono consolidate in un unico file IDL, questo può migliorare notevolmente il tempo di compilazione.However, if you consolidate all of your project's runtime classes into a single IDL file, then that can significantly improve build time. Se sono presenti dipendenze di import complesse (o circolari) tra le classi, il consolidamento può essere effettivamente necessario.If you would otherwise have complex (or circular) import dependencies among them, then consolidating may actually be necessary. Inoltre, se le classi di runtime sono consolidate, può risultare più semplice crearle e rivederle.And you may find it easier to author and review your runtime classes if they're together.

Costruttori della classe di runtimeRuntime class constructors

Ecco alcuni punti da eliminare dall'elenco che abbiamo riportato sopra.Here are some points to take away from the listings we've seen above.

  • Ogni costruttore dichiarato nel file con estensione idl determina la generazione di un costruttore nel tipo di implementazione e nel tipo proiettato.Each constructor you declare in your IDL causes a constructor to be generated on both your implementation type and on your projected type. I costruttori dichiarati nel file con estensione idl consentono l'uso della classe di runtime da un'unità di compilazione differente.IDL-declared constructors are used to consume the runtime class from a different compilation unit.
  • Indipendentemente dal fatto che disponga di costruttori dichiarati nel file con estensione idl, un overload del costruttore che accetta std::nullptr_t viene generato nel tipo proiettato.Whether you have IDL-declared constructor(s) or not, a constructor overload that takes std::nullptr_t is generated on your projected type. La chiamata al costruttore std::nullptr_t è il primo dei due passaggi per l'utilizzo della classe di runtime dalla stessa unità di compilazione.Calling the std::nullptr_t constructor is the first of two steps in consuming the runtime class from the same compilation unit. Per altri dettagli e un esempio di codice, vedi Utilizzare API con C++/WinRT.For more details, and a code example, see Consume APIs with C++/WinRT.
  • Se stai utilizzando la classe di runtime dalla stessa unità di compilazione, potrai anche implementare costruttori non predefiniti direttamente sul tipo di implementazione (che si trova in MyRuntimeClass.h).If you're consuming the runtime class from the same compilation unit, then you can also implement non-default constructors directly on the implementation type (which, remember, is in MyRuntimeClass.h).

Nota

Se prevedi che la classe di runtime venga utilizzata da un'unità di compilazione diversa (scenario comune), includi i costruttori nel file con estensione idl (almeno un costruttore predefinito).If you expect your runtime class to be consumed from a different compilation unit (which is common), then include constructor(s) in your IDL (at least a default constructor). In questo modo, otterrai anche un'implementazione di factory insieme al tipo di implementazione.By doing that, you'll also get a factory implementation alongside your implementation type.

Se intendi creare e utilizzare la classe di runtime solo all'interno della stessa unità di compilazione, non dichiarare eventuali costruttori nel file con estensione idl.If you want to author and consume your runtime class only within the same compilation unit, then don't declare any constructor(s) in your IDL. Un'implementazione di factory non è necessaria e non verrà pertanto generata.You don't need a factory implementation, and one won't be generated. Il costruttore predefinito del tipo di implementazione verrà eliminato, ma potrai facilmente modificarlo e usarlo per impostazione predefinita.Your implementation type's default constructor will be deleted, but you can easily edit it and default it instead.

Se intendi creare e utilizzare la classe di runtime solo all'interno della stessa unità di compilazione e hai bisogno di parametri di costruttori, crea i costruttori necessari direttamente sul tipo di implementazione.If you want to author and consume your runtime class only within the same compilation unit, and you need constructor parameters, then author the constructor(s) that you need directly on your implementation type.

Metodi, proprietà ed eventi delle classi di runtimeRuntime class methods, properties, and events

Abbiamo visto che il flusso di lavoro consiste nell'uso del linguaggio IDL per dichiarare la classe di runtime e i relativi membri e che successivamente i prototipi e le implementazioni dello stub vengono generati in modo automatico dagli strumenti.We've seen that the workflow is to use IDL to declare your runtime class and its members, and then the tooling generates prototypes and stub implementations for you. I prototipi generati automaticamente per i membri della classe di runtime possono essere modificati in modo da passare intorno a tipi diversi da quelli dichiarati nel codice IDL.As for those autogenerated prototypes for the members of your runtime class, you can edit them so that they pass around different types from the types that you declare in your IDL. Questa modifica può tuttavia essere apportata solo se il tipo dichiarato in IDL può essere inoltrato a quello dichiarato nella versione implementata.But you can do that only as long as the type that you declare in IDL can be forwarded to the type that you declare in the implemented version.

Ecco alcuni esempi.Here are some examples.

  • I tipi di parametro consentono una certa flessibilità.You can relax parameter types. Se ad esempio in IDL il metodo accetta un tipo SomeClass, puoi scegliere di modificarlo in IInspectable nella tua implementazione.For example, if in IDL your method takes a SomeClass, then you could choose to change that to IInspectable in your implementation. Questo è possibile perché qualsiasi tipo SomeClass può essere inoltrato a IInspectable, anche se ovviamente l'inverso non è possibile.This works because any SomeClass can be forwarded to IInspectable (the reverse, of course, wouldn't work).
  • Puoi accettare un parametro copiabile per valore, anziché per riferimento.You can accept a copyable parameter by value, instead of by reference. Puoi ad esempio modificare SomeClass const& in SomeClass.For example, change SomeClass const& to SomeClass. Questo è necessario quando devi evitare di acquisire un riferimento in una coroutine (vedi Passaggio di parametri).That's necessary when you need to avoid capturing a reference into a coroutine (see Parameter-passing).
  • Anche per il valore restituito è consentita una certa flessibilità.You can relax the return value. Puoi ad esempio modificare void in winrt::fire_and_forget.For example, you can change void to winrt::fire_and_forget.

Gli ultimi due valori sono molto utili quando si scrive un gestore eventi asincrono.The last two are very useful when you're writing an asynchronous event handler.

Creazione di istanze e restituzione di interfacce e tipi di implementazioneInstantiating and returning implementation types and interfaces

In questa sezione prendiamo come esempio un tipo di implementazione denominato MyType, che implementa le interfacce IStringable e IClosable.For this section, let's take as an example an implementation type named MyType, which implements the IStringable and IClosable interfaces.

Puoi derivare MyType direttamente da winrt::implements (non è una classe di runtime).You can derive MyType directly from winrt::implements (it's not a runtime class).

#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

struct MyType : implements<MyType, IStringable, IClosable>
{
    winrt::hstring ToString(){ ... }
    void Close(){}
};

In alternativa puoi generarlo dal file con estensione idl (è una classe di runtime).Or you can generate it from IDL (it's a runtime class).

// MyType.idl
namespace MyProject
{
    runtimeclass MyType: Windows.Foundation.IStringable, Windows.Foundation.IClosable
    {
        MyType();
    }    
}

Non puoi allocare direttamente il tipo di implementazione.You can't directly allocate your implementation type.

MyType myimpl; // error C2259: 'MyType': cannot instantiate abstract class

Puoi tuttavia passare da MyType a un oggetto IStringable o IClosable, che puoi usare o restituire come parte della proiezione chiamando il modello di funzione winrt::make.But you can go from MyType to an IStringable or IClosable object that you can use or return as part of your projection by calling the winrt::make function template. make restituisce l'interfaccia predefinita del tipo di implementazione.make returns the implementation type's default interface.

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

Nota

Se tuttavia fai riferimento al tipo dall'interfaccia utente XAML, nello stesso progetto saranno presenti sia un tipo di implementazione sia un tipo proiettato.However, if you're referencing your type from your XAML UI, then there will be both an implementation type and a projected type in the same project. In tal caso, make restituisce un'istanza del tipo proiettato.In that case, make returns an instance of the projected type. Per un esempio di codice di questo scenario, vedi Controlli XAML, binding a una proprietà C++/WinRT.For a code example of that scenario, see XAML controls; bind to a C++/WinRT property.

Possiamo usare istringable (nell'esempio di codice precedente) solo per chiamare i membri dell'interfaccia IStringable.We can only use istringable (in the code example above) to call the members of the IStringable interface. Un'interfaccia C++/WinRT (che è un'interfaccia proiettata) deriva tuttavia da winrt::Windows::Foundation::IUnknown.But a C++/WinRT interface (which is a projected interface) derives from winrt::Windows::Foundation::IUnknown. Puoi quindi chiamare IUnknown::as (o IUnknown::try_as) sull'interfaccia per eseguire una query su altre interfacce o tipi proiettati, che potrai usare o restituire.So, you can call IUnknown::as (or IUnknown::try_as) on it to query for other projected types or interfaces, which you can also either use or return.

istringable.ToString();
IClosable iclosable = istringable.as<IClosable>();
iclosable.Close();

Se è necessario accedere a tutti i membri dell'implementazione e quindi restituire un'interfaccia a un chiamante, usa il modello di funzione winrt::make_self.If you need to access all of the implementation's members, and then later return an interface to a caller, then use the winrt::make_self function template. make_self restituisce winrt::com_ptr che esegue il wrapping del tipo di implementazione.make_self returns a winrt::com_ptr wrapping the implementation type. Puoi accedere ai membri di tutte le relative interfacce (usando l'operatore freccia), eseguirne la restituzione a un chiamante oppure chiamare as su di esso e restituire l'oggetto di interfaccia risultante a un chiamante.You can access the members of all of its interfaces (using the arrow operator), you can return it to a caller as-is, or you can call as on it and return the resulting interface object to a caller.

winrt::com_ptr<MyType> myimpl = winrt::make_self<MyType>();
myimpl->ToString();
myimpl->Close();
IClosable iclosable = myimpl.as<IClosable>();
iclosable.Close();

La classe MyType non fa parte della proiezione. È l'implementazione.The MyType class is not part of the projection; it's the implementation. In questo modo puoi tuttavia chiamare i relativi metodi di implementazione direttamente, senza il sovraccarico di una chiamata di funzione virtuale.But this way you can call its implementation methods directly, without the overhead of a virtual function call. Nell'esempio precedente, anche se MyType::ToString usa la stessa firma del metodo proiettato su IStringable, eseguiamo direttamente la chiamata al metodo non virtuale, senza attraversare l'interfaccia ABI (Application Binary Interface).In the example above, even though MyType::ToString uses the same signature as the projected method on IStringable, we're calling the non-virtual method directly, without crossing the application binary interface (ABI). L'oggetto com_ptr contiene semplicemente un puntatore allo struct MyType. Puoi quindi accedere anche a eventuali altri dettagli interni di MyType tramite la variabile myimpl e l'operatore freccia.The com_ptr simply holds a pointer to the MyType struct, so you can also access any other internal details of MyType via the myimpl variable and the arrow operator.

Se disponi di un oggetto di interfaccia che rappresenta un'interfaccia sull'implementazione, puoi tornare all'implementazione tramite il modello di funzione winrt::get_self.In the case where you have an interface object, and you happen to know that it's an interface on your implementation, then you can get back to the implementation using the winrt::get_self function template. Anche in questo caso, si tratta di una tecnica per evitare chiamate di funzione virtuali, che consente di passare direttamente all'implementazione.Again, it's a technique that avoids virtual function calls, and lets you get directly at the implementation.

Nota

Se non hai installato la versione 10.0.17763.0 o successiva di Windows SDK (Windows 10, versione 1809), devi chiamare winrt::from_abi anziché winrt::get_self.If you haven't installed the Windows SDK version 10.0.17763.0 (Windows 10, version 1809), or later, then you need to call winrt::from_abi instead of winrt::get_self.

Ecco un esempio.Here's an example. È disponibile un altro esempio in Implementare la classe di controllo personalizzata BgLabelControl.There's another example in Implement the BgLabelControl custom control class.

void ImplFromIClosable(IClosable const& from)
{
    MyType* myimpl = winrt::get_self<MyType>(from);
    myimpl->ToString();
    myimpl->Close();
}

Tuttavia, solo l'oggetto di interfaccia originale mantiene un riferimento.But only the original interface object holds on to a reference. Se vuoi mantenerlo, puoi chiamare com_ptr::copy_from.If you want to hold on to it, then you can call com_ptr::copy_from.

winrt::com_ptr<MyType> impl;
impl.copy_from(winrt::get_self<MyType>(from));
// com_ptr::copy_from ensures that AddRef is called.

Il tipo di implementazione stesso non deriva da winrt::Windows::Foundation::IUnknown, quindi non dispone di una funzione as.The implementation type itself doesn't derive from winrt::Windows::Foundation::IUnknown, so it has no as function. Anche in questo caso, come puoi vedere nella precedente funzione ImplFromIClosable, puoi accedere ai membri di tutte le relative interfacce.Even so, as you can see in the ImplFromIClosable function above, you can access the members of all of its interfaces. In questo caso, tuttavia, non restituire l'istanza del tipo di implementazione non elaborato al chiamante.But if you do that, then don't return the raw implementation type instance to the caller. Usa, invece, una delle tecniche già illustrate per restituire un'interfaccia proiettata oppure un elemento com_ptr.Instead, use one of the techniques already shown, and return a projected interface, or a com_ptr.

Se hai un'istanza del tipo di implementazione da passare a una funzione che prevede di ricevere il tipo proiettato corrispondente, puoi procedere con tale operazione, come illustrato nell'esempio di codice seguente.If you have an instance of your implementation type, and you need to pass it to a function that expects the corresponding projected type, then you can do so, as shown in the code example below. grazie a un operatore di conversione presente sul tipo di implementazione (purché il tipo di implementazione sia stato generato tramite lo strumento cppwinrt.exe).A conversion operator exists on your implementation type (provided that the implementation type was generated by the cppwinrt.exe tool) that makes this possible. Puoi passare un valore del tipo di implementazione direttamente a un metodo che prevede un valore del tipo proiettato corrispondente.You can pass an implementation type value directly to a method that expects a value of the corresponding projected type. Da una funzione membro del tipo di implementazione, puoi passare *this a un metodo che prevede un valore del tipo proiettato corrispondente.From an implementation type member function, you can pass *this to a method that expects a value of the corresponding projected type.

// 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);
}
...

Derivazione da un tipo con un costruttore non predefinitoDeriving from a type that has a non-default constructor

ToggleButtonAutomationPeer::ToggleButtonAutomationPeer(ToggleButton) è un esempio di costruttore non predefinito.ToggleButtonAutomationPeer::ToggleButtonAutomationPeer(ToggleButton) is an example of a non-default constructor. Non esiste alcun costruttore predefinito. Per creare una classe ToggleButtonAutomationPeer, è necessario passare un oggetto owner.There's no default constructor so, to construct a ToggleButtonAutomationPeer, you need to pass an owner. Di conseguenza, se esegui la derivazione da ToggleButtonAutomationPeer, è necessario fornire un costruttore che accetti un oggetto owner e lo passi alla base.Consequently, if you derive from ToggleButtonAutomationPeer, then you need to provide a constructor that takes an owner and passes it to the base. Di seguito è riportato l'aspetto previsto.Let's see what that looks like in practice.

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

Il costruttore generato per il tipo di implementazione ha un aspetto simile al seguente.The generated constructor for your implementation type looks like this.

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

A questo punto, non resta che passare tale parametro del costruttore alla classe di base.The only piece missing is that you need to pass that constructor parameter on to the base class. Ti ricordi il modello di polimorfismo F-bound accennato in precedenza?Remember the F-bound polymorphism pattern that we mentioned above? Dopo aver familiarizzato con i dettagli di tale modello in uso in C++/WinRT, potrai individuare il nome della classe di base o semplicemente esaminare il file di intestazione della classe implementazione.Once you're familiar with the details of that pattern as used by C++/WinRT, you can figure out what your base class is called (or you can just look in your implementation class's header file). Di seguito è indicato come chiamare il costruttore della classe in questo caso.This is how to call the base class constructor in this case.

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

Il costruttore della classe di base prevede l'uso di un oggetto ToggleButtonThe base class constructor expects a ToggleButton. e MySpecializedToggleButton è un elemento ToggleButton.And MySpecializedToggleButton is a ToggleButton.

Finché non apporterai la modifica descritta in precedenza (per passare tale parametro del costruttore alla classe di base), il compilatore contrassegnerà il costruttore e indicherà che un costruttore predefinito appropriato non è disponibile su un tipo denominato (in questo caso) MySpecializedToggleButtonAutomationPeer_base<MySpecializedToggleButtonAutomationPeer> .Until you make the edit described above (to pass that constructor parameter on to the base class), the compiler will flag your constructor and point out that there's no appropriate default constructor available on a type called (in this case) MySpecializedToggleButtonAutomationPeer_base<MySpecializedToggleButtonAutomationPeer>. Si tratta effettivamente della classe di base del tipo di implementazione.That's actually the base class of the bass class of your implementation type.

Spazi dei nomi: tipi proiettati, tipi di implementazione e factoryNamespaces: projected types, implementation types, and factories

Come hai già visto in questo argomento, una classe di runtime C++/WinRT esiste sotto forma di più classi C++ in più spazi dei nomi.As you've seen previously in this topic, a C++/WinRT runtime class exists in the form of more than one C++ class in more than one namespace. Di conseguenza, il nome MyRuntimeClass ha un determinato significato nello spazio dei nomi winrt::MyProject e un significato diverso nello spazio dei nomi winrt::MyProject::implementation.So, the name MyRuntimeClass has one meaning in the winrt::MyProject namespace, and a different meaning in the winrt::MyProject::implementation namespace. Presta attenzione allo spazio dei nomi attualmente presente nel contesto e quindi usa gli appositi prefissi se devi usare un nome di uno spazio dei nomi diverso.Be aware of which namespace you currently have in context, and then use namespace prefixes if you need a name from a different namespace. Esaminiamo più in dettaglio gli spazi dei nomi in questione.Let's look more closely at the namespaces in question.

  • winrt::MyProject.winrt::MyProject. Questo spazio dei nomi contiene tipi proiettati.This namespace contains projected types. Un oggetto di un tipo proiettato è un proxy. Si tratta essenzialmente di un puntatore intelligente a un oggetto di supporto, in cui l'oggetto può essere implementato all'intero del progetto o in un'altra unità di compilazione.An object of a projected type is a proxy; it's essentially a smart pointer to a backing object, where that backing object might be implemented here in your project, or it might be implemented in another compilation unit.
  • winrt::MyProject::implementation.winrt::MyProject::implementation. Questo spazio dei nomi contiene tipi di implementazione.This namespace contains implementation types. Un oggetto di un tipo di implementazione non è un puntatore, ma un valore. Si tratta di un oggetto completo dello stack C++.An object of an implementation type is not a pointer; it's a value—a full C++ stack object. Non devi creare un tipo di implementazione direttamente. In alternativa, chiama winrt::make passando il tipo di implementazione come parametro del modello.Don't construct an implementation type directly; instead, call winrt::make, passing your implementation type as the template parameter. Abbiamo visto esempi di winrt::make in azione in una sezione precedente di questo argomento ed è disponibile un altro esempio in Controlli XAML, binding a una proprietà C++/WinRT.We've shown examples of winrt::make in action previously in this topic, and there's another example in XAML controls; bind to a C++/WinRT property. Vedi anche Diagnosi delle allocazioni dirette.Also see Diagnosing direct allocations.
  • winrt::MyProject::factory_implementation.winrt::MyProject::factory_implementation. Questo spazio dei nomi contiene factory.This namespace contains factories. Un oggetto in questo spazio dei nomi supporta IActivationFactory.An object in this namespace supports IActivationFactory.

Questa tabella mostra la qualifica minima dello spazio dei nomi che è necessario usare in contesti diversi.This table shows the minimum namespace qualification you need to use in different contexts.

Spazio dei nomi incluso nel contestoThe namespace that's in context Per specificare il tipo proiettatoTo specify the projected type Per specificare il tipo di implementazioneTo specify the implementation type
winrt::MyProjectwinrt::MyProject MyRuntimeClass implementation::MyRuntimeClass
winrt::MyProject::implementationwinrt::MyProject::implementation MyProject::MyRuntimeClass MyRuntimeClass

Importante

Quando vuoi restituire un tipo proiettato dalla tua implementazione, presta attenzione a non creare istanze del tipo di implementazione scrivendo MyRuntimeClass myRuntimeClass;.When you want to return a projected type from your implementation, be careful not to instantiate the implementation type by writing MyRuntimeClass myRuntimeClass;. Le tecniche e il codice da usare per questo scenario sono illustrate nella precedente sezione Creazione di istanze e restituzione di interfacce e tipi di implementazione di questo argomento.The correct techniques and code for that scenario are shown previously in this topic in the section Instantiating and returning implementation types and interfaces.

Il problema relativo a MyRuntimeClass myRuntimeClass; in questo scenario è dato dal fatto che crea un oggetto winrt::MyProject::implementation::MyRuntimeClass nello stack.The problem with MyRuntimeClass myRuntimeClass; in that scenario is that it creates a winrt::MyProject::implementation::MyRuntimeClass object on the stack. Questo oggetto (un tipo di implementazione) si comporta in un certo senso come il tipo proiettato. È infatti possibile richiamare metodi su di esso nello stesso modo e può anche essere convertito in un tipo proiettato.That object (of implementation type) behaves like the projected type in some ways—you can invoke methods on it in the same way; and it even converts to a projected type. L'oggetto viene tuttavia distrutto, in base alle normali regole di C++, se l'ambito viene chiuso.But the object destructs, as per normal C++ rules, when the scope exits. Se pertanto hai restituito un tipo proiettato (un puntatore intelligente) all'oggetto, tale puntatore è ora scollegato.So, if you returned a projected type (a smart pointer) to that object, then that pointer is now dangling.

Questo tipo di bug di danneggiamento della memoria è difficile da diagnosticare.This memory corruption type of bug is difficult to diagnose. Pertanto, per le build di debug, un'asserzione C++/WinRT aiuta a rilevare questo errore usando un rilevatore di stack.So, for debug builds, a C++/WinRT assertion helps you catch this mistake, using a stack-detector. Tuttavia, poiché le coroutine sono allocate sull'heap, l'errore non viene rilevato facilmente se si trova all'interno di una coroutine.But coroutines are allocated on the heap, so you won't get help with this mistake if you make it inside a coroutine. Per altre informazioni, vedi Diagnosi delle allocazioni dirette.For more info, see Diagnosing direct allocations.

Uso di tipi proiettati e tipi di implementazione con varie funzionalità di C++/WinRTUsing projected types and implementation types with various C++/WinRT features

La tabella seguente elenca i vari punti in cui le funzionalità C++/WinRT prevedono l'uso di un tipo e il tipo previsto (proiettato, implementazione o entrambi).Here are various places where a C++/WinRT features expects a type, and what kind of type it expects (projected type, implementation type, or both).

FunzionalitàFeature Tipo accettatoAccepts NoteNotes
T (rappresenta un puntatore intelligente)T (representing a smart pointer) ProiettatoProjected Vedi l'avvertimento in Spazi dei nomi: tipi proiettati, tipi di implementazione e factory sull'uso del tipo di implementazione per errore.See the caution in Namespaces: projected types, implementation types, and factories about using the implementation type by mistake.
agile_ref<T> EntrambiBoth Se usi il tipo di implementazione, l'argomento del costruttore deve essere com_ptr<T>.If you use the implementation type, then the constructor argument must be com_ptr<T>.
com_ptr<T> ImplementazioneImplementation Se usi il tipo proiettato, viene generato questo errore: 'Release' is not a member of 'T'.Using the projected type generates the error: 'Release' is not a member of 'T'.
default_interface<T> EntrambiBoth Se usi il tipo di implementazione, viene restituita la prima interfaccia implementata.If you use the implementation type, then the first implemented interface is returned.
get_self<T> ImplementazioneImplementation Se usi il tipo proiettato, viene generato questo errore: '_abi_TrustLevel': is not a member of 'T'.Using the projected type generates the error: '_abi_TrustLevel': is not a member of 'T'.
guid_of<T>() EntrambiBoth Restituisce il GUID dell'interfaccia predefinita.Returns the GUID of the default interface.
IWinRTTemplateInterface<T>
ProiettatoProjected Se usi il tipo di implementazione, viene eseguita la compilazione, ma è un errore. Vedi l'avvertimento in Spazi dei nomi: tipi proiettati, tipi di implementazione e factory.Using the implementation type compiles, but it's a mistake—see the caution in Namespaces: projected types, implementation types, and factories.
make<T> ImplementazioneImplementation Se usi il tipo proiettato, viene generato questo errore: 'implements_type': is not a member of any direct or indirect base class of 'T'Using the projected type generates the error: 'implements_type': is not a member of any direct or indirect base class of 'T'
make_agile(T const&amp;) EntrambiBoth Se usi il tipo di implementazione, l'argomento deve essere com_ptr<T>.If you use the implementation type, then the argument must be com_ptr<T>.
make_self<T> ImplementazioneImplementation Se usi il tipo proiettato, viene generato questo errore: 'Release': is not a member of any direct or indirect base class of 'T'Using the projected type generates the error: 'Release': is not a member of any direct or indirect base class of 'T'
name_of<T> ProiettatoProjected Se usi il tipo di implementazione, ottieni il GUID sotto forma di stringa dell'interfaccia predefinita.If you use the implementation type, then you get the stringified GUID of the default interface.
weak_ref<T> EntrambiBoth Se usi il tipo di implementazione, l'argomento del costruttore deve essere com_ptr<T>.If you use the implementation type, then the constructor argument must be com_ptr<T>.

Acconsentire esplicitamente alla costruzione uniforme e all'accesso diretto all'implementazioneOpt in to uniform construction, and direct implementation access

Questa sezione illustra una funzionalità di C++/WinRT 2.0 per il consenso esplicito, anche se è abilitata per impostazione predefinita per i nuovi progetti.This section describes a C++/WinRT 2.0 feature that's opt-in, although it is enabled by default for new projects. Per un progetto esistente, devi acconsentire esplicitamente configurando lo strumento cppwinrt.exe.For an existing project, you'll need to opt in by configuring the cppwinrt.exe tool. In Visual Studio imposta la proprietà del progetto Proprietà comuni > C++/WinRT > Ottimizzato su .In Visual Studio, set project property Common Properties > C++/WinRT > Optimized to Yes. Questa operazione ha come risultato l'aggiunta di <CppWinRTOptimized>true</CppWinRTOptimized> al file del progettoThat has the effect of adding <CppWinRTOptimized>true</CppWinRTOptimized> to your project file. e ha lo stesso effetto dell'aggiunta dell'opzione quando viene richiamato cppwinrt.exe dalla riga di comando.And it has the same effect as adding the switch when invoking cppwinrt.exe from the command line.

L'opzione -opt[imize] abilita una funzionalità spesso definita come costruzione uniforme.The -opt[imize] switch enables what's often called uniform construction. Con la costruzione uniforme (o unificata), usi la proiezione di linguaggio C++/WinRT per creare e usare i tipi di implementazione (tipi implementati dal componente per l'utilizzo da parte delle applicazioni) in modo efficiente e senza difficoltà per il caricatore.With uniform (or unified) construction, you use the C++/WinRT language projection itself to create and use your implmentation types (types implemented by your component, for consumption by applications) efficiently and without any loader difficulties.

Prima di descrivere la funzionalità, è opportuno illustrare la situazione senza la costruzione uniforme.Before describing the feature, let's first show the situation without uniform construction. A tale scopo, è possibile iniziare con questa classe di esempio di Windows Runtime.To illustrate, we'll begin with this example Windows Runtime class.

// MyClass.idl
namespace MyProject
{
    runtimeclass MyClass
    {
        MyClass();
        void Method();
        static void StaticMethod();
    }
}

Se sei uno sviluppatore C++ che ha familiarità con l'uso della libreria C++/WinRT, puoi decidere di usare la classe in questo modo.As a C++ developer familiar with using the C++/WinRT library, you might want to use the class like this.

using namespace winrt::MyProject;

MyClass c;
c.Method();
MyClass::StaticMethod();

Questa decisione è assolutamente ragionevole, purché il codice di utilizzo mostrato non risieda nello stesso componente che implementa la classe.And that would be perfectly reasonable provided that the consuming code shown didn't reside within the same component that implements this class. Essendo una proiezione di linguaggio, C++/WinRT ti protegge come sviluppatore dall'interfaccia ABI, ovvero dall'interfaccia applicativa binaria basata su COM definita da Windows Runtime.As a language projection, C++/WinRT shields you as a developer from the ABI (the COM-based application binary interface that the Windows Runtime defines). C++/WinRT non chiama direttamente nell'implementazione, ma passa attraverso l'interfaccia ABI.C++/WinRT doesn't call directly into the implementation; it travels through the ABI.

Di conseguenza, nella riga di codice in cui costruisci un oggetto MyClass (MyClass c;), la proiezione C++/WinRT chiama RoGetActivationFactory per recuperare la class factory o la factory di attivazione e quindi usa tale factory per creare l'oggetto.Consequently, on the line of code where you're constructing a MyClass object (MyClass c;), the C++/WinRT projection calls RoGetActivationFactory to retrieve the class or activation factory, and then uses that factory to create the object. Analogamente, l'ultima riga usa la factory per creare quella che sembra una chiamata a un metodo statico.The last line likewise uses the factory to make what appears to be a static method call. Ciò richiede che la classe sia registrata e che il modulo implementi il punto di ingresso DllGetActivationFactory.All of this requires that your class be registered, and that your module implements the DllGetActivationFactory entry point. C++/WinRT dispone di una cache di factory molto rapida, quindi non si verificano problemi per un'applicazione che utilizza il componente.C++/WinRT has a very fast factory cache, so none of this causes a problem for an application consuming your component. La difficoltà è rappresentata dal fatto che, all'interno del componente, è stata appena eseguita un'operazione leggermente problematica.The trouble is that, within your component, you've just done something that's a little problematic.

In primo luogo, indipendentemente dalla rapidità della cache di factory di C++/WinRT, la chiamata tramite RoGetActivationFactory (o persino le successive chiamate tramite la cache di factory) sarà comunque più lenta rispetto alla chiamata diretta nell'implementazione.Firstly, no matter how fast the C++/WinRT factory cache is, calling through RoGetActivationFactory (or even subsequent calls through the factory cache) will always be slower than calling directly into the implementation. Una chiamata a RoGetActivationFactory seguita da IActivationFactory::ActivateInstance, a sua volta seguita da QueryInterface, non può ovviamente essere efficiente quanto l'uso di un'espressione C++ new per un tipo definito in locale.A call to RoGetActivationFactory followed by IActivationFactory::ActivateInstance followed by QueryInterface is obviously not going to be as efficient as using a C++ new expression for a locally-defined type. Pertanto, gli sviluppatori C++/WinRT esperti sono abituati a usare la funzione helper winrt::make o winrt::make_self per creare oggetti all'interno di un componente.As a consequence, seasoned C++/WinRT developers are accustomed to using the winrt::make or winrt::make_self helper functions when creating objects within a component.

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

Tuttavia, come puoi vedere, non si tratta di una soluzione comoda o breve.But, as you can see, that's not nearly as convenient nor concise. Devi usare una funzione helper per creare l'oggetto e distinguere il tipo di implementazione dal tipo proiettato.You must use a helper function to create the object, and you must also disambiguate between the implementation type and the projected type.

In secondo luogo, l'uso della proiezione per creare la classe implica che la relativa factory di attivazione venga memorizzata nella cache.Secondly, using the projection to create the class means that its activation factory will be cached. Questo è in genere il risultato da ottenere, ma se la factory risiede nello stesso modulo (DLL) che effettua la chiamata, in pratica hai bloccato la DLL impedendone lo scaricamento.Normally, that's what you want, but if the factory resides in the same module (DLL) that's making the call, then you've effectively pinned the DLL and prevented it from ever unloading. In molti casi questo aspetto non è importante, ma alcuni componenti di sistema devono supportare lo scaricamento.In many cases, that doesn't matter; but some system components must support unloading.

È a questo punto che entra in gioco il termine costruzione uniforme.This is where the term uniform construction comes in. Indipendentemente dal fatto che il codice di creazione risieda in un progetto che semplicemente utilizza la classe o nel progetto che effettivamente la implementa, puoi liberamente usare la stessa sintassi per creare l'oggetto.Regardless of whether creation code resides in a project that's merely consuming the class, or whether it resides in the project that's actually implementing the class, you can freely use the same syntax to create the object.

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

Quando crei il progetto del componente con l'opzione -opt[imize], la chiamata tramite la proiezione di linguaggio esegue la compilazione fino alla stessa chiamata efficiente alla funzione winrt::make che crea direttamente il tipo di implementazione.When you build your component project with the -opt[imize] switch, the call through the language projection compiles down to the same efficient call to the winrt::make function that directly creates the implementation type. Ciò rende la sintassi semplice e prevedibile, evita le ripercussioni sulle prestazioni derivanti dalla chiamata tramite la factory, nonché il blocco del componente nel corso del processo.That makes your syntax simple and predictable, it avoids any performance hit of calling through the factory, and it avoids pinning the component in the process. Oltre che per i progetti dei componenti, questo è utile anche per le applicazioni XAML.In addition to component projects, this is also useful for XAML applications. Ignorando RoGetActivationFactory per le classi implementate nella stessa applicazione, puoi crearle (senza che debbano essere registrate) negli stessi modi con cui puoi crearle quando si trovano all'esterno del componente.Bypassing RoGetActivationFactory for classes implemented in the same application allows you to construct them (without needing to be registered) in all the same ways you could if they were outside your component.

La costruzione uniforme si applica a qualsiasi chiamata gestita dalla factory dietro le quinte.Uniform construction applies to any call that's served by the factory under the hood. In pratica, ciò significa che l'ottimizzazione riguarda sia i costruttori che i membri statici.Practically, that means that the optimization serves both constructors and static members. Di seguito è riportato di nuovo tale esempio originale.Here's that original example again.

MyClass c;
c.Method();
MyClass::StaticMethod();

Senza -opt[imize], la prima e l'ultima istruzione richiedono chiamate tramite l'oggetto factory.Without -opt[imize], the first and last statements require calls through the factory object. Con -opt[imize], nessuna delle due istruzioni le richiede.With -opt[imize], neither of them do. Tali chiamate inoltre vengono compilate direttamente a fronte dell'implementazione e possono persino essere impostate come inline.And those calls are compiled directly against the implementation, and even have the potential to be inlined. A questo punto entra in campo l'altro termine spesso usato quando si parla di -opt[imize], ovvero l'accesso diretto all'implementazione.Which speaks to the other term often used when talking about -opt[imize], namely direct implementation access.

Le proiezioni di linguaggio sono pratiche da usare ma, quando hai la possibilità di accedere direttamente all'implementazione, puoi e anzi devi sfruttare tale opportunità per produrre il codice più efficiente possibile.Language projections are convenient but, when you can directly access the implementation, you can and should take advantage of that to produce the most efficient code possible. C++/WinRT può eseguire automaticamente questa operazione, senza obbligarti a rinunciare alla sicurezza e alla produttività della proiezione.C++/WinRT can do that for you, without forcing you to leave the safety and productivity of the projection.

Si tratta di un'importante novità perché il componente deve cooperare per consentire alla proiezione di linguaggio di raggiungere e accedere direttamente ai relativi tipi di implementazione.This is a breaking change because the component must cooperate in order to allow the language projection to reach in and directly access its implementation types. Poiché C++/WinRT è una libreria costituita solo da un'intestazione, puoi esaminarne l'interno e vedere cosa accade.As C++/WinRT is a header-only library, you can look inside and see what's going on. Senza -opt[imize], il costruttore MyClass e il membro StaticMethod sono definiti dalla proiezione come mostrato di seguito.Without -opt[imize], the MyClass constructor, and StaticMethod member, are defined by the projection like this.

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

Non è necessario seguire tutti i dettagli sopra riportati. L'obiettivo è mostrare che entrambe le chiamate comportano una chiamata a una funzione denominata call_factory.It's not necessary to follow all of the above; the intent is to show that both calls involve a call to a function named call_factory. Questo indica che tali chiamate coinvolgono la cache di factory e che non accedono direttamente all'implementazione.That's your clue that these calls involve the factory cache, and they're not directly accessing the implementation. Con -opt[imize], queste stesse funzioni non sono definite in alcun modo.With -opt[imize], these same functions aren't defined at all. Vengono invece dichiarate dalla proiezione e spetta al componente impostarne le definizioni.Instead, they're declared by the projection, and their definitions are left up to the component.

Il componente può quindi fornire definizioni in grado di chiamare direttamente nell'implementazione.The component can then provide definitions that call directly into the implementation. È a questo punto che si giunge all'importante novità.Now we've arrived at the breaking change. Queste definizioni vengono generate automaticamente quando usi sia -component che -opt[imize] e si trovano in un file denominato Type.g.cpp, dove Type è il nome della classe di runtime che viene implementata.Those definitions are generated for you when you use both -component and -opt[imize], and they appear in a file named Type.g.cpp, where Type is the name of the runtime class being implemented. Questo è il motivo per cui possono verificarsi diversi errori del linker quando inizialmente abiliti -opt[imize] in un progetto esistente.That's why you may hit various linker errors when you first enable -opt[imize] in an existing project. Per correggere gli errori, devi includere nell'implementazione il file così generato.You need to include that generated file into your implementation in order to stitch things up.

Nell'esempio MyClass.h può risultare simile al codice riportato di seguito, indipendentemente dal fatto che venga usata o meno l'opzione -opt[imize].In our example, MyClass.h might look like this (regardless of whether -opt[imize] is being used).

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

Tutto inizia a funzionare in MyClass.cpp.Your MyClass.cpp is where it all comes together.

#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()
    {
    }
}

Pertanto, per usare la costruzione uniforme in un progetto esistente, devi modificare il file .cpp di ogni implementazione in modo da inserire #include <Sub/Namespace/Type.g.cpp> dopo l'inclusione (e la definizione) della classe di implementazione.So, to use uniform construction in an existing project, you need to edit each implementation's .cpp file so that you #include <Sub/Namespace/Type.g.cpp> after the inclusion (and definition) of the implementation class. Questo file fornisce le definizioni delle funzioni che la proiezione ha lasciato non definite.That file provides the definitions of those functions that the projection left undefined. All'interno del file MyClass.g.cpp tali definizioni sono simili a quanto mostrato di seguito.Here's what those definitions look like inside the MyClass.g.cpp file.

namespace winrt::MyProject
{
    MyClass::MyClass() :
        MyClass(make<MyProject::implementation::MyClass>())
    {
    }
    void MyClass::StaticMethod()
    {
        return MyProject::implementation::MyClass::StaticMethod();
    }
}

La proiezione viene così completata con chiamate efficienti direttamente nell'implementazione, vengono evitate chiamate alla cache di factory e il linker viene soddisfatto.And that nicely completes the projection with efficient calls directly into the implementation, avoiding calls to the factory cache, and satisfying the linker.

L'ultima operazione eseguita automaticamente da -opt[imize] è la modifica dell'implementazione del file module.g.cpp del progetto (ovvero del file che ti aiuta a implementare le esportazioni di DllGetActivationFactory e DllCanUnloadNow della DLL), in modo che le compilazioni incrementali tendano a essere notevolmente più rapide eliminando il rigido accoppiamento dei tipi richiesto da C++/WinRT 1.0.The final thing that -opt[imize] does for you is to change the implementation of your project's module.g.cpp (the file that helps you to implement your DLL's DllGetActivationFactory and DllCanUnloadNow exports) in such a way that incremental builds will tend to be much faster by eliminating the strong type coupling that was required by C++/WinRT 1.0. In questo caso si parla spesso di factory con cancellazione di tipi.This is often referred to as type-erased factories. Senza -opt[imize], il file module.g.cpp generato per il componente inizia includendo le definizioni di tutte le classi di implementazione, MyClass.h in questo esempio.Without -opt[imize], the module.g.cpp file that's generated for your component starts off by including the definitions of all of your implementation classes—the MyClass.h, in this example. Crea quindi direttamente la factory di implementazione per ogni classe come mostrato di seguito.It then directly creates the implementation factory for each class like this.

if (requal(name, L"MyProject.MyClass"))
{
    return winrt::detach_abi(winrt::make<winrt::MyProject::factory_implementation::MyClass>());
}

Anche in questo caso, non devi seguire tutti i dettagli.Again, you don't need to follow all of the details. È però utile vedere come ciò richieda la definizione completa di tutte le classi implementate dal componente.What's useful to see is that this requires the complete definition for any and all classes implemented by your component. Ciò può incidere in modo significativo sul ciclo interno, in quanto ogni modifica relativa a una singola implementazione causerà la ricompilazione di module.g.cpp.This can have a dramatic effect on your inner loop, as any change to a single implementation will cause module.g.cpp to recompile. Con -opt[imize] ciò non avviene più.With -opt[imize], this is no longer the case. Si possono invece notare due novità riguardanti il file module.g.cpp generato.Instead, two things happen to the generated module.g.cpp file. La prima è che il file non include più classi di implementazione.The first is that it no longer includes any implementation classes. In questo esempio non include MyClass.h.In this example, it won't include MyClass.h at all. Crea invece le factory di implementazione senza avere informazioni sulla relativa implementazione.Instead, it creates the implementation factories without any knowledge of their implementation.

void* winrt_make_MyProject_MyClass();
 
if (requal(name, L"MyProject.MyClass"))
{
    return winrt_make_MyProject_MyClass();
}

Non è ovviamente necessario includerne le definizioni e spetta al linker risolvere la definizione della funzione winrt_make_Component_Class.Obviously, there's no need to include their definitions, and it's up to the linker to resolve the winrt_make_Component_Class function's definition. Non devi quindi preoccuparti di questo aspetto perché anche il file MyClass.g.cpp che viene generato automaticamente, e che hai incluso in precedenza per supportare la costruzione uniforme, definisce tale funzione.Of course, you don't need to think about this, because the MyClass.g.cpp file that gets generated for you (and that you previously included in order to support uniform construction) also defines this function. Di seguito è riportato l'intero file MyClass.g.cpp generato per questo esempio.Here's the entirety of the MyClass.g.cpp file that's generated for this example.

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

Come puoi notare, la funzione winrt_make_MyProject_MyClass crea direttamente la factory dell'implementazione.As you can see, the winrt_make_MyProject_MyClass function directly creates your implementation's factory. Questo significa che puoi modificare tranquillamente qualsiasi implementazione senza che module.g.cpp debba essere ricompilato.This all means that you can happily change any given implementation, and the module.g.cpp needn't be recompiled at all. module.g.cpp verrà aggiornato e dovrà essere ricompilato solo se aggiungi o rimuovi classi di Windows Runtime.It's only when you add or remove Windows Runtime classes that module.g.cpp will be updated, and need to be recompiled.

Override dei metodi virtuali della classe di baseOverriding base class virtual methods

Una classe derivata può avere problemi con i metodi virtuali se la classe di base e quella derivata sono entrambe definite dall'app, mentre il metodo virtuale è definito in una classe padre di Windows Runtime.Your derived class can have issues with virtual methods if both the base and the derived class are app-defined classes, but the virtual method is defined in a grandparent Windows Runtime class. In pratica, ciò si verifica se le classi sono derivate da classi XAML.In practice, this happens if you derive from XAML classes. La parte restante di questa sezione è una continuazione dell'esempio riportato in Classi derivate.The rest of this section continues from the example in Derived classes.

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

La gerarchia è Windows::UI::Xaml::Controls::Page <- BasePage <- DerivedPage.The hierarchy is Windows::UI::Xaml::Controls::Page <- BasePage <- DerivedPage. Il metodo BasePage::OnNavigatedFrom esegue correttamente l'override di Page::OnNavigatedFrom, mentre DerivedPage::OnNavigatedFrom non esegue l'override di BasePage::OnNavigatedFrom.The BasePage::OnNavigatedFrom method correctly overrides Page::OnNavigatedFrom, but DerivedPage::OnNavigatedFrom doesn't override BasePage::OnNavigatedFrom.

In questo caso, DerivedPage riutilizza la vtable IPageOverrides di BasePage. Ciò significa che non riesce a eseguire l'override del metodo IPageOverrides::OnNavigatedFrom.Here, DerivedPage reuses the IPageOverrides vtable from BasePage, which means that it fails to override the IPageOverrides::OnNavigatedFrom method. Una possibile soluzione consiste nel definire BasePage come una classe modello e includerne l'intera implementazione in un file di intestazione, ma ciò ha l'effetto di complicare eccessivamente le cose.One potential solution requires BasePage to itself be a template class, and to have its implementation entirely in a header file, but that makes things unacceptably complicated.

Come soluzione alternativa, puoi dichiarare il metodo OnNavigatedFrom come esplicitamente virtuale nella classe di base.As a workaround, declare the OnNavigatedFrom method as explicitly virtual in the base class. In questo modo, quando la voce vtable per DerivedPage::IPageOverrides::OnNavigatedFrom chiama BasePage::IPageOverrides::OnNavigatedFrom, il producer chiama BasePage::OnNavigatedFrom e quest'ultimo, a causa della relativa virtualizzazione, finisce col chiamare DerivedPage::OnNavigatedFrom.That way, when the vtable entry for DerivedPage::IPageOverrides::OnNavigatedFrom calls BasePage::IPageOverrides::OnNavigatedFrom, the producer calls BasePage::OnNavigatedFrom, which (due to its virtualness), ends up calling DerivedPage::OnNavigatedFrom.

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

A tale scopo è necessario che tutti i membri della gerarchia di classi siano concordi sul valore restituito e sui tipi di parametro del metodo OnNavigatedFrom.This requires that all members of the class hierarchy agree on the return value and parameter types of the OnNavigatedFrom method. In caso contrario, devi usare la versione precedente come metodo virtuale ed eseguire il wrapping delle alternative.If they disagree, then you should use the version above as the virtual method, and wrap the alternates.

API importantiImportant APIs