Generación de eventos en componentes de Windows RuntimeRaising events in Windows Runtime components

Nota

Para obtener más información sobre la generación de eventos en un componente Windows Runtime de c++/WinRT , vea crear eventos en c++/WinRT.For more info about raising events in a C++/WinRT Windows Runtime Component, see Author events in C++/WinRT.

Si el componente de Windows Runtime genera un evento de un tipo delegado definido por el usuario en un subproceso en segundo plano (subproceso de trabajo) y desea que JavaScript pueda recibir el evento, puede implementarlo y/o generarlo de alguna de estas maneras.If your Windows Runtime component raises an event of a user-defined delegate type on a background thread (worker thread), and you want JavaScript to be able to receive the event, then you can implement and/or raise it in any one of these ways.

  • (Opción 1) Genere el evento a través de Windows. UI. Core. CoreDispatcher para calcular las referencias del evento en el contexto del subproceso de JavaScript.(Option 1) Raise the event through the Windows.UI.Core.CoreDispatcher to marshal the event to the JavaScript thread context. Aunque esta suele ser la mejor opción, en algunos escenarios es posible que no proporcione el rendimiento más rápido.Although typically this is the best option, in some scenarios it might not provide the fastest performance.
  • (Opción 2) Usar ** Windows. Foundation. EventHandler <Object> ** (pero perder la información de tipo de evento).(Option 2) Use Windows.Foundation.EventHandler<Object> (but lose the event type information). Si la opción 1 no es factible, o si su rendimiento no es el adecuado, se trata de una buena opción siempre que se acepte la pérdida de información de tipo.If Option 1 is not feasible, or if its performance is not adequate, then this is a good second choice provided that loss of type information is acceptable. Si va a crear un componente de Windows Runtime de C#, el tipo Windows. Foundation. <Object> EventHandler no está disponible; en su lugar, ese tipo se proyecta en System. EventHandler, por lo que debe usarlo en su lugar.If you're authoring a C# Windows Runtime Component, then the Windows.Foundation.EventHandler<Object> type is not available; instead, that type is projected to System.EventHandler, so you should use that instead.
  • (Opción 3) Crea tu propio proxy y código auxiliar para el componente.(Option 3) Create your own proxy and stub for the component. Esta opción es la más difícil de implementar, pero conserva la información de tipo y podría proporcionar un mejor rendimiento en comparación con la opción 1 en escenarios exigentes.This option is the most difficult to implement, but it preserves type information and might provide better performance compared to Option 1 in demanding scenarios.

Si solo se genera un evento en un subproceso en segundo plano sin usar una de estas opciones, un cliente de JavaScript no recibirá el evento.If you just raise an event on a background thread without using one of these options, a JavaScript client will not receive the event.

FondoBackground

Todos los componentes y aplicaciones de Windows Runtime son fundamentalmente objetos COM, independientemente del lenguaje que se usa para crearlos.All Windows Runtime components and apps are fundamentally COM objects, no matter what language you use to create them. En la API de Windows, la mayoría de los componentes son objetos COM ágiles que se pueden comunicar igual de bien con los objetos en el subproceso en segundo plano y en el subproceso de IU.In the Windows API, most of the components are agile COM objects that can communicate equally well with objects on the background thread and on the UI thread. Si no se puede realizar un objeto COM ágil, se requerirán objetos auxiliares conocidos como proxies y códigos auxiliares para comunicarse con otros objetos COM a través de los límites del subproceso de IU y el subproceso en segundo plano.If a COM object can’t be made agile, then it requires helper objects known as proxies and stubs to communicate with other COM objects across the UI thread-background thread boundary. (En términos COM, esto se conoce como una comunicación entre subprocesamientos controlados).(In COM terms, this is known as communication between thread apartments.)

La mayoría de los objetos de la API de Windows son ágiles o tienen proxies y códigos auxiliares integrados.Most of the objects in the Windows API are either agile or have proxies and stubs built in. Sin embargo, no se pueden crear proxies ni códigos auxiliares para los tipos genéricos como Windows.Foundation.TypedEventHandler<TSender, TResult> porque no son tipos completos hasta que se proporciona el argumento de tipo.However, proxies and stubs can’t be created for generic types such as Windows.Foundation.TypedEventHandler<TSender, TResult> because they are not complete types until you provide the type argument. Solo en el caso de los clientes de JavaScript la falta de proxys o códigos auxiliares se convierte en un problema, pero si deseas que tu componente se pueda usar desde JavaScript así como desde un lenguaje de .NET o C++, a continuación debes usar una de las tres opciones siguientes.It's only with JavaScript clients that the lack of proxies or stubs becomes an issue, but if you want your component to be usable from JavaScript as well as from C++ or a .NET language, then you must use one of the following three options.

(Opción 1) Generar el evento a través de CoreDispatcher(Option 1) Raise the event through the CoreDispatcher

Puede enviar eventos de cualquier tipo de delegado definido por el usuario mediante Windows.UI.Core.CoreDispatcher, y JavaScript podrá recibirlos.You can send events of any user-defined delegate type by using the Windows.UI.Core.CoreDispatcher, and JavaScript will be able to receive them. Si no estás seguro de qué opción usar, prueba con esta primero.If you are unsure which option to use, try this one first. Si la latencia entre la activación de los eventos y el controlador de eventos se convierte en un problema, prueba una de las otras opciones.If latency between the event firing and the event handling becomes an issue, then try one of the other options.

El siguiente ejemplo muestra cómo usar CoreDispatcher para generar un evento fuertemente tipado.The following example shows how to use the CoreDispatcher to raise a strongly-typed event. Observa que el argumento de tipo es Notificación del sistema, y no Objeto.Notice that the type argument is Toast, not 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
}

(Opción 2) Usar EventHandler<Object> y perder información de tipo(Option 2) Use EventHandler<Object> but lose type information

Nota

Si va a crear un componente de Windows Runtime de C#, el tipo Windows. Foundation. <Object> EventHandler no está disponible; en su lugar, ese tipo se proyecta en System. EventHandler, por lo que debe usarlo en su lugar.If you're authoring a C# Windows Runtime Component, then the Windows.Foundation.EventHandler<Object> type is not available; instead, that type is projected to System.EventHandler, so you should use that instead.

Otra manera de enviar un evento desde un subproceso en segundo plano es usar el objeto Windows. Foundation. EventHandler < > como el tipo del evento.Another way to send an event from a background thread is to use Windows.Foundation.EventHandler<Object> as the type of the event. Windows proporciona esta creación de instancias concreta del tipo genérico y proporciona un proxy y un código auxiliar para esta.Windows provides this concrete instantiation of the generic type and provides a proxy and stub for it. El inconveniente es que se pierde la información de tipo de tus argumentos de evento y el remitente.The downside is that the type information of your event args and sender is lost. Los clientes de C++ y. NET deben saber a través de la documentación qué tipo recuperar cuando se recibe el evento.C++ and .NET clients must know through documentation what type to cast back to when the event is received. Los clientes de JavaScript no necesitan la información de tipo original.JavaScript clients don’t need the original type information. Encuentran las propiedades de los argumentos en función de sus nombres en los metadatos.They find the arg properties, based on their names in the metadata.

En este ejemplo se muestra cómo usar Windows.Foundation.EventHandler<Object> en C#:This example shows how to use Windows.Foundation.EventHandler<Object> in 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);
        }
    }
}

Este evento se consume en el lado de JavaScript de la forma siguiente:You consume this event on the JavaScript side like this:

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

(Opción 3) Crear tu propio proxy y código auxiliar(Option 3) Create your own proxy and stub

Para posibles mejoras de rendimiento en los tipos de eventos definidos por el usuario que tienen información de tipo totalmente conservada, tienes que crear tus propios objetos proxy y de código auxiliar e incrustarlos en el paquete de la aplicación.For potential performance gains on user-defined event types that have fully-preserved type information, you have to create your own proxy and stub objects and embed them in your app package. Por lo general, tienes que usar esta opción solo en raras ocasiones donde ninguna de las dos opciones sea adecuada.Typically, you have to use this option only in rare situations where neither of the other two options are adequate. Además, no hay ninguna garantía de que esta opción proporcione un mejor rendimiento que las otras dos opciones.Also, there is no guarantee that this option will provide better performance than the other two options. El rendimiento real depende de muchos factores.Actual performance depends on many factors. Usar el generador de perfiles de Visual Studio u otras herramientas de generación de perfiles para determinar el rendimiento real de tu aplicación y si el evento es en realidad un cuello de botella.Use the Visual Studio profiler or other profiling tools to measure actual performance in your application and determine whether the event is in fact a bottleneck.

En el resto de este artículo se muestra cómo usar C# para crear un componente básico de Windows Runtime y, a continuación, usar C++ para crear un archivo DLL para que el proxy y el código auxiliar que habilitarán JavaScript puedan consumir un evento Windows.Foundation.TypedEventHandler<TSender, TResult> generado por el componente en una operación asincrónica.The rest of this article shows how to use C# to create a basic Windows Runtime component, and then use C++ to create a DLL for the proxy and stub that will enable JavaScript to consume a Windows.Foundation.TypedEventHandler<TSender, TResult> event that's raised by the component in an async operation. (También puedes usar C++ o Visual Basic para crear el componente.(You can also use C++ or Visual Basic to create the component. Los pasos que se relacionan con la creación de los servidores proxy y código auxiliar son los mismos). Este tutorial se basa en la creación de una muestra de componente en el proceso de Windows Runtime (C++ / CX) y ayuda a explica sus objetivos.The steps that are related to creating the proxies and stubs are the same.) This walkthrough is based on Creating a Windows Runtime in-process component sample (C++/CX) and helps explain its purposes.

Este tutorial tiene estas partes.This walkthrough has these parts.

  • Aquí crearás dos clases básicas de Windows Runtime.Here you will create two basic Windows Runtime classes. Una clase expone un evento de tipo Windows.Foundation.TypedEventHandler<TSender, TResult> y la otra clase es el tipo que se devuelve a JavaScript como argumento para TValue.One class exposes an event of type Windows.Foundation.TypedEventHandler<TSender, TResult> and the other class is the type that's returned to JavaScript as the argument for TValue. Estas clases no se pueden comunicar con JavaScript hasta que completes los pasos posteriores.These classes can't communicate with JavaScript until you complete the later steps.
  • Esta aplicación activa el objeto de clase principal, llama a un método y controla un evento generado por el componente de Windows Runtime.This app activates the main class object, calls a method, and handles an event that's raised by the Windows Runtime component.
  • Estos son necesarios para las herramientas que generan las clases de proxy y código auxiliar.These are required by the tools that generate the proxy and stub classes.
  • A continuación, usas el archivo IDL para generar el código fuente C para el proxy y el código auxiliar.You then use the IDL file to generate the C source code for the proxy and stub.
  • Registra los objetos proxy-código auxiliar para que el tiempo de ejecución de COM pueda encontrarlos y haz referencia a la DLL de proxy-código auxiliar en el proyecto de aplicación.Register the proxy-stub objects so that the COM runtime can find them, and reference the proxy-stub DLL in the app project.

Para crear el componente de Windows RuntimeTo create the Windows Runtime component

En la barra de menús de Visual Studio, elija archivo > nuevo proyecto.In Visual Studio, on the menu bar, choose File > New Project. En el cuadro de diálogo Nuevo proyecto , expande JavaScript > Universal de Windows y, a continuación, selecciona Aplicación vacía.In the New Project dialog box, expand JavaScript > Universal Windows and then select Blank App. Nombra el proyecto ToasterApplication y después selecciona el botón Aceptar.Name the project ToasterApplication and then choose the OK button.

Agregar un componente de Windows Runtime de C# a la solución: en el Explorador de soluciones, abre el menú contextual de la solución y, a continuación, elige Agregar > Nuevo proyecto.Add a C# Windows Runtime component to the solution: In Solution Explorer, open the shortcut menu for the solution and then choose Add > New Project. Expanda Visual C# > Microsoft Store y, a continuación, seleccione Windows Runtime componente.Expand Visual C# > Microsoft Store and then select Windows Runtime Component. Asigna al proyecto el nombre de ToasterComponent y después selecciona el botón Aceptar .Name the project ToasterComponent and then choose the OK button. ToasterComponent será el espacio de nombres de raíz para los componentes que crearás en pasos posteriores.ToasterComponent will be the root namespace for the components you will create in later steps.

En el Explorador de soluciones, abre el menú contextual para la solución y, a continuación, elige Propiedades.In Solution Explorer, open the shortcut menu for the solution and then choose Properties. En el cuadro de diálogo Páginas de propiedades, selecciona Propiedades de configuración en el panel izquierdo y luego, en la parte superior del cuadro de diálogo, establece Configuración en Depurar y Plataforma en x86, x64 o ARM.In the Property Pages dialog box, select Configuration Properties in the left pane, and then at the top of the dialog box, set Configuration to Debug and Platform to x86, x64, or ARM. Elija el botón Aceptar .Choose the OK button.

Importante   Plataforma = cualquier CPU no funcionará porque no es válido para el archivo DLL de Win32 de código nativo que agregará a la solución más adelante.Important Platform = Any CPU won’t work because it's not valid for the native-code Win32 DLL that you'll add to the solution later.

En el Explorador de soluciones, cambia el nombre class1.cs por ToasterComponent.cs para que coincida con el nombre del proyecto.In Solution Explorer, rename class1.cs to ToasterComponent.cs so that it matches the name of the project. Visual Studio cambia automáticamente el nombre de la clase en el archivo para que coincida con el nuevo nombre de archivo.Visual Studio automatically renames the class in the file to match the new file name.

En el archivo .cs, agrega una directiva using para el espacio de nombres Windows.Foundation para introducir TypedEventHandler en el ámbito.In the .cs file, add a using directive for the Windows.Foundation namespace to bring TypedEventHandler into scope.

Cuando necesitas proxies y códigos auxiliares, tu componente debe utilizar interfaces para exponer sus miembros públicos.When you require proxies and stubs, your component must use interfaces to expose its public members. En ToasterComponent.cs, define una interfaz para el notificador y otra para la notificación del sistema que produce el notificador.In ToasterComponent.cs, define an interface for the toaster, and another one for the Toast that the toaster produces.

Nota:   En C# puede omitir este paso.Note In C# you can skip this step. En su lugar, crea primero una clase y, a continuación, abre su menú contextual y elige Refactorizar > Extraer interfaz.Instead, first create a class, and then open its shortcut menu and choose Refactor > Extract Interface. En el código que se genera, otorga manualmente acceso público a las interfaces.In the code that's generated, manually give the interfaces public accessibility.

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

        }
        public interface IToast
        {
            String ToastType { get; }
        }

La interfaz de IToast tiene una cadena que se puede recuperar para describir el tipo de notificación del sistema.The IToast interface has a string that can be retrieved to describe the type of toast. La interfaz de IToaster tiene un método para realizar la notificación del sistema y un evento para indicar que se realiza la notificación del sistema.The IToaster interface has a method to make toast, and an event to indicate that the toast is made. Dado que este evento devuelve la parte determinada (es decir, el tipo) de la notificación del sistema, se conoce como un evento tipado.Because this event returns the particular piece (that is, type) of toast, it's known as a typed event.

A continuación, necesitamos clases que implementen estas interfaces y sean públicas y estén selladas de modo que sean accesibles desde la aplicación de JavaScript que programarás más adelante.Next, we need classes that implement these interfaces, and are public and sealed so that they are accessible from the JavaScript app that you'll program later.

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

En el código anterior, creamos la notificación del sistema y, a continuación, hacemos girar un elemento de trabajo del grupo de subprocesos para iniciar la notificación.In the preceding code, we create the toast and then spin up a thread-pool work item to fire the notification. Aunque el IDE podría sugerir que apliques la palabra clave "await" para la llamada asincrónica, no es necesario en este caso porque el método no realiza ningún trabajo que dependa de los resultados de la operación.Although the IDE might suggest that you apply the await keyword to the async call, it isn’t necessary in this case because the method doesn’t do any work that depends on the results of the operation.

Nota:   La llamada asincrónica en el código anterior usa ThreadPool. RunAsync únicamente para mostrar una manera sencilla de desencadenar el evento en un subproceso en segundo plano.Note The async call in the preceding code uses ThreadPool.RunAsync solely to demonstrate a simple way to fire the event on a background thread. Este método en concreto se podría escribir como se muestra en el ejemplo siguiente y funcionaría correctamente porque el programador de tareas de .NET automáticamente calcula la referencia de las llamadas asincrónicas y "await" al subproceso de IU.You could write this particular method as shown in the following example, and it would work fine because the .NET Task scheduler automatically marshals async/await calls back to the UI thread.  

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

Si compilas el proyecto ahora, se debería compilar limpiamente.If you build the project now, it should build cleanly.

Para programar la aplicación JavaScriptTo program the JavaScript app

Ahora puedes agregar un botón a la aplicación JavaScript para que utilice la clase que acabamos de definir para crear el Toast.Now we can add a button to the JavaScript app to cause it to use the class we just defined to make toast. Antes de hacer esto, debemos agregar una referencia al proyecto ToasterComponent que acabamos de crear.Before we do this, we must add a reference to the ToasterComponent project we just created. En Explorador de soluciones, abra el menú contextual del proyecto ToasterApplication, elija agregar > referenciasy, a continuación, elija el botón Agregar nueva referencia .In Solution Explorer, open the shortcut menu for the ToasterApplication project, choose Add > References, and then choose the Add New Reference button. En el cuadro de diálogo Agregar referencia, en el panel izquierdo bajo Solución, selecciona el proyecto de componente. A continuación, en el panel central, selecciona ToasterComponent.In the Add Reference dialog box, in the left pane under Solution, select the component project, and then in the middle pane, select ToasterComponent. Elija el botón Aceptar .Choose the OK button.

En Explorador de soluciones, abra el menú contextual del proyecto ToasterApplication y, a continuación, elija establecer como proyecto de inicio.In Solution Explorer, open the shortcut menu for the ToasterApplication project and then choose Set as Startup Project.

Al final del archivo default.js, agrega un espacio de nombres para las funciones que llamarán al componente y a las que el componente devolverán la llamada.At the end of the default.js file, add a namespace to contain the functions to call the component and be called back by it. El espacio de nombres tendrá dos funciones, una para crear el Toast y otra para controlar el evento de finalización de Toast.The namespace will have two functions, one to make toast and one to handle the toast-complete event. La implementación de makeToast crea un objeto tostador, registra el controlador de eventos y realiza la notificación del sistema.The implementation of makeToast creates a Toaster object, registers the event handler, and makes the toast. Hasta ahora, el controlador de eventos no hace mucho, tal como se muestra a continuación:So far, the event handler doesn’t do much, as shown here:

    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 in this case event is a
           // Toast class, with a toastType string.
           var toastType = event.toastType;

           document.getElementById('toastOutput').innerHTML = "<p>Made " + toastType + " toast</p>";
        },
    });

La función makeToast se debe enlazar a un botón.The makeToast function must be hooked up to a button. Actualice default.html para que incluya un botón y algo de espacio para generar el resultado de crear el Toast:Update default.html to include a button and some space to output the result of making toast:

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

Si no usamos un TypedEventHandler, ahora podremos ejecutar la aplicación en el equipo local y hacer clic en el botón para realizar la notificación del sistema.If we weren’t using a TypedEventHandler, we would now be able to run the app on the local machine and click the button to make toast. Pero en nuestra aplicación, no ocurre nada.But in our app, nothing happens. Para averiguar por qué, vamos a depurar el código administrado que activa el ToastCompletedEvent.To find out why, let’s debug the managed code that fires the ToastCompletedEvent. Detenga el proyecto y, a continuación, en la barra de menús, elija depurar propiedades de la > aplicación del tostador.Stop the project, and then on the menu bar, choose Debug > Toaster Application properties. Cambie tipo de depurador a solo administrado.Change Debugger Type to Managed Only. De nuevo en la barra de menús, elija depurar > excepcionesy, a continuación, seleccione excepciones de Common Language Runtime.Again on the menu bar, choose Debug > Exceptions, and then select Common Language Runtime Exceptions.

Ahora ejecuta la aplicación y haz clic en el botón para crear el Toast.Now run the app and click the make-toast button. El depurador detecta una excepción de conversión no válida.The debugger catches an invalid cast exception. Aunque en el mensaje no resulta evidente, esta excepción se produce porque faltan servidores proxy para esa interfaz.Although it’s not obvious from its message, this exception is occurring because proxies are missing for that interface.

falta el proxy

El primer paso para crear un servidor proxy y un código auxiliar para un componente es agregar un identificador o un GUID único a las interfaces.The first step in creating a proxy and stub for a component is to add a unique ID or GUID to the interfaces. No obstante, el formato del GUID depende de si el código se escribe en C#, Visual Basic, u otro lenguaje de .NET o C++.However, the GUID format to use differs depending on whether you're coding in C#, Visual Basic, or another .NET language, or in C++.

Para generar GUID para las interfaces del componente (C# y otros lenguajes .NET)To generate GUIDs for the component's interfaces (C# and other .NET languages)

En la barra de menús, elija herramientas > crear GUID.On the menu bar, choose Tools > Create GUID. En el cuadro de diálogo, seleccione 5.In the dialog box, select 5. [GUID ("xxxxxxxx-xxxx... XXXX ") ] .[Guid("xxxxxxxx-xxxx...xxxx")]. Elige el botón Nuevo GUID y, a continuación, elige el botón Copiar.Choose the New GUID button and then choose the Copy button.

herramienta Generador de GUID

Vuelva a la definición de la interfaz y, a continuación, pegue el nuevo GUID justo delante de la interfaz IToaster, tal como se muestra en el ejemplo siguiente.Go back to the interface definition, and then paste the new GUID just before the IToaster interface, as shown in the following example. (No use el GUID del ejemplo.(Don't use the GUID in the example. Cada interfaz debe tener su propio GUID).Every unique interface should have its own GUID.)

[Guid("FC198F74-A808-4E2A-9255-264746965B9F")]
        public interface IToaster...

Agregue una directiva using para el espacio de nombres System. Runtime. InteropServices.Add a using directive for the System.Runtime.InteropServices namespace.

Repita estos pasos para la interfaz IToast.Repeat these steps for the IToast interface.

Para generar GUID para las interfaces del componente (C++)To generate GUIDs for the component's interfaces (C++)

En la barra de menús, elija herramientas > crear GUID.On the menu bar, choose Tools > Create GUID. En el cuadro de diálogo, seleccione 3.In the dialog box, select 3. Static const struct GUID = {...}.static const struct GUID = {...}. Elige el botón Nuevo GUID y, a continuación, elige el botón Copiar.Choose the New GUID button and then choose the Copy button.

Pegue el GUID justo delante de la definición de la interfaz IToaster.Paste the GUID just before the IToaster interface definition. Después de pegarlo, el GUID debe parecerse al del ejemplo siguiente.After you paste, the GUID should resemble the following example. (No use el GUID del ejemplo.(Don't use the GUID in the example. Cada interfaz debe tener su propio GUID).Every unique interface should have its own GUID.)

// {F8D30778-9EAF-409C-BCCD-C8B24442B09B}
    static const GUID <<name>> = { 0xf8d30778, 0x9eaf, 0x409c, { 0xbc, 0xcd, 0xc8, 0xb2, 0x44, 0x42, 0xb0, 0x9b } };

Agregue una directiva using para Windows. Foundation. Metadata para traer GuidAttribute en el ámbito.Add a using directive for Windows.Foundation.Metadata to bring GuidAttribute into scope.

Ahora convierte manualmente el const GUID en un GuidAttribute para darle formato, tal como se muestra en el ejemplo siguiente.Now manually convert the const GUID to a GuidAttribute so that it's formatted as shown in the following example. Observe que las llaves se reemplazan por corchetes y paréntesis, y que se quita el punto y coma final.Notice that the curly braces are replaced with brackets and parentheses, and the trailing semicolon is removed.

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

Repita estos pasos para la interfaz IToast.Repeat these steps for the IToast interface.

Ahora que las interfaces tienen identificadores únicos, podemos crear un archivo IDL alimentando el archivo. winmd en la herramienta de línea de comandos winmdidl y, a continuación, generar el código fuente de C para el proxy y el código auxiliar alimentando ese archivo IDL en la herramienta de línea de comandos MIDL.Now that the interfaces have unique IDs, we can create an IDL file by feeding the .winmd file into the winmdidl command-line tool, and then generate the C source code for the proxy and stub by feeding that IDL file into the MIDL command-line tool. Visual Studio hace esto automáticamente si creamos eventos posteriores a la compilación, tal como se muestra en los pasos siguientes.Visual Studio do this for us if we create post-build events as shown in the following steps.

Para generar el código fuente del servidor proxy y del código auxiliarTo generate the proxy and stub source code

Para agregar un evento posterior a la compilación personalizado, en el Explorador de soluciones, abre el menú contextual del proyecto ToasterComponent y, a continuación, elige Propiedades.To add a custom post-build event, in Solution Explorer, open the shortcut menu for the ToasterComponent project and then choose Properties. En el panel izquierdo de las páginas de propiedades, selecciona Eventos de compilación y, a continuación, elige el botón Edición posterior a la compilación.In the left pane of the property pages, select Build Events, and then choose the Edit Post-build button. Agrega los comandos siguientes a la línea de comandos de ejecución posterior a la compilación.Add the following commands to the post-build command line. (Primero se debe llamar al archivo por lotes para establecer las variables de entorno para encontrar la herramienta winmdidl).(The batch file must be called first to set the environment variables to find the winmdidl tool.)

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    En el caso de una configuración de proyecto ARM o x64, cambie el parámetro MIDL/env a x64 o ARM32.Important  For an ARM or x64 project configuration, change the MIDL /env parameter to x64 or arm32.

Para asegurarse de que el archivo IDL se regenera cada vez que se cambia el archivo. winmd, cambie ejecutar el evento posterior a la compilación a cuando la compilación actualice la salida del proyecto.To make sure the IDL file is regenerated every time the .winmd file is changed, change Run the post-build event to When the build updates the project output. La página de propiedades eventos de compilación debe ser similar a esta:  eventos de compilaciónThe Build Events property page should resemble this: build events

Recompila la solución para generar y compilar el código IDL.Rebuild the solution to generate and compile the IDL.

Para comprobar que MIDL ha compilado la solución correctamente, busque ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c, y dlldata.c en el directorio del proyecto ToasterComponent.You can verify that MIDL correctly compiled the solution by looking for ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c, and dlldata.c in the ToasterComponent project directory.

Para compilar el servidor proxy y el código auxiliar en un archivo DLLTo compile the proxy and stub code into a DLL

Ahora que tiene los archivos necesarios, puede compilarlos para generar un archivo DLL, que es un archivo de C++.Now that you have the required files, you can compile them to produce a DLL, which is a C++ file. Para simplificar esta tarea, agregue un nuevo proyecto para compilar los servidores proxy.To make this as easy as possible, add a new project to support building the proxies. Abra el menú contextual de la solución ToasterApplication y, a continuación, elija agregar > nuevo proyecto.Open the shortcut menu for the ToasterApplication solution and then choose Add > New Project. En el panel izquierdo del cuadro de diálogo nuevo proyecto , expanda Visual C++ > > ventanas uniuniversales de Windowsy, a continuación, en el panel central, seleccione dll (aplicaciones para UWP).In the left pane of the New Project dialog box, expand Visual C++ > Windows > Univeral Windows, and then in the middle pane, select DLL (UWP apps). (Observe que este no es un proyecto de componente de Windows Runtime de C++). Asigne un nombre a los servidores proxy del proyecto y elija el botón Aceptar .(Notice that this is NOT a C++ Windows Runtime Component project.) Name the project Proxies and then choose the OK button. Estos archivos los actualizarán los eventos posteriores a la compilación cuando se produzca algún cambio en la clase de C#.These files will be updated by the post-build events when something changes in the C# class.

De forma predeterminada, el proyecto Proxies genera archivos .h de encabezado y archivos .cpp de C++.By default, the Proxies project generates header .h files and C++ .cpp files. Dado que un archivo DLL se compila a partir de los archivos generados desde MIDL, los archivos .h y .cpp no son necesarios.Because the DLL is built from the files produced from MIDL, the .h and .cpp files are not required. En Explorador de soluciones, abra el menú contextual para ellos, elija quitary, a continuación, confirme la eliminación.In Solution Explorer, open the shortcut menu for them, choose Remove, and then confirm the deletion.

Ahora que el proyecto está vacío, puede volver a agregar los archivos generados por MIDL.Now that the project is empty, you can add back the MIDL-generated files. Abra el menú contextual del proyecto proxies y, a continuación, elija agregar > elemento existente.Open the shortcut menu for the Proxies project, and then choose Add > Existing Item. En el cuadro de diálogo, navegue hasta el directorio del proyecto ToasterComponent y selecciona estos archivos: ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c y dlldata.c.In the dialog box, navigate to the ToasterComponent project directory and select these files: ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c, and dlldata.c files. Elija el botón Agregar.Choose the Add button.

En el proyecto Proxies, crea un archivo .def para definir las exportaciones de DLL descritas en dlldata.c.In the Proxies project, create a .def file to define the DLL exports described in dlldata.c. Abra el menú contextual del proyecto y, a continuación, elija agregar > nuevo elemento.Open the shortcut menu for the project, and then choose Add > New Item. En el panel izquierdo del cuadro de diálogo, seleccione Código y, a continuación, en el panel central, seleccione Archivo de definición de módulo.In the left pane of the dialog box, select Code and then in the middle pane, select Module-Definition File. Asigne al archivo el nombre proxies. def y, a continuación, elija el botón Agregar .Name the file proxies.def and then choose the Add button. Abre este archivo .def y modifícalo para que incluya las exportaciones definidas en dlldata.c:Open this .def file and modify it to include the EXPORTS that are defined in dlldata.c:

EXPORTS
    DllCanUnloadNow         PRIVATE
    DllGetClassObject       PRIVATE

Si compilas ahora el proyecto, se producirá un error.If you build the project now, it will fail. Para compilarlo correctamente, debes cambiar el modo en que se compila y se vincula.To correctly compile this project, you have to change how the project is compiled and linked. En Explorador de soluciones, abra el menú contextual del proyecto proxies y, a continuación, elija propiedades.In Solution Explorer, open the shortcut menu for the Proxies project and then choose Properties. Cambie las páginas de propiedades como se indica a continuación.Change the property pages as follows.

En el panel izquierdo, seleccione C/C++ > preprocesadory, a continuación, en el panel derecho, seleccione definiciones de preprocesador, elija el botón de flecha abajo y, a continuación, seleccione Editar.In the left pane, select C/C++ > Preprocessor, and then in the right pane, select Preprocessor Definitions, choose the down-arrow button, and then select Edit. Agregues estas definiciones al cuadro:Add these definitions in the box:

WIN32;_WINDOWS

En C/C++ > encabezados precompilados, cambie encabezado precompilado a no usar encabezados precompiladosy, a continuación, elija el botón aplicar .Under C/C++ > Precompiled Headers, change Precompiled Header to Not Using Precompiled Headers, and then choose the Apply button.

En vinculador > general, cambie omitir biblioteca de importación a yes y, a continuación, elija el botón aplicar .Under Linker > General, change Ignore Import Library to Yes, and then choose the Apply button.

En vinculador > entrada, seleccione dependencias adicionales, elija el botón de flecha abajo y, a continuación, seleccione Editar.Under Linker > Input, select Additional Dependencies, choose the down-arrow button, and then select Edit. Agrega este texto al cuadro:Add this text in the box:

rpcrt4.lib;runtimeobject.lib

No pegues estas bibliotecas directamente en la fila de la lista.Do not paste these libs directly into the list row. Utilice el cuadro de edición para asegurarse de que MSBuild en Visual Studio mantendrá las dependencias adicionales correctas.Use the Edit box to ensure that MSBuild in Visual Studio will maintain the correct additional dependencies.

Cuando haya realizado estos cambios, elija el botón Aceptar en el cuadro de diálogo páginas de propiedades .When you have made these changes, choose the OK button in the Property Pages dialog box.

A continuación, toma una dependencia del proyecto ToasterComponent.Next, take a dependency on the ToasterComponent project. Esto garantiza que el proyecto Toaster se compilará antes de que lo haga el proyecto Proxies.This ensures that the Toaster will build before the proxy project builds. Esto es necesario porque el proyecto Toaster es el responsable de generar los archivos para compilar el servidor proxy.This is required because the Toaster project is responsible for generating the files to build the proxy.

Abra el menú contextual del proyecto Proxies y, a continuación, elija Dependencias del proyecto.Open the shortcut menu for the Proxies project and then choose Project Dependencies. Active las casillas para indicar que el proyecto Proxies depende del proyecto ToasterComponent y garantizar que Visual Studio los compilará en el orden correcto.Select the check boxes to indicate that the Proxies project depends on the ToasterComponent project, to ensure that Visual Studio builds them in the correct order.

Compruebe que la solución se compila correctamente; para ello, elija Compilar > recompilar solución en la barra de menús de Visual Studio.Verify that the solution builds correctly by choosing Build > Rebuild Solution on the Visual Studio menu bar.

Para registrar el servidor proxy y el código auxiliarTo register the proxy and stub

En el proyecto ToasterApplication, abra el menú contextual de Package. appxmanifest y, a continuación, elija abrir con.In the ToasterApplication project, open the shortcut menu for package.appxmanifest and then choose Open With. En el cuadro de diálogo Abrir con, seleccione Editor de texto XML y, a continuación, elija el botón Aceptar .In the Open With dialog box, select XML Text Editor and then choose the OK button. Vamos a pegar algunos XML que proporciona un registro de la extensión Windows. activatableClass. proxyStub y que se basan en los GUID del proxy.We're going to paste in some XML that provides a windows.activatableClass.proxyStub extension registration and which are based on the GUIDs in the proxy. Para encontrar los GUID que se usarán en el archivo .appxmanifest, abra ToasterComponent_i.c.To find the GUIDs to use in the .appxmanifest file, open ToasterComponent_i.c. Busque entradas similares a las del ejemplo siguiente.Find entries that resemble the ones in the following example. Observe también las definiciones de IToast, IToaster y una tercera interfaz, un controlador de eventos con tipo que tiene dos parámetros: un sistema de notificación y una notificación del sistema.Also notice the definitions for IToast, IToaster, and a third interface—a typed event handler that has two parameters: a Toaster and Toast. Esto coincide con el evento que se define en la clase tostadora.This matches the event that's defined in the Toaster class. Tenga en cuenta que los GUID de IToast y IToaster coinciden con los GUID que se definen en las interfaces del archivo de C#.Notice that the GUIDs for IToast and IToaster match the GUIDs that are defined on the interfaces in the C# file. Dado que la interfaz del controlador de eventos con tipo se genera automáticamente, el GUID para esta interfaz también se genera automáticamente.Because the typed event handler interface is autogenerated, the GUID for this interface is also autogenerated.

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

Ahora copiaremos los GUID, los pegaremos en package. appxmanifest en un nodo que agregaremos y denominaremos extensiones y, a continuación, volveremos a darles formato.Now we copy the GUIDs, paste them in package.appxmanifest in a node that we add and name Extensions, and then reformat them. La entrada de manifiesto se parece a la del ejemplo siguiente, pero de nuevo, recuerde que debe usar sus propios GUID.The manifest entry resembles the following example—but again, remember to use your own GUIDs. Observa que el GUID del ClassId del archivo XML es el mismo que el de ITypedEventHandler2.Notice that the ClassId GUID in the XML is the same as ITypedEventHandler2. Esto se debe a que ese GUID es el primero que aparece en el archivo ToasterComponent_i.c.This is because that GUID is the first one that's listed in ToasterComponent_i.c. Aquí, los GUID no distinguen mayúsculas de minúsculas.The GUIDs here are case-insensitive. En lugar de cambiar manualmente el formato de los GUID para IToast y IToaster, puede volver a las definiciones de interfaz y obtener el valor de GuidAttribute, que tiene el formato correcto.Instead of manually reformatting the GUIDs for IToast and IToaster, you can go back into the interface definitions and get the GuidAttribute value, which has the correct format. En C++, hay un GUID con el formato correcto en el comentario.In C++, there is a correctly-formatted GUID in the comment. En cualquier caso, debes cambiar manualmente el formato del GUID que se utiliza para el ClassId y el controlador de eventos.In any case, you must manually reformat the GUID that's used for both the ClassId and the event handler.

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

Pegue el nodo XML de las extensiones como un elemento secundario directo del nodo paquete y un elemento del mismo nivel, por ejemplo, el nodo recursos.Paste the Extensions XML node as a direct child of the Package node, and a peer of, for example, the Resources node.

Antes de continuar, es importante que de asegure de que:Before moving on, it’s important to ensure that:

  • El ClassId ProxyStub se establece en el primer GUID del archivo ToasterComponent _ i. c.The ProxyStub ClassId is set to the first GUID in the ToasterComponent_i.c file. Use el primer GUID que aparece definido en ese archivo para el ClassId.Use the first GUID that's defined in this file for the classId. (Podría ser el mismo que el GUID de ITypedEventHandler2).(This might be the same as the GUID for ITypedEventHandler2.)
  • La ruta de acceso es la ruta de acceso relativa del paquete del proxy binario.The Path is the package relative path of the proxy binary. (En este tutorial, proxies.dll está en la misma carpeta que ToasterApplication.winmd).(In this walkthrough, proxies.dll is in the same folder as ToasterApplication.winmd.)
  • Los GUID están en el formato correcto.The GUIDs are in the correct format. (Es fácil equivocarse con esto).(This is easy to get wrong.)
  • Los identificadores de interfaz del manifiesto coinciden con los IID del _ archivo ToasterComponent i. c.The interface IDs in the manifest match the IIDs in ToasterComponent_i.c file.
  • Los nombres de interfaz son únicos en el manifiesto.The interface names are unique in the manifest. Dado que el sistema no utiliza estos nombres, puede elegir los que desees.Because these are not used by the system, you can choose the values. Conviene que elijas nombres de interfaz que coincidan claramente con las interfaces que has definido.It is a good practice to choose interface names that clearly match interfaces that you have defined. Para las interfaces generadas, los nombres deben indicar que se trata de interfaces generadas.For generated interfaces, the names should be indicative of the generated interfaces. Puede usar el archivo ToasterComponent _ i. c para ayudarle a generar nombres de interfaz.You can use the ToasterComponent_i.c file to help you generate interface names.

Si intenta ejecutar la solución ahora, obtendrá un error en el que se le informará de que proxies.dll no forma parte de la carga.If you try to run the solution now, you will get an error that proxies.dll is not part of the payload. Abra el menú contextual de la carpeta referencias en el proyecto ToasterApplication y, a continuación, elija Agregar referencia.Open the shortcut menu for the References folder in the ToasterApplication project and then choose Add Reference. Active la casilla situada junto al proyecto Proxies.Select the check box next to the Proxies project. Además, asegúrate de que la casilla situada junto a ToasterComponent también está seleccionada.Also, make sure that the check box next to ToasterComponent is also selected. Elija el botón Aceptar .Choose the OK button.

Ahora el proyecto debería compilarse.The project should now build. Ejecute el proyecto y compruebe que puede crear el Toast.Run the project and verify that you can make toast.