Rozszerzanie aplikacji za pomocą usług, rozszerzeń i pakietów

Windows udostępnia kilka technologii, które umożliwiają aplikacji udostępnianie funkcji innym aplikacjom lub korzystanie z dodatków innych firm. W tym artykule porównaliśmy dostępne opcje rozszerzalności dla aplikacji klasycznych Zestaw SDK do aplikacji systemu Windows.

Omówienie opcji rozszerzalności

Technologia Description Wymagany identyfikator pakietu Minimalny system operacyjny
App Services Komunikacja żądań/odpowiedzi między aplikacjami za pośrednictwem AppServiceConnection Yes Windows 10 1607
Rozszerzenia aplikacji Model wtyczek — aplikacja hosta odnajduje zawartość z pakietów rozszerzeń Yes Windows 10 1607
Rozszerzenia pakietów Szersza rozszerzalność na poziomie pakietu z uap17:PackageExtension Yes Windows 11
Pakiety opcjonalne Dodatkowe pakiety zawartości, które uzupełniają główną aplikację Yes Windows 10 1709
Pakiety zasobów Język, skala i zasoby dostępności rozdzielone według rynku Yes Windows 10

Wybieranie odpowiedniej technologii

Używaj usług aplikacji, gdy

  • Potrzebna jest dwukierunkowa komunikacja między oddzielnymi aplikacjami.
  • Aplikacja konsumenta wysyła żądanie i czeka na odpowiedź.
  • Chcesz udostępnić interfejs podobny do interfejsu API innym aplikacjom.

Przykład: usługa tłumaczenia, którą inne aplikacje mogą wywoływać w celu tłumaczenia tekstu.

Używaj rozszerzeń aplikacji, gdy

  • Aplikacja wymaga modelu wtyczek, w którym inne firmy udostępniają zawartość, motywy lub dodatki.
  • Rozszerzenia są wykrywane w czasie działania zainstalowanych pakietów.
  • Rozszerzenia zapewniają dane lub konfigurację, a nie kod wykonywalny (wykonywanie kodu powinno korzystać z usług App Services).

Przykład: Edytor obrazów, który odnajduje pakiety filtrów z zainstalowanych pakietów rozszerzeń.

Użyj rozszerzeń pakietu, gdy

  • Potrzebna jest szersza rozszerzalność na poziomie pakietu na Windows 11.
  • Rozszerzenia wymagają dostępu do większej PublicFolder ilości zawartości pakietu, niż umożliwia model.

Użyj opcjonalnych pakietów, gdy

  • Masz dodatkową zawartość (DLC, funkcje premium) dystrybuowaną jako oddzielne pakiety.
  • Zawartość jest autorem tego samego wydawcy.

Wzorce architektury

Usługa App Service z odnajdywaniem rozszerzeń

Łączenie rozszerzeń aplikacji z usługami App Services w celu uzyskania pełnej architektury wtyczki:

  1. Aplikacja hostująca używa AppExtensionCatalog do wykrywania zainstalowanych rozszerzeń.
  2. Każde rozszerzenie deklaruje właściwości, które opisują jego możliwości.
  3. Gdy użytkownik aktywuje rozszerzenie, aplikacja hostująca łączy się z usługą aplikacji rozszerzenia w celu komunikacji dwukierunkowej.
┌─────────────────┐      ┌──────────────────┐
│   Host app       │      │  Extension app    │
│                  │      │                   │
│ AppExtension     │◄────►│ AppExtension      │
│   Catalog        │      │   declaration     │
│                  │      │                   │
│ AppService       │◄────►│ AppService        │
│   Connection     │      │   provider        │
└─────────────────┘      └──────────────────┘

Tylko rozszerzenie zawartości

W przypadku prostszych scenariuszy, w których rozszerzenia zapewniają zawartość statyczną (motywy, szablony, pliki danych):

  1. Aplikacja hosta odnajduje rozszerzenia za pomocą polecenia AppExtensionCatalog.
  2. Odczytuje pliki z rozszerzenia PublicFolder.
  3. Usługa App Service nie jest wymagana.

Różnice w rozszerzalności platformy UWP

Opisane tutaj technologie rozszerzalności działają tak samo w klasycznych aplikacjach Zestaw SDK do aplikacji systemu Windows, jak w UWP, pod jednym warunkiem: tożsamości pakietu MSIX. Wszystkie funkcje rozszerzalności opierają się na manifeście pakietu dla deklaracji i katalogu pakietów na potrzeby odnajdywania.

Jeśli aplikacja klasyczna nie jest pakietowana, nie można korzystać z tych technologii rozszerzeń. Rozważ alternatywne podejścia, takie jak:

  • Interfejsy wtyczek oparte na modelu COM
  • Odnajdywanie rozszerzeń opartych na systemie plików
  • Nazwane potoki lub inne mechanizmy IPC

Odnajdywanie wtyczek opartych na plikach dla aplikacji rozpakowanych

W przypadku rozpakowanych aplikacji WinUI 3 można zaimplementować system wtyczek przy użyciu .NET AssemblyLoadContext do ładowania rozszerzeń ze znanego folderu:

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;
    }
}

Warning

Ładowanie zestawów z dysku bez walidacji jest zagrożeniem bezpieczeństwa. W środowisku produkcyjnym przed załadowaniem sprawdź podpisy zestawów (takie jak Authenticode), ogranicz uprawnienia ACL katalogu wtyczek i rozważ uruchomienie wtyczek w osobnym procesie z ograniczonymi uprawnieniami.

Zdefiniuj kontrakt interfejsu współużytkowanego w osobnym zestawie, do którego odwołuje się zarówno host, jak i wtyczki:

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

Note

Użycie isCollectible: true w AssemblyLoadContext umożliwia wyładowywanie wtyczek w czasie wykonywania. Takie podejście pozwala uniknąć problemów z wersjonowaniem, które MEF (Managed Extensibility Framework) może wprowadzać w aplikacjach komputerowych.