diff --git a/dev/Templates/Dotnet/Test-DotnetNewTemplates.ps1 b/dev/Templates/Dotnet/Test-DotnetNewTemplates.ps1
index e9c02679a9..2be6926f3f 100644
--- a/dev/Templates/Dotnet/Test-DotnetNewTemplates.ps1
+++ b/dev/Templates/Dotnet/Test-DotnetNewTemplates.ps1
@@ -453,31 +453,95 @@ function Get-LatestOfficialWasdkVersion {
$sourceFeed = 'https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/flat2/microsoft.windowsappsdk/index.json'
$excludedVersionPrefixes = @('2.63.')
$token = $env:SYSTEM_ACCESSTOKEN
- if ([string]::IsNullOrWhiteSpace($token)) {
- Write-Warning "SYSTEM_ACCESSTOKEN is not set; cannot query the package source for versions."
- return $null
- }
try {
- [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
- $response = Invoke-RestMethod -Uri $sourceFeed -Headers @{ Authorization = "Bearer $token" } -TimeoutSec 30 -ErrorAction Stop
- $latest = $null
- foreach ($v in $response.versions) {
- if ($v -match '-') { continue } # skip prerelease
- $excluded = $false
- foreach ($p in $excludedVersionPrefixes) { if ($v.StartsWith($p)) { $excluded = $true; break } }
- if ($excluded) { continue }
- $parsed = $null
- if (-not [version]::TryParse($v, [ref]$parsed)) { continue }
- if (($null -eq $latest) -or ($parsed -gt $latest.Ver)) {
- $latest = [pscustomobject]@{ Raw = $v; Ver = $parsed }
+ # SYSTEM_ACCESSTOKEN is a pipeline variable. Use nuget CLI when running outside of pipeline runs
+ if ([string]::IsNullOrWhiteSpace($token)) {
+ Write-Warning "SYSTEM_ACCESSTOKEN is not set; cannot query package source for latest official Microsoft.WindowsAppSDK version. Using NuGet CLI instead"
+ $packageId = 'Microsoft.WindowsAppSDK'
+ $sourceFeed = 'https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json'
+ $json = & dotnet package search $packageId `
+ --source $sourceFeed `
+ --exact-match `
+ --format json `
+ --verbosity quiet
+
+ if ($LASTEXITCODE -ne 0) {
+ throw "dotnet package search failed with exit code $LASTEXITCODE."
}
- }
- if ($latest) {
- Write-Host "Latest official Microsoft.WindowsAppSDK = $($latest.Raw) (excluded prerelease and $($excludedVersionPrefixes -join ', ')*)"
+
+ $response = $json | ConvertFrom-Json
+
+ $problems = @()
+
+ if ($response.PSObject.Properties['problems']) {
+ $problems += @($response.problems)
+ }
+
+ foreach ($result in @($response.searchResult)) {
+ if ($result.PSObject.Properties['problems']) {
+ $problems += @($result.problems)
+ }
+ }
+
+ if ($problems.Count -gt 0) {
+ throw ($problems | ForEach-Object { $_.text } | Join-String -Separator "`n")
+ }
+
+ $versions = foreach ($result in @($response.searchResult)) {
+ foreach ($package in @($result.packages)) {
+ if ($package.id -ine $packageId) {
+ continue
+ }
+
+ if ($package.PSObject.Properties['version']) {
+ $package.version
+ }
+ elseif ($package.PSObject.Properties['latestVersion']) {
+ $package.latestVersion
+ }
+ }
+ }
+ $latest = $versions |
+ Where-Object {
+ $version = $_
+ -not ($excludedVersionPrefixes | Where-Object {
+ $version.StartsWith($_)
+ })
+ } |
+ ForEach-Object {
+ [pscustomobject]@{
+ Raw = $_
+ Ver = [version]$_
+ }
+ } |
+ Sort-Object Ver -Descending |
+ Select-Object -First 1
+
return $latest.Raw
}
- Write-Warning "No official Microsoft.WindowsAppSDK version found on the package source."
- return $null
+ else
+ {
+ [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
+ $response = Invoke-RestMethod -Uri $sourceFeed -Headers @{ Authorization = "Bearer $token" } -TimeoutSec 30 -ErrorAction Stop
+ $latest = $null
+ foreach ($v in $response.versions) {
+ if ($v -match '-') { continue } # skip prerelease
+ $excluded = $false
+ foreach ($p in $excludedVersionPrefixes) { if ($v.StartsWith($p)) { $excluded = $true; break } }
+ if ($excluded) { continue }
+ $parsed = $null
+ if (-not [version]::TryParse($v, [ref]$parsed)) { continue }
+ if (($null -eq $latest) -or ($parsed -gt $latest.Ver)) {
+ $latest = [pscustomobject]@{ Raw = $v; Ver = $parsed }
+ }
+ }
+ if ($latest) {
+ Write-Host "Latest official Microsoft.WindowsAppSDK = $($latest.Raw) (excluded prerelease and $($excludedVersionPrefixes -join ', ')*)"
+ return $latest.Raw
+ }
+ Write-Warning "No official Microsoft.WindowsAppSDK version found on the package source."
+ return $null
+ }
}
catch {
Write-Warning "Failed to query the package source for versions: $($_.Exception.Message)"
diff --git a/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/BlankApp/ProjectTemplate.csproj b/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/BlankApp/ProjectTemplate.csproj
index 335a7f49ba..121711f2cd 100644
--- a/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/BlankApp/ProjectTemplate.csproj
+++ b/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/BlankApp/ProjectTemplate.csproj
@@ -21,9 +21,9 @@
-
-
-
+
+
+
diff --git a/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WapProj/WapProjTemplate.wapproj b/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WapProj/WapProjTemplate.wapproj
index 4f99481659..700188c0c7 100644
--- a/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WapProj/WapProjTemplate.wapproj
+++ b/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WapProj/WapProjTemplate.wapproj
@@ -42,11 +42,11 @@
$guid1$
- $targetplatformversion$
- $targetplatformminversion$
+ $ext_targetplatformversion$
+ $ext_targetplatformminversion$
$ext_DotNetVersion$-windows$(TargetPlatformVersion);$(AssetTargetFallback)
$currentuiculturename$
- $if$($includeKeyFile$==true)
+ $if$($ext_includeKeyFile$==true)
$projectname$_TemporaryKey.pfx
$endif$
false
@@ -56,7 +56,7 @@
Designer
- $if$($includeKeyFile$==true)
+ $if$($ext_includeKeyFile$==true)
$endif$
diff --git a/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WapProj/WinUI.Desktop.Cs.WapProj.vstemplate b/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WapProj/WinUI.Desktop.Cs.WapProj.vstemplate
index 5520e98b34..21850532c4 100644
--- a/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WapProj/WinUI.Desktop.Cs.WapProj.vstemplate
+++ b/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WapProj/WinUI.Desktop.Cs.WapProj.vstemplate
@@ -40,19 +40,9 @@
Wide310x150Logo.scale-200.png
-
- Microsoft.VisualStudio.Universal.TemplateWizards, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
- Microsoft.VisualStudio.Universal.TemplateWizards.PlatformVersion.Wizard
-
Microsoft.VisualStudio.WinRT.TemplateWizards, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.VisualStudio.WinRT.TemplateWizards.UpdatePublisherInManifestWizard
-
- 10.0.17763.0
- true
- true
- true
-
diff --git a/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WinUI.Desktop.Cs.PackagedApp.vstemplate b/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WinUI.Desktop.Cs.PackagedApp.vstemplate
index 0f22e1f822..a559a4ed99 100644
--- a/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WinUI.Desktop.Cs.PackagedApp.vstemplate
+++ b/dev/Templates/Source/ProjectTemplates/Desktop/CSharp/PackagedApp/WinUI.Desktop.Cs.PackagedApp.vstemplate
@@ -26,10 +26,23 @@
+
+
+
WapProj\WinUI.Desktop.Cs.WapProj.vstemplate
BlankApp\WinUI.Desktop.Cs.BlankApp.vstemplate
+
+ Microsoft.VisualStudio.Universal.TemplateWizards, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ Microsoft.VisualStudio.Universal.TemplateWizards.PlatformVersion.Wizard
+
+
+ 10.0.17763.0
+ true
+ true
+ true
+
diff --git a/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/Package-managed.appxmanifest b/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/Package-managed.appxmanifest
index 6dfdf482cb..3ee00ff8e4 100644
--- a/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/Package-managed.appxmanifest
+++ b/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/Package-managed.appxmanifest
@@ -11,7 +11,7 @@
diff --git a/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/WapProjTemplate.wapproj b/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/WapProjTemplate.wapproj
index de6d40d79d..7036f3c298 100644
--- a/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/WapProjTemplate.wapproj
+++ b/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/WapProjTemplate.wapproj
@@ -43,10 +43,10 @@
$guid1$
- $targetplatformversion$
- $targetplatformminversion$
+ $ext_targetplatformversion$
+ $ext_targetplatformminversion$
$currentuiculturename$
- $if$($includeKeyFile$==true)
+ $if$($ext_includeKeyFile$==true)
$projectname$_TemporaryKey.pfx
$endif$
false
@@ -56,7 +56,7 @@
Designer
- $if$($includeKeyFile$==true)
+ $if$($ext_includeKeyFile$==true)
$endif$
diff --git a/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/WinUI.Desktop.CppWinRT.WapProj.vstemplate b/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/WinUI.Desktop.CppWinRT.WapProj.vstemplate
index 1e8db89c9d..88b1740baa 100644
--- a/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/WinUI.Desktop.CppWinRT.WapProj.vstemplate
+++ b/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WapProj/WinUI.Desktop.CppWinRT.WapProj.vstemplate
@@ -39,21 +39,8 @@
-
- Microsoft.VisualStudio.Universal.TemplateWizards, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
- Microsoft.VisualStudio.Universal.TemplateWizards.PlatformVersion.Wizard
-
-
-
- Microsoft.VisualStudio.WinRT.TemplateWizards, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
- Microsoft.VisualStudio.WinRT.TemplateWizards.UpdatePublisherInManifestWizard
-
WindowsAppSDK.Cpp.Extension.Dev17, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
WindowsAppSDK.TemplateUtilities.NuGetPackageInstaller
-
- 10.0.17763.0
- true
-
diff --git a/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WinUI.Desktop.CppWinRT.PackagedApp.vstemplate b/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WinUI.Desktop.CppWinRT.PackagedApp.vstemplate
index 08c4b03884..5ab8a9dfb2 100644
--- a/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WinUI.Desktop.CppWinRT.PackagedApp.vstemplate
+++ b/dev/Templates/Source/ProjectTemplates/Desktop/CppWinRT/PackagedApp/WinUI.Desktop.CppWinRT.PackagedApp.vstemplate
@@ -27,5 +27,18 @@
WapProj\WinUI.Desktop.CppWinRT.WapProj.vstemplate
BlankApp\WinUI.Desktop.CppWinRT.BlankApp.vstemplate
-
+
+
+
+ Microsoft.VisualStudio.WinRT.TemplateWizards, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ Microsoft.VisualStudio.WinRT.TemplateWizards.UpdatePublisherInManifestWizard
+
+
+ Microsoft.VisualStudio.Universal.TemplateWizards, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ Microsoft.VisualStudio.Universal.TemplateWizards.PlatformVersion.Wizard
+
+
+ 10.0.17763.0
+ true
+
diff --git a/dev/Templates/VSIX/build-local-VSIX-package/Build-VSIX-Local.ps1 b/dev/Templates/VSIX/build-local-VSIX-package/Build-VSIX-Local.ps1
index 28e2639933..21d54645b1 100644
--- a/dev/Templates/VSIX/build-local-VSIX-package/Build-VSIX-Local.ps1
+++ b/dev/Templates/VSIX/build-local-VSIX-package/Build-VSIX-Local.ps1
@@ -49,6 +49,10 @@
.PARAMETER SkipRestore
Skip NuGet restore (useful if you've already restored).
+.PARAMETER MsBuildVersion
+ Visual Studio version to target for MSBuild. Default: 17 (Visual Studio 2022).
+ Set to 18 for Visual Studio 2026.
+
.EXAMPLE
.\Build-VSIX-Local.ps1
# Build the LocalDev VSIX (default). Then install with .\Install-LocalDev-VSIX.ps1
@@ -82,7 +86,10 @@ param(
[string]$OptionalVSIXVersion = "",
- [switch]$SkipRestore
+ [switch]$SkipRestore,
+
+ [ValidateSet(17, 18)]
+ [double]$MsBuildVersion = 17
)
Set-StrictMode -Version 2.0
@@ -122,9 +129,10 @@ function Find-MSBuild {
exit 1
}
- $msbuildPath = & $vswherePath -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" 2>$null | Select-Object -First 1
+ $range = '[{0},{1})' -f $MsBuildVersion, ($MsBuildVersion + 1)
+ $msbuildPath = & $vswherePath -version $range -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" 2>$null | Select-Object -First 1
if (-not $msbuildPath -or -not (Test-Path $msbuildPath)) {
- Write-Err "MSBuild.exe not found. Please install Visual Studio 2022+ with the '.NET desktop development' workload."
+ Write-Err "MSBuild.exe not found. Please install Visual Studio $MsBuildVersion with the '.NET desktop development' workload."
exit 1
}
@@ -420,6 +428,44 @@ function Invoke-MSBuildBuild {
}
}
+function Test-VsixContainsTemplates {
+ param(
+ [string]$Path
+ )
+
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
+
+ $archive = [System.IO.Compression.ZipFile]::OpenRead($Path)
+ try {
+ $projectTemplates = @(
+ $archive.Entries | Where-Object {
+ $_.FullName.StartsWith("ProjectTemplates/", [System.StringComparison]::OrdinalIgnoreCase) -and
+ $_.FullName.EndsWith(".vstemplate", [System.StringComparison]::OrdinalIgnoreCase)
+ }
+ )
+ $itemTemplates = @(
+ $archive.Entries | Where-Object {
+ $_.FullName.StartsWith("ItemTemplates/", [System.StringComparison]::OrdinalIgnoreCase) -and
+ $_.FullName.EndsWith(".vstemplate", [System.StringComparison]::OrdinalIgnoreCase)
+ }
+ )
+
+ if ($projectTemplates.Count -eq 0 -or $itemTemplates.Count -eq 0) {
+ Write-Err "VSIX template validation failed: $Path"
+ Write-Err " Project templates: $($projectTemplates.Count)"
+ Write-Err " Item templates : $($itemTemplates.Count)"
+ Write-Err "The selected Visual Studio/MSBuild toolset may not support packaging these templates."
+ return $false
+ }
+
+ Write-Step "Verified templates in $($archive.Entries.Count)-entry VSIX: $($projectTemplates.Count) project, $($itemTemplates.Count) item"
+ return $true
+ }
+ finally {
+ $archive.Dispose()
+ }
+}
+
function Copy-VsixOutput {
param(
[string]$DeploymentType
@@ -446,6 +492,10 @@ function Copy-VsixOutput {
foreach ($vsix in $vsixFiles) {
if (-not $seen.ContainsKey($vsix.Name)) {
$seen[$vsix.Name] = $vsix
+ if (-not (Test-VsixContainsTemplates -Path $vsix.FullName)) {
+ exit 1
+ }
+
# Rename to include version: WindowsAppSDK.Cs.Extension.Dev17.Component.vsix
# -> WindowsAppSDK.Cs.Extension.Dev17.Component.99.2026.0416.1640.vsix
$newName = $vsix.BaseName + ".$OptionalVSIXVersion.vsix"
diff --git a/dev/Templates/VSIX/build-local-VSIX-package/build-install-localdev-vsix.ps1 b/dev/Templates/VSIX/build-local-VSIX-package/build-install-localdev-vsix.ps1
index 124566d1d7..97fd602f08 100644
--- a/dev/Templates/VSIX/build-local-VSIX-package/build-install-localdev-vsix.ps1
+++ b/dev/Templates/VSIX/build-local-VSIX-package/build-install-localdev-vsix.ps1
@@ -26,6 +26,10 @@
.PARAMETER UninstallFirst
Run VSIXInstaller /uninstall before installing for a clean reinstall.
+.PARAMETER MsBuildVersion
+ Visual Studio version to target for MSBuild. Default: 17 (Visual Studio 2022).
+ Set to 18 for Visual Studio 2026.
+
.EXAMPLE
.\build-install-localdev-vsix.ps1
# Build + install LocalDev C# + C++ to all VS 17+.
@@ -44,7 +48,10 @@ param(
[string]$VsVersionRange = '[17.0,)',
- [switch]$UninstallFirst
+ [switch]$UninstallFirst,
+
+ [ValidateSet(17, 18)]
+ [double]$MsBuildVersion = 17
)
Set-StrictMode -Version 2.0
@@ -86,7 +93,7 @@ if ($SkipBuild) {
}
Push-Location $scriptDir
try {
- & $buildScript -Deployment LocalDev
+ & $buildScript -Deployment LocalDev -MsBuildVersion $MsBuildVersion
if ($LASTEXITCODE -ne 0) {
Write-Err "Build-VSIX-Local.ps1 exited with code $LASTEXITCODE"
exit $LASTEXITCODE