1414#include " flutter/shell/platform/windows/flutter_windows_view.h"
1515
1616static constexpr char kValueKey [] = " value" ;
17+ static constexpr int kAccessDeniedErrorCode = 5 ;
18+ static constexpr int kErrorSuccess = 0 ;
1719
1820namespace flutter {
1921
@@ -95,36 +97,22 @@ class ScopedGlobalLock {
9597
9698// A Clipboard wrapper that automatically closes the clipboard when it goes out
9799// of scope.
98- class ScopedClipboard {
100+ class ScopedClipboard : public ScopedClipboardInterface {
99101 public:
100102 ScopedClipboard ();
101- ~ScopedClipboard ();
103+ virtual ~ScopedClipboard ();
102104
103105 // Prevent copying.
104106 ScopedClipboard (ScopedClipboard const &) = delete ;
105107 ScopedClipboard& operator =(ScopedClipboard const &) = delete ;
106108
107- // Attempts to open the clipboard for the given window, returning true if
108- // successful.
109- bool Open (HWND window);
109+ int Open (HWND window) override ;
110110
111- // Returns true if there is string data available to get.
112- bool HasString ();
111+ bool HasString () override ;
113112
114- // Returns string data from the clipboard.
115- //
116- // If getting a string fails, returns no value. Get error information with
117- // ::GetLastError().
118- //
119- // Open(...) must have succeeded to call this method.
120- std::optional<std::wstring> GetString ();
113+ std::variant<std::wstring, int > GetString () override ;
121114
122- // Sets the string content of the clipboard, returning true on success.
123- //
124- // On failure, get error information with ::GetLastError().
125- //
126- // Open(...) must have succeeded to call this method.
127- bool SetString (const std::wstring string);
115+ int SetString (const std::wstring string) override ;
128116
129117 private:
130118 bool opened_ = false ;
@@ -138,9 +126,14 @@ ScopedClipboard::~ScopedClipboard() {
138126 }
139127}
140128
141- bool ScopedClipboard::Open (HWND window) {
129+ int ScopedClipboard::Open (HWND window) {
142130 opened_ = ::OpenClipboard (window);
143- return opened_;
131+
132+ if (!opened_) {
133+ return ::GetLastError ();
134+ }
135+
136+ return kErrorSuccess ;
144137}
145138
146139bool ScopedClipboard::HasString () {
@@ -149,40 +142,41 @@ bool ScopedClipboard::HasString() {
149142 ::IsClipboardFormatAvailable (CF_TEXT);
150143}
151144
152- std::optional <std::wstring> ScopedClipboard::GetString () {
145+ std::variant <std::wstring, int > ScopedClipboard::GetString () {
153146 assert (opened_);
154147
155148 HANDLE data = ::GetClipboardData (CF_UNICODETEXT);
156149 if (data == nullptr ) {
157- return std:: nullopt ;
150+ return :: GetLastError () ;
158151 }
159152 ScopedGlobalLock locked_data (data);
153+
160154 if (!locked_data.get ()) {
161- return std:: nullopt ;
155+ return :: GetLastError () ;
162156 }
163- return std::optional<std::wstring>( static_cast <wchar_t *>(locked_data.get () ));
157+ return static_cast <wchar_t *>(locked_data.get ());
164158}
165159
166- bool ScopedClipboard::SetString (const std::wstring string) {
160+ int ScopedClipboard::SetString (const std::wstring string) {
167161 assert (opened_);
168162 if (!::EmptyClipboard ()) {
169- return false ;
163+ return :: GetLastError () ;
170164 }
171165 size_t null_terminated_byte_count =
172166 sizeof (decltype (string)::traits_type::char_type) * (string.size () + 1 );
173167 ScopedGlobalMemory destination_memory (GMEM_MOVEABLE,
174168 null_terminated_byte_count);
175169 ScopedGlobalLock locked_memory (destination_memory.get ());
176170 if (!locked_memory.get ()) {
177- return false ;
171+ return :: GetLastError () ;
178172 }
179173 memcpy (locked_memory.get (), string.c_str (), null_terminated_byte_count);
180174 if (!::SetClipboardData (CF_UNICODETEXT, locked_memory.get ())) {
181- return false ;
175+ return :: GetLastError () ;
182176 }
183177 // The clipboard now owns the global memory.
184178 destination_memory.release ();
185- return true ;
179+ return kErrorSuccess ;
186180}
187181
188182} // namespace
@@ -194,30 +188,38 @@ std::unique_ptr<PlatformHandler> PlatformHandler::Create(
194188 return std::make_unique<PlatformHandlerWin32>(messenger, view);
195189}
196190
197- PlatformHandlerWin32::PlatformHandlerWin32 (BinaryMessenger* messenger,
198- FlutterWindowsView* view)
199- : PlatformHandler(messenger), view_(view) {}
191+ PlatformHandlerWin32::PlatformHandlerWin32 (
192+ BinaryMessenger* messenger,
193+ FlutterWindowsView* view,
194+ std::unique_ptr<ScopedClipboardInterface> clipboard)
195+ : PlatformHandler(messenger), view_(view) {
196+ if (clipboard == nullptr ) {
197+ clipboard_ = std::make_unique<ScopedClipboard>();
198+ } else {
199+ clipboard_ = std::move (clipboard);
200+ }
201+ }
200202
201203PlatformHandlerWin32::~PlatformHandlerWin32 () = default ;
202204
203205void PlatformHandlerWin32::GetPlainText (
204206 std::unique_ptr<MethodResult<rapidjson::Document>> result,
205207 std::string_view key) {
206- ScopedClipboard clipboard ;
207- if (!clipboard. Open (std::get<HWND>(*view_-> GetRenderTarget ())) ) {
208+ int open_result = clipboard_-> Open (std::get<HWND>(*view_-> GetRenderTarget ())) ;
209+ if (open_result != kErrorSuccess ) {
208210 rapidjson::Document error_code;
209- error_code.SetInt (:: GetLastError () );
211+ error_code.SetInt (open_result );
210212 result->Error (kClipboardError , " Unable to open clipboard" , error_code);
211213 return ;
212214 }
213- if (!clipboard. HasString ()) {
215+ if (!clipboard_-> HasString ()) {
214216 result->Success (rapidjson::Document ());
215217 return ;
216218 }
217- std::optional <std::wstring> clipboard_string = clipboard. GetString ();
218- if (!clipboard_string ) {
219+ std::variant <std::wstring, int > get_string_result = clipboard_-> GetString ();
220+ if (std::holds_alternative< int >(get_string_result) ) {
219221 rapidjson::Document error_code;
220- error_code.SetInt (:: GetLastError ( ));
222+ error_code.SetInt (std::get< int >(get_string_result ));
221223 result->Error (kClipboardError , " Unable to get clipboard data" , error_code);
222224 return ;
223225 }
@@ -227,42 +229,54 @@ void PlatformHandlerWin32::GetPlainText(
227229 rapidjson::Document::AllocatorType& allocator = document.GetAllocator ();
228230 document.AddMember (
229231 rapidjson::Value (key.data (), allocator),
230- rapidjson::Value (fml::WideStringToUtf8 (*clipboard_string), allocator),
232+ rapidjson::Value (
233+ fml::WideStringToUtf8 (std::get<std::wstring>(get_string_result)),
234+ allocator),
231235 allocator);
232236 result->Success (document);
233237}
234238
235239void PlatformHandlerWin32::GetHasStrings (
236240 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
237- ScopedClipboard clipboard;
238- if (!clipboard.Open (std::get<HWND>(*view_->GetRenderTarget ()))) {
241+ bool hasStrings;
242+ int open_result = clipboard_->Open (std::get<HWND>(*view_->GetRenderTarget ()));
243+ if (open_result != kErrorSuccess ) {
239244 rapidjson::Document error_code;
240- error_code.SetInt (::GetLastError ());
241- result->Error (kClipboardError , " Unable to open clipboard" , error_code);
242- return ;
245+ error_code.SetInt (open_result);
246+ // Swallow errors of type ERROR_ACCESS_DENIED. These happen when the app is
247+ // not in the foreground and GetHasStrings is irrelevant.
248+ // See https://github.com/flutter/flutter/issues/95817.
249+ if (error_code != kAccessDeniedErrorCode ) {
250+ result->Error (kClipboardError , " Unable to open clipboard" , error_code);
251+ return ;
252+ }
253+ hasStrings = false ;
254+ } else {
255+ hasStrings = clipboard_->HasString ();
243256 }
244257
245258 rapidjson::Document document;
246259 document.SetObject ();
247260 rapidjson::Document::AllocatorType& allocator = document.GetAllocator ();
248261 document.AddMember (rapidjson::Value (kValueKey , allocator),
249- rapidjson::Value (clipboard. HasString () ), allocator);
262+ rapidjson::Value (hasStrings ), allocator);
250263 result->Success (document);
251264}
252265
253266void PlatformHandlerWin32::SetPlainText (
254267 const std::string& text,
255268 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
256- ScopedClipboard clipboard ;
257- if (!clipboard. Open (std::get<HWND>(*view_-> GetRenderTarget ())) ) {
269+ int open_result = clipboard_-> Open (std::get<HWND>(*view_-> GetRenderTarget ())) ;
270+ if (open_result != kErrorSuccess ) {
258271 rapidjson::Document error_code;
259- error_code.SetInt (:: GetLastError () );
272+ error_code.SetInt (open_result );
260273 result->Error (kClipboardError , " Unable to open clipboard" , error_code);
261274 return ;
262275 }
263- if (!clipboard.SetString (fml::Utf8ToWideString (text))) {
276+ int set_result = clipboard_->SetString (fml::Utf8ToWideString (text));
277+ if (set_result != kErrorSuccess ) {
264278 rapidjson::Document error_code;
265- error_code.SetInt (:: GetLastError () );
279+ error_code.SetInt (set_result );
266280 result->Error (kClipboardError , " Unable to set clipboard data" , error_code);
267281 return ;
268282 }
0 commit comments