Criar APIs com C++/WinRTAuthor APIs with C++/WinRT

Este tópico mostra como criar APIs C++/WinRT usando a struct base winrt::implements, direta ou indiretamente.This topic shows how to author C++/WinRT APIs by using the winrt::implements base struct, either directly or indirectly. Sinônimos parar criar, neste contexto, são produzir ou implementar.Synonyms for author in this context are produce, or implement. Este tópico aborda os seguintes cenários para a implementação de APIs em um tipo C++/WinRT, nesta ordem.This topic covers the following scenarios for implementing APIs on a C++/WinRT type, in this order.

Observação

Este tópico aborda os componentes do Windows Runtime, mas apenas no contexto do C++/WinRT.This topic touches on the subject of Windows Runtime components, but only in the context of C++/WinRT. Se você estiver buscando conteúdo sobre componentes do Windows Runtime que abrange todas as linguagens do Windows Runtime, consulte Componentes do Windows Runtime.If you're looking for content about Windows Runtime components that covers all Windows Runtime languages, then see Windows Runtime components.

  • Você não está criando uma classe do Windows Runtime (classe de tempo de execução); você apenas implementará uma ou mais interfaces do Windows Runtime para consumo local dentro de seu aplicativo.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. Você irá derivar diretamente de winrt::implements neste caso, e implementará funções.You derive directly from winrt::implements in this case, and implement functions.
  • Você está criando uma classe de runtime.You are authoring a runtime class. Você poderá estar criando um componente a ser consumido a partir de um aplicativo.You might be authoring a component to be consumed from an app. Ou você poderá estar criando um tipo a ser consumido a partir da interface do usuário XAML e, nesse caso, você estará implementando e consumindo uma classe de runtime dentro da mesma unidade de compilação.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. Nesses casos, você permitirá que as ferramentas gerem classes que derivam de winrt::implements.In these cases, you let the tools generate classes for you that derive from winrt::implements.

Em ambos os casos, o tipo que implementa as APIs C++/WinRT é denominado tipo de implementação.In both cases, the type that implements your C++/WinRT APIs is called the implementation type.

Importante

É importante distinguir o conceito de um tipo de implementação daquele de um tipo projetado.It's important to distinguish the concept of an implementation type from that of a projected type. O tipo projetado está descrito em Consumir APIs com C++/WinRT.The projected type is described in Consume APIs with C++/WinRT.

Se você não estiver criando uma classe de runtimeIf you're not authoring a runtime class

O cenário mais simples é aquele em que você implementará uma interface do Windows Runtime para consumo local.The simplest scenario is where you're implementing a Windows Runtime interface for local consumption. Você não precisa de uma classe de runtime; apenas uma classe C++ comum.You don't need a runtime class; just an ordinary C++ class. Por exemplo, você pode estar escrevendo um aplicativo com base em CoreApplication.For example, you might be writing an app based around CoreApplication.

Observação

Para saber mais sobre como instalar e usar a VSIX (Extensão do Visual Studio) para C++/WinRT e o pacote do NuGet (que juntos fornecem um modelo de projeto e suporte ao build), confira Suporte ao Visual Studio para 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.

No Visual Studio, o modelo de projeto do Aplicativo Core Windows Universal > do Visual Studio C++ > (C++/WinRT) ilustra o padrão CoreApplication.In Visual Studio, the Visual C++ > Windows Universal > Core App (C++/WinRT) project template illustrates the CoreApplication pattern. O padrão começa com a passagem de uma implementação de Windows::ApplicationModel::Core::IFrameworkViewSource para 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 a interface para criar a primeira exibição do aplicativo.CoreApplication uses the interface to create the app's first view. Conceitualmente, IFrameworkViewSource tem esta aparência.Conceptually, IFrameworkViewSource looks like this.

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

Conceitualmente, mais uma vez, a implementação de CoreApplication::Run faz isto.Again conceptually, the implementation of CoreApplication::Run does this.

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

Portanto, como desenvolvedor, implemente a interface IFrameworkViewSource.So you, as the developer, implement the IFrameworkViewSource interface. C++/WinRT têm o modelo de struct base winrt::implements para tornar mais fácil a implementação de uma interface (ou várias) sem precisar recorrer à programação de estilo 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. Você só precisa derivar o tipo de implementose, depois, implementar as funções da interface.You just derive your type from implements, and then implement the interface's functions. Veja aqui como fazer isso.Here's how.

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

Isso é feito pelo IFrameworkViewSource.That's taken care of IFrameworkViewSource. A próxima etapa será retornar um objeto que implementa a interface IFrameworkView.The next step is to return an object that implements the IFrameworkView interface. Você poderá optar por implementar essa interface no Aplicativo também.You can choose to implement that interface on App, too. Este próximo exemplo de código representa um aplicativo básico que receberá, pelo menos, uma janela em funcionamento na área de trabalho.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() {}
};
...

Como seu tipo de Aplicativo é um IFrameworkViewSource, você poderá passar apenas um para Executar.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 você estiver criando uma classe de tempo de execução em um componente do Windows RuntimeIf you're authoring a runtime class in a Windows Runtime component

Se o tipo estiver empacotado em um componente do Windows Runtime para consumo de um aplicativo, então ele precisará ser uma classe de tempo de execução.If your type is packaged in a Windows Runtime component for consumption from an application, then it needs to be a runtime class. Declare uma classe de runtime em um arquivo de IDL (linguagem IDL da Microsoft) (.idl) (confira Como fatorar classes de runtime em arquivos MIDL (.idl)).You declare a runtime class in a Microsoft Interface Definition Language (IDL) (.idl) file (see Factoring runtime classes into Midl files (.idl)).

Cada arquivo IDL resulta em um arquivo .winmd, e o Visual Studio mescla todos eles em um único arquivo com o mesmo nome do namespace raiz.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. O arquivo .winmd final será aquele que os consumidores de seu componente consultarão.That final .winmd file will be the one that the consumers of your component will reference.

Veja abaixo um exemplo de declaração de uma classe de runtime em um arquivo 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;
    }
}

Esse IDL declara uma classe do Windows Runtime (tempo de execução).This IDL declares a Windows Runtime (runtime) class. Uma classe de runtime é um tipo que pode ser ativado e consumido por meio de interfaces COM modernas, normalmente entre limites executáveis.A runtime class is a type that can be activated and consumed via modern COM interfaces, typically across executable boundaries. Quando você adiciona um arquivo IDL ao seu projeto e ao build, a cadeia de ferramentas C++/WinRT (midl.exe e cppwinrt.exe) gera um tipo de implementação.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. Para ver um exemplo do fluxo de trabalho do arquivo IDL em ação, consulte Controles XAML; associar a uma propriedade de C++/WinRT.For an example of the IDL file workflow in action, see XAML controls; bind to a C++/WinRT property.

Usando o exemplo de IDL acima, o tipo de implementação é um struct stub do C++ chamado winrt::MyProject::implementation::MyRuntimeClass nos arquivos de código fonte denominados \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.

O tipo de implementação tem esta aparência.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.

Observe o padrão de polimorfismo associado a F sendo usado (MyRuntimeClass usa a si mesmo como um argumento de modelo para sua base, MyRuntimeClassT).Note the F-bound polymorphism pattern being used (MyRuntimeClass uses itself as a template argument to its base, MyRuntimeClassT). Isso também é chamado de padrão de modelo curiosamente recorrente (CRTP).This is also called the curiously recurring template pattern (CRTP). Se você seguir a cadeia de herança para cima, você se deparará com 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...>

Então, nesse cenário, na raiz da hierarquia de herança está o modelo de struct base winrt::implements mais uma vez.So, in this scenario, at the root of the inheritance hierarchy is the winrt::implements base struct template once again.

Para saber mais, obter códigos e ver um passo a passo da criação APIs em um componente do Windows Runtime, confira Componentes do Windows Runtime com C++/WinRT e Criar eventos em C++/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 você estiver criando uma classe de runtime para ser referenciada em sua interface de usuário XAMLIf you're authoring a runtime class to be referenced in your XAML UI

Se o tipo é referenciado por sua interface de usuário XAML, ele precisará ser uma classe de runtime, mesmo que esteja no mesmo projeto que o 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. Embora eles geralmente sejam ativados entre limites executáveis, uma classe de runtime pode ser usada dentro da unidade de compilação que a implementa.Although they are typically activated across executable boundaries, a runtime class can instead be used within the compilation unit that implements it.

Nesse cenário, você estará criando e consumindo as APIs.In this scenario, you're both authoring and consuming the APIs. O procedimento para a implementação da classe de tempo de execução é basicamente o mesmo para um componente do Windows Runtime.The procedure for implementing your runtime class is essentially the same as that for a Windows Runtime component. Portanto, confira a seção anterior—Se você estiver criando uma classe de tempo de execução em um componente do Windows Runtime.So, see the previous section—If you're authoring a runtime class in a Windows Runtime component. O único detalhe que difere é que, no IDL, a cadeia de ferramentas C++/WinRT gera não apenas um tipo de implementação, mas também um tipo projetado.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. É importante entender que dizer apenas "MyRuntimeClass", neste cenário, pode ser ambíguo. Há várias entidades com esse nome e de tipos diferentes.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 é o nome de uma classe de tempo de execução.MyRuntimeClass is the name of a runtime class. Mas isso é uma abstração: que quer dizer declarado no IDL e implementado em alguma linguagem de programação.But this is really an abstraction: declared in IDL, and implemented in some programming language.
  • MyRuntimeClass é o nome do struct do C++ winrt::MyProject::implementation::MyRuntimeClass, que é a implementação C++/WinRT da classe de tempo de execução.MyRuntimeClass is the name of the C++ struct winrt::MyProject::implementation::MyRuntimeClass, which is the C++/WinRT implementation of the runtime class. Como vimos, se houver projetos de implementação e consumo separados, então, esse struct existirá somente no projeto de implementação.As we've seen, if there are separate implementing and consuming projects, then this struct exists only in the implementing project. Esse é o tipo de implementação ou a implementação.This is the implementation type, or the implementation. Esse tipo é gerado (pela ferramenta cppwinrt.exe) nos arquivos \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 é o nome do tipo projetado na forma do struct do C++ winrt::MyProject::MyRuntimeClass.MyRuntimeClass is the name of the projected type in the form of the C++ struct winrt::MyProject::MyRuntimeClass. Se houver projetos de implementação e consumo separados, então, esse struct existirá somente no projeto de consumo.If there are separate implementing and consuming projects, then this struct exists only in the consuming project. Esse é o tipo projetado ou a projeção.This is the projected type, or the projection. Esse tipo é gerado (por cppwinrt.exe) no arquivo \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 projetado e tipo de implementação

Estas são as partes do tipo projetado relevantes para este tópico.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();
    };
}

Para obter um exemplo de um passo a passo para implementar a interface INotifyPropertyChanged em uma classe de runtime, confira os controles XAML; associar a uma propriedade C++/WinRT.For an example walkthrough of implementing the INotifyPropertyChanged interface on a runtime class, see XAML controls; bind to a C++/WinRT property.

O procedimento para o consumo da classe de runtime neste cenário está descrito em Consumir APIs com C++/WinRT.The procedure for consuming your runtime class in this scenario is described in Consume APIs with C++/WinRT.

Como fatorar classes de runtime em arquivos MIDL (.idl)Factoring runtime classes into Midl files (.idl)

Os modelos de projeto e item do Visual Studio produzem um arquivo IDL separado para cada classe de runtime.The Visual Studio project and item templates produce a separate IDL file for each runtime class. Isso fornece uma correspondência lógica entre um arquivo IDL e seus arquivos de código-fonte gerados.That gives a logical correspondence between an IDL file and its generated source code files.

No entanto, se você consolidar todas as classes de runtime do projeto em um único arquivo IDL, isso poderá melhorar significativamente o tempo de build.However, if you consolidate all of your project's runtime classes into a single IDL file, then that can significantly improve build time. Se, de outra forma, você tiver dependências import complexas (ou circulares) entre eles, a consolidação poderá ser realmente necessária.If you would otherwise have complex (or circular) import dependencies among them, then consolidating may actually be necessary. Talvez você ache mais fácil criar e examinar as classes de runtime se elas estiverem juntas.And you may find it easier to author and review your runtime classes if they're together.

Construtores de classe de runtimeRuntime class constructors

Veja a seguir alguns pontos que devem ser observados nas listagens que vimos acima.Here are some points to take away from the listings we've seen above.

  • Cada construtor que você declarar em seu IDL faz com que um construtor seja gerado tanto no tipo de implementação quanto no tipo projetado.Each constructor you declare in your IDL causes a constructor to be generated on both your implementation type and on your projected type. Os construtores declarados de IDL são usados para consumir a classe de runtime de uma unidade de compilação diferente.IDL-declared constructors are used to consume the runtime class from a different compilation unit.
  • Quer você tenha ou não construtores declarados em IDL, uma sobrecarga de construtor que usa std::nullptr_t será gerada em no tipo projetado.Whether you have IDL-declared constructor(s) or not, a constructor overload that takes std::nullptr_t is generated on your projected type. Chamar o construtor std::nullptr_t é a primeira de duas etapas para consumir a classe de runtime na mesma unidade de compilação.Calling the std::nullptr_t constructor is the first of two steps in consuming the runtime class from the same compilation unit. Para obter mais detalhes e um exemplo de código, confira as informações sobre consumir APIs com C++/WinRT.For more details, and a code example, see Consume APIs with C++/WinRT.
  • Se você estiver consumindo a classe de runtime na mesma unidade de compilação, você também poderá implementar construtores não padrão diretamente no tipo de implementação (que está em 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).

Observação

Se você espera que sua classe de runtime seja consumida por uma unidade de compilação diferente (o que é comum), inclua construtores em seu IDL (pelo menos um construtor padrão).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). Ao fazer isso, você também terá uma implementação do alocador junto com seu tipo de implementação.By doing that, you'll also get a factory implementation alongside your implementation type.

Se você quiser criar e consumir a classe de runtime somente dentro da mesma unidade de compilação, não declare nenhum construtor no 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. Você não precisará de uma implementação de alocador, e ela não será gerada.You don't need a factory implementation, and one won't be generated. O construtor padrão do tipo de implementação será excluído, mas é possível editá-lo facilmente e usá-lo como padrão.Your implementation type's default constructor will be deleted, but you can easily edit it and default it instead.

Se quiser criar e consumir a classe de runtime somente dentro da mesma unidade de compilação, e precisar de parâmetros do construtor, crie s construtores necessários diretamente no tipo de implementação.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.

Métodos, propriedades e eventos de classe de runtimeRuntime class methods, properties, and events

Já vimos que o fluxo de trabalho é usar o IDL para declarar sua classe de runtime e seus membros e, em seguida, a ferramenta gera protótipos e implementações de stub para você.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. Quanto a esses protótipos gerados automaticamente para os membros da classe de runtime, você pode editá-los para que eles passem tipos diferentes dos tipos que você declarar em seu 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. No entanto, você poderá fazer isso somente enquanto o tipo que declarar no IDL possa ser encaminhado para o tipo que declarar na versão implementada.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.

Aqui estão alguns exemplos.Here are some examples.

  • Você pode relaxar os tipos de parâmetro.You can relax parameter types. Por exemplo, se no IDL seu método usar uma SomeClass, você poderá optar por alterá-la para IInspectable em sua implementação.For example, if in IDL your method takes a SomeClass, then you could choose to change that to IInspectable in your implementation. Isso funciona porque qualquer SomeClass pode ser encaminhada para IInspectable (o inverso, obviamente, não funcionaria).This works because any SomeClass can be forwarded to IInspectable (the reverse, of course, wouldn't work).
  • Você pode aceitar um parâmetro copiável por valor, em vez de por referência.You can accept a copyable parameter by value, instead of by reference. Por exemplo, altere SomeClass const& para SomeClass.For example, change SomeClass const& to SomeClass. Isso é necessário quando você precisa evitar capturar uma referência em uma corrotina (consulte Passagem de parâmetros).That's necessary when you need to avoid capturing a reference into a coroutine (see Parameter-passing).
  • Você pode relaxar o valor retornado.You can relax the return value. Por exemplo, você pode alterar void para winrt::fire_and_forget.For example, you can change void to winrt::fire_and_forget.

Os dois últimos são muito úteis quando você está escrevendo um manipulador de eventos assíncronos.The last two are very useful when you're writing an asynchronous event handler.

Criação de instâncias, retorno de tipos de implementação e interfacesInstantiating and returning implementation types and interfaces

Nesta seção, teremos como exemplo um tipo de implementação denominado MyType, que implementa as interfaces IStringable e IClosable.For this section, let's take as an example an implementation type named MyType, which implements the IStringable and IClosable interfaces.

Você pode derivar a MyType diretamente de winrt::implements (não é uma classe de 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(){}
};

Ou você poderá gerá-la em um IDL (esta é uma classe de 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();
    }    
}

Você não pode alocar diretamente seu tipo de implementação.You can't directly allocate your implementation type.

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

Porém, você pode ir de MyType a um objeto IStringable ou IClosable que você pode usar ou retornar como parte da projeção chamando o modelo de função 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 retorna a interface padrão do tipo de implementação.make returns the implementation type's default interface.

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

Observação

No entanto, se você estiver fazendo referência a seu tipo na interface de usuário XAML, haverá um tipo de implementação e um tipo projetado no mesmo projeto.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. Nesse caso, make retorna uma instância do tipo projetado.In that case, make returns an instance of the projected type. Para obter um exemplo de código desse cenário, confira os controles XAML; associar a uma propriedade C++/WinRT.For a code example of that scenario, see XAML controls; bind to a C++/WinRT property.

É possível apenas usar istringable (no exemplo de código acima) para chamar os membros da interface IStringable.We can only use istringable (in the code example above) to call the members of the IStringable interface. Mas a interface C++/WinRT (que é uma interface projetada) deriva de winrt::Windows::Foundation::IUnknown.But a C++/WinRT interface (which is a projected interface) derives from winrt::Windows::Foundation::IUnknown. Portanto, você poderá chamar IUnknown::as (ou IUnknown::try_as) nela para consultar outros tipos ou interfaces projetadas, que você também poderá usar ou retornar.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 você precisar acessar todos os membros da implementação, e depois retornar uma interface para um chamador, use o modelo de função 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 retorna um winrt::com_ptr embalando o tipo de implementação.make_self returns a winrt::com_ptr wrapping the implementation type. É possível acessar os membros de todas as suas interfaces (usando o operador de seta), retorná-los para o chamador em sua forma atual, ou poderá chamá-los como estão nelas e retornar o objeto de interface resultante para um chamador.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();

A classe MyType não é parte da projeção; ela é a implementação.The MyType class is not part of the projection; it's the implementation. Mas, dessa forma você poderá chamar seus métodos de implementação diretamente, sem a sobrecarga de uma chamada de função virtual.But this way you can call its implementation methods directly, without the overhead of a virtual function call. No exemplo acima, mesmo que MyType::ToString use a mesma assinatura do método projetado em IStringable, você chamará o método não virtual diretamente, sem cruzar a interface binária do aplicativo (ABI).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). O com_ptr simplesmente contém um ponteiro para o struct MyType, portanto você também poderá acessar qualquer outro detalhe interno de MyType por meio da variável myimpl e do operador de seta.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.

Nos casos em que você tem um objeto de interface e sabe que é uma interface na implementação, será possível voltar para a implementação usando o modelo de função 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. Novamente, esta é uma técnica que evita chamadas de função virtuais e permite obter diretamente na implementação.Again, it's a technique that avoids virtual function calls, and lets you get directly at the implementation.

Observação

Se você ainda não instalou o SDK do Windows, versão 10.0.17763.0 (Windows 10, versão 1809) ou posterior, você precisará chamar winrt::from_abi em vez de 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.

Veja um exemplo.Here's an example. Há outro exemplo no artigo Implementar a classe personalizada 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();
}

Mas somente o objeto original da interface mantém uma referência.But only the original interface object holds on to a reference. Se você quiser mantê-lo nele, poderá chamar 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.

O tipo de implementação em si não deriva de winrt::Windows::Foundation::IUnknown, portanto, ele não tem a função as.The implementation type itself doesn't derive from winrt::Windows::Foundation::IUnknown, so it has no as function. Mesmo assim, como você pode ver na função ImplFromIClosable acima, é possível acessar os membros de todas as suas interfaces.Even so, as you can see in the ImplFromIClosable function above, you can access the members of all of its interfaces. Porém, se você fizer isso, não retorne a instância do tipo de implementação bruta ao chamador.But if you do that, then don't return the raw implementation type instance to the caller. Em vez disso, use uma das técnicas já exibidas e retorne uma interface projetada ou um com_ptr.Instead, use one of the techniques already shown, and return a projected interface, or a com_ptr.

Se tiver uma instância do tipo de implementação e precisar passá-la para uma função que espera o tipo projetado correspondente, você poderá fazer isso como mostrado no código de exemplo abaixo.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. Há um operador de conversão no tipo de implementação (desde que o tipo de implementação tenha sido gerado pela ferramenta cppwinrt.exe), que torna isso possível.A conversion operator exists on your implementation type (provided that the implementation type was generated by the cppwinrt.exe tool) that makes this possible. Você poderá passar um valor do tipo de implementação diretamente para um método que espera um valor do tipo projetado correspondente.You can pass an implementation type value directly to a method that expects a value of the corresponding projected type. A partir de uma função de membro do tipo de implementação, você poderá passar *this para um método que espera um valor do tipo projetado correspondente.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);
}
...

Derivando de um tipo que tem um construtor não padrãoDeriving from a type that has a non-default constructor

ToggleButtonAutomationPeer::ToggleButtonAutomationPeer(ToggleButton) é um exemplo de um construtor não padrão.ToggleButtonAutomationPeer::ToggleButtonAutomationPeer(ToggleButton) is an example of a non-default constructor. Não há nenhum construtor padrão, portanto, para construir um ToggleButtonAutomationPeer, você precisará passar um owner.There's no default constructor so, to construct a ToggleButtonAutomationPeer, you need to pass an owner. Consequentemente, se derivar de ToggleButtonAutomationPeer, será necessário fornecer um construtor que leve um owner e passe-o para a base.Consequently, if you derive from ToggleButtonAutomationPeer, then you need to provide a constructor that takes an owner and passes it to the base. Vamos ver um exemplo disso na prática.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);
    };
}

O construtor gerado para o tipo de implementação tem esta aparência.The generated constructor for your implementation type looks like this.

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

A única informação ausente é que você precisa passar esse parâmetro do construtor para a classe base.The only piece missing is that you need to pass that constructor parameter on to the base class. Lembra-se do padrão de polimorfismo F associado, mencionado acima?Remember the F-bound polymorphism pattern that we mentioned above? Depois de se familiarizar com os detalhes desse padrão, conforme usado pelo C++/WinRT, você poderá descobrir como sua classe base é chamada (ou você poderá examinar apenas o arquivo de cabeçalho da classe de implementação).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). É como chamar o construtor da classe base nesse caso.This is how to call the base class constructor in this case.

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

O construtor de classe base espera um ToggleButton.The base class constructor expects a ToggleButton. E MySpecializedToggleButton é um ToggleButton.And MySpecializedToggleButton is a ToggleButton.

Até você fazer a edição descrita acima (para passar esse parâmetro do construtor para a classe base), o compilador sinalizará o construtor e mostrará que não há um construtor padrão apropriado disponível em um tipo chamado (neste 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>. Essa é, na verdade, a classe base da classe base do seu tipo de implementação.That's actually the base class of the bass class of your implementation type.

Namespaces: tipos projetados, tipos de implementação e fábricasNamespaces: projected types, implementation types, and factories

Como você viu anteriormente neste tópico, uma classe de runtime C++/WinRT existe na forma de mais de uma classe C++ em mais de um namespace.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. Sendo assim, o nome MyRuntimeClass tem um significado no namespace winrt::MyProject e um significado diferente no namespace 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. Observe qual namespace que você tem no contexto atualmente e, em seguida, use os prefixos de namespace se precisar de um nome de um namespace diferente.Be aware of which namespace you currently have in context, and then use namespace prefixes if you need a name from a different namespace. Vamos examinar com mais detalhes os namespaces em questão.Let's look more closely at the namespaces in question.

  • winrt::MyProject.winrt::MyProject. Este namespace contém tipos projetados.This namespace contains projected types. Um objeto de tipo projetado é um proxy; ele é essencialmente um ponteiro inteligente para um objeto de backup, podendo esse objeto de backup ser implementado aqui em seu projeto ou em outra unidade de compilação.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. Este namespace contém tipos de implementação.This namespace contains implementation types. Um objeto de um tipo de implementação não é um ponteiro; ele é um valor—um objeto de pilha C++ completo.An object of an implementation type is not a pointer; it's a value—a full C++ stack object. Não construa um tipo de implementação diretamente; em vez disso, chame winrt::make passando o tipo de implementação como o parâmetro de modelo.Don't construct an implementation type directly; instead, call winrt::make, passing your implementation type as the template parameter. Mostramos exemplos de winrt::make em ação anteriormente neste tópico, e há outro exemplo em Controles XAML; associar a uma propriedade de 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. Confira também Diagnosticar alocações diretas.Also see Diagnosing direct allocations.
  • winrt::MyProject::factory_implementation.winrt::MyProject::factory_implementation. Este namespace contém fábricas.This namespace contains factories. Um objeto no namespace dá suporte a IActivationFactory.An object in this namespace supports IActivationFactory.

Esta tabela mostra a qualificação de namespace mínima que você precisa usar em diferentes contextos.This table shows the minimum namespace qualification you need to use in different contexts.

O namespace que está no contextoThe namespace that's in context Para especificar o tipo projetadoTo specify the projected type Para especificar o tipo de implementaçãoTo specify the implementation type
winrt::MyProjectwinrt::MyProject MyRuntimeClass implementation::MyRuntimeClass
winrt::MyProject::implementationwinrt::MyProject::implementation MyProject::MyRuntimeClass MyRuntimeClass

Importante

Quando quiser retornar um tipo projetado de sua implementação, tenha cuidado para não criar uma instância do tipo de implementação escrevendo 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;. As técnicas e o código corretos para esse cenário são mostrados anteriormente neste tópico, na seção Criando instâncias e retornando tipos de implementação e interfaces.The correct techniques and code for that scenario are shown previously in this topic in the section Instantiating and returning implementation types and interfaces.

O problema com MyRuntimeClass myRuntimeClass; nesse cenário é que ele cria um objeto winrt::MyProject::implementation::MyRuntimeClass na pilha.The problem with MyRuntimeClass myRuntimeClass; in that scenario is that it creates a winrt::MyProject::implementation::MyRuntimeClass object on the stack. Esse objeto (do tipo de implementação) se comporta como o tipo projetado de algumas maneiras—é possível invocar métodos nele da mesma forma e ele até mesmo é convertido em um tipo de projetado.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. Mas o objeto é destruído, de acordo com as regras normais de C++, quando o escopo é encerrado.But the object destructs, as per normal C++ rules, when the scope exits. Portanto, se você tiver retornado um tipo projetado (um ponteiro inteligente) para o objeto, esse ponteiro estará pendente.So, if you returned a projected type (a smart pointer) to that object, then that pointer is now dangling.

Esse tipo de bug com corrupção de memória é difícil de diagnosticar.This memory corruption type of bug is difficult to diagnose. Portanto, para compilações de depuração, uma asserção C++/WinRT ajuda a identificar esse erro usando um detector de pilha.So, for debug builds, a C++/WinRT assertion helps you catch this mistake, using a stack-detector. No entanto, as corrotinas são alocadas no heap, de modo que você não obterá ajuda com esse erro se ele ocorrer dentro de uma corrotina.But coroutines are allocated on the heap, so you won't get help with this mistake if you make it inside a coroutine. Para saber mais, confira Diagnosticar alocações diretas.For more info, see Diagnosing direct allocations.

Usando tipos projetados e tipos de implementação com vários recursos de C++/WinRTUsing projected types and implementation types with various C++/WinRT features

Veja vários lugares onde um recurso de C++/WinRT espera um tipo e qual tipo ele espera (tipo projetado, tipo de implementação ou ambos).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).

RecursoFeature AceitaAccepts ObservaçõesNotes
T (representando um ponteiro inteligente)T (representing a smart pointer) ProjetadoProjected Consulte o aviso em Namespaces: tipos projetados, tipos de implementação e fábricas sobre o uso do tipo de implementação por engano.See the caution in Namespaces: projected types, implementation types, and factories about using the implementation type by mistake.
agile_ref<T> AmbosBoth Se você usar o tipo de implementação, o argumento do construtor deverá ser com_ptr<T>.If you use the implementation type, then the constructor argument must be com_ptr<T>.
com_ptr<T> ImplementaçãoImplementation Usar o tipo projetado gera o erro: 'Release' is not a member of 'T'.Using the projected type generates the error: 'Release' is not a member of 'T'.
default_interface<T> AmbosBoth Se você usar o tipo de implementação, a primeira interface implementada será retornada.If you use the implementation type, then the first implemented interface is returned.
get_self<T> ImplementaçãoImplementation Usar o tipo projetado gera o erro: '_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>() AmbosBoth Retorna o GUID da interface padrão.Returns the GUID of the default interface.
IWinRTTemplateInterface<T>
ProjetadoProjected Usar o tipo de implementação compila, mas isso é um erro—consulte o aviso em Namespaces: tipos projetados, tipos de implementação e fábricas.Using the implementation type compiles, but it's a mistake—see the caution in Namespaces: projected types, implementation types, and factories.
make<T> ImplementaçãoImplementation Usar o tipo projetado gera o erro: '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;) AmbosBoth Se você usar o tipo de implementação, o argumento deverá ser com_ptr<T>.If you use the implementation type, then the argument must be com_ptr<T>.
make_self<T> ImplementaçãoImplementation Usar o tipo projetado gera o erro: '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> ProjetadoProjected Se usar o tipo de implementação, você receberá o GUID em cadeias de caracteres da interface padrão.If you use the implementation type, then you get the stringified GUID of the default interface.
weak_ref<T> AmbosBoth Se você usar o tipo de implementação, o argumento do construtor deverá ser com_ptr<T>.If you use the implementation type, then the constructor argument must be com_ptr<T>.

Aceitar a construção uniforme e o acesso direto de implementaçãoOpt in to uniform construction, and direct implementation access

Esta seção descreve um recurso do C++/WinRT 2.0 que é aceito, embora esteja habilitado por padrão para novos projetos.This section describes a C++/WinRT 2.0 feature that's opt-in, although it is enabled by default for new projects. Para um projeto existente, será necessário aceitar configurando a ferramenta cppwinrt.exe.For an existing project, you'll need to opt in by configuring the cppwinrt.exe tool. No Visual Studio, defina a propriedade do projeto Common Properties > C++/WinRT > Optimized como Yes.In Visual Studio, set project property Common Properties > C++/WinRT > Optimized to Yes. Isso tem o efeito de adicionar <CppWinRTOptimized>true</CppWinRTOptimized> ao arquivo de projeto.That has the effect of adding <CppWinRTOptimized>true</CppWinRTOptimized> to your project file. E tem o mesmo efeito que adicionar a opção ao invocar cppwinrt.exe da linha de comando.And it has the same effect as adding the switch when invoking cppwinrt.exe from the command line.

A opção -opt[imize] habilita o que geralmente é chamado de construção uniforme.The -opt[imize] switch enables what's often called uniform construction. Com a construção uniforme (ou unificada), você usa a própria projeção de linguagem do C++/WinRT para criar e usar seus tipos de implementação (tipos implementados por seu componente, para consumo por aplicativos) com eficiência e sem nenhuma dificuldade de carregador.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.

Antes de descrever o recurso, vamos mostrar primeiro a situação sem a construção uniforme.Before describing the feature, let's first show the situation without uniform construction. Para ilustrar, começaremos com o exemplo da classe do 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();
    }
}

Conforme o desenvolvedor do C++ se familiariza com a biblioteca do C++/WinRT, convém usar a classe como esta.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();

E isso seria perfeitamente razoável, desde que o código de consumo mostrado não resida no mesmo componente que implementa essa classe.And that would be perfectly reasonable provided that the consuming code shown didn't reside within the same component that implements this class. Como uma projeção de linguagem, o C++/WinRT protege você, como desenvolvedor, da ABI (a interface binária de aplicativo baseada em COM definida pelo 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). O C++/WinRT não chama diretamente na implementação; ele viaja pela ABI.C++/WinRT doesn't call directly into the implementation; it travels through the ABI.

Consequentemente, na linha de código em que você está construindo um objeto MyClass (MyClass c;), a projeção do C++/WinRT chama RoGetActivationFactory para recuperar a classe ou a fábrica de ativação e, em seguida, o usa essa fábrica para criar o objeto.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. A última linha, da mesma forma, usa a fábrica para fazer o que parece ser uma chamada de método estático.The last line likewise uses the factory to make what appears to be a static method call. Tudo isso exige que as classes sejam registradas e que seu módulo implemente o ponto de entrada DllGetActivationFactory.All of this requires that your class be registered, and that your module implements the DllGetActivationFactory entry point. O C++/WinRT tem um cache de fábrica muito rápido; portanto, nada disso causa um problema para um aplicativo que consome seu componente.C++/WinRT has a very fast factory cache, so none of this causes a problem for an application consuming your component. O problema é que, em seu componente, você acabou de fazer algo um pouco problemático.The trouble is that, within your component, you've just done something that's a little problematic.

Em primeiro lugar, não importa a rapidez do cache de fábrica do C++/WinRT, chamar por meio de RoGetActivationFactory (ou até mesmo chamadas subsequentes por meio do cache de fábrica) sempre será mais lento do que chamar diretamente na implementação.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. Uma chamada a RoGetActivationFactory seguida por IActivationFactory::ActivateInstance seguido por QueryInterface não será obviamente tão eficiente quanto usar a expressão new em C++ para um tipo definido localmente.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. Como consequência, os desenvolvedores do C++/WinRT experientes estão acostumados a usar as funções auxiliares winrt::make ou winrt::make_self ao criar objetos dentro de um 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>() };

Mas, como você pode ver, isso não é tão conveniente nem conciso.But, as you can see, that's not nearly as convenient nor concise. É necessário usar uma função auxiliar para criar o objeto, além de desfazer a ambiguidade entre o tipo de implementação e o tipo projetado.You must use a helper function to create the object, and you must also disambiguate between the implementation type and the projected type.

Em segundo lugar, o uso da projeção para criar a classe significa que sua fábrica de ativação será armazenada em cache.Secondly, using the projection to create the class means that its activation factory will be cached. Normalmente, é isso o que você deseja, mas se a fábrica residir no mesmo módulo (DLL) que está realizando a chamada, você terá efetivamente fixado a DLL e impedido que ela nunca fosse descarregada.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. Em muitos casos, isso não importa; no entanto, alguns componentes do sistema devem dar suporte ao descarregamento.In many cases, that doesn't matter; but some system components must support unloading.

É aí que entra o termo construção uniforme.This is where the term uniform construction comes in. Independentemente de o código de criação residir em um projeto que simplesmente consuma a classe ou residir no projeto que realmente implemente a classe, você pode usar livremente a mesma sintaxe para criar o objeto.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 você cria seu projeto de componente com a opção -opt[imize], a chamada por meio da projeção de idioma é compilada para a mesma chamada eficiente à função winrt::make que cria diretamente o tipo de implementação.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. Isso torna sua sintaxe simples e previsível e evita qualquer impacto de chamar por meio da fábrica e a fixação do componente no 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. Além dos projetos de componente, isso também é útil para aplicativos XAML.In addition to component projects, this is also useful for XAML applications. Ignorar RoGetActivationFactory para classes implementadas no mesmo aplicativo permite que você as construa (sem precisar estar registrado) de todas as formas possíveis se elas estivessem fora do 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.

A construção uniforme aplica-se a qualquer chamada atendida pela fábrica nos bastidores.Uniform construction applies to any call that's served by the factory under the hood. Praticamente, isso significa que a otimização funciona tanto para construtores quanto para membros estáticos.Practically, that means that the optimization serves both constructors and static members. Veja o exemplo original novamente.Here's that original example again.

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

Sem -opt[imize], a primeira e a última instruções exigem chamadas por meio do objeto de fábrica.Without -opt[imize], the first and last statements require calls through the factory object. Com -opt[imize], nem uma delas exige.With -opt[imize], neither of them do. E essas chamadas são compiladas diretamente com relação à implementação e, inclusive, têm o potencial de serem embutidas.And those calls are compiled directly against the implementation, and even have the potential to be inlined. O que conversa com o outro termo frequentemente usado ao falar sobre -opt[imize], ou seja, o acesso direto de implementação.Which speaks to the other term often used when talking about -opt[imize], namely direct implementation access.

As projeções de linguagem são convenientes, mas, quando você pode acessar diretamente a implementação, você pode e deve aproveitar isso para produzir o código mais eficiente possível.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. O C++/WinRT pode fazer isso por você, sem forçá-lo a deixar a segurança e a produtividade da projeção.C++/WinRT can do that for you, without forcing you to leave the safety and productivity of the projection.

Essa é uma alteração da falha, porque o componente deve cooperar para permitir que a projeção de linguagem alcance e acesse diretamente seus tipos de implementação.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. Como o C++/WinRT é uma biblioteca somente de cabeçalho, você pode olhar para dentro e ver o que está acontecendo.As C++/WinRT is a header-only library, you can look inside and see what's going on. Sem -opt[imize], o construtor MyClass e o membro StaticMethod são definidos pela projeção como esta.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(); });
    }
}

Não é necessário seguir todas as instruções acima; a intenção é mostrar que as duas chamadas envolvem uma chamada para uma função chamada 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. Essa é a pista de que essas chamadas envolvem o cache de fábrica e não estão acessando diretamente a implementação.That's your clue that these calls involve the factory cache, and they're not directly accessing the implementation. Com -opt[imize], essas mesmas funções não são definidas.With -opt[imize], these same functions aren't defined at all. Em vez disso, elas são declaradas pela projeção e suas definições são deixadas até o componente.Instead, they're declared by the projection, and their definitions are left up to the component.

O componente pode, então, fornecer definições que chamam diretamente para a implementação.The component can then provide definitions that call directly into the implementation. Agora, chegamos à alteração da falha.Now we've arrived at the breaking change. Essas definições são geradas quando você usa -component e -opt[imize], e são exibidas em um arquivo chamado Type.g.cpp, em que Type é o nome da classe de runtime que está sendo implementada.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. É por isso que você pode atingir vários erros de vinculador quando habilita -opt[imize] pela primeira vez em um projeto existente.That's why you may hit various linker errors when you first enable -opt[imize] in an existing project. É necessário incluir esse arquivo gerado para sua implementação a fim de unir as coisas.You need to include that generated file into your implementation in order to stitch things up.

Em nosso exemplo, MyClass.h pode ter essa aparência (independentemente se -opt[imize] está sendo usado).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>
    {
    };
}

Seu MyClass.cpp é o local em que tudo isso se reúne.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()
    {
    }
}

Portanto, para usar a construção uniforme em um projeto existente, é necessário editar o arquivo .cpp de cada implementação para que você #include <Sub/Namespace/Type.g.cpp> depois da inclusão (e da definição) da classe de implementação.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. Esse arquivo fornece as definições dessas funções que a projeção deixou indefinida.That file provides the definitions of those functions that the projection left undefined. Veja a aparência dessas definições dentro do arquivo MyClass.g.cpp.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();
    }
}

E isso conclui perfeitamente a projeção com chamadas eficientes diretamente para a implementação, evitando chamadas ao cache de fábrica e atendendo ao vinculador.And that nicely completes the projection with efficient calls directly into the implementation, avoiding calls to the factory cache, and satisfying the linker.

A última coisa que o -opt[imize] faz por você é alterar a implementação do module.g.cpp do seu projeto (o arquivo que ajuda você a implementar as exportações DllGetActivationFactory e DllCanUnloadNow de sua DLL) de tal forma que os builds incrementais tendem a ser muito mais rápidos ao eliminar o acoplamento de tipo forte que era exigido pelo 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. Isso é geralmente conhecido como fábricas de tipo apagado.This is often referred to as type-erased factories. Sem -opt[imize], o arquivo module.g.cpp gerado para seu componente começa incluindo as definições de todas as suas classes de implementação—the MyClass.h, neste exemplo.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. Em seguida, ele cria diretamente a fábrica de implementação para cada classe como esta.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>());
}

Novamente, você não precisa seguir todos os detalhes.Again, you don't need to follow all of the details. É útil ver que isso requer a definição completa para todas as classes implementadas por seu componente.What's useful to see is that this requires the complete definition for any and all classes implemented by your component. Isso pode ter um efeito significativo sobre seu loop interno, uma vez que qualquer alteração em uma única implementação fará module.g.cpp ser recompilado.This can have a dramatic effect on your inner loop, as any change to a single implementation will cause module.g.cpp to recompile. Com o -opt[imize], isso não acontece mais.With -opt[imize], this is no longer the case. Em vez disso, duas coisas acontecem com o arquivo module.g.cpp gerado.Instead, two things happen to the generated module.g.cpp file. A primeira é que ele não inclui mais nenhuma classe de implementação.The first is that it no longer includes any implementation classes. Neste exemplo, ele não incluirá MyClass.h.In this example, it won't include MyClass.h at all. Em vez disso, ele cria as fábricas de implementação sem conhecer sua implementação.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();
}

Obviamente, não há necessidade de incluir suas definições, e cabe ao vinculador resolver a definição da função 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. É claro que você não precisa pensar nisso, porque o arquivo MyClass.g.cpp gerado para você (e que você anteriormente incluiu para dar suporte à construção uniforme) também define essa função.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. Veja a totalidade do arquivo MyClass.g.cpp gerado para este exemplo.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();
    }
}

Como você pode ver, a função winrt_make_MyProject_MyClass cria diretamente a fábrica de sua implementação.As you can see, the winrt_make_MyProject_MyClass function directly creates your implementation's factory. Isso tudo significa que você pode alterar qualquer implementação determinada e que o module.g.cpp não precisa ser recompilado.This all means that you can happily change any given implementation, and the module.g.cpp needn't be recompiled at all. Somente quando você adiciona ou remove classes do Windows Runtime que o module.g.cpp será atualizado e precisará ser recompilado.It's only when you add or remove Windows Runtime classes that module.g.cpp will be updated, and need to be recompiled.

Como substituir métodos virtuais da classe baseOverriding base class virtual methods

A classe derivada poderá ter problemas com métodos virtuais se a classe base e a derivada forem classes definidas pelo aplicativo, mas o método virtual for definido em uma classe avô do 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. Na prática, isso acontecerá se for feita a derivação de classes XAML.In practice, this happens if you derive from XAML classes. O restante desta seção continua após o exemplo dado em Classes derivadas.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);
    };
}

A hierarquia é Windows::UI::Xaml::Controls::Page <- BasePage <- DerivedPage.The hierarchy is Windows::UI::Xaml::Controls::Page <- BasePage <- DerivedPage. O método BasePage::OnNavigatedFrom substitui corretamente Page::OnNavigatedFrom, mas DerivedPage::OnNavigatedFrom não substitui BasePage::OnNavigatedFrom.The BasePage::OnNavigatedFrom method correctly overrides Page::OnNavigatedFrom, but DerivedPage::OnNavigatedFrom doesn't override BasePage::OnNavigatedFrom.

Aqui, DerivedPage reutiliza a vtable IPageOverrides de BasePage, o que significa que ela não substitui o método IPageOverrides::OnNavigatedFrom.Here, DerivedPage reuses the IPageOverrides vtable from BasePage, which means that it fails to override the IPageOverrides::OnNavigatedFrom method. Uma possível solução exige que a própria BasePage seja uma classe de modelo e tenha sua implementação inteiramente em um arquivo de cabeçalho, mas isso torna as coisas complicadas e inaceitáveis.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.

Como alternativa, declare o método OnNavigatedFrom como explicitamente virtual na classe base.As a workaround, declare the OnNavigatedFrom method as explicitly virtual in the base class. Dessa forma, quando a entrada de vtable para DerivedPage::IPageOverrides::OnNavigatedFrom chamar BasePage::IPageOverrides::OnNavigatedFrom, o produtor chamará BasePage::OnNavigatedFrom, que (devido à sua natureza virtual) acabará chamando 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);
    };
}

Isso exige que todos os membros da hierarquia de classe concordem com o valor retornado e os tipos de parâmetro do método OnNavigatedFrom.This requires that all members of the class hierarchy agree on the return value and parameter types of the OnNavigatedFrom method. Se eles discordarem, você deverá usar a versão acima como o método virtual e encapsular as alternativas.If they disagree, then you should use the version above as the virtual method, and wrap the alternates.

APIs importantesImportant APIs