test(windows): safely clean failed clipboard leases

refs #4314
This commit is contained in:
Jonathan Liebig
2026-09-18 04:33:06 +02:00
parent 190eff4fc8
commit 4370befbe2
2 changed files with 24 additions and 16 deletions
+2 -2
View File
@@ -352,7 +352,7 @@ try {
if ($captureTrace.Contains('transport=win32-serialized')) { $row.input_transport = 'win32-serialized' }
}
if ($null -ne $clipboardSequence) {
if (-not [HerdrInputGauntlet.Desktop]::ClearOwnedClipboard($clipboardSequence)) { throw 'Could not clear test-owned clipboard' }
if (-not [HerdrInputGauntlet.Desktop]::ClearOwnedClipboard($window, $clipboardSequence)) { throw 'Could not clear test-owned clipboard' }
$clipboardSequence = $null
}
if ($mouseReporting) { $null = Observer-Request $plan 'mouse-off'; $mouseReporting = $false }
@@ -374,7 +374,7 @@ try {
if ($null -ne $cursorPosition -and [HerdrInputGauntlet.Desktop]::GetForegroundWindow() -eq $window) {
[HerdrInputGauntlet.Desktop]::RestoreCursor($cursorPosition); $cursorPosition = $null
}
if ($null -ne $clipboardSequence -and -not [HerdrInputGauntlet.Desktop]::ClearOwnedClipboard($clipboardSequence)) { $document.cleanup_errors += 'Could not clear test-owned clipboard' }
if ($null -ne $clipboardSequence -and -not [HerdrInputGauntlet.Desktop]::ClearOwnedClipboard($window, $clipboardSequence)) { $document.cleanup_errors += 'Could not clear test-owned clipboard' }
[IO.File]::WriteAllText((Join-Path $work 'probe-stop'), '')
if (Test-Path (Join-Path $work 'ready.json')) {
try {
+22 -14
View File
@@ -143,6 +143,7 @@ namespace HerdrInputGauntlet {
}
public static uint SetEmptyClipboard(IntPtr owner,string text) {
if(owner==IntPtr.Zero) throw new Exception("Clipboard owner is required");
if(!OpenClipboard(owner)) throw new Exception("Clipboard busy; refusing replacement");
IntPtr memory=IntPtr.Zero;
try {
@@ -156,13 +157,7 @@ namespace HerdrInputGauntlet {
if(!EmptyClipboard() || SetClipboardData(13,memory)==IntPtr.Zero) throw new Exception("Clipboard write failed");
memory=IntPtr.Zero; // ownership transferred to Windows
} finally { if(memory!=IntPtr.Zero) GlobalFree(memory); CloseClipboard(); }
// Closing can synthesize additional formats and advance the sequence.
// Reopen before adopting it so another writer cannot become our lease.
if(!OpenClipboard(owner)) throw new Exception("Clipboard busy; cannot establish test ownership");
try {
if(owner==IntPtr.Zero || GetClipboardOwner()!=owner) throw new Exception("Clipboard ownership changed; refusing cleanup lease");
return GetClipboardSequenceNumber();
} finally { CloseClipboard(); }
return AdoptClipboardLease(owner);
}
public static uint SetEmptyClipboardImage(IntPtr owner,string text) {
if(owner==IntPtr.Zero) throw new Exception("Clipboard owner is required");
@@ -198,15 +193,28 @@ namespace HerdrInputGauntlet {
if(textMemory!=IntPtr.Zero) GlobalFree(textMemory);
CloseClipboard();
}
if(!OpenClipboard(owner)) throw new Exception("Clipboard busy; cannot establish test ownership");
try {
if(owner==IntPtr.Zero || GetClipboardOwner()!=owner) throw new Exception("Clipboard ownership changed; refusing cleanup lease");
return GetClipboardSequenceNumber();
} finally { CloseClipboard(); }
return AdoptClipboardLease(owner);
}
public static bool ClearOwnedClipboard(uint sequence) {
static uint AdoptClipboardLease(IntPtr owner) {
// Closing can synthesize additional formats and advance the sequence.
uint sequence=GetClipboardSequenceNumber();
try {
if(!OpenClipboard(owner)) throw new Exception("Clipboard busy; cannot establish test ownership");
try {
if(GetClipboardOwner()!=owner || GetClipboardSequenceNumber()!=sequence)
throw new Exception("Clipboard ownership changed; refusing cleanup lease");
return sequence;
} finally { CloseClipboard(); }
} catch(Exception error) {
if(!ClearOwnedClipboard(owner,sequence))
throw new Exception("Could not clean clipboard after ownership verification failed",error);
throw;
}
}
public static bool ClearOwnedClipboard(IntPtr owner,uint sequence) {
if(owner==IntPtr.Zero) return false;
if(!OpenClipboard(IntPtr.Zero)) return false;
try { return GetClipboardSequenceNumber()!=sequence || EmptyClipboard(); }
try { return GetClipboardSequenceNumber()!=sequence || GetClipboardOwner()!=owner || EmptyClipboard(); }
finally { CloseClipboard(); }
}
public static string Title(IntPtr hwnd) { var text=new StringBuilder(1024); GetWindowText(hwnd,text,text.Capacity); return text.ToString(); }