From 3ab1fb15b639f13fa46ab5fcfceb581e20b3682a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 19 Dec 2024 16:34:32 +0800 Subject: [PATCH] Fix clipboard action under sta thread issue --- .../NativeMethods.txt | 5 +- .../UserSettings/Settings.cs | 2 +- Flow.Launcher.Infrastructure/Win32Helper.cs | 141 ++++++++++++++++++ Flow.Launcher/PublicAPIInstance.cs | 45 +++--- 4 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 Flow.Launcher.Infrastructure/Win32Helper.cs diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt index f117534a1..d8777ff27 100644 --- a/Flow.Launcher.Infrastructure/NativeMethods.txt +++ b/Flow.Launcher.Infrastructure/NativeMethods.txt @@ -16,4 +16,7 @@ WM_KEYUP WM_SYSKEYDOWN WM_SYSKEYUP -EnumWindows \ No newline at end of file +EnumWindows + +OleInitialize +OleUninitialize \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 0c7de10fd..5493ad724 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -230,7 +230,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings [JsonIgnore] public ObservableCollection BuiltinShortcuts { get; set; } = new() { - new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText), + new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", () => Win32Helper.StartSTATaskAsync(Clipboard.GetText).Result), new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath) }; diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs new file mode 100644 index 000000000..76e9cfe5b --- /dev/null +++ b/Flow.Launcher.Infrastructure/Win32Helper.cs @@ -0,0 +1,141 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Windows.Win32; + +namespace Flow.Launcher.Infrastructure +{ + /// + /// Provides static helper for Win32. + /// Codes are edited from: https://github.com/files-community/Files. + /// + public static class Win32Helper + { + public static Task StartSTATaskAsync(Action action) + { + var taskCompletionSource = new TaskCompletionSource(); + Thread thread = new(() => + { + PInvoke.OleInitialize(); + + try + { + action(); + taskCompletionSource.SetResult(); + } + catch (System.Exception) + { + taskCompletionSource.SetResult(); + } + finally + { + PInvoke.OleUninitialize(); + } + }) + { + IsBackground = true, + Priority = ThreadPriority.Normal + }; + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return taskCompletionSource.Task; + } + + public static Task StartSTATaskAsync(Func func) + { + var taskCompletionSource = new TaskCompletionSource(); + Thread thread = new(async () => + { + PInvoke.OleInitialize(); + + try + { + await func(); + taskCompletionSource.SetResult(); + } + catch (System.Exception) + { + taskCompletionSource.SetResult(); + } + finally + { + PInvoke.OleUninitialize(); + } + }) + { + IsBackground = true, + Priority = ThreadPriority.Normal + }; + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return taskCompletionSource.Task; + } + + public static Task StartSTATaskAsync(Func func) + { + var taskCompletionSource = new TaskCompletionSource(); + + Thread thread = new(() => + { + PInvoke.OleInitialize(); + + try + { + taskCompletionSource.SetResult(func()); + } + catch (System.Exception) + { + taskCompletionSource.SetResult(default); + } + finally + { + PInvoke.OleUninitialize(); + } + }) + { + IsBackground = true, + Priority = ThreadPriority.Normal + }; + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return taskCompletionSource.Task; + } + + public static Task StartSTATaskAsync(Func> func) + { + var taskCompletionSource = new TaskCompletionSource(); + + Thread thread = new(async () => + { + PInvoke.OleInitialize(); + try + { + taskCompletionSource.SetResult(await func()); + } + catch (System.Exception) + { + taskCompletionSource.SetResult(default); + } + finally + { + PInvoke.OleUninitialize(); + } + }) + { + IsBackground = true, + Priority = ThreadPriority.Normal + }; + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return taskCompletionSource.Task; + } + } +} diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index f4712770d..dcdb798ff 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; @@ -117,35 +117,38 @@ namespace Flow.Launcher ShellCommand.Execute(startInfo); } - public void CopyToClipboard(string stringToCopy, bool directCopy = false, bool showDefaultNotification = true) + public async void CopyToClipboard(string stringToCopy, bool directCopy = false, bool showDefaultNotification = true) { if (string.IsNullOrEmpty(stringToCopy)) return; - var isFile = File.Exists(stringToCopy); - if (directCopy && (isFile || Directory.Exists(stringToCopy))) + await Win32Helper.StartSTATaskAsync(() => { - var paths = new StringCollection + var isFile = File.Exists(stringToCopy); + if (directCopy && (isFile || Directory.Exists(stringToCopy))) { - stringToCopy - }; + var paths = new StringCollection + { + stringToCopy + }; - Clipboard.SetFileDropList(paths); + Clipboard.SetFileDropList(paths); - if (showDefaultNotification) - ShowMsg( - $"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}", - GetTranslation("completedSuccessfully")); - } - else - { - Clipboard.SetDataObject(stringToCopy); + if (showDefaultNotification) + ShowMsg( + $"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}", + GetTranslation("completedSuccessfully")); + } + else + { + Clipboard.SetDataObject(stringToCopy); - if (showDefaultNotification) - ShowMsg( - $"{GetTranslation("copy")} {GetTranslation("textTitle")}", - GetTranslation("completedSuccessfully")); - } + if (showDefaultNotification) + ShowMsg( + $"{GetTranslation("copy")} {GetTranslation("textTitle")}", + GetTranslation("completedSuccessfully")); + } + }); } public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible;