Skip to content

msi: eliminate Warning 1946 dialog by moving shortcut properties to custom action#40219

Open
yeelam-gordon wants to merge 5 commits intomasterfrom
fix/shortcut-property-retry
Open

msi: eliminate Warning 1946 dialog by moving shortcut properties to custom action#40219
yeelam-gordon wants to merge 5 commits intomasterfrom
fix/shortcut-property-retry

Conversation

@yeelam-gordon
Copy link
Copy Markdown
Contributor

@yeelam-gordon yeelam-gordon commented Apr 17, 2026

Summary

Eliminates Warning 1946 ΓÇö the modal dialog popup (or console warning) during WSL installation caused by shortcut property sharing violations.

Before

During MSI install, the engine sets shortcut properties via the MsiShortcutProperty table. If any process holds the .lnk file open (search indexer, AV, PowerToys Run, shell extensions), ShortcutPropertyCreate fails with ERROR_SHARING_VIOLATION (HRESULT 32):

  • Double-click .msi: Modal dialog popup blocks the install until user clicks OK
  • wsl --update: Warning text printed to console

Warning 1946 dialog

This affects any process holding the file ΓÇö not just PowerToys (#12759 reported it without PowerToys installed).

After

  • No Warning 1946 ΓÇö ShortcutProperty entries removed from WiX, so the MSI engine never attempts ShortcutPropertyCreate
  • Properties still set ΓÇö new RepairShortcutProperties deferred custom action sets all properties via COM IPropertyStore with non-exclusive access (STGM_SHARE_DENY_NONE) and exponential backoff retry (50msΓåÆ100msΓåÆ200ms...ΓåÆ1s cap, 5s total timeout)
  • No modal dialog, no console warning, no user impact

Properties handled

Shortcut Property Value
WSL.lnk System.AppUserModel.ID Microsoft.WSL
WSL.lnk System.AppUserModel.ToastActivatorCLSID {2B9C59C3-...}
WSL Settings.lnk System.AppUserModel.IsSystemComponent true

Why not just suppress the warning?

  • MsiSetExternalUI callback suppression only works for wsl --update (INSTALLUILEVEL_NONE)
  • Direct .msi double-click uses the MSI engine's built-in UI ΓÇö no way to suppress the modal dialog from the package side
  • EmbeddedUI could work but requires a new DLL for filtering one warning
  • Removing ShortcutProperty entries is the only approach that eliminates the warning across all install paths

Lifecycle coverage

Scenario CA runs? Correct?
Fresh install Yes Sets properties on new shortcuts
Major upgrade Yes Old MSI uninstalls (skips), new MSI installs (runs)
Uninstall No Shortcuts deleted by RemoveFile
Repair Yes Re-applies properties
Same-version reinstall Yes Idempotent

Testing

8 real MSI install scenarios with active FileSystemWatcher contention — each ran full uninstall→lock→install→verify cycle:

[PASS] Scenario 1 : No contention          (MSI=0 W1946=0 AppId=Microsoft.WSL)
[PASS] Scenario 2 : Old PowerToys Run      (MSI=0 W1946=0 AppId=Microsoft.WSL)
[PASS] Scenario 3 : New PowerToys Run      (MSI=0 W1946=0 AppId=Microsoft.WSL)
[PASS] Scenario 4 : AV deep scan           (MSI=0 W1946=0 AppId=Microsoft.WSL)
[PASS] Scenario 5 : Search indexer         (MSI=0 W1946=0 AppId=Microsoft.WSL)
[PASS] Scenario 6 : Another installer      (MSI=0 W1946=0 AppId=Microsoft.WSL)
[PASS] Scenario 7 : Slow process 500ms     (MSI=0 W1946=0 AppId=Microsoft.WSL)
[PASS] Scenario 8 : Unknown process #12759 (MSI=0 W1946=0 AppId=Microsoft.WSL)
TOTAL: 8 passed, 0 failed

Each scenario verified: no Warning 1946 in MSI log, CA returned success, MSI exit 0, AppUserModel.ID and ToastActivatorCLSID set correctly via COM IPropertyStore read-back.

Test script: test/shortcut_property_test/Test-AllScenariosMsi.ps1 (run as admin)

PR Checklist

Changes

File Change
msipackage/package.wix.in Remove ShortcutProperty entries, add RepairShortcutProperties CA declaration + sequence
src/windows/wslinstall/DllMain.cpp RepairShortcutProperties + SetShortcutPropertyWithRetry implementation
src/windows/wslinstall/wslinstall.def Export RepairShortcutProperties
src/windows/wslinstall/CMakeLists.txt Link propsys.lib
test/shortcut_property_test/ Comprehensive MSI contention test

@yeelam-gordon yeelam-gordon requested a review from a team as a code owner April 17, 2026 09:02
Copilot AI review requested due to automatic review settings April 17, 2026 09:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the WSL MSI installer to eliminate MSI Warning 1946 dialogs/warnings caused by MsiShortcutProperty updates failing under .lnk sharing contention, by moving shortcut property setting into a deferred custom action that retries via IPropertyStore.

Changes:

  • Removes WiX ShortcutProperty entries for WSL shortcuts and adds a deferred RepairShortcutProperties custom action scheduled in InstallExecuteSequence.
  • Implements RepairShortcutProperties and a retry helper in wslinstall.dll to set shortcut properties via COM with backoff on sharing/lock violations.
  • Adds a PowerShell test harness for running install/uninstall scenarios under simulated shortcut file contention.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
msipackage/package.wix.in Drops ShortcutProperty table usage and schedules new RepairShortcutProperties custom action.
src/windows/wslinstall/DllMain.cpp Adds COM-based shortcut property repair helper + exported custom action implementation.
src/windows/wslinstall/wslinstall.def Exports RepairShortcutProperties from wslinstall.dll.
src/windows/wslinstall/CMakeLists.txt Links propsys.lib for property system APIs.
test/shortcut_property_test/Test-AllScenariosMsi.ps1 Adds a contention-oriented MSI install scenario runner script.
test/shortcut_property_test/.gitignore Ignores temp/log artifacts generated by the new test script.

Comment thread src/windows/wslinstall/DllMain.cpp Outdated
Comment thread src/windows/wslinstall/DllMain.cpp Outdated
Comment thread src/windows/wslinstall/DllMain.cpp Outdated
Comment thread test/shortcut_property_test/Test-AllScenariosMsi.ps1 Outdated
Comment thread src/windows/wslinstall/DllMain.cpp
@yeelam-gordon yeelam-gordon force-pushed the fix/shortcut-property-retry branch 2 times, most recently from 65b6191 to 67bbfb6 Compare April 17, 2026 09:59
Copilot AI review requested due to automatic review settings April 17, 2026 09:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread src/windows/wslinstall/DllMain.cpp Outdated
Comment thread src/windows/wslinstall/DllMain.cpp Outdated
Comment thread src/windows/wslinstall/DllMain.cpp Outdated
…ustom action

Warning 1946 occurs when the MSI engine's built-in ShortcutPropertyCreate
fails due to a sharing violation on the .lnk file. When run with UI
(double-clicking the .msi), this shows a MODAL DIALOG that blocks the
install until the user clicks OK. When run via wsl --update, it prints
a warning to the console. Both are disruptive to users.

Root cause: any process holding the .lnk file open (search indexer, AV,
PowerToys Run, shell extensions) races with MsiShortcutProperty writes.
The MSI engine has no retry logic and no way to suppress the dialog.

Fix: Remove all <ShortcutProperty> declarations from the WiX file so
the MSI engine never attempts ShortcutPropertyCreate (eliminating the
warning/dialog source entirely). Instead, a new RepairShortcutProperties
deferred custom action sets all shortcut properties using COM
IPropertyStore with:
- STGM_READWRITE | STGM_SHARE_DENY_NONE (non-exclusive file access)
- Retry loop (10 attempts, 100ms delay) on sharing violations
- Best-effort (returns NOERROR even on failure, with TraceLogging)

Properties handled:
- WSL.lnk: AppUserModel.ID, AppUserModel.ToastActivatorCLSID
- WSL Settings.lnk: AppUserModel.IsSystemComponent

Also suppress Warning 1946 in MsiMessageCallback for the wsl --update
console path as a defense-in-depth measure.

Refs: #13469, #11276, #12759

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread msipackage/package.wix.in Outdated
Comment thread src/windows/wslinstall/DllMain.cpp Outdated
Comment thread src/windows/wslinstall/DllMain.cpp Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread msipackage/package.wix.in
yeelam-gordon and others added 2 commits April 17, 2026 20:10
Replace GetProductInfo(LOWORD(GetVersion()), ...) || true with a simple
block scope. The actual workstation check already uses GetVersionExW,
so the outer GetProductInfo call was dead code that triggered C4996.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Document that the custom action repairs both WSL.lnk and WSL Settings.lnk
shortcuts, with references to the relevant GitHub issues.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 17, 2026 12:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread src/windows/wslinstall/DllMain.cpp Outdated
Copy link
Copy Markdown
Member

@benhillis benhillis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test to InstallerTests that exhibits this behavior.

@ptrivedi
Copy link
Copy Markdown

@yeelam-gordon: the copilot comment about GetVersionExW is worth looking at. LGTM otherwise, once the InstallerTests related comment is addressed

Copy link
Copy Markdown
Collaborator

@OneBlue OneBlue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit conflicted on what we should do here. This warning isn't great, but it's very likely caused by another application locking our shortcut.

The warning isn't great, but it's also non-fatal, and only requires user action when the MSI is installation manually, which is relatively rare.

Another parameter to this is that installer changes are high-risk. For instance a bug that would crash the installer during a custom action could leave the user's machine in a broken state, which has a very high potential of negative impact.

Additionally, this new logic doesn't actually solve the issue, but only retries for a given amount of time, and then fails without warning, which would make root causing this harder.

Taking all of this into account, my opinion would be to leave the warning as-is for now and look into root-causing the issue if we truly want to eliminate the warning.

@benhillis : what are you thoughts on this ?

Use the existing IsWindowsServer() helper (from VersionHelpers.h) instead
of deprecated GetVersionExW for workstation detection (per ptrivedi/copilot).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@yeelam-gordon
Copy link
Copy Markdown
Contributor Author

Addressed review feedback in latest push:

  • @ptrivedi / @copilot-pull-request-reviewer (GetVersionExW): Replaced with \IsWindowsServer()\ from VersionHelpers.h — already used elsewhere in this file (line 749). Cleaner, no deprecation warning, no pragma suppress needed ✓

…etsShortcutProperties, InstallSetsShortcutPropertiesUnderContention)

Agent-Logs-Url: https://github.com/microsoft/WSL/sessions/cdab304d-bddc-494b-8a86-c6d5cfdafd1b

Co-authored-by: yeelam-gordon <73506701+yeelam-gordon@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 21, 2026 11:22
@yeelam-gordon yeelam-gordon review requested due to automatic review settings April 21, 2026 11:22
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 21, 2026

Please add a test to InstallerTests that exhibits this behavior.

Added two tests in commit ee97abb:

  1. InstallSetsShortcutProperties — does a fresh uninstall+install and verifies via IPropertyStore that WSL.lnk has System.AppUserModel.ID = "Microsoft.WSL" and System.AppUserModel.ToastActivatorCLSID set to the expected CLSID.

  2. InstallSetsShortcutPropertiesUnderContention — after a fresh install, holds a write-denying lock on WSL.lnk (simulating a search indexer / AV / PowerToys Run), then calls the exported RepairShortcutProperties from wslinstall.dll directly. A background thread releases the lock after 500 ms, exercising the exponential-backoff retry loop. Verifies the function returns NOERROR and properties are set correctly.

Also added propsys.lib to test/windows/CMakeLists.txt for PropVariantClear / IPropertyStore linkage.

@ptrivedi
Copy link
Copy Markdown

I'm a bit conflicted on what we should do here. This warning isn't great, but it's very likely caused by another application locking our shortcut.

The warning isn't great, but it's also non-fatal, and only requires user action when the MSI is installation manually, which is relatively rare.

Another parameter to this is that installer changes are high-risk. For instance a bug that would crash the installer during a custom action could leave the user's machine in a broken state, which has a very high potential of negative impact.

Additionally, this new logic doesn't actually solve the issue, but only retries for a given amount of time, and then fails without warning, which would make root causing this harder.

Taking all of this into account, my opinion would be to leave the warning as-is for now and look into root-causing the issue if we truly want to eliminate the warning.

@benhillis : what are you thoughts on this ?

Isn't the root cause a third-party process locking the .lnk? I understand it would be good to find what process, but we've seen that it could be different ones. My thought is, do these properties have to be set in the installer? What if they are applied later during startup or something? Do they need the banner etc. on the very first install before WSL has even run?

@benhillis
Copy link
Copy Markdown
Member

This is a ton of code to work around what seems to be an issue with the MSI infrastructure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants