From 4d20aa2cb7e8316ea5cc4caf84e58e13d19022ce Mon Sep 17 00:00:00 2001 From: Derek Zhao Date: Sat, 10 Oct 2026 22:19:05 +0800 Subject: [PATCH 1/2] fix(windows): resolve 0x800701C0 install error by cleaning orphaned AppContainer profiles - Fixes #39 and #38 - Windows 11 (24H2/25H2, Build 26100/26200) enables PROCESS_MITIGATION_REDIRECTION_TRUST_POLICY on AppXSvc (SYSTEM), which blocks traversing or deleting user-created filesystem junctions inside %LOCALAPPDATA%\Packages\, returning ERROR_UNTRUSTED_MOUNT_POINT (0x800701C0) when DeleteAppContainerProfile is called. - In-place updates no longer needlessly call Remove-AppxPackage before Add-AppxPackage, avoiding leaving residual unlinked AppContainer state and instead using -ForceApplicationShutdown -ForceUpdateFromAnyVersion directly. - Clear orphaned AppContainer profiles, junctions, and directories via Userenv DeleteAppContainerProfile and safe directory unbinding before registering if the package is not currently registered. - Add retry mechanism to Add-AppxPackage that purges orphaned profiles and retries upon failure. - Ensure Unregister-ShareTarget.ps1 purges AppContainer profiles on uninstallation. --- .../packaging/msi/Register-ShareTarget.ps1 | 108 +++++++++++++++++- .../packaging/msi/Unregister-ShareTarget.ps1 | 77 +++++++++++++ windows/scripts/register-dev.ps1 | 94 ++++++++++++++- 3 files changed, 269 insertions(+), 10 deletions(-) diff --git a/windows/packaging/msi/Register-ShareTarget.ps1 b/windows/packaging/msi/Register-ShareTarget.ps1 index a465beb..eedc072 100644 --- a/windows/packaging/msi/Register-ShareTarget.ps1 +++ b/windows/packaging/msi/Register-ShareTarget.ps1 @@ -79,15 +79,113 @@ try { } } + function Clear-AppContainerProfiles { + Log "Starting AppContainer profile cleanup..." + $code = @" +using System; +using System.Runtime.InteropServices; +public static class UserenvNative { + [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int DeleteAppContainerProfile(string pszAppContainerName); +} +"@ + if (-not ([System.Management.Automation.PSTypeName]'UserenvNative').Type) { + Add-Type -TypeDefinition $code -ErrorAction SilentlyContinue + } + + $patterns = @('WeChatBridge.Windows.ShareTarget_*', 'ChatBridge.Windows.ShareTarget_*') + $families = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) + + $packagesRoot = Join-Path $env:LOCALAPPDATA 'Packages' + if (Test-Path -LiteralPath $packagesRoot) { + foreach ($pat in $patterns) { + Get-ChildItem -LiteralPath $packagesRoot -Filter $pat -Directory -ErrorAction SilentlyContinue | + ForEach-Object { [void]$families.Add($_.Name) } + } + } + + $mappingPath = 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings' + if (Test-Path -LiteralPath $mappingPath) { + Get-ItemProperty "$mappingPath\*" -ErrorAction SilentlyContinue | + Where-Object { $_.Moniker -match '^(wechatbridge|chatbridge)\.windows\.sharetarget_' } | + ForEach-Object { [void]$families.Add($_.Moniker) } + } + $storagePath = 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage' + if (Test-Path -LiteralPath $storagePath) { + Get-ChildItem -LiteralPath $storagePath -ErrorAction SilentlyContinue | + Where-Object { $_.PSChildName -match '^(wechatbridge|chatbridge)\.windows\.sharetarget_' } | + ForEach-Object { [void]$families.Add($_.PSChildName) } + } + + foreach ($family in $families) { + Log "Cleaning AppContainer profile $family" + $packageDir = Join-Path $packagesRoot $family + if (Test-Path -LiteralPath $packageDir) { + Get-ChildItem -LiteralPath $packageDir -Recurse -Force -ErrorAction SilentlyContinue | + Where-Object { $_.Attributes -band [System.IO.FileAttributes]::ReparsePoint } | + ForEach-Object { + try { + if ($_.PSIsContainer) { + [System.IO.Directory]::Delete($_.FullName, $false) + } else { + [System.IO.File]::Delete($_.FullName) + } + } catch { + Log "Warning: could not delete junction $($_.FullName): $($_.Exception.Message)" + } + } + try { + Remove-Item -LiteralPath $packageDir -Recurse -Force -ErrorAction SilentlyContinue + } catch { + Log "Warning: could not delete package folder $packageDir: $($_.Exception.Message)" + } + } + + try { + if (([System.Management.Automation.PSTypeName]'UserenvNative').Type) { + $delResult = [UserenvNative]::DeleteAppContainerProfile($family) + Log "DeleteAppContainerProfile($family) -> $('0x{0:X8}' -f ($delResult -band 0xffffffff))" + } + } catch { + Log "Warning: DeleteAppContainerProfile failed: $($_.Exception.Message)" + } + + if (Test-Path -LiteralPath $mappingPath) { + Get-ChildItem -LiteralPath $mappingPath -ErrorAction SilentlyContinue | ForEach-Object { + $m = (Get-ItemProperty -LiteralPath $_.PSPath -Name 'Moniker' -ErrorAction SilentlyContinue).Moniker + if ($m -and $m.Equals($family, [StringComparison]::OrdinalIgnoreCase)) { + Remove-Item -LiteralPath $_.PSPath -Recurse -Force -ErrorAction SilentlyContinue + } + } + } + $storageDir = Join-Path $storagePath $family + if (Test-Path -LiteralPath $storageDir) { + Remove-Item -LiteralPath $storageDir -Recurse -Force -ErrorAction SilentlyContinue + } + } + } + Get-Process -Name 'WeChatBridge.ShareTarget', 'WeChatBridge.Windows' -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue - # Also sweep the short-lived 1.0.7 ChatBridge.* registration — same - # publisher, so its share-menu row would linger next to 微信流's. - Get-AppxPackage -ErrorAction SilentlyContinue | - Where-Object { $_.Name -in 'WeChatBridge.Windows.ShareTarget', 'ChatBridge.Windows.ShareTarget' } | + # Sweep the short-lived 1.0.7 ChatBridge.* registration if present + Get-AppxPackage -Name 'ChatBridge.Windows.ShareTarget' -ErrorAction SilentlyContinue | Remove-AppxPackage -ErrorAction SilentlyContinue - Add-AppxPackage -Path $msix -ExternalLocation $InstallRoot + + # Clean orphaned AppContainer profiles if WeChatBridge.Windows.ShareTarget is not registered + $existing = Get-AppxPackage -Name 'WeChatBridge.Windows.ShareTarget' -ErrorAction SilentlyContinue + if (-not $existing) { + Clear-AppContainerProfiles + } + + try { + Add-AppxPackage -Path $msix -ExternalLocation $InstallRoot -ForceApplicationShutdown -ForceUpdateFromAnyVersion + } + catch { + Log "Add-AppxPackage initial attempt failed ($($_.Exception.Message)). Cleaning AppContainer profiles and retrying..." + Clear-AppContainerProfiles + Add-AppxPackage -Path $msix -ExternalLocation $InstallRoot -ForceApplicationShutdown -ForceUpdateFromAnyVersion + } $registered = Get-AppxPackage -Name 'WeChatBridge.Windows.ShareTarget' -ErrorAction SilentlyContinue if (-not $registered) { throw 'Package registration did not stick.' } if ($registered.Status -ne 'Ok') { throw "Registered package is unhealthy: $($registered.Status)" } diff --git a/windows/packaging/msi/Unregister-ShareTarget.ps1 b/windows/packaging/msi/Unregister-ShareTarget.ps1 index 420f6a9..4970ef8 100644 --- a/windows/packaging/msi/Unregister-ShareTarget.ps1 +++ b/windows/packaging/msi/Unregister-ShareTarget.ps1 @@ -6,3 +6,80 @@ Get-Process -Name 'WeChatBridge.ShareTarget', 'WeChatBridge.Windows' -ErrorActio Get-AppxPackage -ErrorAction SilentlyContinue | Where-Object { $_.Name -in 'WeChatBridge.Windows.ShareTarget', 'ChatBridge.Windows.ShareTarget' } | Remove-AppxPackage -ErrorAction SilentlyContinue + +function Clear-AppContainerProfiles { + $code = @" +using System; +using System.Runtime.InteropServices; +public static class UserenvNativeUnregister { + [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int DeleteAppContainerProfile(string pszAppContainerName); +} +"@ + if (-not ([System.Management.Automation.PSTypeName]'UserenvNativeUnregister').Type) { + Add-Type -TypeDefinition $code -ErrorAction SilentlyContinue + } + + $patterns = @('WeChatBridge.Windows.ShareTarget_*', 'ChatBridge.Windows.ShareTarget_*') + $families = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) + + $packagesRoot = Join-Path $env:LOCALAPPDATA 'Packages' + if (Test-Path -LiteralPath $packagesRoot) { + foreach ($pat in $patterns) { + Get-ChildItem -LiteralPath $packagesRoot -Filter $pat -Directory -ErrorAction SilentlyContinue | + ForEach-Object { [void]$families.Add($_.Name) } + } + } + + $mappingPath = 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings' + if (Test-Path -LiteralPath $mappingPath) { + Get-ItemProperty "$mappingPath\*" -ErrorAction SilentlyContinue | + Where-Object { $_.Moniker -match '^(wechatbridge|chatbridge)\.windows\.sharetarget_' } | + ForEach-Object { [void]$families.Add($_.Moniker) } + } + $storagePath = 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage' + if (Test-Path -LiteralPath $storagePath) { + Get-ChildItem -LiteralPath $storagePath -ErrorAction SilentlyContinue | + Where-Object { $_.PSChildName -match '^(wechatbridge|chatbridge)\.windows\.sharetarget_' } | + ForEach-Object { [void]$families.Add($_.PSChildName) } + } + + foreach ($family in $families) { + $packageDir = Join-Path $packagesRoot $family + if (Test-Path -LiteralPath $packageDir) { + Get-ChildItem -LiteralPath $packageDir -Recurse -Force -ErrorAction SilentlyContinue | + Where-Object { $_.Attributes -band [System.IO.FileAttributes]::ReparsePoint } | + ForEach-Object { + try { + if ($_.PSIsContainer) { + [System.IO.Directory]::Delete($_.FullName, $false) + } else { + [System.IO.File]::Delete($_.FullName) + } + } catch {} + } + Remove-Item -LiteralPath $packageDir -Recurse -Force -ErrorAction SilentlyContinue + } + + try { + if (([System.Management.Automation.PSTypeName]'UserenvNativeUnregister').Type) { + [void][UserenvNativeUnregister]::DeleteAppContainerProfile($family) + } + } catch {} + + if (Test-Path -LiteralPath $mappingPath) { + Get-ChildItem -LiteralPath $mappingPath -ErrorAction SilentlyContinue | ForEach-Object { + $m = (Get-ItemProperty -LiteralPath $_.PSPath -Name 'Moniker' -ErrorAction SilentlyContinue).Moniker + if ($m -and $m.Equals($family, [StringComparison]::OrdinalIgnoreCase)) { + Remove-Item -LiteralPath $_.PSPath -Recurse -Force -ErrorAction SilentlyContinue + } + } + } + $storageDir = Join-Path $storagePath $family + if (Test-Path -LiteralPath $storageDir) { + Remove-Item -LiteralPath $storageDir -Recurse -Force -ErrorAction SilentlyContinue + } + } +} + +Clear-AppContainerProfiles diff --git a/windows/scripts/register-dev.ps1 b/windows/scripts/register-dev.ps1 index 2d2beb8..f499215 100644 --- a/windows/scripts/register-dev.ps1 +++ b/windows/scripts/register-dev.ps1 @@ -67,15 +67,99 @@ if ($signature.Status -ne 'Valid') { throw "The MSIX signature is not trusted ($($signature.Status): $($signature.StatusMessage)). Trust the development certificate in Cert:\CurrentUser\Root and retry." } -Get-AppxPackage -ErrorAction SilentlyContinue | - Where-Object { $_.Name -in 'WeChatBridge.Windows.ShareTarget', 'ChatBridge.Windows.ShareTarget' } | +function Clear-AppContainerProfiles { + $code = @" +using System; +using System.Runtime.InteropServices; +public static class UserenvNativeDev { + [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int DeleteAppContainerProfile(string pszAppContainerName); +} +"@ + if (-not ([System.Management.Automation.PSTypeName]'UserenvNativeDev').Type) { + Add-Type -TypeDefinition $code -ErrorAction SilentlyContinue + } + + $patterns = @('WeChatBridge.Windows.ShareTarget_*', 'ChatBridge.Windows.ShareTarget_*') + $families = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) + + $packagesRoot = Join-Path $env:LOCALAPPDATA 'Packages' + if (Test-Path -LiteralPath $packagesRoot) { + foreach ($pat in $patterns) { + Get-ChildItem -LiteralPath $packagesRoot -Filter $pat -Directory -ErrorAction SilentlyContinue | + ForEach-Object { [void]$families.Add($_.Name) } + } + } + + $mappingPath = 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings' + if (Test-Path -LiteralPath $mappingPath) { + Get-ItemProperty "$mappingPath\*" -ErrorAction SilentlyContinue | + Where-Object { $_.Moniker -match '^(wechatbridge|chatbridge)\.windows\.sharetarget_' } | + ForEach-Object { [void]$families.Add($_.Moniker) } + } + $storagePath = 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage' + if (Test-Path -LiteralPath $storagePath) { + Get-ChildItem -LiteralPath $storagePath -ErrorAction SilentlyContinue | + Where-Object { $_.PSChildName -match '^(wechatbridge|chatbridge)\.windows\.sharetarget_' } | + ForEach-Object { [void]$families.Add($_.PSChildName) } + } + + foreach ($family in $families) { + $packageDir = Join-Path $packagesRoot $family + if (Test-Path -LiteralPath $packageDir) { + Get-ChildItem -LiteralPath $packageDir -Recurse -Force -ErrorAction SilentlyContinue | + Where-Object { $_.Attributes -band [System.IO.FileAttributes]::ReparsePoint } | + ForEach-Object { + try { + if ($_.PSIsContainer) { + [System.IO.Directory]::Delete($_.FullName, $false) + } else { + [System.IO.File]::Delete($_.FullName) + } + } catch {} + } + Remove-Item -LiteralPath $packageDir -Recurse -Force -ErrorAction SilentlyContinue + } + + try { + if (([System.Management.Automation.PSTypeName]'UserenvNativeDev').Type) { + [void][UserenvNativeDev]::DeleteAppContainerProfile($family) + } + } catch {} + + if (Test-Path -LiteralPath $mappingPath) { + Get-ChildItem -LiteralPath $mappingPath -ErrorAction SilentlyContinue | ForEach-Object { + $m = (Get-ItemProperty -LiteralPath $_.PSPath -Name 'Moniker' -ErrorAction SilentlyContinue).Moniker + if ($m -and $m.Equals($family, [StringComparison]::OrdinalIgnoreCase)) { + Remove-Item -LiteralPath $_.PSPath -Recurse -Force -ErrorAction SilentlyContinue + } + } + } + $storageDir = Join-Path $storagePath $family + if (Test-Path -LiteralPath $storageDir) { + Remove-Item -LiteralPath $storageDir -Recurse -Force -ErrorAction SilentlyContinue + } + } +} + +Get-AppxPackage -Name 'ChatBridge.Windows.ShareTarget' -ErrorAction SilentlyContinue | Remove-AppxPackage -ErrorAction SilentlyContinue +$existing = Get-AppxPackage -Name 'WeChatBridge.Windows.ShareTarget' -ErrorAction SilentlyContinue +if (-not $existing) { + Clear-AppContainerProfiles +} try { - Add-AppxPackage -Path $packagePath -ExternalLocation $installRoot + Add-AppxPackage -Path $packagePath -ExternalLocation $installRoot -ForceApplicationShutdown -ForceUpdateFromAnyVersion } catch { - $hresult = ('0x{0:X8}' -f ($_.Exception.HResult -band 0xffffffff)) - throw "Add-AppxPackage failed ($hresult): $($_.Exception.Message)" + Clear-AppContainerProfiles + try { + Add-AppxPackage -Path $packagePath -ExternalLocation $installRoot -ForceApplicationShutdown -ForceUpdateFromAnyVersion + } + catch { + $hresult = ('0x{0:X8}' -f ($_.Exception.HResult -band 0xffffffff)) + throw "Add-AppxPackage failed ($hresult): $($_.Exception.Message)" + } } $registered = Get-AppxPackage -Name 'WeChatBridge.Windows.ShareTarget' -ErrorAction SilentlyContinue if (-not $registered) { throw 'Add-AppxPackage returned but the package is not registered.' } From 10ef22f6bcc8a862cd8ba5a7a8d1c4084820c207 Mon Sep 17 00:00:00 2001 From: Derek Zhao Date: Sat, 10 Oct 2026 23:17:37 +0800 Subject: [PATCH 2/2] fix(windows): ensure retry handler unregisters old external location on 0x80073D0B --- windows/packaging/msi/Register-ShareTarget.ps1 | 4 +++- windows/scripts/register-dev.ps1 | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/windows/packaging/msi/Register-ShareTarget.ps1 b/windows/packaging/msi/Register-ShareTarget.ps1 index eedc072..942d146 100644 --- a/windows/packaging/msi/Register-ShareTarget.ps1 +++ b/windows/packaging/msi/Register-ShareTarget.ps1 @@ -182,7 +182,9 @@ public static class UserenvNative { Add-AppxPackage -Path $msix -ExternalLocation $InstallRoot -ForceApplicationShutdown -ForceUpdateFromAnyVersion } catch { - Log "Add-AppxPackage initial attempt failed ($($_.Exception.Message)). Cleaning AppContainer profiles and retrying..." + Log "Add-AppxPackage initial attempt failed ($($_.Exception.Message)). Unregistering old package, cleaning AppContainer profiles and retrying..." + Get-AppxPackage -Name 'WeChatBridge.Windows.ShareTarget' -ErrorAction SilentlyContinue | + Remove-AppxPackage -ErrorAction SilentlyContinue Clear-AppContainerProfiles Add-AppxPackage -Path $msix -ExternalLocation $InstallRoot -ForceApplicationShutdown -ForceUpdateFromAnyVersion } diff --git a/windows/scripts/register-dev.ps1 b/windows/scripts/register-dev.ps1 index f499215..1c89154 100644 --- a/windows/scripts/register-dev.ps1 +++ b/windows/scripts/register-dev.ps1 @@ -152,6 +152,8 @@ try { Add-AppxPackage -Path $packagePath -ExternalLocation $installRoot -ForceApplicationShutdown -ForceUpdateFromAnyVersion } catch { + Get-AppxPackage -Name 'WeChatBridge.Windows.ShareTarget' -ErrorAction SilentlyContinue | + Remove-AppxPackage -ErrorAction SilentlyContinue Clear-AppContainerProfiles try { Add-AppxPackage -Path $packagePath -ExternalLocation $installRoot -ForceApplicationShutdown -ForceUpdateFromAnyVersion