Mayo de 2018

Volumen 33, número 5

Plataforma universal de Windows: Eliminación de brechas entre UWP y Win32

Por Andrew Whitechapel

Uno de los temas importantes para la actualización más reciente de Windows ha sido cerrar los huecos entre la plataforma Universal de Windows (UWP) y los modelos de aplicación de Win32 tradicionales. Como parte de este esfuerzo, Microsoft introdujo tres entre las principales mejoras:

  • Creación de varias instancias
  • Aplicaciones de consola UWP
  • Un mayor acceso al sistema de archivos

Las características están relacionadas, aunque independientes en gran medida. Es decir, puede crear una aplicación de varias instancias que es una aplicación de ventana normal o una aplicación de consola y es posible que o puede no necesitará más amplio sistema de archivos tener acceso. De igual forma, puede crear una aplicación de consola no, no de instancias múltiples regular que tiene acceso amplio sistema de archivos. Una restricción es que una aplicación de consola debe configurarse para admitir la creación de varias instancias.

Creación de varias instancias

Creación de varias instancias de Win32, Linux y otros entornos de modelo de aplicación, ha sido siempre el valor predeterminado. En el UWP contrasta, el valor predeterminado siempre ha sido creación de instancias únicas, y, de hecho, la creación de instancias múltiples no se admite la en absoluto hasta ahora.

Sin la creación de varias instancias, algunas aplicaciones se vuelven a ordenar a una arquitectura de varias ventanas en su lugar, lo que normalmente implica mucho trabajo y los resultados de la complejidad y fragilidad. Tendrá que dedicar un gran esfuerzo en la administración de las ventanas, en lugar de centrarse en los requisitos de dominio. Proceso único multi-ventana también sufre problemas de confiabilidad: Si se bloquea la instancia única, desactiva todas sus ventanas; Esto no es cierto para la creación de varias instancias, donde cada instancia se ejecuta como un proceso independiente.

En el modelo de instancia única, el usuario puede activar una aplicación de varias maneras: a través de una derivación de icono de inicio; a través de una dirección URL o protocolo la activación; Haga doble clic en un archivo con una extensión que está registrado en la aplicación; y así sucesivamente. La primera activación (de cualquier tipo) inicia la aplicación. Después de eso, las activaciones posteriores basta con llamar a la instancia en ejecución de la aplicación, lo que la aplicación puede controlar invalidando el método OnActivated.

La nueva característica de creación de varias instancias permite que las aplicaciones UWP para que funcione como aplicaciones de Win32: Si está ejecutando una instancia de una aplicación y se recibe una solicitud de activación posteriores, la plataforma no llamará a activar la instancia existente. En su lugar, creará una nueva instancia en un proceso independiente.

Dado que esta característica depende en gran medida por Win32 paridad, lo inicialmente solo se admite en escritorios y Windows IoT. Se introdujeron dos niveles de compatibilidad para la creación de instancias múltiples:

  • Aplicación de UWP de varias instancias: Se trata de un caso simple, donde la aplicación solo desea declarar que debe ser varias instancias.
  • Aplicación de UWP de redirección de varias instancias: Se trata en el caso complejo, donde la aplicación desea tener varias instancias, pero también desea tener un decir en exactamente cómo se activa cada instancia.

En ambos casos, se proporciona una plantilla de proyecto de Visual Studio, como se muestra en figura 1.

Nuevas plantillas de proyecto para las aplicaciones de varias instancias
Figura 1 nuevas plantillas de proyecto para las aplicaciones de varias instancias

Para un caso simple, la plantilla de proyecto genera código que es casi idéntico al código de la plantilla de aplicación en blanco. La única diferencia es el uso del atributo SupportsMultipleInstances en el manifiesto de aplicación. Hay dos declaraciones adicionales: La primera es para los espacios de nombres desktop4 y iot2 XML en la parte superior del manifiesto:

xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"
  IgnorableNamespaces="uap mp desktop4 iot2">

El segundo, agrega el atributo en el elemento < Application >:

<Application
  Id="App8" Executable="$targetnametoken$.exe" EntryPoint="App8.App"
  desktop4:SupportsMultipleInstances="true"
  iot2:SupportsMultipleInstances="true">

Si va a actualizar el código de aplicación existente en lugar de generar una nueva aplicación, puede simplemente agregar manualmente estas entradas al manifiesto. Una vez hecho esto, puede compilar la aplicación e iniciar varias instancias. Con este manifiesto de entrada, cada vez que se activa la aplicación, si una derivación de mosaico o cualquier otra activación contrato el admite la aplicación, como la asociación de archivo o protocolo de inicio: cada activación dará como resultado una instancia independiente. Tan simple como eso.

Redirección de varias instancias

Para la mayoría de las aplicaciones, todo lo que necesita hacer es agregar la entrada de manifiesto, pero para las aplicaciones que desea un mayor grado de control sobre sus activaciones de instancia, puede usar la segunda plantilla. Esto agrega las mismas entradas manifiestos exactas y también agrega un archivo adicional (Program.cs para las aplicaciones de C#) o Program.cpp para C++ que contiene una función de principal estándar, como se muestra en figura 2. Utiliza una nueva clase AppInstance presentada en esta versión.

Figura 2 estándar Main (función) para la redirección de varias instancias

static void Main(string[] args)
{
  IActivatedEventArgs activatedArgs = AppInstance.GetActivatedEventArgs();
  if (AppInstance.RecommendedInstance != null)
  {
    AppInstance.RecommendedInstance.RedirectActivationTo();
  }
  else
  {
    uint number = CryptographicBuffer.GenerateRandomNumber();
    string key = (number % 2 == 0) ? "even" : "odd";
    var instance = AppInstance.FindOrRegisterInstanceForKey(key);
    if (instance.IsCurrentInstance)
    {
      global::Windows.UI.Xaml.Application.Start((p) => new App());
    }
    else
    {
      instance.RedirectActivationTo();
    }
  }
}

Lo primero que hace la función es obtener los argumentos de activación de esta instancia. La aplicación le gustaría utilizar la información contenida en estos argumentos como parte de su lógica de redirección.

En algunos escenarios, la plataforma podría indicar una instancia recomendada. Si es así, puede redirigir esta activación a esa instancia en su lugar, si lo desea, mediante el método AppInstance.RedirectActivationTo. En otras palabras, la aplicación puede decidir permitir que esta solicitud de activación se redirija a una instancia existente en su lugar. Si realiza la redirección, a continuación, se activa la instancia de destino y se invoca su método OnActivated, y finaliza esta nueva instancia.

Si la plataforma no indica una instancia preferida, continúe y crear una clave. El código de ejemplo crea una clave de un número aleatorio, pero normalmente sería reemplazar este código y crear una clave basada en lógica definida por la aplicación. Normalmente esto se basa en los argumentos de activación recuperados versiones anteriores. Por ejemplo, si los argumentos de activación eran de tipo FileActivatedEventArgs, la aplicación podría utilizar el nombre de archivo especificado como parte de la clave. Una vez que ha creado la clave, páselo al método FindOrRegisterInstanceForKey, que devuelve un objeto AppInstance que representa una instancia de esta aplicación. Para determinar a qué instancia para devolver, el método hace dos cosas:

  • Busca una instancia existente de la aplicación que ya se ha registrado esta clave.
  • Si no hay ninguna instancia existente ya registró esta clave, registra la instancia actual con esta clave.

Si se ha registrado correctamente esta instancia, ahora puede simplemente siga adelante y realice la inicialización de la aplicación normal. Para una aplicación XAML, esto significa que al llamar a Application.Start con una nueva instancia de la clase App. Si alguna otra instancia ya registró esta clave, ahora puede redirigir esta activación a esa instancia en su lugar y permitir que esta instancia terminar. Por ejemplo, piense en una aplicación que realiza cambios en archivos. Si el usuario tiene Foo.doc abierto para los cambios en la aplicación y, a continuación, intente volver a abrir Foo.doc, la aplicación puede elegir redirigir la activación de la segunda a la instancia que ya tiene abierto de Foo.doc y evitar que el usuario pueda abrir el mismo archivo en varias instancias. La lógica para decidir si desea redirigir o no y qué instancia para seleccionar como destino de redirección es completamente definidas por la aplicación.

Para las aplicaciones XAML, el método Main es normalmente se generado automáticamente y se oculta del programador. Este comportamiento se suprime en la plantilla "varias instancias con redirección". Si va a actualizar una aplicación existente, puede suprimir los principales predeterminado agregando DISABLE_XAML_GENERATED_MAIN a la lista de símbolos de compilación condicional en las propiedades de compilación de la aplicación. En algunos tipos de aplicación, por ejemplo, una aplicación de DirectX de C++, la función principal no está oculto. Aparte de eso, el uso de la aplicación de DirectX de las nuevas API sigue los mismos patrones como en el ejemplo XAML.

Tenga en cuenta que una aplicación sólo puede utilizar los métodos GetActivatedEventArgs y RedirectActivationTo durante principal; Si se denominan en ningún otro lugar, se producirá un error. Se trata como si desea participar en el redireccionamiento de activación, tiene que hacer esto muy temprano en la vida del proceso de aplicación y, por supuesto, antes de cualquier windows se crean.

Por otro lado, puede usar las propiedades y métodos AppInstance restantes en cualquier momento. En concreto, puede usar FindOrRegisterInstanceForKey para actualizar la clave de la instancia actual, siempre que lo necesite. Por ejemplo, si la clave se basa en un nombre de archivo y cerrar más adelante en este archivo, debería actualizar el registro de clave en ese momento. También puede usar el método de eliminación del registro para anular el registro por completo si por algún motivo que ya no desea que esta instancia concreta para participar en el redireccionamiento de activación. Además, en cualquier momento, puede utilizar el método AppInstance.GetInstances para obtener una lista de todas las instancias registradas de la aplicación, incluidas sus claves, por lo que puede analizar su estado.

Consideraciones adicionales

Creación de varias instancias es una mejora importante, y la versión inicial cubre solo los escenarios principales. En concreto, se incluye una aplicación de primer plano de varias instancias, las aplicaciones de consola y mayoría de las tareas de fondo fuera de proceso incluidos los servicios de aplicaciones de compatibilidad con. Sin embargo, hay que no se admite en esta versión para las tareas de ApplicationTrigger o las tareas en segundo plano en proceso.

Durante el desarrollo, Microsoft dedicado mucho tiempo a probar una amplia gama de aplicaciones existentes de la tienda para ver cómo se realizan cuando varias instancias. Con esto, Microsoft había aprendido que las aplicaciones se dividen en tres categorías generales:

  • Aplicaciones que no tienen ningún motivo para tener varias instancias. Estas aplicaciones simplemente no participación en la característica.
  • Aplicaciones que deseen tener varias instancias y siguen funcionando correctamente sin realizar ningún cambio en el código. Estas aplicaciones simplemente pueden participar en varias instancias y llamar a hacerlo.
  • Aplicaciones que deseen tener varias instancias, pero necesitan trabajar para permitir las diferencias en el modelo de ejecución.

El problema con las aplicaciones de la tercera categoría común es que están usando algunos recursos central, quizás una memoria caché, o una base de datos u otro archivo y cuándo una sola instancia ha ha sin ningún riesgo suponiendo que la aplicación tiene acceso exclusivo a este recurso. Una vez que participación en la creación de instancias múltiples, puede haber varias instancias que se intenta tener acceso al recurso. En este escenario, la aplicación necesita realizar el trabajo para sincronizar el acceso, bloqueo de lecturas y escrituras y así sucesivamente, en otras palabras, toda la sincronización habitual emite considere la posibilidad de que necesitan aplicaciones de Win32 tradicionales.

Por ejemplo obvio, considere el uso de almacenamiento local de la aplicación. Este es un ejemplo de un recurso donde está restringido el acceso a un paquete, no es una base de proceso, y por supuesto todas las instancias de una aplicación comparten el mismo paquete. Aunque cada instancia de la aplicación se ejecuta como un proceso independiente, entonces todos usarán el mismo almacenamiento local y la configuración, tal como está representado por la API de ApplicationData.Current. Si va a realizar operaciones de acceso a datos en el almacenamiento local, debe considerar cómo protegerse contra los conflictos. Una opción es usar los archivos de instancia único, donde las operaciones de una instancia no se entran en conflicto con ningún otro. O bien, si desea usar un archivo común a través de varias instancias, debe bloquear y desbloquear el acceso al archivo correctamente. Puede usar los mecanismos estándar como una exclusión mutua con nombre para este.

Consola de aplicaciones UWP

Otra diferencia obvia en el panorama UWP es la capacidad para crear una aplicación de consola sin periféricos. En Win32 y otros entornos, puede crear una herramienta de línea de comandos que utiliza la ventana de consola para la entrada y salida. Por lo tanto, hemos agregado esta compatibilidad también. De nuevo, hay una nueva plantilla de proyecto de Visual Studio, y al igual que con las aplicaciones de varias instancias, esto genera entradas de manifiesto adicionales. Esta característica también está restringida al escritorio e IoT: no menos porque solo esas SKU tienen ahora realmente ventanas de la consola. Se declaran los mismos espacios de nombres XML. El elemento < Application > incluye los atributos SupportsMultipleInstances y subsistema, con el conjunto de subsistema en "consola". Aplicaciones de consola deben tener varias instancias, éste es el modelo esperado para mover desde las aplicaciones de consola Win32 tradicionales de aplicaciones. Además, la aplicación incluye un AppExecutionAlias, y este enfoque también tiene el nuevo atributo de subsistema, tal como se muestra en figura 3.

Figura 3 entradas de manifiesto adicionales para una aplicación de consola

<Application Id="App"
  Executable="$targetnametoken$.exe"
  EntryPoint="App9.App"
  desktop4:Subsystem="console"
  desktop4:SupportsMultipleInstances="true"
  iot2:Subsystem="console"
  iot2:SupportsMultipleInstances="true">
...
  <Extensions>
    <uap5:Extension
      Category="windows.appExecutionAlias"
      Executable="App9.exe"
      EntryPoint="App9.App">
      <uap5:AppExecutionAlias
         desktop4:Subsystem="console" 
        iot2:Subsystem="console">
        <uap5:ExecutionAlias Alias="App9.exe"/>
      </uap5:AppExecutionAlias>
    </uap5:Extension>
  </Extensions>
</Application>

Puede cambiar el valor de Alias en un valor adecuado para la aplicación. Una vez más, al igual que con la creación de varias instancias, la generación de código incluye un archivo Program.cs o Program.cpp. El código generado proporciona un ejemplo de cómo podría implementar la función main necesaria, tal como se muestra en el ejemplo de C++ en figura 4. Puede reemplazar todo el código dentro de main con su propio código personalizado.

Figura 4 plantilla genera código para una función Main de la aplicación de consola

int __cdecl main()
{
  // You can get parsed command-line arguments from the CRT globals.
  wprintf(L"Parsed command-line arguments:\n");
  for (int i = 0; i < __argc; i++)
  {
    wprintf(L"__argv[%d] = %S\n", i, __argv[i]);
  }
  wprintf(L"Press Enter to continue:");
  getchar();
}

Una vez que se ha compilado y se ha implementado la aplicación, se puede ejecutar desde un símbolo del sistema normal, ventana de PowerShell o Windows-R, como se muestra en figura 5. Tenga en cuenta que, puesto que la aplicación utiliza la ventana de consola, no se espera crear todas las demás ventanas, y de hecho, no se admite. En su lugar, la aplicación ahora puede usar todas las APIs System.Console, además de muchas API Win32 tradicional que ahora se han agregado a la lista aprobada específicamente para admitir aplicaciones de consola.

Ejecutar una aplicación de UWP de la consola desde la línea de comandos
Figura 5 ejecuta una aplicación de UWP de la consola desde la línea de comandos

Con esta característica, por último, puede compilar aplicaciones de línea de comandos de consola que sacar partido de las ventajas de UWP, incluido el embalaje APPX, almacenar publicación, actualizaciones fáciles y así sucesivamente.

Un mayor acceso al sistema de archivos

Hasta ahora, una aplicación de UWP solo ha sido capaz de obtener acceso a determinadas carpetas específicas, como la biblioteca de imágenes y la biblioteca de música y, a continuación, sólo si la aplicación declara como funciones en su manifiesto. Además, la aplicación podía tener acceso a cualquier otro lugar en el sistema de archivos cuando se genera un cuadro de diálogo FilePicker y pedir al usuario que elija una ubicación, lo que concede los permisos de aplicación.

Ahora, la característica principal tercera agregada para Win32 paridad aumenta el nivel de acceso de sistema de archivos para aplicaciones UWP. Esto se realiza mediante la inclusión de dos maneras:

  • Acceso implícito al directorio de trabajo actual.
  • Acceso amplio sistema de archivos controlado por una capacidad restringida.

Cualquier aplicación UWP (una aplicación de ventana normal o una aplicación de consola) que declara un AppExecutionAlias ahora tiene acceso implícito a los archivos y carpetas en el directorio de trabajo actual y hacia abajo, cuando se activa desde una línea de comandos. El directorio de trabajo actual es desde cualquier ubicación de sistema de archivos que el usuario decide ejecutar la AppExecutionAlias. Esto se debaten durante mucho tiempo, como el modelo UWP siempre ha sido muy prudentes a la hora de conceder acceso al sistema de archivos a las aplicaciones. En general, se decidió que el usuario decide ejecutar la aplicación desde una ubicación determinada es equivalente al usuario elegir una ubicación en un cuadro de diálogo de FilePicker, en cuanto a la concesión de permisos.

Es importante tener en cuenta que la aplicación tendrá exactamente los mismos permisos de archivo que el usuario que ejecuta la aplicación, por lo que puede haber archivos o carpetas no se puede tener acceso la aplicación, porque el usuario no puede tener acceso a ellos, o bien. Por ejemplo, si el usuario no puede ver un archivo oculto cuando ejecuta un comando dir, la aplicación también podrá ver ese archivo oculto.

Para aprovechar las ventajas de esta característica, puede codificar la invalidación OnActivated para buscar CommandLineActivatedEventArgs. Esto incluye CurrentDirectoryPath, que en este caso será la ubicación de sistema de archivos desde la que el usuario ejecuta la AppExecutionAlias. Figura 6 muestra un ejemplo; aquí, la aplicación extrae el directorio actual y pasa a la MainPage.

Figura 6 reemplazar OnActivated para la activación de línea de comandos

protected override void OnActivated(IActivatedEventArgs args)
{
  switch (args.Kind)
  {
    case ActivationKind.CommandLineLaunch:
      CommandLineActivatedEventArgs cmdLineArgs =
         args as CommandLineActivatedEventArgs;
      CommandLineActivationOperation operation = cmdLineArgs.Operation;
      string activationPath = operation.CurrentDirectoryPath;
      Frame rootFrame = Window.Current.Content as Frame;
      if (rootFrame == null)
      {
        rootFrame = new Frame();
        Window.Current.Content = rootFrame;
      }
      rootFrame.Navigate(typeof(MainPage), activationPath);
      Window.Current.Activate();
      break;
  }
}

A continuación, puede codificar la invalidación de MainPage OnNavigatedTo para recuperar esta ruta de acceso de la entrada NavigationEventArgs, tal y como se muestra en figura 7. En este ejemplo, la aplicación está inicializando un StorageFolder desde esta ruta de acceso y, a continuación, crear un control de vista de árbol para los archivos y carpetas desde aquí hacia abajo.

Figura 7 compilar un árbol de sistema de archivos desde el directorio de trabajo actual

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
  string activationPath = e.Parameter as string;
  argumentsText.Text = activationPath;
  fileTreeView.RootNodes.Clear();
  try
  {
    StorageFolder folder =
       await StorageFolder.GetFolderFromPathAsync(activationPath);
    if (folder != null)
    {
      TreeViewNode rootNode = new TreeViewNode() { Content = folder.Name };
      IReadOnlyList<StorageFolder> folders = await folder.GetFoldersAsync();
      GetDirectories(folders, rootNode);
      fileTreeView.RootNodes.Add(rootNode);
    }
  }
  catch (Exception ex)
  {
    Debug.WriteLine(ex.Message);
  }
}

Nueva capacidad

La segunda manera que se proporciona más acceso al sistema de archivos es a través de una nueva capacidad restringida. Para ello, se debe declarar el espacio de nombres XML restrictedcapabilities en la parte superior de su manifiesto de aplicación e incluir broadFileSystemAccess en la lista de < capacidades >:

xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap mp uap5 rescap">
...
  <Capabilities>
    <rescap:Capability Name="broadFileSystemAccess" />
  </Capabilities>

Si se declara ninguna capacidad restringida, esto desencadena escrutinio adicional en el momento de que enviar el paquete al almacén para la publicación. Si la aplicación se concede esta capacidad, tendrá el mismo acceso al sistema de archivos que el usuario que ejecuta la aplicación. No solo desde el directorio de trabajo actual, pero donde quiera que el usuario tiene acceso. No es necesario un AppExecutionAlias si tienen esta capacidad. Porque se trata de una característica tan eficaz, Microsoft le otorgará la capacidad solo si el desarrollador de aplicaciones proporciona motivos imperiosos para la solicitud, una descripción de cómo se usará y una explicación de cómo Esto beneficia al usuario.

Si se declara la capacidad de broadFileSystemAccess, no es necesario declarar cualquiera de las capacidades más dirigida a un ámbito de sistema de archivos (documentos, imágenes o vídeos); de hecho, una aplicación no debe declarar broadFileSystemAccess y cualquiera de las otras tres funciones de sistema de archivos.

Incluso después de que la aplicación se ha concedido la capacidad, también hay una comprobación en tiempo de ejecución, porque esto constituye un problema de privacidad para el usuario. Al igual que otros problemas de privacidad, la aplicación activará una solicitud de consentimiento de usuario por primera vez. Si el usuario elige denegar el permiso, la aplicación debe ser resistente a este. El usuario puede también cambiar de opinión en cualquier momento, e ir a la página de sistema de archivo correspondiente en la lista de privacidad en la configuración, como se muestra en figura 8.

Nueva página de sistema de archivo de configuración
Figura 8 nueva página de sistema de archivo de configuración

Tenga en cuenta que para aprovechar el acceso al directorio de trabajo actual y los permisos de broadFileSystemAccess, el código debe usar las API de Windows.Storage de WinRT para el control de archivo.

Resumen

Una de las estrategias a largo plazo con UWP es cerrar las brechas con tecnologías anteriores de la aplicación, especialmente Win32, por lo que UWP es una opción viable para cada vez más diferentes tipos de aplicación con el tiempo. Con la introducción de la compatibilidad con true creación de varias instancias, las aplicaciones UWP de consola y un mayor acceso al sistema de archivos, tres más grandes pasos se han realizado en este viaje. Código de ejemplo está disponible en bit.ly/2GtzM3T, y puede encontrar las plantillas de proyecto de Visual Studio en bit.ly/2HApmii y bit.ly/2FEIAXu.


Andrew Whitechapeles un administrador de programas en la división de Microsoft Windows, responsable del flujo de trabajo de activación de aplicación para la plataforma Universal de Windows.

Gracias a los siguientes expertos técnicos por revisar este artículo: Jason Holmes, Tim Kurtzman, Anis Mohammed Khaja Mohideen


Discuta sobre este artículo en el foro de MSDN Magazine