My WPF app was running on .NET Framework 4.8 and being distributed as anyCPU to the Store. With the migration to .NET 6, I'm going to redistribute the app as x64, x86 and arm64.
When trying to publish it as an appxbundle, I noticed that arm64 is not available:
When publishing as anyCPU, I get this error:
'Project.csproj' was resolved using '.NETCoreApp,Version=v6.0' instead of the project target framework '.NETFramework,Version=v4.5.1'. This project may not be fully compatible with your project.
I thought that the WAP project was an older version and that's why it had no arm64 variant, so I tried to update it somehow. Without finding any way to update it, I created a new one, referencing the same binary (my project) and got this:
But when publishing, I get this error:
The specified RuntimeIdentifier 'win-ARM64' is not recognized.
What's the migration path for my case? Also, with the new Windows 11 Store, is there any way to use the same app name and use MSIX or just MSI for distribution?
Trying to add msix packages when the latest package was an appxbundle gives me this error:
A previous submission for this app was released with a Windows 10/11 .msixbundle or .appxbundle. Subsequent submissions must continue to contain a Windows 10/11 .msixbundle or .appxbundle.
It worked by publishing the app as a msixbundle
instead.
At first, I tried to publish as three separated msix
(x86, x64 and arm64), but since I had published as appxbundle
, I needed to continue publishing as a bundle, per Microsoft Store's rules.
The process to build a msix
and msixbundle
was not straight-forward as I had no experience with it and as there's not much info available, besides some cryptic documentation from Microsoft.
The first step was trying to understand a msix
package file structure and what's needed to build one.
My initial idea was to take an installer and use the MSIX Packing Tool to generate a msix
package. Unfortunately, there's little control of what's set up or not and the process is quite tiresome (building a special installer just to convert using the tool).
I opted for building my own msix
using command line, via a set of loose files instead.
The process is somewhat simple, discovering what to do at first was not.
I needed to copy all my files to a folder, then set a manifest file called AppxManifest.xml
like so:
The steps are:
AppxManifest.xml
, just changing version and architecture as needed:<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap6="http://schemas.microsoft.com/appx/manifest/uap/windows10/6" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" IgnorableNamespaces="uap uap6 uap10 rescap">
<Identity Name="App ID" Publisher="Publisher ID" Version="0.0.0.0" ProcessorArchitecture="x64"/>
<Properties>
<DisplayName>App Name</DisplayName>
<PublisherDisplayName>Publisher Name</PublisherDisplayName>
<Description>App Description</Description>
<Logo>Assets\StoreLogo.png</Logo>
<uap10:PackageIntegrity>
<uap10:Content Enforcement="on" />
</uap10:PackageIntegrity>
</Properties>
<Resources>
<Resource Language="en-us" />
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22000.1" />
</Dependencies>
<Applications>
<Application Id="A Simple ID" Executable="AppName.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements BackgroundColor="transparent" DisplayName="AppName" Square150x150Logo="Assets/AppName-Square150x150Logo.png" Square44x44Logo="Assets/AppName-Square44x44Logo.png" Description="App Description">
<uap:DefaultTile Wide310x150Logo="Assets/AppName-Wide310x150Logo.png" Square310x310Logo="Assets/AppName-Square310x310Logo.png" Square71x71Logo="Assets/AppName-Square71x71Logo.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
msix
installer UI.I used a config file by calling the MakePri
CLI app:
.\makepri.exe new
/v
/cf '.\Resources\PriConfig.xml' --The config file bellow
/pr '.\Output' --The output folder where all files are located
/mn '.\Output\AppxManifest.xml' --The manifest file created before.
/o
/of '.\Output\Resources.pri'
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources targetOsVersion="10.0.0" majorVersion="1">
<packaging>
<autoResourcePackage qualifier="Scale"/>
</packaging>
<index root="\" startIndexAt="\">
<default>
<qualifier name="Language" value="en-us"/>
<qualifier name="Contrast" value="standard"/>
<qualifier name="Scale" value="100"/>
<qualifier name="HomeRegion" value="001"/>
<qualifier name="TargetSize" value="256"/>
<qualifier name="LayoutDirection" value="LTR"/>
<qualifier name="Theme" value="dark"/>
<qualifier name="AlternateForm" value=""/>
<qualifier name="DXFeatureLevel" value="DX9"/>
<qualifier name="Configuration" value=""/>
<qualifier name="DeviceFamily" value="Universal"/>
</default>
<indexer-config type="folder" foldernameAsQualifier="true" filenameAsQualifier="true" qualifierDelimiter=".">
<exclude type="extension" value=".dll" doNotTraverse="true" doNotIndex="true"/>
<exclude type="extension" value=".exe" doNotTraverse="true" doNotIndex="true"/>
<exclude type="extension" value=".xml" doNotTraverse="true" doNotIndex="true"/>
<exclude type="extension" value=".txt" doNotTraverse="true" doNotIndex="true"/>
<exclude type="extension" value=".json" doNotTraverse="true" doNotIndex="true"/>
</indexer-config>
</index>
</resources>
It's important to erase your previous *.pri
files when you generate another one, or else it would index it too.
msix
package, one for each architecture. I just had to call makeappx.exe
passing this command:makeappx.exe pack /d ".\Output" /p ".\AppName.Version.Architecture.msix" /v /o
Replacing the version and architecture as needed.
This step I had a slowdown. I had the idea to sign the package using a code signing certificate (as I so with my normal installer releases), but I found out that I could not, at least not for the Store version, since I need to use the same publisher ID in the AppxManifest.xml
as it's set in the Microsoft Store configuration for my user.
And to be able to sign the package, I needed to set the publisher ID to be the same as the subject name of the certificate.
In the end, the msix/msixbundle
package that I publish to the Store is not code signed, but the one that I will distribute outside the store will be.
To sign, I just need use this command:
signtool.exe sign /f CERTIFICA-PATH /p PASSWORD /tr TIMESTAMP-URL /v /td sha256 /fd sha256 /du WEBSITE /a PATH-TO-MSIX-FILE
It will fail if the publisher ID is different from the certificate subject.
appxbundle
), I needed to continue publishing as bundle (as msixbundle
) now.To create a bundle, just copy all msix
(three in my case, x86, x64 and arm64) to a folder with nothing else inside and call this command:
makeappx.exe bundle /bv 0.0.0.0 /d "PATH-TO-FOLDER-WITH-MISX" /p "PATH-TO-OUTPUT-MSIXBUNDLE"