Ampliar la aplicación con servicios, extensiones y paquetes

Windows proporciona varias tecnologías que permiten a la aplicación proporcionar funcionalidad a otras aplicaciones o consumir complementos de terceros. En este artículo se comparan las opciones de extensibilidad disponibles para SDK de Aplicaciones para Windows aplicaciones de escritorio.

Introducción a las opciones de extensibilidad

Tecnología Description Se requiere la identidad del paquete Sistema operativo mínimo
App Services Comunicación de solicitud y respuesta entre aplicaciones a través de AppServiceConnection Windows 10 1607
Extensiones de aplicación Modelo de complemento: la aplicación host detecta contenido de paquetes de extensión Windows 10 1607
Extensiones de paquete Extensibilidad de nivel de paquete más amplia con uap17:PackageExtension Windows 11
Paquetes opcionales Paquetes de contenido adicionales que complementan una aplicación principal Windows 10 1709
Paquetes de recursos Recursos de idioma, escala y accesibilidad separados por el mercado Windows 10

Elección de la tecnología adecuada

Use servicios de aplicaciones cuando

  • Necesita comunicación bidireccional entre aplicaciones independientes.
  • La aplicación de consumidor envía una solicitud y espera una respuesta.
  • Necesita exponer una interfaz tipo API a otras aplicaciones.

Ejemplo: servicio de traducción al que otras aplicaciones pueden llamar para traducir texto.

Usa extensiones de apps cuando

  • La aplicación necesita un modelo de complemento en el que terceros proporcionan contenido, temas o complementos.
  • Las extensiones se detectan en tiempo de ejecución desde paquetes instalados.
  • Las extensiones proporcionan datos o configuración, no código ejecutable (la ejecución de código debe usar app services).

Ejemplo: un editor de imágenes que detecta paquetes de filtros de paquetes de extensión instalados.

Usar extensiones de paquete cuando

  • Necesita una mayor extensibilidad a nivel de paquete en Windows 11.
  • Las extensiones necesitan tener acceso a más contenido del paquete que el PublicFolder modelo permite.

Utiliza paquetes opcionales cuando

  • Tiene contenido adicional (DLC, características premium) distribuidos como paquetes independientes.
  • El mismo publicador crea el contenido.

Patrones de arquitectura

App Service con detección de extensiones

Combina extensiones de aplicación con servicios de la aplicación para una arquitectura completa de complementos:

  1. La aplicación host usa AppExtensionCatalog para detectar extensiones instaladas.
  2. Cada extensión declara propiedades que describen sus funcionalidades.
  3. Cuando el usuario activa una extensión, la aplicación host se conecta al servicio de aplicaciones de la extensión para la comunicación bidireccional.
┌─────────────────┐      ┌──────────────────┐
│   Host app       │      │  Extension app    │
│                  │      │                   │
│ AppExtension     │◄────►│ AppExtension      │
│   Catalog        │      │   declaration     │
│                  │      │                   │
│ AppService       │◄────►│ AppService        │
│   Connection     │      │   provider        │
└─────────────────┘      └──────────────────┘

Solo extensión de contenido

Para escenarios más sencillos en los que las extensiones proporcionan contenido estático (temas, plantillas, archivos de datos):

  1. La aplicación host detecta extensiones a través de AppExtensionCatalog.
  2. Lee los archivos de la extensión PublicFolder.
  3. No se necesita ningún servicio de aplicaciones.

Diferencias con la extensibilidad de UWP

Las tecnologías de extensibilidad que se describen aquí funcionan de la misma manera en SDK de Aplicaciones para Windows aplicaciones de escritorio que en UWP, con un requisito: identidad de paquete MSIX. Todas las características de extensibilidad se basan en el manifiesto del paquete para las declaraciones y el catálogo de paquetes para la detección.

Si la aplicación de escritorio está desempaquetada, no puede usar estas tecnologías de extensibilidad. Considere enfoques alternativos como:

  • Interfaces de complemento basadas en COM
  • Detección de extensiones basadas en el sistema de archivos
  • Canalizaciones con nombre u otros mecanismos IPC

Detección de complementos basados en archivos para aplicaciones desempaquetadas

En el caso de las aplicaciones WinUI 3 sin empaquetar, puede implementar un sistema de complementos mediante .NET AssemblyLoadContext para cargar extensiones desde una carpeta conocida:

public class PluginLoader
{
    private readonly string _pluginDirectory;

    public PluginLoader(string pluginDirectory)
    {
        _pluginDirectory = pluginDirectory;
    }

    public IEnumerable<T> LoadPlugins<T>() where T : class
    {
        if (!Directory.Exists(_pluginDirectory))
            yield break;

        foreach (var dll in Directory.GetFiles(_pluginDirectory, "*.dll"))
        {
            var context = new PluginLoadContext(dll);
            var assembly = context.LoadFromAssemblyPath(Path.GetFullPath(dll));

            foreach (var type in assembly.GetTypes()
                .Where(t => typeof(T).IsAssignableFrom(t) && !t.IsAbstract))
            {
                if (Activator.CreateInstance(type) is T plugin)
                    yield return plugin;
            }
        }
    }
}

// Custom AssemblyLoadContext to isolate plugin dependencies
public class PluginLoadContext : AssemblyLoadContext
{
    private readonly AssemblyDependencyResolver _resolver;

    public PluginLoadContext(string pluginPath) : base(isCollectible: true)
    {
        _resolver = new AssemblyDependencyResolver(pluginPath);
    }

    protected override Assembly? Load(AssemblyName assemblyName)
    {
        var path = _resolver.ResolveAssemblyToPath(assemblyName);
        return path != null ? LoadFromAssemblyPath(path) : null;
    }
}

Advertencia

La carga de ensamblados desde el disco sin validación es un riesgo de seguridad. En producción, compruebe las firmas de ensamblado (como Authenticode) antes de cargar, restrinja los permisos de ACL del directorio del complemento y considere la posibilidad de ejecutar complementos en un proceso independiente con privilegios reducidos.

Defina un contrato de interfaz compartida en un ensamblado independiente al que hacen referencia tanto el host como los complementos:

// Contoso.App.Contracts (shared assembly)
public interface IPluginExtension
{
    string Name { get; }
    string Description { get; }
    void Execute(IServiceProvider services);
}

Note

El uso de isCollectible: true en AssemblyLoadContext permite descargar complementos en tiempo de ejecución. Este enfoque evita los problemas de control de versiones que MEF (Managed Extensibility Framework) pueden introducir en las aplicaciones de escritorio.