Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inbox Windows Sandbox Environments extension #2752

Merged
merged 32 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
25f70a5
Windows Sandbox Extension
Apr 26, 2024
919083b
revert bad commit
Apr 26, 2024
9786cb0
Launch Windows Sandbox
Apr 27, 2024
14d2b81
Tracelogging and Appx Check
Apr 29, 2024
c41d217
Revert "revert bad commit"
Apr 29, 2024
5478343
Merge branch 'microsoft:main' into user/kevinve/wsb_extension
kvega005 Apr 29, 2024
8ae2919
Undo changes to HyperV extension
Apr 29, 2024
ac95ef6
Fix packaging for WSB extension
Apr 29, 2024
356bdf0
Fix appx package check
Apr 29, 2024
504855f
Merge branch 'microsoft:main' into user/kevinve/wsb_extension
kvega005 Apr 30, 2024
d72b9f9
Remove any cpu
Apr 30, 2024
748b8b5
Only return Compute System provider if OC is enabled and appx is not …
May 1, 2024
f03e2f2
Merge branch 'main' into user/kevinve/wsb_extension
kvega005 May 1, 2024
6443f10
Add publish profiles
May 1, 2024
4a66d8e
Change compute system display name
May 1, 2024
bc7e18a
Start and stop Windows Sandbox
May 3, 2024
a76d673
Address PR comments
May 3, 2024
a3fb7d1
Merge branch 'user/kevinve/wsb_extension' of https://github.com/kvega…
May 3, 2024
802f1a8
Fix race
May 3, 2024
3b296df
Empty Compute System ID
May 3, 2024
e87869b
Set CPU Count
May 3, 2024
b3d9a02
Set compute system ID.
May 3, 2024
e1a3b5d
Address PR comments
May 3, 2024
5151384
Locking Windows Sandbox in resources file
May 3, 2024
58f7213
Fix indentation in appx package
May 3, 2024
0382ed4
Remove not implemented exceptions
May 3, 2024
7669738
Set constants for default memory and storage
May 5, 2024
5ba0fa6
Rename app description resource
May 5, 2024
b0c6e08
Merge with main
May 7, 2024
b3a8c24
Merge with main
May 21, 2024
72658e9
Fix packaging
May 21, 2024
567c40b
Fix hang on stopping already stopped Compute System
May 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions DevHome.sln
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHome.Customization", "to
{54082587-A435-423F-AE1B-01B906FFA7C5} = {54082587-A435-423F-AE1B-01B906FFA7C5}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WindowsSandboxExtension", "WindowsSandboxExtension", "{4ACF917D-B2CC-4CF2-8EE1-0EBBB52A69F0}"
krschau marked this conversation as resolved.
Show resolved Hide resolved
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsSandboxExtension", "extensions\WindowsSandboxExtension\WindowsSandboxExtension.csproj", "{118E20E8-FD8A-40CF-83A5-F912B9187787}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|arm64 = Debug|arm64
Expand Down Expand Up @@ -636,6 +640,18 @@ Global
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|x64.Build.0 = Release|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|x86.ActiveCfg = Release|x86
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|x86.Build.0 = Release|x86
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Debug|arm64.ActiveCfg = Debug|arm64
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Debug|arm64.Build.0 = Debug|arm64
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Debug|x64.ActiveCfg = Debug|x64
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Debug|x64.Build.0 = Debug|x64
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Debug|x86.ActiveCfg = Debug|x86
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Debug|x86.Build.0 = Debug|x86
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Release|arm64.ActiveCfg = Release|arm64
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Release|arm64.Build.0 = Release|arm64
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Release|x64.ActiveCfg = Release|x64
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Release|x64.Build.0 = Release|x64
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Release|x86.ActiveCfg = Release|x86
{118E20E8-FD8A-40CF-83A5-F912B9187787}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -685,6 +701,8 @@ Global
{D759CD66-494C-4A00-8075-8B65A9891349} = {81AACED5-CFB5-47A6-AFD6-4625AADCFFA3}
{623998FD-B0A6-4980-95D5-A5072301CA10} = {A972EC5B-FC61-4964-A6FF-F9633EB75DFD}
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C} = {623998FD-B0A6-4980-95D5-A5072301CA10}
{4ACF917D-B2CC-4CF2-8EE1-0EBBB52A69F0} = {DCAF188B-60C3-4EDB-8049-BAA927FBCD7D}
{118E20E8-FD8A-40CF-83A5-F912B9187787} = {4ACF917D-B2CC-4CF2-8EE1-0EBBB52A69F0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {030B5641-B206-46BB-BF71-36FF009088FA}
Expand Down
23 changes: 22 additions & 1 deletion Packaging/Package.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@
</com:ExeServer>
</com:ComServer>
</com:Extension>
<com:Extension Category="windows.comServer">
kvega005 marked this conversation as resolved.
Show resolved Hide resolved
<com:ComServer>
<com:ExeServer Executable="DevHome\WindowsSandboxExtension.exe" Arguments="-RegisterProcessAsComServer" DisplayName="WindowsSandboxExtension">
<com:Class Id="6A52115B-083C-4FB1-85F4-BBE23289220E" DisplayName="WindowsSandboxExtension" />
</com:ExeServer>
</com:ComServer>
</com:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="DevHome\CoreWidgetProvider.exe" Arguments="-RegisterProcessAsComServer" DisplayName="CoreWidgetProvider">
Expand Down Expand Up @@ -122,7 +129,21 @@
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.devhome" Id="PG-SP-ID3" PublicFolder="Public" DisplayName="ms-resource:AppDisplayNameWindowsSandboxExt" Description="ms-resource:AppDescriptionWindowsSandboxExt">
<uap3:Properties>
<DevHomeProvider>
<Activation>
<CreateInstance ClassId="6A52115B-083C-4FB1-85F4-BBE23289220E" />
</Activation>
<SupportedInterfaces>
<ComputeSystem />
</SupportedInterfaces>
</DevHomeProvider>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.widgets" DisplayName="ms-resource:WidgetProviderDisplayNameDev" Id="CoreWidgetProvider" PublicFolder="Public">
<uap3:Properties>
<WidgetProvider>
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions extensions/WindowsSandboxExtension/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsSandboxExtension;

internal sealed class Constants
{
public const string WindowsSandboxExe = "WindowsSandbox.exe";
public const string ProviderDisplayName = "Windows Sandbox";
public const string ProviderId = "Microsoft.WindowsSandbox";
public const string Thumbnail = "ms-appx:///Assets/windows-sandbox-thumbnail.jpg";

// We use different icon locations for different builds. Note these are ms-resource URIs, but are used by Dev Home to load the providers icon.
// from the extension package. Extensions that implement the IComputeSystemProvider interface must provide a provider icon in this format.
// Dev Home will use SHLoadIndirectString (https://learn.microsoft.com/windows/win32/api/shlwapi/nf-shlwapi-shloadindirectstring) to load the
// location of the icon from the extension package.Once it gets this location, it will load the icon from the path and display it in the UI.
// Icons should be located in an extension resource.pri file which is generated at build time.
// See the MakePri.exe documentation for how you can view what is in the resource.pri file, so you can find the location of your icon.
// https://learn.microsoft.com/windows/uwp/app-resources/makepri-exe-command-options. (use MakePri.exe in a VS Developer Command Prompt or
// Powershell window)
#if CANARY_BUILD
public const string ExtensionIcon = "ms-resource://Microsoft.Windows.DevHome.Canary/Files/Assets/windows-sandbox-icon.png";
#elif STABLE_BUILD
public const string ExtensionIcon = "ms-resource://Microsoft.Windows.DevHome/Files/Assets/windows-sandbox-icon.png";
#else
public const string ExtensionIcon = "ms-resource://Microsoft.Windows.DevHome.Dev/Files/Assets/windows-sandbox-icon.png";
#endif
}
51 changes: 51 additions & 0 deletions extensions/WindowsSandboxExtension/Helpers/DependencyChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using Windows.Management.Deployment;
using WinRT;

namespace WindowsSandboxExtension.Helpers;

internal sealed class DependencyChecker
{
private const string OptionalComponentName = "Containers-DisposableClientVM";
private const string PackageFamilyName = "MicrosoftWindows.WindowsSandbox_cw5n1h2txyewy";

public static bool IsOptionalComponentEnabled()
kvega005 marked this conversation as resolved.
Show resolved Hide resolved
{
var searcher = new ManagementObjectSearcher($"SELECT InstallState FROM Win32_OptionalFeature WHERE Name = '{OptionalComponentName}'");
var collection = searcher.Get();

foreach (ManagementObject instance in collection)
{
if (instance["InstallState"] != null)
{
var state = Convert.ToInt32(instance.GetPropertyValue("InstallState"), CultureInfo.InvariantCulture);

// 1 means the feature is enabled
return state == 1;
}
}

// Return false if the feature is not found
return false;
}

public static bool IsNewWindowsSandboxExtensionInstalled()
{
PackageManager packageManager = new PackageManager();

var securityId = WindowsIdentity.GetCurrent().Owner?.ToString();
var packages = packageManager.FindPackagesForUser(securityId, PackageFamilyName);

return packages.Any();
}
}
24 changes: 24 additions & 0 deletions extensions/WindowsSandboxExtension/Helpers/Logging.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;

namespace WindowsSandboxExtension.Helpers;

public class Logging
{
public static readonly string LogExtension = ".dhlog";

public static readonly string LogFolderName = "Logs";

public static readonly string DefaultLogFileName = "wsb";
kvega005 marked this conversation as resolved.
Show resolved Hide resolved

private static readonly Lazy<string> _logFolderRoot = new(() => Path.Combine(ApplicationData.Current.TemporaryFolder.Path, LogFolderName));

public static readonly string LogFolderRoot = _logFolderRoot.Value;
}
38 changes: 38 additions & 0 deletions extensions/WindowsSandboxExtension/Helpers/Resources.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Windows.ApplicationModel.Resources;
using Serilog;

namespace WindowsSandboxExtension.Helpers;

internal sealed class Resources
{
private static ResourceLoader? _resourceLoader;

public static string GetResource(string identifier, ILogger? log = null)
{
try
{
if (_resourceLoader == null)
{
var path = ResourceLoader.GetDefaultResourceFilePath();
_resourceLoader = new ResourceLoader(path);
}

return _resourceLoader.GetString(identifier);
}
catch (Exception ex)
{
log?.Error(ex, $"Failed loading resource: {identifier}");

// If we fail, load the original identifier so it is obvious which resource is missing.
return identifier;
}
}
}
1 change: 1 addition & 0 deletions extensions/WindowsSandboxExtension/NativeMethods.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SetForegroundWindow
130 changes: 130 additions & 0 deletions extensions/WindowsSandboxExtension/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Windows.AppLifecycle;
using Microsoft.Windows.DevHome.SDK;
using Serilog;
using Windows.ApplicationModel.Activation;
using WindowsSandboxExtension.Helpers;
using WindowsSandboxExtension.Providers;
using WinRT;

namespace WindowsSandboxExtension;

public sealed class Program
{
public static IHost? Host
{
get; set;
}

[MTAThread]
public static void Main([System.Runtime.InteropServices.WindowsRuntime.ReadOnlyArray] string[] args)
{
// Set up Logging
Environment.SetEnvironmentVariable("DEVHOME_LOGS_ROOT", Path.Join(Helpers.Logging.LogFolderRoot, "WindowsSandbox"));
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings_wsb.json")
kvega005 marked this conversation as resolved.
Show resolved Hide resolved
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();

Log.Information($"Launched with args: {string.Join(' ', args.ToArray())}");

// Force the app to be single instanced.
// Get or register the main instance.
var mainInstance = AppInstance.FindOrRegisterForKey("mainInstance");
var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
if (!mainInstance.IsCurrent)
{
Log.Information($"Not main instance, redirecting.");
mainInstance.RedirectActivationToAsync(activationArgs).AsTask().Wait();
Log.CloseAndFlush();
return;
}

// Build the host container before handling activation.
BuildHostContainer();

// Register for activation redirection.
AppInstance.GetCurrent().Activated += AppActivationRedirected;

if (args.Length > 0 && args[0] == "-RegisterProcessAsComServer")
{
HandleCOMServerActivation();
}
else
{
Log.Warning("Not being launched as a ComServer... exiting.");
}

Log.CloseAndFlush();
}

private static void AppActivationRedirected(object? sender, Microsoft.Windows.AppLifecycle.AppActivationArguments activationArgs)
{
Log.Information($"Redirected with kind: {activationArgs.Kind}");

// Handle COM server.
if (activationArgs.Kind == ExtendedActivationKind.Launch)
{
var launchActivatedEventArgs = activationArgs.Data as ILaunchActivatedEventArgs;
var args = launchActivatedEventArgs?.Arguments.Split();

if (args?.Length > 0 && args[1] == "-RegisterProcessAsComServer")
{
Log.Information($"Activation COM Registration Redirect: {string.Join(' ', args.ToList())}");
HandleCOMServerActivation();
}
}
}

/// <summary>
/// Creates the host container for the Windows Sandbox Extension application. This can be used to register
/// services and other dependencies throughout the application.
/// </summary>
private static void BuildHostContainer()
{
Host = Microsoft.Extensions.Hosting.Host.
CreateDefaultBuilder().
UseContentRoot(AppContext.BaseDirectory).
UseDefaultServiceProvider((context, options) =>
{
options.ValidateOnBuild = true;
}).
ConfigureServices((context, services) =>
{
// Services
services.AddHttpClient();
services.AddSingleton<IComputeSystemProvider, WindowsSandboxProvider>();
services.AddSingleton<WindowsSandboxExtension>();
}).
Build();
}

private static void HandleCOMServerActivation()
{
Debug.Assert(Host != null, "Host is null");
Log.Information($"Activating COM Server");

// Register and run COM server.
// This could be called by either of the COM registrations, we will do them all to avoid deadlock and bind all on the extension's lifetime.
using var extensionServer = new Microsoft.Windows.DevHome.SDK.ExtensionServer();
var windowsSandboxExtension = Host.Services.GetRequiredService(typeof(WindowsSandboxExtension)).As<WindowsSandboxExtension>();

// We are instantiating extension instance once above, and returning it every time the callback in RegisterExtension below is called.
// This makes sure that only one instance of the extension is alive, which is returned every time the host asks for the IExtension object.
// If you want to instantiate a new instance each time the host asks, create the new instance inside the delegate.
extensionServer.RegisterExtension(() => windowsSandboxExtension, true);

// This will make the main thread wait until the event is signalled by the extension class.
// Since we have single instance of the extension object, we exit as soon as it is disposed.
windowsSandboxExtension.ExtensionDisposedEvent.WaitOne();
Log.Information($"Extension is disposed.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Platform>arm64</Platform>
<RuntimeIdentifier>win-arm64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<PublishReadyToRunComposite Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRunComposite>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Platform>x64</Platform>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<PublishReadyToRunComposite Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRunComposite>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
</PropertyGroup>
</Project>