Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions docs/sdk_patches.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# SDK Header Patches

Patches modify SDK headers during ingestion to add metadata annotations (conditional enums, SAL attributes, etc.) until those changes ship in the official SDK.

## How It Works

Patches are unified diffs in `generation/WinSDK/patches/`, organized by phase:

```
patches/
pre-midl/ ← Applied after SDK copy, before MIDL recompilation
post-midl/ ← Applied after MIDL recompilation and header restores
```

**Pre-MIDL** patches target `.idl` files (or headers included during MIDL compilation). Changes flow through `ConvertMidlAttributesToSalAnnotations.ps1` → MIDL → generated `.h`.

**Post-MIDL** patches target `.h` files directly. Use this for headers not generated by MIDL (e.g., `Uxtheme.h`).

Both phases run automatically during `UpdateSDK.ps1` / `RecompileIdlFilesForScraping.ps1`. If a patch fails to apply, the script errors out — this signals the SDK changed and the patch needs regeneration.

## Naming Convention

```
<filename>.<reason>.patch
```

Each patch is a single logical change. Multiple patches per file are supported:

```
patches/post-midl/
Uxtheme.h.set-theme-app-properties-enum.patch
Uxtheme.h.some-other-fix.patch
```

Patches are applied in sorted filename order within each phase.

## Creating a Patch

1. Run `UpdateSDK.ps1` (or `RecompileIdlFilesForScraping.ps1`) to get a pristine SDK copy with existing patches applied.
2. Edit the target file in `generation/WinSDK/RecompiledIdlHeaders/`.
3. Generate the patch:
```powershell
git diff -- "generation/WinSDK/RecompiledIdlHeaders/um/MyHeader.h" `
> "generation/WinSDK/patches/post-midl/MyHeader.h.my-reason.patch"
```
4. Commit both the patch file and the modified header.

For IDL files, use `pre-midl/`:
```powershell
git diff -- "generation/WinSDK/RecompiledIdlHeaders/um/myfile.idl" `
> "generation/WinSDK/patches/pre-midl/myfile.idl.my-reason.patch"
```

## Updating a Patch After an SDK Update

When `UpdateSDK.ps1` is run with a new SDK version:
1. Fresh SDK headers are copied (overwriting all previous content).
2. Patches are re-applied automatically.
3. **If a patch fails**: the SDK incorporated the fix (or changed the surrounding code). Regenerate or remove the patch.

To regenerate a failing patch against the new SDK:
1. Comment out the failing patch temporarily (or move it aside).
2. Re-run `RecompileIdlFilesForScraping.ps1` to get the new pristine baseline.
3. Make the edit, generate the new patch, commit.

## Removing a Patch

When the official SDK ships a fix, delete the `.patch` file and re-run `UpdateSDK.ps1`. The header will now contain the fix natively.

## Examples

**Header patch** (`post-midl/Uxtheme.h.set-theme-app-properties-enum.patch`): Would add a conditional `SET_THEME_APP_PROPERTIES_FLAGS` enum under `#ifdef _WIN32METADATA_` and update function signatures.

**IDL patch** (`pre-midl/myfile.idl.my-reason.patch`): Would add a `cpp_quote` block to an IDL file. MIDL compiles it and the change appears in the generated `.h`.
Empty file.
Empty file.
78 changes: 78 additions & 0 deletions scripts/ApplySDKPatches.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# ApplySDKPatches.ps1
# Applies SDK header patches from generation/WinSDK/patches/{phase}/ directories.
#
# Patches are unified diffs (generated by `git diff`) with paths relative to the repo root.
# They are organized by phase:
# patches/pre-midl/ — Applied after SDK copy, before MIDL recompilation.
# Use for .idl files and headers included during MIDL compilation.
# patches/post-midl/ — Applied after MIDL recompilation and header restores.
# Use for .h files not generated by MIDL, or direct header patches.
#
# Naming convention: <filename>.<reason>.patch
# e.g. Uxtheme.h.set-theme-app-properties-enum.patch
# amstream.idl.output-state-metadata-marker.patch
# Multiple patches per file are supported. Each should be a single logical change.
# Patches are applied in sorted filename order within each phase.
#
# Creating a new patch:
# 1. Run UpdateSDK.ps1 (or RecompileIdlFilesForScraping.ps1) to get a pristine SDK copy
# 2. Edit the target file in RecompiledIdlHeaders/
# 3. From the repo root:
# git diff -- "generation/WinSDK/RecompiledIdlHeaders/um/<file>" `
# > "generation/WinSDK/patches/<phase>/<file>.<reason>.patch"
# 4. The patched file in RecompiledIdlHeaders/ stays as-is (it's the committed result)
#
# IMPORTANT: When updating an existing patch after an SDK update, the fresh SDK copy is already
# the correct baseline. Edit the file, re-run `git diff`, and overwrite the patch file.
#
# Usage:
# . .\ApplySDKPatches.ps1 -Phase pre-midl # Apply pre-MIDL patches (IDL files, etc.)
# . .\ApplySDKPatches.ps1 -Phase post-midl # Apply post-MIDL patches (header files)

[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateSet("pre-midl", "post-midl")]
[string]$Phase
)

. "$PSScriptRoot\CommonUtils.ps1"

$patchDir = Join-Path $windowsWin32ProjectRoot "patches\$Phase"

if (!(Test-Path $patchDir)) {
Write-Host "No $Phase patches directory found. Skipping."
return
}

$patchFiles = Get-ChildItem -Path $patchDir -Filter "*.patch" -Recurse | Sort-Object FullName
if ($patchFiles.Count -eq 0) {
Write-Host "No $Phase patch files found. Skipping."
return
}

$appliedCount = 0
$failedCount = 0

foreach ($patchFile in $patchFiles) {
$relativeName = $patchFile.FullName.Substring($patchDir.Length + 1)
Write-Host "Applying $Phase patch: $relativeName..."

# Anchor git to repo root so patch paths (generation/WinSDK/...) resolve correctly
git -C $rootDir apply $patchFile.FullName 2>&1 | ForEach-Object { Write-Host " $_" }
if ($LASTEXITCODE -eq 0) {
Write-Host " Applied successfully" -ForegroundColor Green
$appliedCount++
} else {
Write-Warning " FAILED to apply patch: $relativeName"
Write-Warning " The SDK headers may have changed. Regenerate this patch against the new SDK."
$failedCount++
}
}

Write-Host "SDK $Phase patches: $appliedCount applied, $failedCount failed (of $($patchFiles.Count))."

if ($failedCount -gt 0) {
Write-Error "$Phase patches failed. Regenerate failing patches against the new SDK headers."
exit 1
}
8 changes: 8 additions & 0 deletions scripts/RecompileIdlFilesForScraping.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ Copy-Item "$d3dIncludeDir\dxgiformat.*" "$recompiledIdlHeadersDir\shared" -Recur
Write-Host "Copying additional headers from $windowsWin32ProjectRoot\AdditionalHeaders to $recompiledIdlHeadersDir\um..."
Copy-Item "$windowsWin32ProjectRoot\AdditionalHeaders\*" "$recompiledIdlHeadersDir\um" -Recurse

Write-Host "Applying SDK patches to IDL files (pre-MIDL)..."
& $PSScriptRoot\ApplySDKPatches.ps1 -Phase pre-midl
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

Write-Host "Converting MIDL attributes to SAL annotations..."
$idlFilesToRecompile = [System.Collections.ArrayList]@()
$idlFilesToExclude = "cellularapi_oem", "certbcli", "dxgicommon", "dxgitype", "microsoft.diagnostics.appanalysis", "PortableDeviceConnectImports", "wincrypt" | ForEach-Object { "$_.idl" }
Expand Down Expand Up @@ -183,4 +187,8 @@ foreach ($winHvHeader in $winHvHeadersToRestore) {
git checkout origin/main $fullPath
}

Write-Host "Applying SDK patches to header files (post-MIDL)..."
& $PSScriptRoot\ApplySDKPatches.ps1 -Phase post-midl
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

$ErrorActionPreference = "Stop"
Loading