From 1c8c7b7a67f259507a3a86164b759efaeb46fcc8 Mon Sep 17 00:00:00 2001 From: rgdevment Date: Mon, 27 Apr 2026 11:18:47 -0400 Subject: [PATCH 1/2] feat: add msix startup task configuration and improve error logging --- app/lib/shell/msix_startup_task.dart | 10 +++++++--- app/pubspec.yaml | 5 +++++ app/windows/runner/startup_task_channel.cpp | 13 +++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/lib/shell/msix_startup_task.dart b/app/lib/shell/msix_startup_task.dart index 9b1bb593..39c1291f 100644 --- a/app/lib/shell/msix_startup_task.dart +++ b/app/lib/shell/msix_startup_task.dart @@ -41,7 +41,7 @@ class MsixStartupTask { return _parseState(raw); } on PlatformException catch (e) { AppLogger.error( - 'MsixStartupTask.getState failed: ${e.code} ${e.message}', + 'MsixStartupTask.getState failed: ${e.code} ${e.message} — ${e.details}', ); return null; } @@ -54,7 +54,9 @@ class MsixStartupTask { }); return _parseState(raw); } on PlatformException catch (e) { - AppLogger.error('MsixStartupTask.enable failed: ${e.code} ${e.message}'); + AppLogger.error( + 'MsixStartupTask.enable failed: ${e.code} ${e.message} — ${e.details}', + ); return null; } } @@ -66,7 +68,9 @@ class MsixStartupTask { }); return _parseState(raw); } on PlatformException catch (e) { - AppLogger.error('MsixStartupTask.disable failed: ${e.code} ${e.message}'); + AppLogger.error( + 'MsixStartupTask.disable failed: ${e.code} ${e.message} — ${e.details}', + ); return null; } } diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 547645b3..040e64d7 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -31,6 +31,11 @@ dev_dependencies: flutter_lints: ^6.0.0 msix: ^3.16.8 +msix_config: + startup_task: + task_id: CopyPasteStartup + enabled: true + flutter: uses-material-design: true diff --git a/app/windows/runner/startup_task_channel.cpp b/app/windows/runner/startup_task_channel.cpp index df43277c..409a1906 100644 --- a/app/windows/runner/startup_task_channel.cpp +++ b/app/windows/runner/startup_task_channel.cpp @@ -83,6 +83,16 @@ void RegisterStartupTaskChannel(flutter::FlutterViewController* controller) { winrt::init_apartment(); using namespace winrt::Windows::ApplicationModel; auto task = StartupTask::GetAsync(wtask_id).get(); + if (!task) { + shared_result->Error( + "task_not_found", + "StartupTask not found in manifest", + flutter::EncodableValue( + "No startup task with id '" + + winrt::to_string(wtask_id) + + "' is declared in the AppxManifest.")); + return; + } if (method == "getState") { shared_result->Success( flutter::EncodableValue(StateToString(task.State()))); @@ -101,6 +111,9 @@ void RegisterStartupTaskChannel(flutter::FlutterViewController* controller) { char code_buf[32]; snprintf(code_buf, sizeof(code_buf), "0x%08X", static_cast(e.code())); + // E_INVALIDARG (0x80070057) from GetAsync means the TaskId is not + // declared in the AppxManifest. Ensure windows.startupTask is + // present in the manifest with a matching TaskId attribute. shared_result->Error("winrt_error", code_buf, flutter::EncodableValue( winrt::to_string(e.message()))); From b84c7cb663cfaaa494fdc139bd748c0d683ac263 Mon Sep 17 00:00:00 2001 From: rgdevment Date: Mon, 27 Apr 2026 11:28:44 -0400 Subject: [PATCH 2/2] refactor: simplify onboarding logic for Windows and Linux; enhance clipboard listener registration --- app/lib/main.dart | 5 +++-- listener/windows/listener_plugin.cpp | 18 ++++++++++++++++++ listener/windows/listener_plugin.h | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index e47aa3ee..4df0d366 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -331,9 +331,9 @@ class _CopyPasteAppState extends State final isUpdate = _config.lastRunVersion != AppConfig.appVersion; final windowsNeedsOnboarding = - Platform.isWindows && (!_config.hasSeenOnboarding || isUpdate); + Platform.isWindows && !_config.hasSeenOnboarding; final linuxNeedsOnboarding = - Platform.isLinux && (!_config.hasCompletedOnboarding || isUpdate); + Platform.isLinux && !_config.hasCompletedOnboarding; final desktopNeedsOnboarding = windowsNeedsOnboarding || linuxNeedsOnboarding; final showOnStart = @@ -534,6 +534,7 @@ class _CopyPasteAppState extends State void _startListening() { if (!Platform.isWindows && !Platform.isMacOS && !Platform.isLinux) return; + AppLogger.info('_startListening: subscribing to clipboard event stream'); _listenerSubscription = widget.listener.onEvent.listen( _onClipboardEvent, onError: (Object e, StackTrace s) { diff --git a/listener/windows/listener_plugin.cpp b/listener/windows/listener_plugin.cpp index 83990054..46ad1adf 100644 --- a/listener/windows/listener_plugin.cpp +++ b/listener/windows/listener_plugin.cpp @@ -227,6 +227,8 @@ void ListenerPlugin::StartListening( std::lock_guard lock(sink_mutex_); sink_ = std::move(sink); + clipboard_format_registered_ = false; + HWND hwnd = registrar_->GetView() ? registrar_->GetView()->GetNativeWindow() : nullptr; @@ -236,6 +238,7 @@ void ListenerPlugin::StartListening( HWND topHwnd = hwnd ? GetAncestor(hwnd, GA_ROOT) : nullptr; if (topHwnd) { AddClipboardFormatListener(topHwnd); + clipboard_format_registered_ = true; } window_proc_id_ = registrar_->RegisterTopLevelWindowProcDelegate( @@ -258,6 +261,7 @@ void ListenerPlugin::StopListening() { KillTimer(topHwnd, kClipboardTimerId); RemoveClipboardFormatListener(topHwnd); } + clipboard_format_registered_ = false; std::lock_guard lock(sink_mutex_); sink_ = nullptr; @@ -265,6 +269,20 @@ void ListenerPlugin::StopListening() { std::optional ListenerPlugin::HandleWindowMessage( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + // Deferred clipboard registration: if AddClipboardFormatListener was not + // called during StartListening (e.g. window handle not ready at plugin-start + // time after an MSIX/standalone update), register it here on the first + // window message. The hwnd passed to RegisterTopLevelWindowProcDelegate is + // always the top-level window, so no GetAncestor() call is needed. + if (!clipboard_format_registered_) { + if (AddClipboardFormatListener(hwnd)) { + clipboard_format_registered_ = true; + OutputDebugStringA( + "[CopyPaste Listener] AddClipboardFormatListener registered " + "deferred (was not ready at StartListening)\n"); + } + } + if (message == WM_CLIPBOARDUPDATE) { KillTimer(hwnd, kClipboardTimerId); SetTimer(hwnd, kClipboardTimerId, kClipboardTimerDelayMs, nullptr); diff --git a/listener/windows/listener_plugin.h b/listener/windows/listener_plugin.h index 1608e2f4..f8717634 100644 --- a/listener/windows/listener_plugin.h +++ b/listener/windows/listener_plugin.h @@ -60,6 +60,8 @@ class ListenerPlugin : public flutter::Plugin { ULONGLONG last_write_tick_ = 0; + bool clipboard_format_registered_ = false; + UINT cf_rtf_ = 0; UINT cf_html_ = 0; UINT cf_exclude_history_ = 0;