Skip to content

Instantly share code, notes, and snippets.

@Aetopia
Created July 17, 2025 06:32
Show Gist options
  • Select an option

  • Save Aetopia/e1149311f8dd10584550be5ed5231e89 to your computer and use it in GitHub Desktop.

Select an option

Save Aetopia/e1149311f8dd10584550be5ed5231e89 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Windows.ApplicationModel;
using Windows.Globalization;
using Windows.Management.Deployment;
using Windows.System;
sealed class MicrosoftStore
{
readonly struct _ : INotifyCompletion
{
public void OnCompleted(Action @delegate)
{
var @this = SynchronizationContext.Current;
try { SynchronizationContext.SetSynchronizationContext(null); @delegate(); }
finally { SynchronizationContext.SetSynchronizationContext(@this); }
}
public bool IsCompleted => SynchronizationContext.Current is null;
public _ GetAwaiter() => this;
public void GetResult() { }
}
const string DeliveryUri = "https://fe3cr.delivery.mp.microsoft.com/ClientWebService/client.asmx";
const string DeliverySecuredUri = $"{DeliveryUri}/secured";
const string DisplayCatalogUri = "https://displaycatalog.mp.microsoft.com/v7.0/products/{0}?languages=iv&market={1}";
const string TraceUri = "https://cloudflare.com/cdn-cgi/trace";
static readonly PackageManager _packageManager = new();
static readonly HttpClient _httpClient = new();
MicrosoftStore(string syncUpdates, string displayCatalogUri) => (_syncUpdates, _displayCatalogUri) = (syncUpdates, displayCatalogUri);
readonly string _syncUpdates, _displayCatalogUri;
static IEnumerable<XElement> LocalDescendants(XElement @this, string name)
{
foreach (var element in @this.Descendants().AsParallel())
{
if (element.Name.LocalName == name)
yield return element;
}
}
static XElement LocalElement(XElement @this, string name)
{
foreach (var element in @this.Elements().AsParallel())
if (element.Name.LocalName == name) return element;
return null;
}
static async Task<string> GetGeographicRegionAsync()
{
using StreamReader reader = new(await _httpClient.GetStreamAsync(TraceUri));
string @string; while ((@string = await reader.ReadLineAsync()) is not null)
{
var strings = @string.Split('=');
if (strings[0] is "loc") return strings[1];
}
return new GeographicRegion().CodeTwoLetter;
}
static async Task<Uri> GetAppxPackageUriAsync(Tuple<bool, string, string> appxPackage)
{
using StringContent content = new(string.Format(EmbeddedResources.GetExtendedUpdateInfo2, appxPackage.Item2, appxPackage.Item3), Encoding.UTF8, "application/soap+xml");
using var message = await _httpClient.PostAsync(DeliverySecuredUri, content);
message.EnsureSuccessStatusCode();
using var stream = await message.Content.ReadAsStreamAsync();
foreach (var element in LocalDescendants(XElement.Load(stream), "Url"))
{
Uri uri = new(element.Value);
if (uri.Host is "tlu.dl.delivery.mp.microsoft.com") return uri;
}
return null;
}
static async Task<string> GetCookieAsync()
{
using StringContent content = new(EmbeddedResources.GetCookie, Encoding.UTF8, "application/soap+xml");
using var message = await _httpClient.PostAsync(DeliveryUri, content);
message.EnsureSuccessStatusCode();
using var stream = await message.Content.ReadAsStreamAsync();
return LocalDescendants(XElement.Load(stream), "EncryptedData").First().Value;
}
internal static async Task<MicrosoftStore> CreateAsync()
{
await new _();
if (RuntimeInformation.OSArchitecture is not Architecture.X64)
throw new PlatformNotSupportedException();
var displayCatalogUri = string.Format(DisplayCatalogUri, "{0}", await GetGeographicRegionAsync());
var syncUpdates = string.Format(EmbeddedResources.SyncUpdates, await GetCookieAsync(), "{0}");
return new(syncUpdates, displayCatalogUri);
}
async Task<string> GetWuCategoryIdAsync(string productId)
{
var displayCatalogUri = string.Format(_displayCatalogUri, productId);
using var stream = await _httpClient.GetStreamAsync(displayCatalogUri);
using var reader = JsonReaderWriterFactory.CreateJsonReader(stream, XmlDictionaryReaderQuotas.Max);
return XElement.Load(reader).Descendants("WuCategoryId").First().Value;
}
internal async Task<List<Lazy<Task<Uri>>>> GetAsync(string productId)
{
await new _();
List<Lazy<Task<Uri>>> packageUris = [];
var syncUpdates = await SyncUpdatesAsync(await GetWuCategoryIdAsync(productId));
var appxPackages = GetAppxPackages(syncUpdates, GetAppxPackageInstallData(syncUpdates));
foreach (var appxPackage in appxPackages)
packageUris.Add(new(() => GetAppxPackageUriAsync(appxPackage)));
return packageUris;
}
async Task<XElement> SyncUpdatesAsync(string wuCategoryId)
{
using StringContent content = new(string.Format(_syncUpdates, wuCategoryId), Encoding.UTF8, "application/soap+xml");
using var message = await _httpClient.PostAsync(DeliveryUri, content);
message.EnsureSuccessStatusCode();
return XElement.Parse(WebUtility.HtmlDecode(await message.Content.ReadAsStringAsync()));
}
Dictionary<string, bool> GetAppxPackageInstallData(XElement syncUpdates)
{
Dictionary<string, bool> appxPackageInstallData = [];
foreach (var element in LocalDescendants(syncUpdates, "AppxPackageInstallData"))
{
var packageFileName = element.Attribute("PackageFileName").Value;
if (packageFileName[packageFileName.LastIndexOf('.') + 1] is 'e') continue;
var id = LocalElement(element.Parent.Parent.Parent, "ID").Value;
var mainPackage = bool.Parse(element.Attribute("MainPackage").Value);
appxPackageInstallData.Add(id, mainPackage);
}
return appxPackageInstallData;
}
List<Tuple<bool, string, string>> GetAppxPackages(XElement syncUpdates, Dictionary<string, bool> appxPackageInstallData)
{
Dictionary<string, StrongBox<(int PackageRank, string PackageVersion, bool MainPackage, XElement UpdateInfo)>> appxPackages = [];
foreach (var updateInfo in LocalDescendants(syncUpdates, "UpdateInfo"))
{
var id = LocalElement(updateInfo, "ID").Value;
if (!appxPackageInstallData.TryGetValue(id, out var mainPackage))
continue;
var packageMoniker = LocalDescendants(updateInfo, "AppxMetadata").First().Attribute("PackageMoniker").Value;
var packageIdentity = packageMoniker.Split('_');
var architecture = packageIdentity[2];
if (architecture is not "neutral" && architecture is not "x64") continue;
var packageFamilyName = $"{packageIdentity[0]}_{packageIdentity[4]}";
var packageRank = int.Parse(LocalDescendants(updateInfo, "Properties").First().Attribute("PackageRank").Value);
if (!appxPackages.TryGetValue(packageFamilyName, out var strongBox)) appxPackages.Add(packageFamilyName, new()
{
Value = new()
{
PackageRank = packageRank,
PackageVersion = packageIdentity[1],
MainPackage = mainPackage,
UpdateInfo = updateInfo,
}
});
else if (strongBox.Value.PackageRank < packageRank)
{
strongBox.Value.PackageRank = packageRank;
strongBox.Value.PackageVersion = packageIdentity[1];
strongBox.Value.UpdateInfo = updateInfo;
}
}
return GetInstallableAppxPackages(appxPackages);
}
List<Tuple<bool, string, string>> GetInstallableAppxPackages(Dictionary<string, StrongBox<(int PackageRank, string PackageVersion, bool MainPackage, XElement XElement)>> appxPackages)
{
List<Tuple<bool, string, string>> updateIdentities = [];
foreach (var appxPackage in appxPackages)
{
var (_, packageVersion, mainPackage, updateInfo) = appxPackage.Value.Value;
var packages = _packageManager.FindPackagesForUser(string.Empty, appxPackage.Key);
var package = packages.FirstOrDefault(_ => _.Id.Architecture is ProcessorArchitecture.X64 || mainPackage);
if (package is not null)
{
if (package.SignatureKind is not PackageSignatureKind.Store)
return [];
var installedPackageVersion = package.Id.Version;
Version installedVersion = new(installedPackageVersion.Major, installedPackageVersion.Minor, installedPackageVersion.Build, installedPackageVersion.Revision);
if (new Version(packageVersion) <= installedVersion)
if (mainPackage) return []; else continue;
}
var updateIdentity = LocalDescendants(updateInfo, "UpdateIdentity").First();
var updateId = updateIdentity.Attribute("UpdateID").Value;
var revisionNumber = updateIdentity.Attribute("RevisionNumber").Value;
updateIdentities.Add(new(mainPackage, updateId, revisionNumber));
}
updateIdentities.Sort((x, y) => x.Item1 ? 1 : -1); return updateIdentities;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment