rubrapack Manual←↑→

40 MSIX packages

What a program has to write so that Windows reads, installs and runs an MSIX package. Facts are tagged as in Package formats for implementers: [spec] for Microsoft Learn (package manifest and block map schemas) and ECMA-376 Part 2 (Open Packaging Conventions); [observed] for packages written by Windows' own packaging API (IAppxFactory/IAppxPackageWriter in AppxPackaging.dll, part of Windows) and for rubrapack's packages read back through IAppxPackageReader and installed with Add-AppxPackage on Windows 11. rubrapack writes what is described here.

40.1 The ZIP archive#

40.2 The block map (AppxBlockMap.xml)#

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<BlockMap xmlns="http://schemas.microsoft.com/appx/2010/blockmap"
          xmlns:b4="http://schemas.microsoft.com/appx/2021/blockmap" IgnorableNamespaces="b4"
          HashMethod="http://www.w3.org/2001/04/xmlenc#sha256">
  <File Name="data\text.txt" Size="218890" LfhSize="43">
    <Block Hash="(base64 SHA-256 of 65536 plain bytes)" Size="(compressed bytes of this block)"/>
    ...
    <b4:FileHash Hash="(base64 SHA-256 of the whole file)"/>
  </File>
  ...
</BlockMap>

40.3 [Content_Types].xml#

One line: a Default per file extension in order of first use (lower case), xml as application/vnd.ms-appx.manifest+xml, and Override PartName="/AppxBlockMap.xml" as application/vnd.ms-appx.blockmap+xml. rubrapack adds an Override for each file without an extension, and one for /AppxManifest.xml when a payload .xml file took the xml default. [observed]

40.4 The manifest (AppxManifest.xml)#

The smallest desktop application that Windows installs and starts (namespaces foundation/windows10, uap/windows10, restrictedcapabilities): [spec] [observed]

40.5 The virtual registry (Registry.dat, User.dat)#

Registry hives at the package root (the REGF format: Registry hive files (REGF)). Measured with a packaged app on Windows 11 (26100): [observed]

40.6 The virtual file system (VFS\...)#

Files under VFS\<folder> appear to the app at the real location; the real folder does not change. Measured: ProgramFilesX64 (%ProgramFiles%), SystemX64 (System32), Common AppData (%ProgramData%) - and none for AppData or Local AppData, as Microsoft Learn says. The other names rubrapack uses (ProgramFilesX86, ProgramFilesCommonX64/X86, SystemX86, Windows) are those Microsoft Learn lists. [spec] [observed]

40.7 The resource index (resources.pri) [observed]#

ms-resource:Name in the manifest and a logo Assets\Logo.png that exists only as Assets\Logo.scale-200.png are looked up in resources.pri. No specification is published; this is the layout Windows SDK's makepri.exe writes, as far as a package needs it, and what makepri's own dump and Windows read back from rubrapack's files. All numbers little-endian; strings in qualifier and name tables UTF-16 unless noted.

40.8 Extensions#

What rubrapack writes into an application's <Extensions> (after uap:VisualElements), and only this: a source feature with no row here is an error, and one whose Windows build is above the package's MinVersion asks for min-version to be raised. [spec] Microsoft Learn, "Integrate your desktop app with Windows using packaging extensions" and the element pages. The last column is what was checked by installing a package on Windows 11 (26100). [observed]

SourceElement (category)NamespaceMin buildCapabilityChecked on Windows 11
[assoc]uap:Extension windows.fileTypeAssociation > uap3:FileTypeAssociation (Name, Parameters) > uap:DisplayName, uap:SupportedFileTypes > uap:FileTypeuap, uap314393runFullTrustopening a .rpx/.rpy file starts the program with the file
[protocol]uap3:Extension windows.protocol > uap3:Protocol (Name, Parameters)uap314393runFullTruststarting a scheme: URI starts the program with it
[msix-extension] aliasuap3:Extension windows.appExecutionAlias (Executable, EntryPoint) > uap3:AppExecutionAlias > desktop:ExecutionAlias (Alias)uap3, desktop14393runFullTrustthe alias appears in %LOCALAPPDATA%\Microsoft\WindowsApps and starts the program
[msix-extension] startup taskdesktop:Extension windows.startupTask (Executable, EntryPoint) > desktop:StartupTask (TaskId, Enabled, DisplayName)desktop14393runFullTrustregistered after the first start
[shortcut] Desktopdesktop7:Extension windows.shortcut > desktop7:Shortcut (File $(Desktop)\<name>.lnk, Icon, Arguments, Description)desktop719645runFullTrustthe shortcut is on the user's desktop and starts the program
[shortcut] Programs, StartMenunone: the application's own Start entry---the Start menu lists the application
[font]uap4:Extension windows.sharedFonts > uap4:SharedFonts > uap4:Font (File Fonts\<name>), in the first applicationuap415063-other programs see the font while the package is installed, and not after

40.9 Bundles (.msixbundle)#

A ZIP archive laid out like a package (ZIP64 entries with data descriptors), as Windows' bundle writer (IAppxBundleWriter) makes it: [observed]

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Bundle xmlns="http://schemas.microsoft.com/appx/2013/bundle" SchemaVersion="5.0" xmlns:b4="http://schemas.microsoft.com/appx/2018/bundle" xmlns:b5="http://schemas.microsoft.com/appx/2019/bundle" IgnorableNamespaces="b4 b5">
	<Identity Name="Example.App" Publisher="CN=Example" Version="1.0.0.0"/>
	<Packages>
		<Package Type="application" Version="1.0.0.0" Architecture="x64" FileName="Example.App_1.0.0.0_x64.msix" Offset="66" Size="61340">
			<Resources>
				<Resource Language="en-US"/>
			</Resources>
			<b4:Dependencies>
				<b4:TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.26100.0"/>
			</b4:Dependencies>
		</Package>
	</Packages>
</Bundle>

40.10 Signatures (AppxSignature.p7x)#

Worked out against Windows' signer (mssign32!SignerSignEx2 with APPX_SIP_CLIENT_DATA; the PowerShell cmdlet cannot sign a package) and checked by installing rubrapack's signed packages. [observed]

40.11 Installing an unsigned package#

40.12 Worked example: the tutorial's hello.msix#

The MSIX of tutorial chapter 17 (--unsigned-test, x64) is 11119 bytes. It begins with the local header of its first file, hello.exe:

OffsetBytesFieldValue
0x0050 4b 03 04signaturePK\3\4
0x042d 00version needed45 (4.5, ZIP64)
0x0608 00flags0x0008: sizes follow the data
0x0808 00method8 (deflate)
0x0A00 00 21 00time, date1980-01-01 00:00
0x0E00 00 00 00 00 00 00 00 00 00 00 00CRC, sizes0 (in the data descriptor)
0x1A09 00 00 00name, extra length9, 0
0x1E68 65 6c 6c 6f 2e 65 78 65namehello.exe

The 6706 deflated bytes of hello.exe follow, then its data descriptor: 50 4b 07 08 ac a3 c6 2e 32 1a 00 00 00 00 00 00 00 46 00 00 00 00 00 00 - PK\7\8, the CRC-32 2EC6A3AC, and the compressed and plain sizes as 8 bytes each (6706, 17920).

Its entry in AppxBlockMap.xml:

<File Name="hello.exe" Size="17920" LfhSize="39">
  <Block Hash="K7HbHzXLkhbwTo3dcUr0vs35DlYX27qMOexDOTR8e2k=" Size="6704"/>

The file is smaller than 64 KiB, so it is one block. Its Hash is the base64 of the SHA-256 of the 17920 plain bytes (K7HbHzXLkhbwTo3dcUr0vs35DlYX27qMOexDOTR8e2k= computed here), Size the 6704 bytes of its deflate part - the compressed size 6706 minus the 2-byte final block - and LfhSize the local header's 39 bytes (30 + the 9-byte name).