Public/Add-MSIXFixNotepadPlusPlus.ps1

function Add-MSIXFixNotepadPlusPlus {
<#
.SYNOPSIS
    Applies PSF and shell extension fixes to a Notepad++ MSIX package.

.DESCRIPTION
    Merges the inner NppShell.msix sparse shell extension into the main manifest,
    corrects the NppShell.dll package-relative path, and applies the Package Support
    Framework (PSF) with RegLegacyFixup, FileRedirectionFixup, and InstalledLocationVirtualization.


.PARAMETER MsixFile
    Path to the Notepad++ MSIX file to modify.

.PARAMETER MSIXFolder
    Temporary extraction folder. Defaults to a unique path under %TEMP%.

.PARAMETER OutputFilePath
    Path for the repackaged MSIX. Defaults to overwriting the source file.

.PARAMETER Subject
    Publisher subject string (CN=...). When provided, Set-MSIXPublisher is called.

.PARAMETER Version
    Package version to set (e.g. '8.9.4.0').

.PARAMETER Force
    Overwrites existing files in the extraction folder without prompting.

.PARAMETER KeepMSIXFolder
    Keeps the temporary extraction folder after packing.

.EXAMPLE
    Add-MSIXFixNotepadPlusPlus -MsixFile "C:\Packages\NotepadPlusPlus.msix" -Verbose

.EXAMPLE
    Add-MSIXFixNotepadPlusPlus `
        -MsixFile "C:\Packages\NotepadPlusPlus.msix" -force -verbose

.NOTES
    Requires an active PSF framework set via Set-MSIXActivePSFFramework.
    https://www.nick-it.de
    Andreas Nick, 2026
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            Position = 0)]
        [System.IO.FileInfo] $MsixFile,

        [System.IO.DirectoryInfo] $MSIXFolder = ($env:Temp + '\MSIX_TEMP_' + [System.Guid]::NewGuid().ToString()),

        [System.IO.FileInfo] $OutputFilePath = $null,

        [string] $Subject = '',
        [string] $Version = '',

        [switch] $Force,
        [switch] $KeepMSIXFolder
    )

    process {
        if ($null -eq $OutputFilePath) {
            $OutputFilePath = $MsixFile
        }

        try {
            $null = Open-MSIXPackage -MsixFile $MsixFile -Force:$Force -MSIXFolder $MSIXFolder

            if ($Subject -ne '') {
                Set-MSIXPublisher -MSIXFolder $MSIXFolder -PublisherSubject $Subject
            }

            if ($Version -ne '') {
                Set-MSIXPackageVersion -MSIXFolder $MSIXFolder -MSVersion $Version
                Write-Verbose "Package version set to $Version"
            }

            Invoke-MSIXCleanup -MSIXFolder $MSIXFolder

            # Merge NppShell.msix (IExplorerCommand + com:SurrogateServer) into the main manifest.
            # The sparse package cannot be deployed inside a full MSIX container because the
            # WindowsApps path is read-only; merging avoids that runtime failure.
            Import-MSIXSparseShellExtension -MSIXFolder $MSIXFolder `
                -SparsePackagePath 'VFS\ProgramFilesX64\Notepad++\contextMenu\NppShell.msix' `
                -Verbose:($PSBoundParameters['Verbose'] -eq $true)

            # The sparse manifest declares Path="NppShell.dll" relative to its own root.
            # After merging into the main package the path must be package-relative so that
            # the COM surrogate host (dllhost.exe) can locate the DLL inside the VFS.
            $manifestPath = Join-Path $MSIXFolder.FullName 'AppxManifest.xml'
            $xml = New-Object System.Xml.XmlDocument
            $xml.Load($manifestPath)
            $nppDllPath = 'VFS\ProgramFilesX64\Notepad++\contextMenu\NppShell.dll'
            foreach ($classNode in @($xml.SelectNodes("//*[local-name()='Class' and @Path='NppShell.dll']"))) {
                $classNode.SetAttribute('Path', $nppDllPath)
                Write-Verbose "Updated com:Class Path to: $nppDllPath"
            }
            $xml.Save($manifestPath)

            Add-MSIXInstalledLocationVirtualization -MSIXFolderPath $MSIXFolder

            Add-MSIXPsfFrameworkFiles -MSIXFolder $MSIXFolder

            $apps = Get-MSIXApplications -MSIXFolder $MSIXFolder
            if ($null -eq $apps -or $apps.Count -eq 0) {
                Write-Warning "No application entries found in AppxManifest.xml."
            }
            else {
                foreach ($app in $apps) {
                    Write-Verbose "Adding PSF shim for application: $($app.Id)"
                    $null = Add-MSXIXPSFShim -MSIXFolder $MSIXFolder -MISXAppID $app.Id
                }
            }

            Add-MSIXPSFDefaultRegLegacy -MSIXFolder $MSIXFolder
            Add-MSIXPSFDefaultFRF -MSIXFolder $MSIXFolder

            Close-MSIXPackage -MSIXFolder $MSIXFolder -MSIXFile $OutputFilePath -Force:$Force -KeepMSIXFolder:$KeepMSIXFolder
            "Notepad++ fix applied: $OutputFilePath"
        }
        catch {
            Write-Error "Error applying Notepad++ fix: $_"
        }
    }
}