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

A partir de .NET Framework 4.5, puede usar código administrado para crear sus 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 crea un componente para usarlo con JavaScript o C++, debe ser consciente de las diferencias en la manera en que esos lenguajes admiten Windows en tiempo de ejecución.

Nota Nota

Si está 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, considere 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:

Internamente, los tipos de Windows en tiempo de ejecución de su componente pueden usar cualquier funcionalidad de .NET Framework que se permiten en una aplicación de Tienda Windows. (Consulte Información general de .NET para aplicaciones de la Tienda Windows para obtener más información). 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 públicos 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, puede crear sus 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 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 el 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 su modelo de programación requiere polimorfismo, puede crear una interfaz pública e implementarla en las clases que deben ser polimórficas.

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

Cuando pruebe su componente como parte de una aplicación de Tienda Windows mediante C++, puede 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. Abra el menú contextual de su proyecto de Visual C++ y elija Propiedades.

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

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

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

Cuando pruebe su 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 y Visual Studio Express, no puede depurar código JavaScript y código administrado al mismo tiempo.

Para depurar código administrado en lugar de JavaScript

  1. Abra el menú contextual de su proyecto de JavaScript y elija Propiedades.

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

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

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

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. Consulte el 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, use el tipo del mismo nombre en el espacio de nombres System.

  • Para UInt8, use System.Byte.

  • Para Char16, use System.Char.

  • Para la interfaz IInspectable, use System.Object.

Si C# o Visual Basic proporcionan una palabra clave de lenguaje para cualquiera de estos tipos, puedes usar 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 común, 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 los tipos de Windows en tiempo de ejecución que .NET Framework asigna, vea 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, puede usar cualquiera de las interfaces que implementa como un tipo de parámetro o un tipo de valor devuelto de un miembro. Por ejemplo, puede 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>>.

Nota importante Importante

JavaScript usa 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 iteran 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. Puede ver este ejemplo en el artículo Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript.

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 del componente, o en las firmas de los miembros de Windows en tiempo de ejecución cuando los usa 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 su componente desde JavaScript, consulte la sección “Devolver tipos administrados desde el componente” en el Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript.

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. Consulte Pasar matrices a un componente de Windows en tiempo de ejecución.

En Windows en tiempo de ejecución, los métodos se pueden sobrecargar. Sin embargo, si declara varias sobrecargas con el mismo número de parámetros, solo debe aplicar el atributo Windows.Foundation.Metadata.DefaultOverloadAttribute a 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;
        } 

Nota de precauciónPrecaución

JavaScript 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 se puede aplicar el atributo DefaultOverloadAttribute a constructores. Todos los constructores de una clase deben tener números de parámetros diferentes.

A partir de Windows 8,1, Windows en tiempo de ejecución incluye una interfaz IStringable cuyo único método, IStringable.ToString, proporciona la compatibilidad básica de formato comparable a la proporcionada por Object.ToString. Si decide implementar IStringable en un tipo público administrado que se exporte en un componente de Windows en tiempo de ejecución, se aplican las restricciones siguientes:

  • Solo puede definir la interfaz IStringable en una relación “la clase implementa”, como

    public class NewClass : IStringable
    

    en C# o

    Public Class NewClass : Implements IStringable
    

    en Visual Basic.

  • No puede implementar IStringable en una interfaz.

  • No puede declarar un parámetro como de tipo IStringable.

  • IStringable no puede ser el tipo de valor devuelto de un método, propiedad o campo.

  • No se puede ocultar la implementación de IStringable de las clases base mediante una definición de método como la siguiente:

    
    public class NewClass : IStringable
    {
       public new string ToString()
       {
          return "New ToString in NewClass";
       }
    }
    
    

    En su lugar, la implementación de IStringable.ToString debe invalidar siempre la implementación de la clase base. Solo puede ocultar una implementación de ToString invocándola en una instancia de clase fuertemente tipada.

Tenga en cuenta que en determinadas condiciones, las llamadas de código nativo a un tipo administrado que implemente IStringable u oculte la implementación de ToString pueden generar un comportamiento inesperado.

Para implementar un método asincrónico en su componente, agregue “Async” al final del nombre de método y devuelva 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>.

Puede usar tareas de .NET Framework (la clase Task y la clase genérica Task<TResult>) para implementar su 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 usa 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, puede usar 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();
        }




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, puede usar 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, consulte el Tutorial: Crear un componente simple en C# o Visual Basic y llamarlo desde JavaScript.

Observe que puede usar 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 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 usa 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);
            });
        }

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

Puede producir cualquier tipo de excepción que esté incluida en API de .NET para aplicaciones de la Tienda Windows. 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 su componente no controla la excepción, se produce una excepción correspondiente en el código que llamó a su 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 puede tener acceso al texto de mensaje original desde código de JavaScript.

    Nota 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 use 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. Consulte Excepciones (C++/CX).

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

Cuando produce una excepción desde su componente, puede 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 de su componente. El valor 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 Nota

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

Cuando declara un tipo para contener los datos para su evento, tiene 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 su componente de Windows en tiempo de ejecución se usa 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 implementa descriptores de acceso de eventos personalizados (declarar un evento con la palabra clave Custom, en Visual Basic), debe seguir el patrón de eventos de Windows en tiempo de ejecución en su implementación. Consulte Eventos personalizados y descriptores de acceso de eventos en componentes de Windows en tiempo de ejecución. Observe que cuando controla el evento de código de C# o Visual Basic, sigue pareciendo un evento de .NET Framework ordinario.

Después de crear un componente de Windows en tiempo de ejecución para uso propio, es posible que descubra que la funcionalidad que encapsula resulta útil a otros desarrolladores. Tienes dos opciones para empaquetar un componente para distribuirlo a otros desarrolladores. Consulte Distribuir un componente administrado de Windows en tiempo de ejecución.

Para obtener más información sobre las características del lenguaje Visual Basic y C#, y la compatibilidad de .NET Framework con Windows en tiempo de ejecución, consulte la Referencia del lenguaje Visual Basic y C#.

Mostrar:
© 2015 Microsoft