-
Notifications
You must be signed in to change notification settings - Fork 184
Harden clipboard writes against "clipboard in use" failures #649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
ce04231
Harden clipboard writes against "clipboard in use" failures
609ccb6
Clean up clipboard hardening after review
888fee3
update claude code, for better quality
8e00a2e
chore: update plugin hashes [skip ci]
github-actions[bot] e153e6b
update
f865495
Merge branch 'clipboard-hardening' of https://github.com/LogExperts/L…
b53d6d6
chore: update plugin hashes [skip ci]
github-actions[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| using LogExpert.UI.Extensions; | ||
|
|
||
| using NUnit.Framework; | ||
|
|
||
| using Vanara.PInvoke; | ||
|
|
||
| namespace LogExpert.Tests.Extensions; | ||
|
|
||
| /// <summary> | ||
| /// Tests for the hardened clipboard writes (see issue with "Requested Clipboard operation | ||
| /// did not succeed" / ExternalException, previously reported in #195). When another | ||
| /// application holds the clipboard open, LogExpert must report failure instead of crashing. | ||
| /// </summary> | ||
| [TestFixture] | ||
| [Apartment(ApartmentState.STA)] | ||
| [NonParallelizable] | ||
| [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Unit Tests")] | ||
| public class ClipboardHelperTests | ||
| { | ||
| [Test] | ||
| public void TrySetText_ClipboardAvailable_PlacesTextAndReturnsTrue () | ||
| { | ||
| var ok = ClipboardHelper.TrySetText("LogExpert clipboard test"); | ||
|
|
||
| Assert.That(ok, Is.True); | ||
| Assert.That(GetClipboardTextWithRetry(), Is.EqualTo("LogExpert clipboard test")); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Reads the clipboard text, retrying briefly. Clipboard managers and similar tools | ||
| /// open the clipboard to inspect new content right after it changes, which can make an | ||
| /// immediate read-back fail or come up empty even though the write succeeded. | ||
| /// </summary> | ||
| private static string GetClipboardTextWithRetry () | ||
| { | ||
| for (var i = 0; i < 20; i++) | ||
| { | ||
| var text = Clipboard.GetText(); | ||
| if (!string.IsNullOrEmpty(text)) | ||
| { | ||
| return text; | ||
| } | ||
|
|
||
| Thread.Sleep(100); | ||
| } | ||
|
|
||
| return string.Empty; | ||
| } | ||
|
|
||
| [Test] | ||
| public void TrySetText_ClipboardHeldByAnotherWindow_ReturnsFalseInsteadOfThrowing () | ||
| { | ||
| using ClipboardLock clipboardLock = new(); | ||
|
|
||
| var ok = ClipboardHelper.TrySetText("some text"); | ||
|
|
||
| Assert.That(ok, Is.False); | ||
| } | ||
|
|
||
| [Test] | ||
| public void TrySetDataObject_ClipboardAvailable_PlacesDataAndReturnsTrue () | ||
| { | ||
| var ok = ClipboardHelper.TrySetDataObject("LogExpert data object test"); | ||
|
|
||
| Assert.That(ok, Is.True); | ||
| Assert.That(GetClipboardTextWithRetry(), Is.EqualTo("LogExpert data object test")); | ||
| } | ||
|
|
||
| [Test] | ||
| public void TrySetDataObject_ClipboardHeldByAnotherWindow_ReturnsFalseInsteadOfThrowing () | ||
| { | ||
| using ClipboardLock clipboardLock = new(); | ||
|
|
||
| var ok = ClipboardHelper.TrySetDataObject("some data"); | ||
|
|
||
| Assert.That(ok, Is.False); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Holds the Win32 clipboard open from a background thread (without closing it), | ||
| /// which makes every clipboard access in other threads/processes fail — the same | ||
| /// situation an external clipboard-monitoring tool causes. | ||
| /// </summary> | ||
| private sealed class ClipboardLock : IDisposable | ||
| { | ||
| private readonly Thread _thread; | ||
| private readonly ManualResetEventSlim _acquired = new(false); | ||
| private readonly ManualResetEventSlim _release = new(false); | ||
|
|
||
| public ClipboardLock () | ||
| { | ||
| _thread = new Thread(() => | ||
| { | ||
| if (!User32.OpenClipboard(HWND.NULL)) | ||
| { | ||
| throw new InvalidOperationException("Test setup failed: could not open the clipboard"); | ||
| } | ||
|
|
||
| _acquired.Set(); | ||
| _release.Wait(); | ||
| _ = User32.CloseClipboard(); | ||
|
Hirogen marked this conversation as resolved.
Dismissed
|
||
| }) | ||
| { | ||
| IsBackground = true | ||
| }; | ||
|
|
||
| _thread.Start(); | ||
| _ = _acquired.Wait(TimeSpan.FromSeconds(5)); | ||
| } | ||
|
|
||
| public void Dispose () | ||
| { | ||
| _release.Set(); | ||
| _ = _thread.Join(TimeSpan.FromSeconds(5)); | ||
| } | ||
| } | ||
| } | ||
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
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
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
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.