Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1167830
Add Microsoft.Build.MsixPackaging SDK
shmuelie Jun 4, 2026
85f2853
Add unit tests for Microsoft.Build.MsixPackaging
shmuelie Jun 4, 2026
02c2095
Add MsixPackaging sample project
shmuelie Jun 4, 2026
75fb334
Add MsixPackaging projects to solution
shmuelie Jun 4, 2026
869c4a2
Fix latent StyleCop violations in MSIX task sources
shmuelie Jun 5, 2026
e286eb5
Use Microsoft.Windows.SDK.BuildTools.MSIX tasks for tooling, packing,…
shmuelie Jun 5, 2026
68dd193
Fix MsixPackaging sample layout and dev-time task path
shmuelie Jun 5, 2026
f2d5af1
Add timestamp and Azure/Key Vault signing options
shmuelie Jun 9, 2026
5728a39
Validate publisher and certificate before signing
shmuelie Jun 9, 2026
598d38f
Add auto-generated test certificate for local signing
shmuelie Jun 9, 2026
436d178
Add symbol package (.msixsym) generation
shmuelie Jun 9, 2026
d82a630
Add App Installer (.appinstaller) generation
shmuelie Jun 9, 2026
07dad9f
Add multi-architecture MSIX bundle (.msixbundle) support
shmuelie Jun 9, 2026
83cd815
Add Store upload package (.msixupload) creation
shmuelie Jun 9, 2026
d075414
Expose new MSIX properties in the VS property page
shmuelie Jun 9, 2026
45eacc1
Demonstrate new MSIX features in the sample
shmuelie Jun 9, 2026
a4e43ff
Document signing, bundle, and distribution features in the README
shmuelie Jun 9, 2026
83622a6
Ship the test-certificate script in the SDK package
shmuelie Jun 9, 2026
98cb01e
Package multiple app types in the MSIX sample
shmuelie Jul 17, 2026
7e5495e
Document the multi-app sample in the README
shmuelie Jul 17, 2026
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
180 changes: 179 additions & 1 deletion MSBuildSdks.sln

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions samples/MsixPackaging/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<!-- Point the SDK tasks at the locally-built assembly for development -->
<PropertyGroup>
<_MsixSdkTasksAssembly>$(MSBuildThisFileDirectory)..\..\artifacts\bin\Microsoft.Build.MsixPackaging\debug_netstandard2.0\Microsoft.Build.MsixPackaging.dll</_MsixSdkTasksAssembly>
</PropertyGroup>
</Project>
Binary file added samples/MsixPackaging/Images/StoreLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions samples/MsixPackaging/Package.base.appxmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Package.base.appxmanifest — Sample
===================================
Base manifest template for the Microsoft.Build.MsixPackaging sample package.
AppxFragment.xml entries from referenced projects are inserted at the marker.
-->
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap uap3 uap5 desktop rescap">

<Identity
Name="MsixPackagingSdk.Sample"
Publisher="CN=MsixPackagingSdkSample"
Version="1.0.0.0"
ProcessorArchitecture="x64" />

<Properties>
<DisplayName>MsixPackaging Sample</DisplayName>
<PublisherDisplayName>Microsoft.Build.MsixPackaging</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
<Description>Sample package built with Microsoft.Build.MsixPackaging.</Description>
</Properties>

<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.26100.0" />
</Dependencies>

<Resources>
<Resource Language="en-us" />
</Resources>

<Applications>
<!-- APPX_FRAGMENTS_INSERTED_HERE -->
</Applications>

<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
</Capabilities>

</Package>
21 changes: 21 additions & 0 deletions samples/MsixPackaging/SampleConsoleApp/AppxFragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Application
Id="SampleConsoleApp"
Executable="SampleConsoleApp\SampleConsoleApp.exe"
EntryPoint="Windows.FullTrustApplication">
<uap3:VisualElements
DisplayName="Sample Console App"
Description="A sample console app packaged with Microsoft.Build.MsixPackaging"
Square150x150Logo="Images\SampleConsoleApp.Square44x44Logo.png"
Square44x44Logo="Images\SampleConsoleApp.Square44x44Logo.png"
BackgroundColor="transparent"
VisualGroup="MsixPackaging Sample" />
<Extensions>
<uap5:Extension Category="windows.appExecutionAlias"
Executable="SampleConsoleApp\SampleConsoleApp.exe"
EntryPoint="Windows.FullTrustApplication">
<uap5:AppExecutionAlias>
<uap5:ExecutionAlias Alias="sampleconsoleapp.exe" />
</uap5:AppExecutionAlias>
</uap5:Extension>
</Extensions>
</Application>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions samples/MsixPackaging/SampleConsoleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Console.WriteLine("Hello from SampleConsoleApp inside an MSIX package!");
13 changes: 13 additions & 0 deletions samples/MsixPackaging/SampleConsoleApp/SampleConsoleApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>SampleConsoleApp</AssemblyName>
<!-- Architectures this app can be published for (required for MSIX bundle builds). -->
<RuntimeIdentifiers>win-x64;win-x86;win-arm64</RuntimeIdentifiers>
</PropertyGroup>

</Project>
63 changes: 63 additions & 0 deletions samples/MsixPackaging/SamplePackaging.msbuildproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!--
SamplePackaging.msbuildproj
===========================
Sample project that consumes Microsoft.Build.MsixPackaging to package several
apps of different types into a single MSIX:

- SampleConsoleApp (net10.0, console, with a CLI execution alias)
- SampleWpfApp (net10.0-windows, WPF GUI, Start Menu only)
- SampleWinFormsApp (net10.0-windows, WinForms GUI, Start Menu only)

Each app publishes independently with its own target framework; their
per-project AppxFragment.xml entries are merged into Package.base.appxmanifest.
During development, the SDK targets are imported directly from the source tree
rather than from a NuGet package.

Production usage would be:
<Project Sdk="Microsoft.Build.MsixPackaging/1.0.0">
-->
<Project>

<!-- Import SDK props (includes NoTargets Sdk.props) -->
<Import Project="..\..\src\MsixPackaging\Sdk\Sdk.props" />

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<MsixFileName>MsixPackagingSample</MsixFileName>

<!-- Demonstrate signing with an auto-generated test certificate (no .pfx needed). -->
<MsixSigningEnabled>true</MsixSigningEnabled>
<MsixGenerateTestCertificate>true</MsixGenerateTestCertificate>

<!-- Demonstrate the symbol package (.msixsym) and App Installer (.appinstaller). -->
<MsixSymbolPackageEnabled>true</MsixSymbolPackageEnabled>
<MsixAppInstallerEnabled>true</MsixAppInstallerEnabled>
<MsixAppInstallerUri>https://example.com/apps/MsixPackagingSample.appinstaller</MsixAppInstallerUri>

<!--
Multi-architecture bundle (.msixbundle) and Store upload (.msixupload) are
opt-in because they build every architecture. Try them with:

dotnet build SamplePackaging.msbuildproj `
/p:MsixBundleEnabled=true `
"/p:MsixBundlePlatforms=x64|x86" `
/p:MsixStoreUploadEnabled=true

Every referenced app declares the matching <RuntimeIdentifiers> so restore covers them.
-->
</PropertyGroup>

<!-- App projects to include in the MSIX (each publishes with its own TFM). -->
<ItemGroup>
<ProjectReference Include="SampleConsoleApp\SampleConsoleApp.csproj"
LayoutDir="SampleConsoleApp" />
<ProjectReference Include="SampleWpfApp\SampleWpfApp.csproj"
LayoutDir="SampleWpfApp" />
<ProjectReference Include="SampleWinFormsApp\SampleWinFormsApp.csproj"
LayoutDir="SampleWinFormsApp" />
</ItemGroup>

<!-- Import SDK targets (includes NoTargets Sdk.targets + packaging pipeline) -->
<Import Project="..\..\src\MsixPackaging\Sdk\Sdk.targets" />

</Project>
12 changes: 12 additions & 0 deletions samples/MsixPackaging/SampleWinFormsApp/AppxFragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Application
Id="SampleWinFormsApp"
Executable="SampleWinFormsApp\SampleWinFormsApp.exe"
EntryPoint="Windows.FullTrustApplication">
<uap3:VisualElements
DisplayName="Sample WinForms App"
Description="A sample WinForms app packaged with Microsoft.Build.MsixPackaging"
Square150x150Logo="Images\SampleWinFormsApp.Square44x44Logo.png"
Square44x44Logo="Images\SampleWinFormsApp.Square44x44Logo.png"
BackgroundColor="transparent"
VisualGroup="MsixPackaging Sample" />
</Application>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions samples/MsixPackaging/SampleWinFormsApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Windows.Forms;

namespace SampleWinFormsApp;

internal static class Program
{
[STAThread]
private static void Main()
{
ApplicationConfiguration.Initialize();

var form = new Form
{
Text = "Sample WinForms App",
Width = 480,
Height = 240,
};
form.Controls.Add(new Label
{
Text = "Hello from SampleWinFormsApp inside an MSIX package!",
Dock = DockStyle.Fill,
TextAlign = System.Drawing.ContentAlignment.MiddleCenter,
});

Application.Run(form);
}
}
14 changes: 14 additions & 0 deletions samples/MsixPackaging/SampleWinFormsApp/SampleWinFormsApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyName>SampleWinFormsApp</AssemblyName>
<!-- Architectures this app can be published for (required for MSIX bundle builds). -->
<RuntimeIdentifiers>win-x64;win-x86;win-arm64</RuntimeIdentifiers>
</PropertyGroup>

</Project>
5 changes: 5 additions & 0 deletions samples/MsixPackaging/SampleWpfApp/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Application x:Class="SampleWpfApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
</Application>
8 changes: 8 additions & 0 deletions samples/MsixPackaging/SampleWpfApp/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace SampleWpfApp;

/// <summary>
/// Interaction logic for App.xaml.
/// </summary>
public partial class App : System.Windows.Application
{
}
12 changes: 12 additions & 0 deletions samples/MsixPackaging/SampleWpfApp/AppxFragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Application
Id="SampleWpfApp"
Executable="SampleWpfApp\SampleWpfApp.exe"
EntryPoint="Windows.FullTrustApplication">
<uap3:VisualElements
DisplayName="Sample WPF App"
Description="A sample WPF app packaged with Microsoft.Build.MsixPackaging"
Square150x150Logo="Images\SampleWpfApp.Square44x44Logo.png"
Square44x44Logo="Images\SampleWpfApp.Square44x44Logo.png"
BackgroundColor="transparent"
VisualGroup="MsixPackaging Sample" />
</Application>
10 changes: 10 additions & 0 deletions samples/MsixPackaging/SampleWpfApp/MainWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Window x:Class="SampleWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Sample WPF App" Width="480" Height="240">
<Grid>
<TextBlock Text="Hello from SampleWpfApp inside an MSIX package!"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="18" TextWrapping="Wrap" />
</Grid>
</Window>
12 changes: 12 additions & 0 deletions samples/MsixPackaging/SampleWpfApp/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace SampleWpfApp;

/// <summary>
/// Interaction logic for MainWindow.xaml.
/// </summary>
public partial class MainWindow : System.Windows.Window
{
public MainWindow()
{
InitializeComponent();
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions samples/MsixPackaging/SampleWpfApp/SampleWpfApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<AssemblyName>SampleWpfApp</AssemblyName>
<!-- Architectures this app can be published for (required for MSIX bundle builds). -->
<RuntimeIdentifiers>win-x64;win-x86;win-arm64</RuntimeIdentifiers>
</PropertyGroup>

</Project>
78 changes: 78 additions & 0 deletions src/MsixPackaging.UnitTests/MergeAppxFragmentsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Licensed under the MIT license.

using Microsoft.Build.MsixPackaging.Tasks;
using Shouldly;
using System.Text;
using Xunit;

namespace Microsoft.Build.MsixPackaging.UnitTests
{
public class MergeAppxFragmentsTests
{
[Fact]
public void IsValidMsixVersion_ValidVersion_ReturnsTrue()
{
MergeAppxFragments.IsValidMsixVersion("1.0.0.0").ShouldBeTrue();
MergeAppxFragments.IsValidMsixVersion("10.20.30.40").ShouldBeTrue();
MergeAppxFragments.IsValidMsixVersion("65535.65535.65535.65535").ShouldBeTrue();
}

[Fact]
public void IsValidMsixVersion_InvalidVersion_ReturnsFalse()
{
MergeAppxFragments.IsValidMsixVersion("1.0.0").ShouldBeFalse();
MergeAppxFragments.IsValidMsixVersion("1.0.0.0.0").ShouldBeFalse();
MergeAppxFragments.IsValidMsixVersion("1.0.0.abc").ShouldBeFalse();
MergeAppxFragments.IsValidMsixVersion(string.Empty).ShouldBeFalse();
}

[Fact]
public void IsStructuredFragment_WithAppxFragmentRoot_ReturnsTrue()
{
MergeAppxFragments.IsStructuredFragment("<AppxFragment><Application /></AppxFragment>").ShouldBeTrue();
}

[Fact]
public void IsStructuredFragment_WithPlainApplication_ReturnsFalse()
{
MergeAppxFragments.IsStructuredFragment("<Application Id=\"App1\" />").ShouldBeFalse();
}

[Fact]
public void PatchAttribute_PatchesVersionInIdentityElement()
{
var xml = "<Package><Identity Name=\"Test\" Version=\"1.0.0.0\" ProcessorArchitecture=\"x64\" /></Package>";
var result = MergeAppxFragments.PatchAttribute(xml, "Version", "2.0.0.0");
result.ShouldContain("Version=\"2.0.0.0\"");
result.ShouldNotContain("Version=\"1.0.0.0\"");
}

[Fact]
public void PatchAttribute_PatchesArchitectureInIdentityElement()
{
var xml = "<Package><Identity Name=\"Test\" Version=\"1.0.0.0\" ProcessorArchitecture=\"x64\" /></Package>";
var result = MergeAppxFragments.PatchAttribute(xml, "ProcessorArchitecture", "arm64");
result.ShouldContain("ProcessorArchitecture=\"arm64\"");
result.ShouldNotContain("ProcessorArchitecture=\"x64\"");
}

[Fact]
public void PatchAttribute_NoIdentityElement_ReturnsUnchanged()
{
var xml = "<Package><Properties><DisplayName>Test</DisplayName></Properties></Package>";
var result = MergeAppxFragments.PatchAttribute(xml, "Version", "2.0.0.0");
result.ShouldBe(xml);
}

[Fact]
public void AppendIndented_AppendsWithIndentation()
{
var sb = new StringBuilder();
MergeAppxFragments.AppendIndented(sb, "<Application Id=\"App1\" />");
var result = sb.ToString();
result.ShouldContain(" <Application Id=\"App1\" />");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net472;net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AssemblyShader">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Build.Framework" />
<PackageReference Include="Microsoft.Build.Utilities.Core" />
<PackageReference Include="Microsoft.NET.Test.Sdk" ShadeDependencies="NuGet.Frameworks" />
<PackageReference Include="MSBuild.ProjectCreation" />
<PackageReference Include="Shouldly" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MsixPackaging\Microsoft.Build.MsixPackaging.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="..\MsixPackaging\build\**" Link="build\%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
<None Include="..\MsixPackaging\Sdk\**" Link="Sdk\%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
Loading
Loading