-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Add a shorter timeout for retrieving clipboard data #81339
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,24 +5,28 @@ | |
| using System; | ||
| using System.Composition; | ||
| using System.Runtime.InteropServices; | ||
| using System.Threading; | ||
|
|
||
| // Use of System.Windows.Forms over System.Windows is intentional here. S.W.F has logic in its clipboard impl to help | ||
| // with common errors. | ||
| using System.Windows.Forms; | ||
| using Microsoft.CodeAnalysis.ErrorReporting; | ||
| using Microsoft.CodeAnalysis.Host.Mef; | ||
| using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.Editor.StringCopyPaste; | ||
|
|
||
| [ExportWorkspaceService(typeof(IStringCopyPasteService), ServiceLayer.Host), Shared] | ||
| internal sealed class WpfStringCopyPasteService : IStringCopyPasteService | ||
| [method: ImportingConstructor] | ||
| [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] | ||
| internal sealed class WpfStringCopyPasteService() : IStringCopyPasteService | ||
| { | ||
| private const string RoslynFormat = nameof(RoslynFormat); | ||
| // Similar to what WinForms does, except that instead of blocking for up to 1s, we only block for up to 250ms. | ||
| // https://github.com/dotnet/winforms/blob/0f76e65878b1a0958175f17c4360b8198f8b36ba/src/System.Windows.Forms/src/System/Windows/Forms/Clipboard.cs#L31 | ||
| private const int RetryTimes = 5; | ||
| private const int RetryDelay = 50; | ||
|
|
||
| [ImportingConstructor] | ||
| [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] | ||
| public WpfStringCopyPasteService() | ||
| { | ||
| } | ||
| private const string RoslynFormat = nameof(RoslynFormat); | ||
|
|
||
| private static string GetFormat(string key) | ||
| => $"{RoslynFormat}-{key}"; | ||
|
|
@@ -33,7 +37,9 @@ public bool TrySetClipboardData(string key, string data) | |
|
|
||
| try | ||
| { | ||
| var dataObject = Clipboard.GetDataObject(); | ||
| var dataObject = GetDataObject(); | ||
| if (dataObject is null) | ||
| return false; | ||
|
|
||
| var copy = new DataObject(); | ||
|
|
||
|
|
@@ -45,9 +51,7 @@ public bool TrySetClipboardData(string key, string data) | |
|
|
||
| copy.SetData(GetFormat(key), data); | ||
|
|
||
| // Similar to what WinForms does, except that instead of blocking for up to 1s, we only block for up to 250ms. | ||
| // https://github.com/dotnet/winforms/blob/0f76e65878b1a0958175f17c4360b8198f8b36ba/src/System.Windows.Forms/src/System/Windows/Forms/Clipboard.cs#L31 | ||
| Clipboard.SetDataObject(copy, copy: false, retryTimes: 5, retryDelay: 50); | ||
| Clipboard.SetDataObject(copy, copy: false, RetryTimes, RetryDelay); | ||
| return true; | ||
| } | ||
| catch (ExternalException ex) when ((uint)ex.ErrorCode == CLIPBRD_E_CANT_OPEN) | ||
|
|
@@ -62,11 +66,58 @@ public bool TrySetClipboardData(string key, string data) | |
| return false; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Similar to <see cref="Clipboard.GetDataObject"/> except that this will only block a max of 250ms, not a full second. | ||
| /// </summary> | ||
| private static IDataObject? GetDataObject() | ||
| => GetDataObject(RetryTimes, RetryDelay); | ||
|
|
||
| /// <summary> | ||
| /// Copied from https://github.com/dotnet/winforms/blob/0f76e65878b1a0958175f17c4360b8198f8b36ba/src/System.Windows.Forms/src/System/Windows/Forms/Clipboard.cs#L139 | ||
| /// </summary> | ||
| private static IDataObject? GetDataObject(int retryTimes, int retryDelay) | ||
| { | ||
| IComDataObject? dataObject = null; | ||
| int hr; | ||
| var retry = retryTimes; | ||
| do | ||
| { | ||
| hr = OleGetClipboard(ref dataObject); | ||
| if (hr != 0) | ||
| { | ||
| if (retry == 0) | ||
| return null; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We caught the throw and returned null. So this has the same effect. It means we don't support rich copy/paste semantics. But that's better than timing out for 1 second :D
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I should have looked further down. |
||
|
|
||
| retry--; | ||
| Thread.Sleep(millisecondsTimeout: retryDelay); | ||
| } | ||
| } | ||
| while (hr != 0); | ||
|
|
||
| if (dataObject is not null) | ||
| { | ||
| if (dataObject is IDataObject ido && !Marshal.IsComObject(dataObject)) | ||
| { | ||
| return ido; | ||
| } | ||
|
|
||
| return new DataObject(dataObject); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| [DllImport("ole32.dll", ExactSpelling = true)] | ||
| public static extern int OleGetClipboard(ref IComDataObject? data); | ||
|
|
||
| public string? TryGetClipboardData(string key) | ||
| { | ||
| try | ||
| { | ||
| var dataObject = Clipboard.GetDataObject(); | ||
| var dataObject = GetDataObject(); | ||
| if (dataObject is null) | ||
| return null; | ||
|
|
||
| var format = GetFormat(key); | ||
| if (dataObject.GetDataPresent(format)) | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have https://github.com/dotnet/roslyn/blob/b9f26f0e3e3b81c8a0ff10449c36f6e579779b82/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs which you might be able to unify this code with.