Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.

Allow CoreWidgetProvider to be disposed #3524

Merged
merged 6 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions common/Services/IExtensionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppExtensions;

namespace DevHome.Common.Services;

Expand All @@ -16,12 +15,10 @@ public interface IExtensionService

Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(Microsoft.Windows.DevHome.SDK.ProviderType providerType, bool includeDisabledExtensions = false);

Task<IEnumerable<IExtensionWrapper>> GetAllExtensionsAsync();
IExtensionWrapper? GetInstalledExtension(string extensionUniqueId);

Task SignalStopExtensionsAsync();

Task<IEnumerable<AppExtension>> GetInstalledAppExtensionsAsync();

public event EventHandler OnExtensionsChanged;

public void EnableExtension(string extensionUniqueId);
Expand Down
3 changes: 2 additions & 1 deletion src/Models/ExtensionWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,13 @@ await Task.Run(() =>
try
{
var hr = PInvoke.CoCreateInstance(Guid.Parse(ExtensionClassId), null, CLSCTX.CLSCTX_LOCAL_SERVER, typeof(IExtension).GUID, out var extensionObj);
extensionPtr = Marshal.GetIUnknownForObject(extensionObj);
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}

extensionPtr = Marshal.GetIUnknownForObject(extensionObj);

_extensionObject = MarshalInterface<IExtension>.FromAbi(extensionPtr);
}
finally
Expand Down
32 changes: 10 additions & 22 deletions src/Services/ExtensionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@
using DevHome.Telemetry;
using Microsoft.UI.Xaml;
using Microsoft.Windows.DevHome.SDK;
using Newtonsoft.Json.Linq;
using Serilog;
using Windows.ApplicationModel;
using Windows.ApplicationModel.AppExtensions;
using Windows.Foundation.Collections;
using YamlDotNet.Core.Tokens;
using static DevHome.Common.Helpers.ManagementInfrastructureHelper;

namespace DevHome.Services;
Expand All @@ -37,11 +35,9 @@ public class ExtensionService : IExtensionService, IDisposable
private const string CreateInstanceProperty = "CreateInstance";
private const string ClassIdProperty = "@ClassId";

#pragma warning disable IDE0044 // Add readonly modifier
private static List<IExtensionWrapper> _installedExtensions = new();
private static List<IExtensionWrapper> _enabledExtensions = new();
private static List<string> _installedWidgetsPackageFamilyNames = new();
#pragma warning restore IDE0044 // Add readonly modifier
private static readonly List<IExtensionWrapper> _installedExtensions = new();
private static readonly List<IExtensionWrapper> _enabledExtensions = new();
private static readonly List<string> _installedWidgetsPackageFamilyNames = new();

public ExtensionService(ILocalSettingsService settingsService)
{
Expand Down Expand Up @@ -158,7 +154,7 @@ private async Task<bool> IsValidDevHomeExtension(Package package)
return (devHomeProvider, classIds);
}

public async Task<IEnumerable<AppExtension>> GetInstalledAppExtensionsAsync()
private async Task<IEnumerable<AppExtension>> GetInstalledAppExtensionsAsync()
{
return await AppExtensionCatalog.Open("com.microsoft.devhome").FindAllAsync();
}
Expand Down Expand Up @@ -227,6 +223,12 @@ public async Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(bo
}
}

public IExtensionWrapper? GetInstalledExtension(string extensionUniqueId)
{
var extension = _installedExtensions.Where(extension => extension.ExtensionUniqueId == extensionUniqueId);
return extension.First() ?? null;
}

private async Task<IEnumerable<string>> GetInstalledWidgetExtensionsAsync()
{
await _getInstalledWidgetsLock.WaitAsync();
Expand Down Expand Up @@ -259,20 +261,6 @@ public async Task<IEnumerable<string>> GetInstalledDevHomeWidgetPackageFamilyNam
return ids;
}

public async Task<IEnumerable<IExtensionWrapper>> GetAllExtensionsAsync()
{
var installedExtensions = await GetInstalledExtensionsAsync();
foreach (var installedExtension in installedExtensions)
{
if (!installedExtension.IsRunning())
{
await installedExtension.StartExtensionAsync();
}
}

return installedExtensions;
}

public async Task SignalStopExtensionsAsync()
{
var installedExtensions = await GetInstalledExtensionsAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static IServiceCollection AddDashboard(this IServiceCollection services,
services.AddSingleton<IWidgetIconService, WidgetIconService>();
services.AddSingleton<IWidgetScreenshotService, WidgetScreenshotService>();
services.AddSingleton<WidgetAdaptiveCardRenderingService>();
services.AddSingleton<IWidgetExtensionService, WidgetExtensionService>();

return services;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Threading.Tasks;

namespace DevHome.Dashboard.Services;

internal interface IWidgetExtensionService
{
bool IsCoreWidgetExtension(string providerDefinitionId);

Task EnsureCoreWidgetExtensionStarted(string providerDefinitionId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Threading.Tasks;
using DevHome.Common.Services;

namespace DevHome.Dashboard.Services;

internal sealed class WidgetExtensionService : IWidgetExtensionService
{
private const string ExtensionUniqueIdStable = "Microsoft.Windows.DevHome_8wekyb3d8bbwe!App!PG-SP-ID1";
private const string ExtensionUniqueIdCanary = "Microsoft.Windows.DevHome.Canary_8wekyb3d8bbwe!App!PG-SP-ID1";
private const string ExtensionUniqueIdDev = "Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe!App!PG-SP-ID1";

private const string ProviderDefinitionStable = "Microsoft.Windows.DevHome_8wekyb3d8bbwe!App!!CoreWidgetProvider";
private const string ProviderDefinitionCanary = "Microsoft.Windows.DevHome.Canary_8wekyb3d8bbwe!App!!CoreWidgetProvider";
private const string ProviderDefinitionDev = "Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe!App!!CoreWidgetProvider";

private readonly IExtensionService _extensionService;

public WidgetExtensionService(IExtensionService extensionService)
{
_extensionService = extensionService;
}

public bool IsCoreWidgetExtension(string providerDefinitionId)
{
return providerDefinitionId.Equals(ProviderDefinitionStable, StringComparison.Ordinal) ||
providerDefinitionId.Equals(ProviderDefinitionCanary, StringComparison.Ordinal) ||
providerDefinitionId.Equals(ProviderDefinitionDev, StringComparison.Ordinal);
}

public async Task EnsureCoreWidgetExtensionStarted(string providerDefinitionId)
{
if (providerDefinitionId.StartsWith(ProviderDefinitionStable, StringComparison.Ordinal))
{
await EnsureExtensionStarted(ExtensionUniqueIdStable);
}
else if (providerDefinitionId.StartsWith(ProviderDefinitionCanary, StringComparison.Ordinal))
{
await EnsureExtensionStarted(ExtensionUniqueIdCanary);
}
else if (providerDefinitionId.StartsWith(ProviderDefinitionDev, StringComparison.Ordinal))
{
await EnsureExtensionStarted(ExtensionUniqueIdDev);
}
}

private async Task EnsureExtensionStarted(string extensionUniqueId)
{
var extensionWrapper = _extensionService.GetInstalledExtension(extensionUniqueId);
if (!extensionWrapper.IsRunning())
{
await extensionWrapper.StartExtensionAsync();
}
}
}
10 changes: 10 additions & 0 deletions tools/Dashboard/DevHome.Dashboard/Views/DashboardView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public partial class DashboardView : ToolPage, IDisposable

private static DispatcherQueue _dispatcherQueue;
private readonly ILocalSettingsService _localSettingsService;
private readonly IWidgetExtensionService _widgetExtensionService;
private bool _disposedValue;

private const string DraggedWidget = "DraggedWidget";
Expand All @@ -67,6 +68,7 @@ public DashboardView()

_dispatcherQueue = Application.Current.GetService<DispatcherQueue>();
_localSettingsService = Application.Current.GetService<ILocalSettingsService>();
_widgetExtensionService = Application.Current.GetService<IWidgetExtensionService>();

#if DEBUG
Loaded += AddResetButton;
Expand Down Expand Up @@ -592,6 +594,14 @@ await Task.Run(async () =>
return;
}

// The WidgetService will start the widget provider, however Dev Home won't know about it and won't be
// able to send disposed events when Dev Home closes. Ensure the provider is started here so we can
// tell the extension to dispose later.
if (_widgetExtensionService.IsCoreWidgetExtension(comSafeWidgetDefinition.ProviderDefinitionId))
{
await _widgetExtensionService.EnsureCoreWidgetExtensionStarted(comSafeWidgetDefinition.ProviderDefinitionId);
}

TelemetryFactory.Get<ITelemetry>().Log(
"Dashboard_ReportPinnedWidget",
LogLevel.Critical,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI;
using DevHome.Common.Extensions;
using DevHome.Common.Helpers;
using DevHome.Common.Services;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
Expand All @@ -27,18 +26,18 @@ public partial class ExtensionLibraryViewModel : ObservableObject
{
private readonly ILogger _log = Log.ForContext("SourceContext", nameof(ExtensionLibraryViewModel));

private readonly string devHomeProductId = "9N8MHTPHNGVV";
private const string DevHomeProductId = "9N8MHTPHNGVV";

private readonly IExtensionService _extensionService;
private readonly DispatcherQueue _dispatcherQueue;

// All internal Dev Home extensions that should allow users to enable/disable them, should add
// their class Ids to this set.
private readonly HashSet<string> _internalClassIdsToBeShownInExtensionsPage = new()
{
private readonly HashSet<string> _internalClassIdsToBeShownInExtensionsPage =
[
HyperVExtensionClassId,
WSLExtensionClassId,
};
];

public ObservableCollection<StorePackageViewModel> StorePackagesList { get; set; }

Expand Down Expand Up @@ -68,7 +67,7 @@ public async Task GetUpdatesButtonAsync()
[RelayCommand]
public async Task LoadedAsync()
{
await GetInstalledExtensionsAsync();
await GetInstalledPackagesAndExtensionsAsync();
GetAvailablePackages();
}

Expand All @@ -77,12 +76,12 @@ private async void OnExtensionsChanged(object? sender, EventArgs e)
await _dispatcherQueue.EnqueueAsync(async () =>
{
ShouldShowStoreError = false;
await GetInstalledExtensionsAsync();
await GetInstalledPackagesAndExtensionsAsync();
GetAvailablePackages();
});
}

private async Task GetInstalledExtensionsAsync()
private async Task GetInstalledPackagesAndExtensionsAsync()
{
var extensionWrappers = await _extensionService.GetInstalledExtensionsAsync(true);

Expand Down Expand Up @@ -166,7 +165,7 @@ private async void GetAvailablePackages()
var productId = productObj.GetNamedString("ProductId");

// Don't show self as available.
if (productId == devHomeProductId)
if (productId == DevHomeProductId)
{
continue;
}
Expand Down
Loading