diff --git a/scripts/test_windows_input.ps1 b/scripts/test_windows_input.ps1 index 6ee042aa..7396bffa 100644 --- a/scripts/test_windows_input.ps1 +++ b/scripts/test_windows_input.ps1 @@ -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 { diff --git a/scripts/windows_input/Native.cs b/scripts/windows_input/Native.cs index decbc58b..42b387b1 100644 --- a/scripts/windows_input/Native.cs +++ b/scripts/windows_input/Native.cs @@ -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(); }