Share via


Generar eventos de componentes de Windows en tiempo de ejecución

Si el componente de Windows en tiempo de ejecución genera un evento de tipo delegado definido por el usuario en un subproceso en segundo plano (subproceso de trabajo) y deseas que JavaScript pueda recibir el evento, puedes implementarlo y/o generarlo de una de estas formas:

  1. (Opción 1) Generar el evento a través de la clase CoreDispatcher para calcular las referencias al evento en el contexto del subproceso de JavaScript. Aunque por lo general esta es la mejor opción, en algunos escenarios el rendimiento podría no ser el óptimo.

  2. (Opción 2) Usar Windows.Foundation.EventHandler<Objeto> (con esta opción se pierde la información de tipo de evento). Si la Opción 1 no es factible o su rendimiento no es el adecuado, esta segunda opción puede resultarte conveniente, siempre y cuando puedas asumir la pérdida de la información de tipo.

  3. (Opción 3) Crear tus propios objetos COM de servidor proxy y de código auxiliar para el componente. Esta opción es más difícil de implementar, pero en escenarios exigentes, conserva la información de tipo y puede proporcionar un mejor rendimiento si se compara con la Opción 1.

Si simplemente generas un evento en un subproceso en segundo plano sin usar ninguna de estas opciones, los clientes de JavaScript no recibirán el evento.

Segundo plano

Todos los componentes y aplicaciones de Windows en tiempo de ejecución son fundamentalmente objetos COM, independientemente del lenguaje que se use para crearlos. En la API de Windows, la mayoría de los componentes son objetos COM ágiles que pueden comunicarse igualmente bien con objetos del subproceso en segundo plano y con objetos del subproceso de interfaz de usuario. Si el objeto COM no es ágil, requiere objetos auxiliares, conocidos como servidores proxy y códigos auxiliares, para comunicarse con otros objetos COM a través de los límites de la subtarea de interfaz de usuario y la subtarea en segundo plano. (En términos COM, esto se conoce como comunicación entre apartamentos de subproceso).

La mayoría de los objetos de la API de Windows son ágiles o bien tienen servidores proxy y códigos auxiliares integrados. Sin embargo, no se pueden crear servidores proxy ni códigos auxiliares para tipos genéricos como Windows.Foundation.TypedEventHandler<TSender, TResult>, ya que estos no son tipos completos hasta que se proporciona el argumento de tipo. La falta de servidores proxy o de códigos auxiliares solo puede resultar un problema con clientes de JavaScript, pero si quieres que el componente se pueda usar desde JavaScript, y desde C++ o cualquier lenguaje de .NET, debes usar una de estas tres opciones.

(Opción 1) Generar el evento a través de la clase CoreDispatcher

Con Windows.UI.Core.CoreDispatcher puedes enviar eventos de cualquier tipo delegado definido por el usuario y JavaScript podrá recibirlos. Si no estás seguro de la opción que debes usar, prueba esta primero. Si la latencia entre el desencadenamiento y el control del evento resulta un problema, prueba una de las otras opciones.

En el ejemplo siguiente se muestra el modo de usar la clase CoreDispatcher para generar un evento fuertemente tipado. Observa que el argumento de tipo es Toast, no 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<Objeto>, pero perdiendo la información de tipo

Otra manera de enviar un evento desde un subproceso en segundo plano es usar Windows.Foundation.EventHandler<Object> como tipo del evento. Windows proporciona esta instancia concreta del tipo genérico, así como un servidor proxy y un código auxiliar para la misma. El inconveniente es que se pierde la información de tipo de los argumentos del evento y el remitente. Los clientes C++ y .NET deben basarse en la documentación para determinar el tipo en que debe volver a convertirse cuando se recibe el evento. Los clientes de JavaScript no necesitan información sobre el tipo original. Estos clientes encuentran las propiedades de los argumentos basándose en los nombres que tienen en los metadatos.

En este ejemplo se muestra cómo utilizar Windows.Foundation.EventHandler<Object> en 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);
        }
    }
}

En JavaScript, usa este evento de la forma siguiente:

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

(Opción 3) Crear tu propio servidor proxy y tu propio código auxiliar

Para mejorar el rendimiento de los tipos de eventos definidos por el usuario que han conservado la información de tipo en su totalidad, deberás crear tus propios objetos de servidor proxy y de código auxiliar e incrustarlos en el paquete de aplicaciones. Normalmente, debes que utilizar esta opción solo en situaciones excepcionales cuando ninguna de las otras dos opciones resulten adecuadas. Además, no hay ninguna garantía de que esta opción proporcione un rendimiento mejor que las otras dos opciones. El rendimiento real depende de muchos factores. Usa el generador de perfiles de Visual Studio u otras herramientas de generación de perfiles para medir el rendimiento real de la aplicación y determinar si el evento es, de hecho, un cuello de botella.

A partir de este punto del artículo se muestra cómo usar C# para crear un componente básico de Windows en tiempo de ejecución y cómo usar C++ para crear un archivo DLL para el servidor proxy y el código auxiliar que permita a JavaScript usar un evento Windows.Foundation.TypedEventHandler<TSender, TResult> que se genere mediante el componente en una operación asincrónica. (También puedes usar C++ o Visual Basic para crear el componente. Los pasos que es necesario seguir para crear servidores proxy y códigos auxiliares son los mismos). Este tutorial está basado en Crear un ejemplo de componente en proceso de Windows en tiempo de ejecución (C++/CX) y te ayudará a comprender sus propósitos.

Este tutorial consta de las partes siguientes:

  1. Crear el componente de Windows en tiempo de ejecución: crearás dos clases básicas de Windows en tiempo de ejecución. Una clase expone un evento de tipo Windows.Foundation.TypedEventHandler<TSender, TResult> y la otra es el tipo que se devuelve a JavaScript como argumento para TValue. Estas clases no podrán comunicarse con JavaScript hasta que no hayas completado los pasos posteriores.

  2. Programar la aplicación JavaScript: esta aplicación activa el objeto de clase principal, llama a un método y controla un evento que se genera mediante el componente de Windows en tiempo de ejecución.

  3. Generar GUID para las interfaces del componente: estos GUID son necesarios para las herramientas que generan las clases de servidor proxy y de código auxiliar.

  4. Generar un archivo IDL para el componente: usarás este archivo IDL para generar el código fuente de C para el servidor proxy y el código auxiliar.

  5. Compilar el servidor proxy y el código auxiliar en un archivo DLL

  6. Registrar y usar el archivo DLL que contiene el servidor proxy y el código auxiliar: registra los objetos de servidor proxy y de código auxiliar para que el runtime de COM pueda encontrarlos, y haz referencia al archivo DLL que contiene el servidor proxy y el código auxiliar en el proyecto de aplicación.

Para crear el componente de Windows en tiempo de ejecución

  1. En la barra de menús de Visual Studio, elige Archivo > Nuevo proyecto. En el cuadro de diálogo Nuevo proyecto, expande JavaScript > Tienda Windows y, a continuación, selecciona Aplicación vacía. Asigna al proyecto el nombre ToasterApplication y, a continuación, elige el botón Aceptar.

  2. Agrega un componente de Windows en tiempo de ejecución 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. Expande Visual C# > Tienda Windows y, a continuación, selecciona Componente de Windows en tiempo de ejecución. Asigna al proyecto el nombre ToasterComponent y, a continuación, elige el botón Aceptar. ToasterComponent será el espacio de nombres raíz para los componentes que crearás en los pasos posteriores.

    En el Explorador de soluciones, abre el menú contextual de la solución y, a continuación, elige Propiedades. En el cuadro de diálogo Páginas de propiedades, selecciona Propiedades de configuración en el panel izquierdo y, a continuación, en la parte superior del cuadro de diálogo, establece Configuración en Debug y Plataforma en x86, x64 o ARM. Elige el botón Aceptar.

    Nota importanteImportante

    Plataforma = Cualquier CPU no funcionará porque no es válido para el archivo DLL para Win32 de código nativo que agregarás a la solución más adelante.

  3. En el Explorador de soluciones, cambia el nombre class1.cs por ToasterComponent.cs para que coincida con el nombre del proyecto. Visual Studio cambia automáticamente el nombre de la clase en el archivo para que coincida con el nuevo nombre de archivo.

  4. En el archivo .cs, agrega una directiva using para que el espacio de nombres Windows.Foundation incluya TypedEventHandler en el ámbito.

  5. Cuando necesites servidores proxy y códigos auxiliares, el componente deberá usar interfaces para exponer sus miembros públicos. En ToasterComponent.cs, define una interfaz para el Toaster y otra para el objeto Toast que genera el Toaster.

    NotaNota

    En C# puedes omitir este paso.En su lugar, crea primero una clase y, a continuación, abre su menú contextual y elige Refactorizar > Extraer interfaz.En el código que se genera, otorga a las interfaces accesibilidad pública de forma manual.

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

    La interfaz IToast tiene una cadena que se puede recuperar para describir el tipo de Toast. La interfaz IToaster dispone de un método para crear el Toast y de un evento para indicar que el Toast se ha creado. Dado que este evento devuelve un determinado fragmento (es decir, el tipo) de Toast, se le conoce como evento con tipo.

  6. A continuación, necesitamos clases que implementen estas interfaces. Estas clases deben ser public y sealed para que sean accesibles desde la aplicación JavaScript que programarás más adelante.

    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 el Toast y, a continuación, giramos un elemento de trabajo del grupo de subprocesos para desencadenar la notificación. Aunque es posible que el IDE te sugiera que apliques la palabra clave await a la llamada asincrónica, en este caso no será necesario porque el método no realiza ningún trabajo que dependa de los resultados de la operación.

    Nota importanteImportante

    La llamada asincrónica del código anterior utiliza ThreadPool.RunAsync únicamente para mostrar una manera sencilla de desencadenar el evento en un subproceso en segundo plano. Si escribes este método tal como se muestra en el ejemplo siguiente funcionará bien porque el programador de tareas de .NET vuelve a calcular automáticamente las referencias de las llamadas asincrónicas/await al subproceso de interfaz de usuario.

    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.

Para programar la aplicación JavaScript

  1. Ahora puedes agregar un botón a la aplicación JavaScript para que utilice la clase que acabamos de definir para crear el Toast. Antes de hacer esto, debemos agregar una referencia al proyecto ToasterComponent que acabamos de crear. En el Explorador de soluciones, abre el menú contextual para el proyecto ToasterApplication, elige Agregar > referencias y, a continuación, elige el botón Agregar nueva referencia. 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. Elige el botón Aceptar.

  2. En el Explorador de soluciones, abre el menú contextual del proyecto ToasterApplication y, a continuación, elige Establecer como proyecto de inicio.

  3. 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. El espacio de nombres tendrá dos funciones, una para crear el Toast y otra para controlar el evento de finalización de Toast. La implementación de makeToast crea un objeto Toaster, registra el controlador de eventos y crea el Toast. Hasta ahora, el controlador de eventos no hace mucho, tal como se muestra a continuación:

    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. La función makeToast debe enlazarse a un botón. Actualiza default.html para que incluya un botón y algo de espacio para generar el resultado de crear el 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 estuviéramos utilizando TypedEventHandler, ahora podríamos ejecutar la aplicación en el equipo local y hacer clic en el botón para crear el Toast. Pero en nuestra aplicación, no ocurre nada. Para averiguar por qué, vamos a depurar el código administrado que desencadena ToastCompletedEvent. Detén el proyecto y, en la barra de menús, elige Depurar > Propiedades de la aplicación Toaster. Cambia Tipo de depurador a Solo administrado. De nuevo en la barra de menús, elige Depurar > Excepciones y, a continuación, selecciona Excepciones de Common Language Runtime.

    Ahora ejecuta la aplicación y haz clic en el botón para crear el Toast. El depurador detecta una excepción de conversión no válida. Aunque en el mensaje no resulta evidente, esta excepción se produce porque faltan servidores proxy para esa interfaz.

    Ventana de error del depurador que indica que falta un servidor 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. 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++.

Para generar los GUID para las interfaces del componente

  • Para C#, Visual Basic u otros lenguajes de .NET:

    1. En la barra de menús, elige Herramientas > Crear GUID. En el cuadro de diálogo, selecciona 5. [Guid(“xxxxxxxx-xxxx...xxxx)]. Elige el botón Nuevo GUID y, a continuación, elige el botón Copiar.

    2. Regresa a la definición de interfaz y, a continuación, pega el nuevo GUID justo delante de la interfaz IToaster, tal como se muestra en el ejemplo siguiente. (No uses el GUID del ejemplo. Cada interfaz debe tener su propio GUID).

      [Guid("FC198F74-A808-4E2A-9255-264746965B9F")]
          public interface IToaster...
      
    3. Agrega una directiva using para el espacio de nombres System.Runtime.InteropServices.

    4. Repite estos pasos para la interfaz IToast.

  • Para C++:

    1. En la barra de menús, elige Herramientas > Crear GUID. En el cuadro de diálogo, selecciona 3. static const struct GUID = {...}. Elige el botón Nuevo GUID y, a continuación, elige el botón Copiar.

      Generador GUID en Visual Studio

    2. Pega el GUID justo delante de la definición de la interfaz IToaster. Después de pegarlo, el GUID debe parecerse al del ejemplo siguiente. (No uses el GUID del ejemplo. Cada interfaz debe tener su propio GUID).

      // {F8D30778-9EAF-409C-BCCD-C8B24442B09B}
          static const GUID <<name>> = { 0xf8d30778, 0x9eaf, 0x409c, { 0xbc, 0xcd, 0xc8, 0xb2, 0x44, 0x42, 0xb0, 0x9b } };
      
    3. Agrega una directiva using para que Windows.Foundation.Metadata incluya GuidAttribute en el ámbito.

    4. Ahora convierte manualmente el const GUID en un GuidAttribute para darle formato, tal como se muestra en el ejemplo siguiente. Observa que las llaves se reemplazan por corchetes y paréntesis, y que se quita el punto y coma final.

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

Ahora que las interfaces tienen identificadores únicos, podemos crear un archivo IDL procesando el archivo .winmd con la herramienta de línea de comandos winmdidl y, a continuación, generar el código fuente de C para el servidor proxy y el código auxiliar procesando ese archivo IDL con la herramienta de línea de comandos MIDL. Visual Studio hace esto automáticamente si creamos eventos posteriores a la compilación, tal como se muestra en los pasos siguientes.

Para generar el código fuente del servidor proxy y del código auxiliar

  1. 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. 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. Agrega los comandos siguientes a la línea de comandos de ejecución posterior a la compilación. (Primero se debe llamar al archivo por lotes para establecer las variables de entorno que permitirán encontrar la herramienta 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"
    
    
    
    Nota importanteImportante

    Para establecer una configuración de proyecto x64 o ARM, cambia el parámetro /env de MIDL por x64 o arm32.

  2. Para asegurarte de que el archivo IDL se volverá a generar cada vez que se modifique el archivo .winmd, cambia Ejecutar el evento posterior a la compilación por Cuando la compilación actualiza la salida del proyecto.

    La página de propiedades Eventos de compilación debe ser similar a esta:

    Pasos posteriores a la compilación en página de propiedades de Visual Studio

  3. Recompila la solución para generar y compilar el código IDL.

    Para comprobar que MIDL ha compilado la solución correctamente, busca ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c, y dlldata.c en el directorio del proyecto ToasterComponent.

Para compilar el servidor proxy y el código auxiliar en un archivo DLL

  1. Ahora que tienes los archivos necesarios, puedes compilarlos para generar un archivo DLL, que es un archivo de C++. Para simplificar esta tarea, agrega un nuevo proyecto para compilar los servidores proxy. Abre el menú contextual de la solución ToasterApplication y, a continuación, elige Agregar > Nuevo proyecto. En el panel izquierdo del cuadro de diálogo Nuevo proyecto, expande Visual C++ > Tienda Windows, y en el panel central, selecciona DLL (aplicaciones de la Tienda Windows). (Observa que este NO es un proyecto de componente de Windows en tiempo de ejecución de C++). Asigna al proyecto el nombre Proxies y, a continuación, elige el botón Aceptar. Estos archivos los actualizarán los eventos posteriores a la compilación cuando se produzca algún cambio en la clase de C#.

  2. De forma predeterminada, el proyecto Proxies genera archivos .h de encabezado y archivos .cpp de C++. Dado que un archivo DLL se compila a partir de los archivos generados desde MIDL, los archivos .h y .cpp no son necesarios. En el Explorador de soluciones, abre el menú contextual de los archivos, elige Quitar y, a continuación, confirma la eliminación.

  3. Ahora que el proyecto está vacío, puedes volver a agregar los archivos generados por MIDL. Abre el menú contextual del proyecto Proxies y, a continuación, elige Agregar > Elemento existente. En el cuadro de diálogo, navega hasta el directorio del proyecto ToasterComponent y selecciona estos archivos: ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c y dlldata.c. Elige el botón Agregar.

  4. En el proyecto Proxies, crea un archivo .def para definir las exportaciones de DLL descritas en dlldata.c. Abre el menú contextual del proyecto y, a continuación, elige Agregar > Nuevo elemento. En el panel izquierdo del cuadro de diálogo, selecciona Código y, a continuación, en el panel central, selecciona Archivo de definición de módulo. Asigna al archivo el nombre proxies.def y, a continuación, elige el botón de Agregar. Abre este archivo .def y modifícalo para que incluya las exportaciones definidas en dlldata.c:

    EXPORTS
        DllCanUnloadNow         PRIVATE
        DllGetClassObject       PRIVATE
    
  5. Si compilas ahora el proyecto, se producirá un error. Para compilarlo correctamente, debes cambiar el modo en que se compila y se vincula. En el Explorador de soluciones, abre el menú contextual del proyecto Proxies y, a continuación, elige Propiedades. Cambia las páginas de propiedades de la forma siguiente:

    • En el panel izquierdo, selecciona C/C++ > Preprocesador y, a continuación, en el panel derecho, selecciona Definiciones de preprocesador, elige el botón de flecha abajo y selecciona Editar. Agrega estas definiciones al cuadro:

      WIN32;_WINDOWS
      
    • En C/C++ > Encabezados precompilados, cambia Encabezado precompilado por No utilizar encabezados precompilados y, a continuación, elige el botón Aplicar.

    • En Vinculador > General, cambia Omitir biblioteca de importación a y, a continuación, elige el botón Aplicar.

    • En Vinculador > Entrada, selecciona Dependencias adicionales, elige el botón de flecha abajo y, a continuación, selecciona Editar. Agrega este texto al cuadro:

      rpcrt4.lib;runtimeobject.lib
      

      No pegues estas bibliotecas directamente en la fila de la lista. Usa el cuadro de edición para garantizar que MSBuild de Visual Studio mantendrá las dependencias adicionales correctas.

    Cuando hayas realizado estos cambios, elige el botón Aceptar del cuadro de diálogo Páginas de propiedades.

  6. A continuación, toma una dependencia del proyecto ToasterComponent. Esto garantiza que el proyecto Toaster se compilará antes de que lo haga el proyecto Proxies. Esto es necesario porque el proyecto Toaster es el responsable de generar los archivos para compilar el servidor proxy.

    Abre el menú contextual del proyecto Proxies y, a continuación, elige Dependencias del proyecto. Activa las casillas para indicar que el proyecto Proxies depende del proyecto ToasterComponent y garantizar que Visual Studio los compilará en el orden correcto.

  7. Comprueba que la solución se compila correctamente; para ello, elige Compilar > Recompilar solución en la barra de menús de Visual Studio.

Para registrar el servidor proxy y el código auxiliar

  1. En el proyecto ToasterApplication, abre el menú contextual de package.appxmanifest y, a continuación, elige Abrir con. En el cuadro de diálogo Abrir con, selecciona Editor XML (texto) y, a continuación, elige el botón Aceptar. Vamos a pegar algunos datos XML que proporcionen un registro de la extensión windows.activatableClass.proxyStub y que estén basados en los GUID del servidor proxy. Para encontrar los GUID que se usarán en el archivo .appxmanifest, abre ToasterComponent_i.c. Busca entradas similares a las del ejemplo siguiente. Observa también las definiciones de IToast, IToaster y una tercera interfaz: un controlador de eventos con tipo que tiene dos parámetros: un Toaster y un Toast. Este coincide con el evento definido en la clase Toaster. Observa que los GUID de IToast e IToaster coinciden con los GUID que están definidos en las interfaces del archivo de C#. 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.

    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 al que asignaremos el nombre Extensions y, a continuación, cambiaremos su formato. La entrada de manifiesto se parece a la del ejemplo siguiente, pero de nuevo, recuerda que debes usar tus propios GUID. Observa que el GUID del ClassId del archivo XML es el mismo que el de ITypedEventHandler2. Esto se debe a que ese GUID es el primero que aparece en el archivo ToasterComponent_i.c. Aquí, los GUID no distinguen mayúsculas de minúsculas. En lugar de cambiar el formato del GUID manualmente para IToast e IToaster, puedes volver a las definiciones de interfaz y obtener el valor de GuidAttribute, que tiene el formato correcto. En C++, hay un GUID con el formato correcto en el comentario. En cualquier caso, debes cambiar manualmente el formato del GUID que se utiliza para el ClassId y el controlador 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>
    

    Pega el nodo XML Extensions como elemento secundario directo del nodo Package, y como elemento del mismo nivel de Resources, por ejemplo.

  2. Antes de continuar, es importante que te asegures de que:

    • El ClassId de ProxyStub está establecido en el primer GUID del archivo ToasterComponent_i.c. Usa el primer GUID que aparece definido en ese archivo para el ClassId. (Podría ser el mismo que el GUID de ITypedEventHandler2).

    • Path es la ruta de acceso relativa del paquete del archivo binario del servidor proxy. (En este tutorial, proxies.dll está en la misma carpeta que ToasterApplication.winmd).

    • Los GUID están en el formato correcto. (Es fácil equivocarse con esto).

    • Los identificadores de interfaz del manifiesto coinciden con los IID del archivo ToasterComponent_i.c.

    • Los nombres de interfaz son únicos en el manifiesto. Dado que el sistema no utiliza estos nombres, puedes elegir los que desees. Conviene que elijas nombres de interfaz que coincidan claramente con las interfaces que has definido. Para las interfaces generadas, los nombres deben indicar que se trata de interfaces generadas. El archivo ToasterComponent_i.c puede ayudarte a generar nombres de interfaz.

  3. Si intentas ejecutar la solución ahora, obtendrás un error en el que se te informará de que proxies.dll no forma parte de la carga. Abre el menú contextual de la carpeta Referencias del proyecto ToasterApplication y, a continuación, elige Agregar referencia. Activa la casilla situada junto al proyecto Proxies. Además, asegúrate de que la casilla situada junto a ToasterComponent también está seleccionada. Elige el botón Aceptar.

    Ahora el proyecto debería compilarse. Ejecuta el proyecto y comprueba que puedes crear el Toast.