Crear componentes de Windows en tiempo de ejecución en C# y Visual Basic

Con .NET Framework 4.5, puedes utilizar código administrado para crear tus propios tipos de Windows en tiempo de ejecución, empaquetados en un componente de Windows en tiempo de ejecución. Puedes usar el componente en aplicaciones de la Tienda Windows con C++, JavaScript, Visual Basic o C#. En este artículo se describen las reglas para crear un componente y se explican algunos aspectos de la compatibilidad de .NET Framework para Windows en tiempo de ejecución. Normalmente, esa compatibilidad está diseñada para ser transparente para el programador de .NET Framework. Sin embargo, si creas un componente para utilizarlo con JavaScript o C++, debes ser consciente de las diferencias en la manera en que esos lenguajes admiten Windows en tiempo de ejecución.

Nota

Si estás creando un componente para usarlo solo en aplicaciones de la Tienda Windows con Visual Basic o C#, y el componente no contiene controles de la Tienda Windows, considera la posibilidad de usar la plantilla Biblioteca de clases (aplicaciones de la Tienda Windows) en lugar de la plantilla Componente de Windows en tiempo de ejecución. Hay menos restricciones en una biblioteca de clases simple.

Este artículo contiene las siguientes secciones:

  • Declarar tipos en componentes de Windows en tiempo de ejecución

  • Depurar tu componente

  • Pasar tipos de Windows en tiempo de ejecución a código administrado

  • Pasar tipos administrados a Windows en tiempo de ejecución

  • Pasar matrices

  • Métodos sobrecargados

  • Operaciones asincrónicas

  • Producir excepciones

  • Declarar y producir eventos

Declarar tipos en componentes de Windows en tiempo de ejecución

Internamente, los tipos de Windows en tiempo de ejecución de tu componente pueden utilizar cualquier funcionalidad de .NET Framework que se permiten en una aplicación de Tienda Windows. (Para obtener más información, consulta Información general de .NET para aplicaciones de la Tienda Windows). Externamente, los miembros de los tipos pueden exponer solo los tipos de Windows en tiempo de ejecución para sus parámetros y valores devueltos. En la lista siguiente se describen las limitaciones en los tipos de .NET Framework que se exponen desde los componentes de Windows en tiempo de ejecución.

  • Los campos, parámetros y valores devueltos de todos los tipos y miembros de público del componente deben ser tipos de Windows en tiempo de ejecución.

    Esta restricción incluye los tipos de Windows en tiempo de ejecución que se crean, así como los tipos proporcionados por el propio Windows en tiempo de ejecución. También incluye una serie de tipos de .NET Framework. La inclusión de estos tipos forma parte de la compatibilidad que .NET Framework proporciona para habilitar el uso natural de Windows en tiempo de ejecución en código administrado: tu código parece que utiliza tipos de .NET Framework familiares en lugar de los tipos subyacentes de Windows en tiempo de ejecución. Por ejemplo, puedes utilizar tipos primitivos de .NET Framework como Int32 y Double, algunos tipos fundamentales como DateTimeOffset y Uri, y algunos tipos de interfaz genérica de uso general como IEnumerable<T> (IEnumerable(Of T) en Visual Basic) y IDictionary<TKey,TValue>. (Observa que los argumentos de estos tipos genéricos deben ser tipos de Windows en tiempo de ejecución). Esto se explica en las secciones Pasar tipos de Windows en tiempo de ejecución a código administrado y Pasar tipos administrados a Windows en tiempo de ejecución, más adelante en este artículo.

  • Las clases públicas y las interfaces pueden contener métodos, propiedades, y eventos. Puedes declarar delegados para tus eventos o usar el delegado EventHandler<T>. Una clase o una interfaz públicas no pueden:

    • Ser genéricas.

    • Implementar una interfaz que no es una interfaz de Windows en tiempo de ejecución. (Sin embargo, puedes crear tus propias interfaces de Windows en tiempo de ejecución e implementarlas).

    • Derivar de tipos que no están en Windows en tiempo de ejecución, como System.Exception y System.EventArgs.

  • Todos los tipos públicos deben tener un espacio de nombres de la raíz que coincida con el nombre de ensamblado, y el nombre de ensamblado no debe comenzar con “Windows”.

    Nota

    De forma predeterminada, los proyectos de Visual Studio tienen espacios de nombres que coinciden con el nombre de ensamblado. En Visual Basic, la instrucción Namespace para este espacio de nombres predeterminado no se muestra en tu código.

  • Las estructuras públicas no pueden tener miembros que no sean campos públicos, y esos campos deben ser tipos de valor o cadenas.

  • Las clases públicas deben ser sealed (NotInheritable en Visual Basic). Si tu modelo de programación requiere polimorfismo, puedes crear una interfaz pública e implementarla en las clases que deben ser polimórficas.

Depurar tu componente

Si tu aplicación de Tienda Windows y tu componente se compilan con código administrado, puedes depurarlos al mismo tiempo.

Cuando pruebes tu componente como parte de una aplicación de Tienda Windows mediante C++, puedes depurar código administrado y nativo al mismo tiempo. El valor predeterminado es código nativo solamente.

Para depurar tanto código de C++ nativo como código administrado

  1. Abre el menú contextual de tu proyecto de Visual C++ y elige Propiedades.

  2. En las páginas de propiedades, en Propiedades de configuración, elige Depuración.

  3. Elige Tipo de depurador y, en el cuadro de lista desplegable, cambia Solo nativo a Mixto (administrado y nativo). Elige Aceptar.

  4. Establece puntos de interrupción en código nativo y administrado.

Cuando pruebes tu componente como parte de una aplicación de Tienda Windows con JavaScript, de forma predeterminada la solución está en modo de depuración de JavaScript. En Visual Studio 2012 y Visual Studio Express 2012 para Windows 8, no puedes depurar código de JavaScript y código administrado al mismo tiempo.

Para depurar código administrado en lugar de JavaScript

  1. Abre el menú contextual de tu proyecto de JavaScript y elige Propiedades.

  2. En las páginas de propiedades, en Propiedades de configuración, elige Depuración.

  3. Elige Tipo de depurador y, en el cuadro de lista desplegable cambia Solo script a Solo administrado. Elige Aceptar.

  4. Establece puntos de interrupción en código administrado y realiza la depuración como de costumbre.

Pasar tipos de Windows en tiempo de ejecución a código administrado

Como se mencionó anteriormente en la sección Declarar tipos en componentes de Windows en tiempo de ejecución, ciertos tipos de .NET Framework pueden aparecer en las firmas de miembros de clases públicas. Esto forma parte de la compatibilidad que .NET Framework proporciona para habilitar el uso natural de Windows en tiempo de ejecución en código administrado. Incluye tipos primitivos y algunas clases e interfaces. Cuando tu componente se utiliza desde JavaScript o desde código de C++, es importante saber cómo los tipos de .NET Framework se muestran al llamador. Consulta Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript para obtener ejemplos con JavaScript. En esta sección se describen los tipos de uso general.

En .NET Framework, los tipos primitivos como la estructura Int32 tienen muchas propiedades y métodos útiles, como el método TryParse. Por el contrario, los tipos primitivos y las estructuras de Windows en tiempo de ejecución solo tienen campos. Cuando pases estos tipos a código administrado, parecerán tipos de .NET Framework, y puedes utilizar las propiedades y los métodos de tipos de.NET Framework como lo harías normalmente. En la lista siguiente se resumen las sustituciones que se realizan automáticamente en el IDE:

  • Para los tipos primitivos de Windows en tiempo de ejecución Int32, Int64, Single, Double, Boolean, String (una colección inmutable de caracteres Unicode), Enum, UInt32, UInt64 y Guid, utiliza el tipo del mismo nombre en el espacio de nombres System.

  • Para UInt8, usa System.Byte.

  • Para Char16, usa System.Char.

  • Para la interfaz IInspectable, usa System.Object.

Si C# o Visual Basic proporcionan una palabra clave de lenguaje para cualquiera de estos tipos, puedes utilizar la palabra clave de lenguaje en su lugar.

Además de los tipos primitivos, algunos tipos básicos de Windows en tiempo de ejecución de uso común aparecen en código administrado como sus equivalentes de .NET Framework. Por ejemplo, supón que tu código de JavaScript usa la clase Windows.Foundation.Uri y deseas pasarla a un método de C# o de Visual Basic. El tipo equivalente en código administrado es la clase System.Uri de .NET Framework, y es el tipo que se usa para el parámetro de método. Puedes indicar cuándo un tipo de Windows en tiempo de ejecución aparece como un tipo de .NET Framework, porque IntelliSense en Visual Studio oculta el tipo de Windows en tiempo de ejecución cuando escribes código administrado y muestra el tipo equivalente de .NET Framework. (Normalmente, los dos tipos tienen el mismo nombre. Sin embargo, debes observar que la estructura Windows.Foundation.DateTime aparece en código administrado como System.DateTimeOffset y no como System.DateTime).

Para algunos tipos de colección de uso general, la asignación se realiza entre las interfaces implementadas por un tipo de Windows en tiempo de ejecución y las interfaces implementadas por el tipo de .NET Framework correspondiente. Como ocurre con los tipos mencionados anteriormente, puedes declarar tipos de parámetro mediante el tipo de .NET Framework. Esto oculta algunas diferencias entre los tipos y hace que la escritura de código de .NET Framework sea más natural. En la tabla siguiente se enumeran los más comunes de estos tipos de interfaz genérica, junto con otras asignaciones de interfaces y clases comunes. Para obtener una lista completa de tipos de Windows en tiempo de ejecución que .NET Framework asigna, consulta Asignaciones de tipos de Windows Runtime en .NET Framework.

Windows en tiempo de ejecución

.NET Framework

IIterable<T>

IEnumerable<T>

IVector<T>

IList<T>

IVectorView<T>

IReadOnlyList<T>

IMap<K, V>

IDictionary<TKey, TValue>

IMapView<K, V>

IReadOnlyDictionary<TKey, TValue>

IKeyValuePair<K, V>

KeyValuePair<TKey, TValue>

IBindableIterable

IEnumerable

IBindableVector

IList

Windows.UI.Xaml.Data.INotifyPropertyChanged

System.ComponentModel.INotifyPropertyChanged

Windows.UI.Xaml.Data.PropertyChangedEventHandler

System.ComponentModel.PropertyChangedEventHandler

Windows.UI.Xaml.Data.PropertyChangedEventArgs

System.ComponentModel.PropertyChangedEventArgs

    

Cuando un tipo implementa más de una interfaz, puedes utilizar cualquiera de las interfaces que implementa como un tipo de parámetro o un tipo de valor devuelto de un miembro. Por ejemplo, puedes pasar o devolver un objeto Dictionary<int, string> (Dictionary(Of Integer, String) en Visual Basic) como IDictionary<int, string>, IReadOnlyDictionary<int, string> o IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>>.

Importante

JavaScript utiliza la interfaz que aparece en primer lugar en la lista de interfaces que un tipo administrado implementa. Por ejemplo, si devuelves Dictionary<int, string> al código de JavaScript, aparecerá como IDictionary<int, string> con independencia de la interfaz que especifiques como tipo de valor devuelto. Esto significa que si la primera interfaz no incluye un miembro que aparece en interfaces posteriores, dicho miembro no es visible para JavaScript.

En Windows en tiempo de ejecución, IMap<K, V> y IMapView<K, V> se recorren en iteración mediante IKeyValuePair. Cuando las pasas a código administrado, aparecen como IDictionary<TKey, TValue> y IReadOnlyDictionary<TKey, TValue>, por lo que utilizas System.Collections.Generic.KeyValuePair<TKey, TValue> naturalmente para enumerarlas.

La forma en que las interfaces aparecen en código administrado afecta a la forma en que aparecen los tipos que implementan estas interfaces. Por ejemplo, la clase PropertySet implementa IMap<K, V>, que aparece en código administrado como IDictionary<TKey, TValue>. PropertySet aparece como si se implementara IDictionary<TKey, TValue> en lugar de IMap<K, V>, por lo que en código administrado parece tener un método Add, que se comporta como el método Add en diccionarios de .NET Framework. No parece tener un método Insert. Puedes ver este ejemplo en el artículo Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript.

Pasar tipos administrados a Windows en tiempo de ejecución

Como se describe en la sección anterior, algunos tipos de Windows en tiempo de ejecución pueden aparecer como tipos de .NET Framework en las firmas de los miembros de tu componente, o en las firmas de los miembros de Windows en tiempo de ejecución cuando los utilizas en el IDE. Cuando pases tipos de .NET Framework a estos miembros o los utilices como los valores devueltos de los miembros de tu componente, aparecerán en el código en el lado opuesto al tipo de Windows en tiempo de ejecución correspondiente. Para obtener ejemplos de los efectos que esto puede tener cuando se llama a tu componente desde JavaScript, consulta la sección “Devolver tipos administrados de tu componente" en Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript.

Pasar matrices

En Windows en tiempo de ejecución, todos los parámetros son para la entrada o la salida; no hay parámetros de ref (ByRef en Visual Basic). El contenido de las matrices que se pasan a tu componente de Windows en tiempo de ejecución debe estar pensado tanto para la entrada como para la salida. Es decir, las matrices no se deben tratar como mutables. Si una matriz se pasa por valor (ByVal en Visual Basic), debes aplicar el atributo ReadOnlyArrayAttribute o el atributo WriteOnlyArrayAttribute para establecer el intento. Consulta Pasar matrices a un componente de Windows en tiempo de ejecución.

Métodos sobrecargados

En Windows en tiempo de ejecución, los métodos se pueden sobrecargar. Sin embargo, si declaras varias sobrecargas con el mismo número de parámetros, debes aplicar el atributo Windows.Foundation.Metadata.DefaultOverloadAttribute a solo una de dichas sobrecargas. Esa sobrecarga es la única a la que puedes llamar desde JavaScript. Por ejemplo, en el código siguiente, la sobrecarga que toma int (Integer en Visual Basic) es la sobrecarga predeterminada.

        public string OverloadExample(string s)
        {
            return s;
        }
        [Windows.Foundation.Metadata.DefaultOverload()] 
        public int OverloadExample(int x)
        {
            return x;
        } 
    Public Function OverloadExample(ByVal s As String) As String
        Return s
    End Function
    <Windows.Foundation.Metadata.DefaultOverload> _
    Public Function OverloadExample(ByVal x As Integer) As Integer
        Return x
    End Function

Advertencia

JavaScript te permite pasar cualquier valor a OverloadExample, y convierte el valor al tipo requerido por el parámetro. Puedes llamar a OverloadExample con “cuarenta y dos”, “42" o "42,3", pero todos estos valores se pasan a la sobrecarga predeterminada. La sobrecarga predeterminada del ejemplo anterior devuelve 0, 42 y 42, respectivamente.

No puedes aplicar el atributo DefaultOverloadAttribute a constructores. Todos los constructores de una clase deben tener números de parámetros diferentes.

Operaciones asincrónicas

Para implementar un método asincrónico en tu componente, agrega “Async” al final del nombre de método y devuelve una de las interfaces de Windows en tiempo de ejecución que representan acciones u operaciones asincrónicas: IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> o IAsyncOperationWithProgress<TResult, TProgress>.

Puedes utilizar tareas de .NET Framework (la clase Task y la clase genérica Task<TResult>) para implementar tu método asincrónico. Debe devolver una tarea que represente una operación en curso, como una tarea que se devuelve desde un método asincrónico escrito en C# o Visual Basic, o una tarea que se devuelve desde el método Task.Run. Si utilizas un constructor para crear la tarea, debes llamar a su método Task.Start antes de devolverlo.

Un método que utiliza await (Await en Visual Basic) requiere la palabra clave async (Async en Visual Basic). Si expones un método de este tipo desde un componente de Windows en tiempo de ejecución, tienes que aplicar la palabra clave async al delegado al que pasas el método Run.

Para las acciones y operaciones asincrónicas que no admiten cancelación o informes de progreso, puedes utilizar el método de extensión WindowsRuntimeSystemExtensions.AsAsyncAction o AsAsyncOperation<TResult> para incluir la tarea en la interfaz adecuada. Por ejemplo, el código siguiente implementa un método asincrónico mediante el método Task.Run para iniciar una tarea. El método de extensión AsAsyncOperation<TResult> devuelve la tarea como una operación asincrónica de Windows en tiempo de ejecución.

        public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
        {
            return Task.Run<IList<string>>(async () =>
            {
                var data = await DownloadDataAsync(id);
                return ExtractStrings(data);
            }).AsAsyncOperation();
        }


    Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
         As IAsyncOperation(Of IList(Of String))

        Return Task.Run(Of IList(Of String))(
            Async Function()
                Dim data = Await DownloadDataAsync(id)
                Return ExtractStrings(data)
            End Function).AsAsyncOperation()
    End Function


El código de JavaScript siguiente muestra cómo se podría llamar al método mediante un objeto WinJS.Promise. La función que se pasa al método then se ejecuta cuando se completa la llamada asincrónica. El parámetro stringList contiene la lista de cadenas devuelta por el método DownloadAsStringAsync, y la función realiza el procesamiento requerido.

function asyncExample(id) {

    var result = SampleComponent.Example.downloadAsStringAsync(id).then(
        function (stringList) {
            // Place code that uses the returned list of strings here.
        });
}

Para las acciones y operaciones asincrónicas que admiten cancelación o informes de progreso, puedes utilizar la clase AsyncInfo para generar una tarea iniciada y enlazar las características de cancelación y de informes de progreso de la tarea con las características de cancelación y de informes de progreso de la interfaz de Windows en tiempo de ejecución adecuada. Para obtener un ejemplo que admita tanto la cancelación como los informes de progreso, consulta Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript.

Observa que puedes utilizar los métodos de la clase AsyncInfo incluso si el método asincrónico no admite cancelación o informes de progreso. Si utilizas una función lambda de Visual Basic o un método anónimo de C#, no tienes que proporcionar parámetros al token ni a la interfaz de IProgress<T>. Si utilizas una función lambda de C#, tienes que proporcionar un parámetro de token pero omitirlo. El ejemplo anterior, que usa el método AsAsyncOperation<TResult>, tiene el mismo aspecto cuando utilizas la sobrecarga del método AsyncInfo.Run<TResult>(Func<CancellationToken, Task<TResult>>) en su lugar:

        public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
        {
            return AsyncInfo.Run<IList<string>>(async (token) =>
            {
                var data = await DownloadDataAsync(id);
                return ExtractStrings(data);
            });
        }
    Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
        As IAsyncOperation(Of IList(Of String))

        Return AsyncInfo.Run(Of IList(Of String))(
            Async Function()
                Dim data = Await DownloadDataAsync(id)
                Return ExtractStrings(data)
            End Function)
    End Function

Si creas un método asincrónico que admite opcionalmente cancelación o informes de progreso, debes considerar si deseas agregar sobrecargas que no tengan parámetros para un token de cancelación o la interfaz de IProgress<T>.

Producir excepciones

Puedes producir cualquier tipo de excepción que esté incluida en .NET para aplicaciones de la Tienda Windows: API admitidas. No puedes declarar tus propios tipos de excepciones públicas en un componente de Windows en tiempo de ejecución, pero puedes declarar e iniciar tipos no públicos.

Si tu componente no controla la excepción, se produce una excepción correspondiente en el código que llamó a tu componente. La manera en que se presenta la excepción al llamador depende de la manera en que el lenguaje de llamada admite Windows en tiempo de ejecución.

  • En JavaScript, la excepción aparece como un objeto en el que el mensaje de excepción se reemplaza por un seguimiento de la pila. Cuando depuras tu aplicación en Visual Studio, puedes ver el texto de mensaje original presentado en el cuadro de diálogo de excepciones del depurador identificado como "Información de WinRT". No puedes tener acceso al texto de mensaje original desde código de JavaScript.

    Nota

    Actualmente, el seguimiento de la pila contiene el tipo de excepción administrada, pero no recomendamos analizar el seguimiento para identificar el tipo de excepción. En su lugar, conviene que uses un valor HRESULT como se describe más adelante en esta sección.

  • En C++, la excepción aparece como una excepción de la plataforma. Si la propiedad HResult de la excepción administrada se puede asignar al HRESULT de una excepción de plataforma concreta, se usa la excepción concreta; de lo contrario, se produce una excepción Platform::COMException. El texto de mensaje de la excepción administrada no está disponible para código de C++. Si se ha producido una excepción de plataforma concreta, aparece el texto de mensaje predeterminado para ese tipo de excepción; de lo contrario, no aparece texto de mensaje. Consulta Excepciones (C++/CX).

  • En C# o en Visual Basic, la excepción es una excepción administrada normal.

Cuando produces una excepción desde tu componente, puedes hacer que el llamador de JavaScript o C++ pueda controlar la excepción más fácilmente produciendo un tipo de excepción no pública cuyo valor de la propiedad HResult sea específico para tu componente. El HRESULT está disponible para un llamador de JavaScript a través de la propiedad number del objeto de excepción y a un llamador de C++ a través de la propiedad COMException::HResult.

Nota

Utiliza un valor negativo para tu HRESULT. Un valor positivo se interpreta como correcto y no se produce ninguna excepción en el llamador de JavaScript o C++.

Declarar y producir eventos

Cuando declaras un tipo para contener los datos para tu evento, tienes que derivar de Object en lugar de derivar de EventArgs, porque EventArgs no es un tipo de Windows en tiempo de ejecución. Utiliza EventHandler<TEventArgs> como tipo de evento, y utiliza tu tipo de argumento de evento como argumento de tipo genérico. Puedes producir el evento exactamente igual que en una aplicación de .NET Framework.

Cuando tu componente de Windows en tiempo de ejecución se utiliza desde JavaScript o C++, el evento sigue el patrón de eventos de Windows en tiempo de ejecución que esos lenguajes esperan. Cuando utilizas el componente desde C# o Visual Basic, el evento aparece como un evento de .NET Framework ordinario. En Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript se proporciona un ejemplo.

Si implementas descriptores de acceso de eventos personalizados (declarar un evento con la palabra clave Custom, en Visual Basic), debes seguir el patrón de eventos de Windows en tiempo de ejecución en tu implementación. Consulta Eventos personalizados y descriptores de acceso de eventos en componentes de Windows en tiempo de ejecución. Observa que cuando controlas el evento de código de C# o Visual Basic, sigue pareciendo un evento de .NET Framework ordinario.

Vea también

Conceptos

Información general de .NET para aplicaciones de la Tienda Windows

.NET para aplicaciones de la Tienda Windows: API admitidas

Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript

Crear componentes de Windows en tiempo de ejecución en

Eventos personalizados y descriptores de acceso de eventos en componentes de Windows en tiempo de ejecución

Pasar matrices a un componente de Windows en tiempo de ejecución

Asignaciones de tipos de Windows Runtime en .NET Framework

Diagnosticar condiciones de error en componentes de Windows en tiempo de ejecución