Skip to content

Commit 46b42e1

Browse files
aclinickCopilot
andcommitted
Skip stub inner packages when parsing bundle manifests
Real-world bundles like Microsoft.DesktopAppInstaller include 'stub' inner packages whose FileName attribute is e.g. 'AppxMetadata\Stub\AppInstaller_x64_stub.msix'. These are metadata-only placeholders used by the platform for delta servicing — they don't carry a real block map. Diffing them threw 'Bundle does not contain expected inner package'. - BundleManifestParser now normalizes backslash separators and skips entries under AppxMetadata/. - New test BundleManifestParser_SkipsStubPackages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 025f534 commit 46b42e1

3 files changed

Lines changed: 36 additions & 1 deletion

File tree

MSIXplainer.Core.Tests/UpdateDiffBundleTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,4 +353,29 @@ [new SyntheticFile("App.exe", Bytes("a", 100))]),
353353
File.Delete(newBundle);
354354
}
355355
}
356+
357+
[Fact]
358+
public void BundleManifestParser_SkipsStubPackages()
359+
{
360+
// Real DesktopAppInstaller bundles include stub packages whose FileName
361+
// attribute is "AppxMetadata\Stub\AppInstaller_x64_stub.msix". These are
362+
// metadata-only placeholders (no real payload), so the parser must skip
363+
// them — otherwise UpdateDiffService later tries to read their block map
364+
// and fails with "Bundle does not contain expected inner package".
365+
var xml = $$"""
366+
<?xml version="1.0" encoding="utf-8"?>
367+
<Bundle xmlns="{{BundleNs.NamespaceName}}">
368+
<Identity Name="Test.App" Publisher="CN=Test" Version="1.0.0.0" />
369+
<Packages>
370+
<Package Type="application" Version="1.0.0.0" Architecture="x64" FileName="App_x64.msix" Size="100" Offset="0" />
371+
<Package Type="application" Version="1.0.0.0" Architecture="x64" FileName="AppxMetadata\Stub\App_x64_stub.msix" Size="50" Offset="0" />
372+
</Packages>
373+
</Bundle>
374+
""";
375+
376+
var inners = BundleManifestParser.Parse(xml);
377+
378+
Assert.Single(inners);
379+
Assert.Equal("App_x64.msix", inners[0].FileName);
380+
}
356381
}

MSIXplainer.Core/Services/BundleManifestParser.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ public static IReadOnlyList<BundleInnerPackage> Parse(string xml)
6262
var fileName = p.Attribute("FileName")?.Value ?? string.Empty;
6363
if (string.IsNullOrEmpty(fileName)) continue;
6464

65+
// Normalize path separator: bundle manifests sometimes use '\' but
66+
// the underlying ZIP entry names always use '/'.
67+
fileName = fileName.Replace('\\', '/');
68+
69+
// Skip stub packages (AppxMetadata/Stub/*.msix). Stubs are metadata-only
70+
// placeholders used by the platform for bundle delta servicing — they
71+
// don't carry a real block map or payload, so they have nothing to diff.
72+
if (fileName.StartsWith("AppxMetadata/", StringComparison.OrdinalIgnoreCase))
73+
continue;
74+
6575
var type = p.Attribute("Type")?.Value ?? "application";
6676
var version = p.Attribute("Version")?.Value ?? string.Empty;
6777
var arch = p.Attribute("Architecture")?.Value ?? "neutral";

MSIXplainer/Package.appxmanifest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<Identity
1212
Name="Clinick.msixplainer"
1313
Publisher="CN=46604BD4-AFD9-4B23-8EB3-10EAF66872A5"
14-
Version="1.0.23.0" />
14+
Version="1.0.24.0" />
1515

1616
<mp:PhoneIdentity PhoneProductId="3a09e9f9-55b1-418a-9e63-9a662f1a29bc" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
1717

0 commit comments

Comments
 (0)