Partilhar via


Acionando eventos em componentes do Tempo de Execução do Windows

Se o seu componente de Tempo de Execução do Windows gerar um evento de um tipo de delegado definido pelo usuário em um thread em segundo plano (thread de trabalho) e você desejar que o JavaScript possa receber o evento, poderá implementá-lo e/ou gerá-lo de uma destas maneiras:

  1. (Opção 1) Gerar o evento por meio do CoreDispatcher para realizar marshaling do evento para o contexto do thread JavaScript. Embora essa seja geralmente a melhor opção, em alguns cenários pode não oferecer o desempenho mais rápido.

  2. (Opção 2) Usar Windows.Foundation.EventHandler<Objeto> (mas perder informações do tipo de evento). Se a Opção 1 não for viável ou o desempenho não for suficiente, esta será uma boa segunda opção se a perda de informações do tipo for aceitável.

  3. (Opção 3) Criar seus próprios objetos COM proxy e stub para o componente. Essa opção é mais difícil de implementar, mas preserva informações do tipo e pode oferecer melhor desempenho em comparação com a Opção 1 em cenários mais exigentes.

Se você apenas gerar um evento em um thread em segundo plano sem usar uma dessas opções, um cliente JavaScript não receberá o evento.

Informações preliminares

Todos os aplicativos e componentes do Tempo de Execução do Windows são basicamente objetos COM, não importa o idioma que você usa para criá-los. Na API do Windows, a maioria dos componentes são objetos COM ágeis que podem se comunicar igualmente bem com objetos no thread em segundo plano e no thread de interface do usuário. Se um objeto COM não puder se tornar ágil, ele exigirá que objetos auxiliares, conhecidos como proxies e stubs, se comuniquem com outros objetos COM através do limite de thread em segundo plano do thread de interface do usuário. (Em termos de COM, isso é conhecido como uma comunicação entre thread apartments.)

A maioria dos objetos na API do Windows é ágil ou tem proxies e stubs internos. No entanto, os proxies e os stubs não podem ser criados para tipos genéricos, como Windows.Foundation.TypedEventHandler<TSender, TResult> porque não são tipos completo até que você forneça o argumento de tipo. É somente com clientes JavaScript que a falta de proxies ou stubs se torna um problema, mas se você quiser que seu componente possa ser usado em JavaScript tão bem quanto em C++ ou uma linguagem .NET, deverá usar uma das três opções a seguir.

(Opção 1) Gerar o evento por meio do CoreDispatcher

Você poderá enviar eventos de qualquer tipo de delegado definido pelo usuário usando o Windows.UI.Core.CoreDispatcher, e o JavaScript poderá recebê-los. Se você não tiver certeza sobre qual opção usar, tente usar esta primeiro. Se a latência entre o acionamento do evento e a manipulação do evento se tornar um problema, tente uma das outras opções.

O exemplo a seguir mostra como usar o CoreDispatcher para gerar um evento fortemente tipado. Observe que o argumento de tipo é Toast, não Object.


public event EventHandler<Toast> ToastCompletedEvent;
private void OnToastCompleted(Toast args)
{
    var completedEvent = ToastCompletedEvent;
    if (completedEvent != null)
    {
        completedEvent(this, args);
    }
}

public void MakeToastWithDispatcher(string message)
{
    Toast toast = new Toast(message);
    // Assume you have a CoreDispatcher at class scope.
    // Initialize it here, then use it from the background thread.
    var window = Windows.UI.Core.CoreWindow.GetForCurrentThread();
    m_dispatcher = window.Dispatcher;

    Task.Run( () =>
    {
        if (ToastCompletedEvent != null)
        {
            m_dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            new DispatchedHandler(() =>
            {
                this.OnToastCompleted(toast);
            })); // end m_dispatcher.RunAsync
         }
     }); // end Task.Run
}

(Opção 2) Usar EventHandler<Objeto> mas perder informações do tipo

Uma outra forma de enviar um evento de um segmento em segundo plano é usar Windows.Foundation.EventHandler<Object> como o tipo do evento. O Windows oferece essa instanciação concreta do tipo genérico e fornece um proxy e um stub para ela. O problema é que as informações de tipo de seus argumentos de evento e emissor são perdidas. Os clientes C++ e .NET devem saber pela documentação que tipo converter de volta a quando o evento é recebido. Os clientes JavaScript não precisam das informações do tipo original. Eles localizam as propriedades de argumento pelos seus nomes nos metadados.

Este exemplo mostra como usar Windows.Foundation.EventHandler<Object> em C#:

public sealed Class1
{
// Declare the event
public event EventHandler<Object> ToastCompletedEvent;

    // Raise the event
    public async void MakeToast(string message)
    {
        Toast toast = new Toast(message);
        // Fire the event from a background thread to allow this thread to continue 
        Task.Run(() =>
        {
            if (ToastCompletedEvent != null)
            {
                OnToastCompleted(toast);
            }
        });
    }

    private void OnToastCompleted(Toast args)
    {
        var completedEvent = ToastCompletedEvent;
        if (completedEvent != null)
        {
            completedEvent(this, args);
        }
    }
}

Você consome este evento no lado JavaScript da seguinte forma:

toastCompletedEventHandler: function (event) {
        
        var toastType = event.toast.toastType; 
        document.getElementById("toasterOutput").innerHTML = "<p>Made " + toastType + " toast</p>";
    },

(Opção 3) Criar seus próprios proxy e stub

Para ganhos potenciais de desempenho em tipos de eventos definidos pelo usuário que possuem informações de tipo totalmente preservadas, você precisa criar seus próprios objetos proxy e stub e inseri-los em seu pacote de aplicativo. Normalmente, você precisa usar esta opção somente em situações raras em que nenhuma das outras duas opções são adequadas. Além disso, não há nenhuma garantia de que esta opção fornecerá um desempenho melhor do que as outras duas. O desempenho real depende de vários fatores. Use o criador de perfil do Visual Studio ou outras ferramentas de criação de perfil para a medir o desempenho real em seu aplicativo e determinar se o evento é na verdade um afunilamento.

O restante deste artigo mostra como usar C# para criar um componente básico do Tempo de Execução do Windows e, em seguida, usar C++ para criar uma DLL para o proxy e o stub que permitirá que o JavaScript consuma um evento Windows.Foundation.TypedEventHandler<TSender, TResult>, que é gerado pelo componente em uma operação assíncrona. (Você também pode usar C++ ou Visual Basic para criar o componente. As etapas relacionadas à criação dos proxies e stubs são as mesmas.) Este passo a passo se baseia no exemplo Criando um componente em andamento do Tempo de Execução do Windows (C++/CX) e ajuda a explicar suas finalidades.

Este passo a passo contém estas partes:

  1. Criar o componente do Tempo de Execução do Windows: aqui você criará duas classes básicas do Tempo de Execução do Windows. Uma classe expõe um evento do tipo Windows.Foundation.TypedEventHandler<TSender, TResult> e a outra classe é o tipo que é retornado ao JavaScript como o argumento para TValue. Essas classes não podem se comunicar com o JavaScript até que você conclua as etapas posteriores.

  2. Programar o aplicativo JavaScript: este aplicativo ativa o objeto da classe principal, chama um método e manipula um evento que é gerado pelo componente do Tempo de Execução do Windows.

  3. Gerar GUIDs para as interfaces do componente: estes são exigidos pelas ferramentas que geram as classes de proxy e stub.

  4. Gerar um arquivo IDL para o componente: você usa o arquivo IDL para gerar o código-fonte C para o proxy e o stub.

  5. Compilar o código de proxy e stub em uma DLL

  6. Registrar e usar a DLL de proxy/stub: registrar os objetos de proxy/stub de modo que o tempo de execução COM possa encontrá-los e referenciar a DLL de proxy/stub no projeto de aplicativo.

Para criar o componente do Tempo de Execução do Windows

  1. Na barra de menus do Visual Studio, escolha Arquivo > Novo Projeto. Na caixa de diálogo Novo Projeto, expanda JavaScript > Windows Store e selecione Aplicativo em Branco. Nomeie o projeto como ToasterApplication e escolha o botão OK.

  2. Adicione um componente do Tempo de Execução do Windows C# à solução: no Gerenciador de Soluções, abra o menu de atalho da solução e escolha Adicionar> Novo Projeto. Expanda Visual C# > Windows Store e selecione Componente do Tempo de Execução do Windows. Nomeie o projeto como ToasterComponent e escolha o botão OK. ToasterComponent será o namespace raiz dos componentes que você criará em etapas posteriores.

    No Gerenciador de Soluções, abra o menu de atalho da solução e escolha Propriedades. Na caixa de diálogo Páginas de Propriedades, selecione Propriedades de Configuração no painel esquerdo e, na parte superior da caixa de diálogo, defina Configuração como Depurar e Plataforma como x86, x64 ou ARM. Escolha o botão OK.

    Importante

    Plataforma = Qualquer CPU não funcionará porque não é válido para a DLL Win32 de código nativo que você adicionará à solução posteriormente.

  3. No Gerenciador de Soluções, renomeie class1.cs como ToasterComponent.cs para que corresponda ao nome do projeto. O Visual Studio automaticamente renomeia a classe no arquivo para corresponder ao novo nome de arquivo.

  4. No arquivo .cs, adicione uma diretiva de uso para que o namespace Windows.Foundation traga TypedEventHandler para o escopo.

  5. Quando você precisar de proxies e stubs, o componente deverá usar interfaces para expor seus membros públicos. Em ToasterComponent.cs, defina uma interface para a torradeira (toaster) e outra para a torrada (toast) que a torradeira produz.

    Dica

    No C#, você pode ignorar esta etapa.Em vez disso, crie primeiro uma classe e depois abra seu menu de atalho e escolha Refatorar> Extrair Interface.No código que é gerado, ofereça manualmente acessibilidade às interfaces públicas.

    public interface IToaster
        {
            void MakeToast(String message);
            event TypedEventHandler<Toaster, Toast> ToastCompletedEvent;
    
        }
        public interface IToast
        {
            String ToastType { get; }
        }
    

    A interface IToast tem uma cadeia de caracteres que pode ser recuperada para descrever o tipo de torrada. A interface IToaster tem um método para fazer a torrada e um evento para indicar que ela foi feita. Como esse evento retorna a parte específica (isto é, o tipo) de torrada, ele é chamado de evento tipado.

  6. Em seguida, nós precisamos de classes que implementem essas interfaces, e que sejam públicas e lacradas para que possam ser acessadas no aplicativo JavaScript que você programará mais tarde.

    public sealed class Toast : IToast
        {
            private string _toastType;
    
            public string ToastType
            {
                get
                {
                    return _toastType;
                }
            }
            internal Toast(String toastType)
            {
                _toastType = toastType;
            }
    
        }
        public sealed class Toaster : IToaster
        {
            public event TypedEventHandler<Toaster, Toast> ToastCompletedEvent;
    
            private void OnToastCompleted(Toast args)
            {
                var completedEvent = ToastCompletedEvent;
                if (completedEvent != null)
                {
                    completedEvent(this, args);
                }
            }
    
            public void MakeToast(string message)
            {
                Toast toast = new Toast(message);
                // Fire the event from a thread-pool thread to enable this thread to continue 
                Windows.System.Threading.ThreadPool.RunAsync(
                (IAsyncAction action) =>
                {
                    if (ToastCompletedEvent != null)
                    {
                        OnToastCompleted(toast);
                    }
                });
            }
        }
    

    No código acima, criamos a torrada e acionamos um item de trabalho do pool de threads para disparar a notificação. Embora o IDE possa sugerir que você aplique a palavra-chave await à chamada assíncrona, ela não é necessária neste caso porque o método não realiza nenhum trabalho que dependa dos resultados da operação.

    Importante

    A chamada assíncrona no código anterior usa ThreadPool.RunAsync exclusivamente para demonstrar um modo simples de acionar o evento em um thread em plano de fundo. Você poderia escrever esse método específico como é mostrado no exemplo a seguir, e ele funcionaria bem porque o Agendador de Tarefas .NET automaticamente realiza marshaling de chamadas em espera/assíncronas para o thread de interface do usuário.

    public async void MakeToast(string message)
    {
        Toast toast = new Toast(message)
        await Task.Delay(new Random().Next(1000));
        OnToastCompleted(toast);
    }
    

    Se você compilar o projeto agora, ele deverá ser compilado corretamente.

Para programar o aplicativo JavaScript

  1. Agora nós podemos adicionar um botão ao aplicativo JavaScript para fazer com que ele use a classe que acabamos de definir para fazer a torrada. Antes de fazermos isso, devemos adicionar uma referência ao projeto ToasterComponent que acabamos de criar. No Gerenciador de Soluções, abra o menu de atalho para o projeto ToasterApplication, escolha Adicionar > Referências e, em seguida, escolha o botão Adicionar Nova Referência. Na caixa de diálogo Adicionar Referência, no painel esquerdo em Solução, selecione o projeto de componente e, no painel intermediário, selecione ToasterComponent. Escolha o botão OK.

  2. No Gerenciador de Soluções, abra o menu de atalho do projeto ToasterApplication e escolha Definir como Projeto de Inicialização.

  3. No final do arquivo default.js, adicione um namespace para conter as funções para chamar o componente e ser chamado novamente por ele. O namespace terá duas funções, uma para fazer a torrada e outra para manipular o evento de finalização da torrada. A implementação de makeToast cria um objeto Toaster, registra o manipulador de eventos e faz a torrada. Até agora, o manipulador de eventos não faz muito, como é mostrado aqui:

    WinJS.Namespace.define("ToasterApplication", {
            makeToast: function () {
    
                var toaster = new ToasterComponent.Toaster();
            //toaster.addEventListener("ontoastcompletedevent", ToasterApplication.toastCompletedEventHandler);
            toaster.ontoastcompletedevent = ToasterApplication.toastCompletedEventHandler;
            toaster.makeToast("Peanut Butter");
    
            },
    
            toastCompletedEventHandler: function (event) {
                // The sender of the event (the delegate’s first type parameter)
                // is mapped to event.target. The second argument of the delegate
                // is contained in event, which means event in this case is a 
                // Toast class, with a toastType string.
                var toastType = event.toastType;
    
                document.getElementById("toasterOutput").innerHTML = "<p>Made " + toastType + " toast</p>";
            },
    
        }); 
    
  4. A função makeToast deve ser vinculada a um botão. Atualize default.html para incluir um botão e algum espaço para gerar o resultado de fazer a torrada:

    <body>
        <h1>Click the button to make toast</h1>
        <button onclick="ToasterApplication.makeToast()">Make Toast!</button>
        <div id="toasterOutput">
            <p>No Toast Yet...</p>
        </div>
    </body>
    

    Se não estivéssemos usando um TypedEventHandler, poderíamos agora executar o aplicativo no computador local e clicar no botão para fazer a torrada. Mas, em nosso aplicativo, nada acontece. Para saber o motivo, vamos depurar o código gerenciado que aciona ToastCompletedEvent. Interrompa o projeto e, na barra de menus, escolha Depurar> Propriedades de Toaster Application. Altere Tipo de Depurador para Apenas Gerenciado. Novamente na barra de menus, escolha Depurar> Exceções e selecione Exceções do Common Language Runtime.

    Agora execute o aplicativo e clique no botão para fazer a torrada. O depurador captura uma exceção de conversão inválida. Embora não seja óbvio em sua mensagem, essa exceção está ocorrendo porque faltam proxies para essa interface.

    Debugger error window indicating missing proxy

A primeira etapa na criação de um proxy e um stub para um componente é adicionar uma ID ou um GUID exclusivo às interfaces. Entretanto, o formato de GUID a ser usado difere dependendo de você estar codificando em C#, Visual Basic, ou outra linguagem .NET, ou C++.

Para gerar GUIDs para as interfaces do componente

  • Para C#, Visual Basic ou outras linguagens .NET:

    1. Na barra de menus, escolha Ferramentas > Criar GUID. Na caixa de diálogo, selecione 5. [Guid(“xxxxxxxx-xxxx...xxxx)]. Selecione o botão de opção Novo GUID e o botão Copiar.

    2. Volte à definição da interface e, em seguida, cole o novo GUID imediatamente antes da interface IToaster, conforme é mostrado no exemplo a seguir. (Não use o GUID no exemplo. Cada interface exclusiva deve ter seu próprio GUID.)

      [Guid("FC198F74-A808-4E2A-9255-264746965B9F")]
          public interface IToaster...
      
    3. Adicione uma diretiva de uso para o namespace System.Runtime.InteropServices.

    4. Repita estas etapas para a interface IToast.

  • Para C++:

    1. Na barra de menus, escolha Ferramentas > Criar GUID. Na caixa de diálogo, selecione 3. static const struct GUID = {…}. Selecione o botão de opção Novo GUID e o botão Copiar.

      GUID Generator in Visual Studio

    2. Cole o GUID imediatamente antes da definição da interface IToaster. Após a colagem, o GUID deve se assemelhar ao exemplo a seguir. (Não use o GUID no exemplo. Cada interface exclusiva deve ter seu próprio GUID.)

      // {F8D30778-9EAF-409C-BCCD-C8B24442B09B}
          static const GUID <<name>> = { 0xf8d30778, 0x9eaf, 0x409c, { 0xbc, 0xcd, 0xc8, 0xb2, 0x44, 0x42, 0xb0, 0x9b } };
      
    3. Adicione uma diretiva de uso para Windows.Foundation.Metadata trazer GuidAttribute para o escopo.

    4. Agora converta manualmente o const GUID em um GuidAttribute de modo que ele seja formatado como mostrado no exemplo a seguir. Observe que as chaves são substituídas por colchetes e parênteses, e o ponto e vírgula à direita é removido.

      // {E976784C-AADE-4EA4-A4C0-B0C2FD1307C3}
          [GuidAttribute(0xe976784c, 0xaade, 0x4ea4, 0xa4, 0xc0, 0xb0, 0xc2, 0xfd, 0x13, 0x7, 0xc3)]
          public interface IToaster
          {...
      
    5. Repita estas etapas para a interface IToast.

Agora que as interfaces têm IDs exclusivas, podemos criar um arquivo IDL alimentando o arquivo .winmd na ferramenta de linha de comando winmdidl e, em seguida, gerar o código-fonte C para o proxy e o stub alimentando o arquivo IDL na ferramenta de linha de comando MIDL. O próprio Visual Studio fará isso se criarmos eventos de pós-compilação, conforme é mostrado nas etapas a seguir.

Para gerar o código-fonte de proxy e stub

  1. Para adicionar um evento de pós-compilação personalizado, no Gerenciador de Soluções, abra o menu de atalho do projeto ToasterComponent e escolha Propriedades. No painel esquerdo das páginas de propriedades, selecione Eventos de Compilação e escolha o botão Editar Pós-compilação. Adicione os comandos a seguir à linha de comando de pós-compilação. (O arquivo em lotes deve ser chamado primeiro para a definição das variáveis de ambiente para localizar a ferramenta winmdidl.)

    call "$(DevEnvDir)..\..\vc\vcvarsall.bat" $(PlatformName)
    winmdidl /outdir:output "$(TargetPath)"
    midl /metadata_dir "%WindowsSdkDir%References\CommonConfiguration\Neutral" /iid "$(ProjectDir)$(TargetName)_i.c" /env win32 /h "$(ProjectDir)$(TargetName).h" /winmd "Output\$(TargetName).winmd" /W1 /char signed /nologo /winrt /dlldata "$(ProjectDir)dlldata.c" /proxy "$(ProjectDir)$(TargetName)_p.c" "Output\$(TargetName).idl"
    
    
    

    Importante

    Para uma configuração de projeto ARM ou x64, altere o parâmetro /env MIDL para x64 ou arm32.

  2. Para assegurar que o arquivo IDL seja regenerado cada vez que o arquivo .winmd for modificado, altere Executar o evento de pós-compilação para Quando a compilação atualiza a saída do projeto.

    A página de propriedades Eventos de Compilação deve ser semelhante a esta:

    Post-build steps in Visual Studio property page

  3. Recompile a solução para gerar e compilar o IDL.

    Para verificar se o MIDL foi compilado corretamente, procure ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c e dlldata.c no diretório do projeto ToasterComponent.

Para compilar o código de proxy e stub em uma DLL

  1. Agora que você tem os arquivos necessários, poderá compilá-los para gerar uma DLL, que é um arquivo c++. Para tornar isso o mais fácil possível, adicione um novo projeto para dar suporte à compilação dos proxies. Abra o menu de atalho da solução ToasterApplication e escolha Adicionar > Novo Projeto. No painel esquerdo da caixa de diálogo Novo Projeto, expanda Visual C++ > Windows Store e, no painel intermediário, selecione DLL (aplicativos da Windows Store). (Observe que esse NÃO é um projeto de Componente do Tempo de Execução do Windows.) Nomeie o projeto como Proxies e escolha o botão OK. Esses arquivos serão atualizados pelos eventos de pós-compilação quando algo mudar na classe C#.

  2. Por padrão, o projeto Proxies gera arquivos de cabeçalho .h e arquivos .cpp C++. Como a DLL é compilada a partir dos arquivos gerados de MIDL, os arquivos .h e .cpp não são necessários. No Gerenciador de Soluções, abra o menu de atalho deles, escolha Remover e confirme a exclusão.

  3. Agora que o projeto está vazio, você pode adicionar novamente os arquivos gerados por MIDL. Abra o menu de atalho do projeto Proxies e escolha Adicionar > Item Existente. Na caixa de diálogo, navegue até o diretório do projeto ToasterComponent e selecione estes arquivos: ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c e dlldata.c. Escolha o botão Adicionar.

  4. No projeto Proxies, crie um arquivo .def para definir as exportações de DLL descritas em dlldata.c. Abra o menu de atalho do projeto e escolha Adicionar > Novo Item. No painel esquerdo da caixa de diálogo, selecione Código e, no painel intermediário, selecione Arquivo de Definição de Módulo. Nomeie o arquivo como proxies.def e escolha o botão Adicionar. Abra o arquivo .def e modifique-o para incluir as exportações (EXPORTS) que são definidas em dlldata.c:

    EXPORTS
        DllCanUnloadNow         PRIVATE
        DllGetClassObject       PRIVATE
    
  5. Se você compilar o projeto agora, ele falhará. Para compilá-lo corretamente, é necessário alterar a forma como o projeto é compilado e vinculado. No Gerenciador de Soluções, abra o menu de atalho do projeto Proxies e escolha Propriedades. Modifique as páginas de propriedades da seguinte maneira:

    • No painel esquerdo, selecione C/C++ > Pré-processador e, no painel direito, selecione Definições do Pré-processador, escolha o botão de seta para baixo e selecione Editar. Adicione estas definições na caixa:

      WIN32;_WINDOWS
      
    • Em C/C++ > Cabeçalhos Pré-Compilados, altere Cabeçalho Pré-compilado para Não Usar Cabeçalhos Pré-compilados e clique no botão Aplicar.

    • Em Vinculador > Geral, altere Ignorar Biblioteca de Importações para Sim e escolha o botão Aplicar.

    • Em Vinculador> Entrada, selecione Dependências Adicionais, escolha o botão de seta para baixo e selecione Editar. Adicione este texto na caixa:

      rpcrt4.lib;runtimeobject.lib
      

      Não cole essas bibliotecas diretamente na linha da lista. Use a caixa Editar para garantir que o MSBuild no Visual Studio manterá as dependências adicionais corretas.

    Depois de fazer essas alterações, escolha o botão OK na caixa de diálogo Páginas de Propriedades.

  6. Em seguida, use uma dependência no projeto ToasterComponent. Isso garante que o Toaster seja compilado antes da compilação do projeto de proxy. Isso é necessário porque o projeto Toaster é responsável por gerar os arquivos para compilar o proxy.

    Abra o menu de atalho do projeto Proxies e escolha Dependências do Projeto. Marque as caixas de seleção para indicar que o projeto Proxies depende do projeto ToasterComponent, para garantir que o Visual Studio compile-os na ordem correta.

  7. Para verificar se a compilação da solução está correta, escolha Compilar > Recompilar Solução na barra de menus do Visual Studio.

Para registrar o proxy e o stub

  1. No projeto ToasterApplication, abra o menu de atalho de package.appxmanifest e escolha Abrir com. Na caixa de diálogo Abrir com, selecione Editor de Texto XML e escolha o botão OK. Agora colaremos algum XML que forneça um registro de extensão windows.activatableClass.proxyStub e que seja baseado nos GUIDs do proxy. Para localizar os GUIDs a serem usados no arquivo .appxmanifest, abra ToasterComponent_i.c. Localize as entradas semelhantes às do exemplo a seguir. Observe também as definições de IToast, IToaster e uma terceira interface — um manipulador de eventos tipado que tem dois parâmetros: Toaster e Toast. Isso corresponde ao evento que é definido na classe Toaster. Observe que os GUIDs para IToast e IToaster correspondem aos GUIDs definidos nas interfaces do arquivo C#. Como a interface do manipulador de eventos tipado é gerada automaticamente, o GUID dessa interface também é gerado automaticamente.

    MIDL_DEFINE_GUID(IID, IID___FITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast,0x1ecafeff,0x1ee1,0x504a,0x9a,0xf5,0xa6,0x8c,0x6f,0xb2,0xb4,0x7d);
    
    
    MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToast,0xF8D30778,0x9EAF,0x409C,0xBC,0xCD,0xC8,0xB2,0x44,0x42,0xB0,0x9B);
    
    
    MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToaster,0xE976784C,0xAADE,0x4EA4,0xA4,0xC0,0xB0,0xC2,0xFD,0x13,0x07,0xC3);
    

    Agora podemos copiar os GUIDs, colá-los em package.appxmanifest em um nó que adicionamos e nomeamos como Extensions e depois reformatá-los. A entrada de manifesto é semelhante ao exemplo a seguir, mas, novamente, lembre-se de usar seus próprios GUIDs. Observe que o GUID de ClassId no XML é igual ao de ITypedEventHandler2. Isso ocorre porque o GUID é o primeiro que é listado em ToasterComponent_i.c. Os GUIDs aqui não diferenciam maiúsculas de minúsculas. Em vez de reformatar manualmente os GUIDs para IToast e IToaster, você pode voltar às definições de interface e obter o valor de GuidAttribute, que tem o formato correto. Em C++, há um GUID formatado corretamente no comentário. De qualquer forma, você deve reformatar manualmente o GUID usado para ClassId e o manipulador de eventos.

    <Extensions> <!—Use your own GUIDs!!!-->
        <Extension Category="windows.activatableClass.proxyStub">
          <ProxyStub ClassId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d">
            <Path>Proxies.dll</Path>
            <Interface Name="IToast" InterfaceId="F8D30778-9EAF-409C-BCCD-C8B24442B09B"/>
            <Interface Name="IToaster"  InterfaceId="E976784C-AADE-4EA4-A4C0-B0C2FD1307C3"/>  
            <Interface Name="ITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast" InterfaceId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d"/>
          </ProxyStub>      
        </Extension>
      </Extensions>
    

    Cole o nó XML Extensions como um filho direto do nó Package e um par, por exemplo, do nó Resources.

  2. Antes de avançarmos, é importante verificar se:

    • ProxyStub ClassId está definido para o primeiro GUID no arquivo ToasterComponent_i.c. Use o primeiro GUID definido nesse arquivo para classId. (Ele pode ser igual ao GUID de ITypedEventHandler2.)

    • Path é o caminho relativo do pacote de binário de proxy. (Neste passo a passo, proxies.dll está na mesma pasta que ToasterApplication.winmd.)

    • Os GUIDs estão no formato correto. (Isso é suscetível a erros.)

    • As IDs de interface no manifesto correspondem a IIDs no arquivo ToasterComponent_i.c.

    • Os nomes de interface são exclusivos no manifesto. Como eles não são usados pelo sistema, você pode escolher os valores. É uma boa prática escolher nomes de interface que correspondam claramente às interfaces que você definiu. Para as interfaces geradas, os nomes devem trazer essa indicação. Você poderá usar o arquivo ToasterComponent_i.c para ajudá-lo a gerar nomes de interface.

  3. Se você tentar executar agora a solução, receberá um erro informando que proxies.dll não faz parte da carga. Abra o menu de atalho da pasta Referências no projeto ToasterApplication e escolha Adicionar Referência. Marque a caixa de seleção ao lado do projeto Proxies. Além disso, verifique se a caixa de seleção ao lado de ToasterComponent também está selecionada. Escolha o botão OK.

    O projeto agora deve ser compilado. Execute o projeto e verifique se você pode fazer a torrada.