Repository navigation
Lite and the Darling Viewer: every clipboard write retries instead of crashing when the clipboard is busy (#4594) - #4600
Merged
Conversation
… on a truncated single-statement copy The Windows clipboard is a shared resource; another process holding it briefly (a clipboard manager, Office, RDP) makes an unguarded write throw and crash the app, the same failure #2833 already fixed for reads. ClipboardText gains a guarded TrySetText, mirroring TryRead's bounded retry, and every Clipboard.SetText/SetDataObject call in the shared plan viewer, blocking chain, deadlock graph, and DataGrid export helpers now routes through it. Copy Query Text on a truncated single-statement plan now hands back the full query the plan was captured from instead of the plan's own 4,000-character showplan-capped copy, matching PerformanceStudio's fix. The guard counts statements the way the Statements grid counts them (descending into stored procedure and UDF bodies), not by outer batch, so a plan captured around EXEC dbo.SomeProc is correctly treated as multi-statement and never hands back the outer EXEC for a truncated body statement. Fixes #4582
…ling Viewer through the guarded writer (#4594) Adds ClipboardText.TrySetDataObject (a SetDataObject sibling to #4593's TrySetText, same bounded retry) and routes every remaining Clipboard.SetText / Clipboard.SetDataObject call in Lite and the Darling Viewer through it, so a busy clipboard retries instead of crashing the app. Sites that already showed a status line on success now show a short failure message instead of silently dropping the copy; sites with no prior status handling keep quietly swallowing a failed copy, matching TrySetText's own behavior. Adds ClipboardWriteCensusTests: no source file under Lite/, Darling/ or PerformanceMonitor.Ui/, other than ClipboardText.cs itself, may call Clipboard.SetText( or Clipboard.SetDataObject( in code.
…oard # Conflicts: # PerformanceMonitor.PlanAnalysis/PlanDisplayText.cs # PerformanceMonitor.Ui/ClipboardText.cs
…dText.TrySetText Merging dev brought in a properties-panel copy path with its own bare-try guard around Clipboard.SetText, added after #4593/#4600 branched. It didn't route through the shared ClipboardText helper, so ClipboardWriteCensusTests failed post-merge. Delegate to ClipboardText.TrySetText instead of duplicating the guard.
erikdarlingdata
marked this pull request as ready for review
September 28, 2026 16:35
This was referenced Sep 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4594. Builds on #4593.
What changed
#4593 added
ClipboardText.TrySetText(inPerformanceMonitor.Ui), a bounded-retry guard aroundClipboard.SetTextthat returnsfalseinstead of throwing when another process momentarily holds theWindows clipboard, and routed the shared plan viewer, blocking chain, deadlock graph and grid-export
copies through it. This PR routes the remaining 18 unguarded clipboard writes in Lite and the Darling
Viewer through the same guard.
ClipboardText.TrySetDataObject(object data, bool copy = false), aSetDataObjectsibling toTrySetTextwith the identical bounded retry, for call sites that useSetDataObject(the existingconvention to avoid WPF's
Clipboard.Flush()) and for the chart bitmap copy, which has no textequivalent.
TrySetText/TrySetDataObject:Helpers/ContextMenuHelper.cs(4 sites, including the chart bitmap copy),Controls/ServerTab.CopyExport.cs,Controls/RecommendationsTab.xaml.cs(2),Windows/SettingsWindow.xaml.cs,Windows/AlertDetailWindow.xaml.cs,Windows/EntraDeviceCodeWindow.xaml.cs.MainWindow.xaml.cs(3),ViewerServerTab.CopyExport.cs,ViewerServerTab.ChartContextMenu.cs,SettingsWindow.xaml.cs(2),AlertDetailWindow.xaml.cs.the MCP command/URL copy buttons), a failed copy now shows "Couldn't copy: the clipboard is in use."
instead of silently dropping it. Where a site had no prior status handling (the grid/row/cell/chart
copies, the alert detail T-SQL copy, the device-code copy), a failed copy is still swallowed quietly,
matching
TrySetText's own default behavior for those paths.Darling/Darling.Tests/ClipboardWriteCensusTests.cs: walks every.csfile underLite/,Darling/andPerformanceMonitor.Ui/(viaCSharpSourceWalker, so comments/strings can't trip orhide it;
deprecated/excluded) and fails if any file other thanClipboardText.csitself still callsClipboard.SetText(orClipboard.SetDataObject(.RED on the pre-fix code
Checked out the census test alone against
2b5d2b3fe(the #4593 tip this branch is stacked on) in athrowaway worktree. It failed, listing the 11 files carrying the 18 unguarded sites:
On this branch's code, the same test passes.
Build and test
Built (macOS,
-p:EnableWindowsTargeting=true,-v q, all 0 warnings / 0 errors):Darling.Tests,Lite/PerformanceMonitorLite.csproj,Lite.Tests,PerformanceMonitor.Darling.Viewer,PerformanceMonitor.Ui.Ran
Darling.Tests.dllin-process (strippedMicrosoft.WindowsDesktop.Appfrom the runtimeconfig):ClipboardWriteCensusTests,Viewer4582Tests,CommentFilterAdoptionTests,DocCommentHygieneTests,and every
*Census*class in the tree (DailyDeadlockWindowCensusTests,EntraProviderPackageCensusTests,FileGrowthRiseUnitCensusTests,McpPayloadContractCensusTests,McpServiceParameterDiSeatCensusTests,MeasurementContractCensusTests,MigrationDataMovingRungCensusPins,PgSettingScrubCandidateCensusTests,PlanForceActionDetailCensus,PlanForceActionDetailCensusTests,RemoteCollectorServiceCancellationCensusTests,SameStatementPileupSourceCensusTests,StoreApplicationNameCensusTests,StoreSessionTimeZonePinCensusTests,WebExceptionTextCensusTests,McpToolsListBudgetTests): Total:360, Errors: 0, Failed: 0, Skipped: 1, Not Run: 0.
Lite.Testsbuilds but does not run on macOS (net10.0-windows,PresentationFramework); no test in thisPR's scope touches Lite.Tests directly (Lite's product code changes are the clipboard call sites only, no
new members/log text), so no Lite.Tests class needed a by-hand read.
CHANGELOG
SECTION: Fixed
ENTRY: - Copying from Lite or the Darling Viewer no longer crashes when another program holds the clipboard ([#4600]) - Every remaining unguarded clipboard write (recommendation copy/AI-prompt buttons, MCP command/URL copy, grid/row/cell copy, chart image copy, alert detail T-SQL copy, sign-in device-code copy) now retries briefly instead of throwing when another process is holding the clipboard.
REF: [#4600]: #4600