bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / win / dtrans / MtaOleClipb.cxx
blobf3e19683419ce47891b84f5beacb73cc2f0fcc54
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 MtaOleClipb.cxx - documentation
23 This class setup a single threaded apartment (sta) thread to deal with
24 the ole clipboard, which runs only in an sta thread.
25 The consequence is that callback from the ole clipboard are in the
26 context of this sta thread. In the soffice applications this may lead
27 to problems because they all use the one and only mutex called
28 SolarMutex.
29 In order to transfer clipboard requests to our sta thread we use a
30 hidden window and forward these requests via window messages.
33 #include <osl/diagnose.h>
34 #include <sal/log.hxx>
36 #include "MtaOleClipb.hxx"
38 #include <svsys.h>
39 #include <win/saldata.hxx>
41 #include <osl/thread.h>
43 #include <wchar.h>
44 #include <process.h>
46 #include <systools/win32/comtools.hxx>
47 #include <systools/win32/retry_if_failed.hxx>
49 #include <comphelper/windowserrorstring.hxx>
51 namespace /* private */
53 const wchar_t g_szWndClsName[] = L"MtaOleReqWnd###";
55 // messages constants
57 const sal_uInt32 MSG_SETCLIPBOARD = WM_USER + 0x0001;
58 const sal_uInt32 MSG_GETCLIPBOARD = WM_USER + 0x0002;
59 const sal_uInt32 MSG_REGCLIPVIEWER = WM_USER + 0x0003;
60 const sal_uInt32 MSG_FLUSHCLIPBOARD = WM_USER + 0x0004;
61 const sal_uInt32 MSG_SHUTDOWN = WM_USER + 0x0005;
63 const sal_uInt32 MAX_WAITTIME = 10000; // msec
64 const sal_uInt32 MAX_WAIT_SHUTDOWN = 10000; // msec
66 const bool MANUAL_RESET = true;
67 const bool INIT_NONSIGNALED = false;
69 /* Cannot use osl conditions because they are blocking
70 without waking up on messages sent by another thread
71 this leads to deadlocks because we are blocking the
72 communication between inter-thread marshalled COM
73 pointers.
74 COM Proxy-Stub communication uses SendMessages for
75 synchronization purposes.
77 class Win32Condition
79 public:
80 Win32Condition() = default;
82 ~Win32Condition() { CloseHandle(m_hEvent); }
84 // wait infinite for own event (or abort event) be signaled
85 // leave messages sent through
86 bool wait(HANDLE hEvtAbort)
88 const HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort };
89 while (true)
91 DWORD dwResult
92 = MsgWaitForMultipleObjects(2, hWaitArray, FALSE, INFINITE, QS_SENDMESSAGE);
94 switch (dwResult)
96 case WAIT_OBJECT_0: // wait successful
97 return true;
99 case WAIT_OBJECT_0 + 1: // wait aborted
100 return false;
102 case WAIT_OBJECT_0 + 2:
104 /* PeekMessage processes all messages in the SendMessage
105 queue that's what we want, messages from the PostMessage
106 queue stay untouched */
107 MSG msg;
108 PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
110 break;
113 default: // WAIT_FAILED?
114 return false;
119 // set the event
120 void set() { SetEvent(m_hEvent); }
122 private:
123 HANDLE m_hEvent = CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr);
125 // prevent copy/assignment
126 Win32Condition(const Win32Condition&) = delete;
127 Win32Condition& operator=(const Win32Condition&) = delete;
130 // we use one condition for every request
132 struct MsgCtx
134 Win32Condition aCondition;
135 HRESULT hr;
138 } /* namespace private */
140 // static member initialization
142 CMtaOleClipboard* CMtaOleClipboard::s_theMtaOleClipboardInst = nullptr;
144 // marshal an IDataObject
146 //inline
147 static HRESULT MarshalIDataObjectInStream( IDataObject* pIDataObject, LPSTREAM* ppStream )
149 OSL_ASSERT( nullptr != pIDataObject );
150 OSL_ASSERT( nullptr != ppStream );
152 *ppStream = nullptr;
153 return CoMarshalInterThreadInterfaceInStream(
154 __uuidof(IDataObject), //The IID of interface to be marshalled
155 pIDataObject, //The interface pointer
156 ppStream //IStream pointer
160 // unmarshal an IDataObject
162 //inline
163 static HRESULT UnmarshalIDataObjectAndReleaseStream( LPSTREAM lpStream, IDataObject** ppIDataObject )
165 OSL_ASSERT( nullptr != lpStream );
166 OSL_ASSERT( nullptr != ppIDataObject );
168 *ppIDataObject = nullptr;
169 return CoGetInterfaceAndReleaseStream(
170 lpStream,
171 __uuidof(IDataObject),
172 reinterpret_cast<LPVOID*>(ppIDataObject));
175 // helper class to ensure that the calling thread has com initialized
177 namespace {
179 class CAutoComInit
181 public:
183 to be safe we call CoInitializeEx
184 although it is not necessary if
185 the calling thread was created
186 using osl_CreateThread because
187 this function calls CoInitializeEx
188 for every thread it creates
190 CAutoComInit( ) : m_hResult( CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ) )
192 if ( S_OK == m_hResult )
193 OSL_FAIL(
194 "com was not yet initialized, the thread was not created using osl_createThread" );
195 else if ( FAILED( m_hResult ) && !( RPC_E_CHANGED_MODE == m_hResult ) )
196 OSL_FAIL(
197 "com could not be initialized, maybe the thread was not created using osl_createThread" );
200 ~CAutoComInit( )
203 we only call CoUninitialize when
204 CoInitializeEx returned S_FALSE, what
205 means that com was already initialize
206 for that thread so we keep the balance
207 if CoInitializeEx returned S_OK what means
208 com was not yet initialized we better
209 let com initialized or we may run into
210 the realm of undefined behaviour
212 if ( m_hResult == S_FALSE )
213 CoUninitialize( );
216 private:
217 HRESULT m_hResult;
222 // ctor
224 CMtaOleClipboard::CMtaOleClipboard( ) :
225 m_hOleThread( nullptr ),
226 m_uOleThreadId( 0 ),
227 // signals that the thread was successfully setup
228 m_hEvtThrdReady(CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr )),
229 m_hwndMtaOleReqWnd( nullptr ),
230 // signals that the window is destroyed - to stop waiting any winproc result
231 m_hEvtWndDisposed(CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr)),
232 m_MtaOleReqWndClassAtom( 0 ),
233 m_pfncClipViewerCallback( nullptr ),
234 m_bRunClipboardNotifierThread( true ),
235 m_hClipboardChangedEvent( m_hClipboardChangedNotifierEvents[0] ),
236 m_hTerminateClipboardChangedNotifierEvent( m_hClipboardChangedNotifierEvents[1] ),
237 m_ClipboardChangedEventCount( 0 )
239 OSL_ASSERT( nullptr != m_hEvtThrdReady );
240 SAL_WARN_IF(!m_hEvtWndDisposed, "vcl.win.dtrans", "CreateEventW failed: m_hEvtWndDisposed is nullptr");
242 s_theMtaOleClipboardInst = this;
244 m_hOleThread = CreateThread(
245 nullptr, 0, CMtaOleClipboard::oleThreadProc, this, 0, &m_uOleThreadId );
246 OSL_ASSERT( nullptr != m_hOleThread );
248 // setup the clipboard changed notifier thread
250 m_hClipboardChangedNotifierEvents[0] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
251 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[0] );
253 m_hClipboardChangedNotifierEvents[1] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
254 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[1] );
256 DWORD uThreadId;
257 m_hClipboardChangedNotifierThread = CreateThread(
258 nullptr, 0, CMtaOleClipboard::clipboardChangedNotifierThreadProc, this, 0, &uThreadId );
260 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierThread );
263 // dtor
265 CMtaOleClipboard::~CMtaOleClipboard( )
267 // block calling threads out
268 if ( nullptr != m_hEvtThrdReady )
269 ResetEvent( m_hEvtThrdReady );
271 // terminate the clipboard changed notifier thread
272 m_bRunClipboardNotifierThread = false;
273 SetEvent( m_hTerminateClipboardChangedNotifierEvent );
275 // unblock whoever could still wait for event processing
276 if (m_hEvtWndDisposed)
277 SetEvent(m_hEvtWndDisposed);
279 sal_uInt32 dwResult = WaitForSingleObject(
280 m_hClipboardChangedNotifierThread, MAX_WAIT_SHUTDOWN );
282 OSL_ENSURE( dwResult == WAIT_OBJECT_0, "clipboard notifier thread could not terminate" );
284 if ( nullptr != m_hClipboardChangedNotifierThread )
285 CloseHandle( m_hClipboardChangedNotifierThread );
287 if ( nullptr != m_hClipboardChangedNotifierEvents[0] )
288 CloseHandle( m_hClipboardChangedNotifierEvents[0] );
290 if ( nullptr != m_hClipboardChangedNotifierEvents[1] )
291 CloseHandle( m_hClipboardChangedNotifierEvents[1] );
293 // end the thread
294 // because DestroyWindow can only be called
295 // from within the thread that created the window
296 sendMessage( MSG_SHUTDOWN );
298 // wait for thread shutdown
299 dwResult = WaitForSingleObject( m_hOleThread, MAX_WAIT_SHUTDOWN );
300 OSL_ENSURE( dwResult == WAIT_OBJECT_0, "OleThread could not terminate" );
302 if ( nullptr != m_hOleThread )
303 CloseHandle( m_hOleThread );
305 if ( nullptr != m_hEvtThrdReady )
306 CloseHandle( m_hEvtThrdReady );
308 if (m_hEvtWndDisposed)
309 CloseHandle(m_hEvtWndDisposed);
311 if ( m_MtaOleReqWndClassAtom )
312 UnregisterClassW( g_szWndClsName, nullptr );
314 OSL_ENSURE( ( nullptr == m_pfncClipViewerCallback ),
315 "Clipboard viewer not properly unregistered" );
318 HRESULT CMtaOleClipboard::flushClipboard( )
320 if ( !WaitForThreadReady( ) )
322 OSL_FAIL( "clipboard sta thread not ready" );
323 return E_FAIL;
326 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId,
327 "flushClipboard from within clipboard sta thread called" );
329 MsgCtx aMsgCtx;
331 const bool bWaitSuccess = postMessage(MSG_FLUSHCLIPBOARD, 0, reinterpret_cast<LPARAM>(&aMsgCtx))
332 && aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
334 return bWaitSuccess ? aMsgCtx.hr : E_ABORT;
337 HRESULT CMtaOleClipboard::getClipboard( IDataObject** ppIDataObject )
339 OSL_PRECOND( nullptr != ppIDataObject, "invalid parameter" );
340 OSL_PRECOND( GetCurrentThreadId( ) != m_uOleThreadId, "getClipboard from within clipboard sta thread called" );
342 if ( !WaitForThreadReady( ) )
344 OSL_FAIL( "clipboard sta thread not ready" );
345 return E_FAIL;
348 CAutoComInit comAutoInit;
350 LPSTREAM lpStream;
352 *ppIDataObject = nullptr;
354 MsgCtx aMsgCtx;
356 const bool bWaitSuccess = postMessage(MSG_GETCLIPBOARD, reinterpret_cast<WPARAM>(&lpStream),
357 reinterpret_cast<LPARAM>(&aMsgCtx))
358 && aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
360 HRESULT hr = bWaitSuccess ? aMsgCtx.hr : E_ABORT;
362 if ( SUCCEEDED( hr ) )
364 hr = UnmarshalIDataObjectAndReleaseStream( lpStream, ppIDataObject );
365 OSL_ENSURE( SUCCEEDED( hr ), "unmarshalling clipboard data object failed" );
368 return hr;
371 // this is an asynchronous method that's why we don't wait until the
372 // request is completed
374 HRESULT CMtaOleClipboard::setClipboard( IDataObject* pIDataObject )
376 if ( !WaitForThreadReady( ) )
378 OSL_FAIL( "clipboard sta thread not ready" );
379 return E_FAIL;
382 CAutoComInit comAutoInit;
384 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "setClipboard from within the clipboard sta thread called" );
386 // because we marshall this request
387 // into the sta thread we better
388 // acquire the interface here so
389 // that the object will not be
390 // destroyed before the ole clipboard
391 // can acquire it
392 // remember: pIDataObject may be NULL
393 // which is a request to clear the
394 // current clipboard content
395 if ( pIDataObject )
396 pIDataObject->AddRef( );
398 postMessage(
399 MSG_SETCLIPBOARD,
400 reinterpret_cast< WPARAM >( pIDataObject ) );
402 // because this is an asynchronous function
403 // the return value is useless
404 return S_OK;
407 // register a clipboard viewer
409 bool CMtaOleClipboard::registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
411 if ( !WaitForThreadReady( ) )
413 OSL_FAIL( "clipboard sta thread not ready" );
414 return false;
417 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "registerClipViewer from within the OleThread called" );
419 MsgCtx aMsgCtx;
421 if (postMessage(MSG_REGCLIPVIEWER, reinterpret_cast<WPARAM>(pfncClipViewerCallback),
422 reinterpret_cast<LPARAM>(&aMsgCtx)))
423 aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
425 return false;
428 // register a clipboard viewer
430 bool CMtaOleClipboard::onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
432 bool bRet = false;
434 // we need exclusive access because the clipboard changed notifier
435 // thread also accesses this variable
436 std::unique_lock aGuard( m_pfncClipViewerCallbackMutex );
438 // register if not yet done
439 if ( ( nullptr != pfncClipViewerCallback ) && ( nullptr == m_pfncClipViewerCallback ) )
441 // SetClipboardViewer sends a WM_DRAWCLIPBOARD message we ignore
442 // this message if we register ourself as clip viewer
443 m_bInRegisterClipViewer = true;
444 bRet = AddClipboardFormatListener(m_hwndMtaOleReqWnd);
445 m_bInRegisterClipViewer = false;
447 // save the new callback function
448 m_pfncClipViewerCallback = pfncClipViewerCallback;
450 else if ( ( nullptr == pfncClipViewerCallback ) && ( nullptr != m_pfncClipViewerCallback ) )
452 m_pfncClipViewerCallback = nullptr;
454 // unregister if input parameter is NULL and we previously registered
455 // as clipboard viewer
456 bRet = RemoveClipboardFormatListener(m_hwndMtaOleReqWnd);
459 return bRet;
462 HRESULT CMtaOleClipboard::onSetClipboard( IDataObject* pIDataObject )
464 return sal::systools::RetryIfFailed(10, 100,
465 [pIDataObject] { return OleSetClipboard(pIDataObject); });
468 HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream )
470 OSL_ASSERT(nullptr != ppStream);
472 IDataObjectPtr pIDataObject;
474 // forward the request to the OleClipboard
475 HRESULT hr
476 = sal::systools::RetryIfFailed(10, 100, [p = &pIDataObject] { return OleGetClipboard(p); });
477 if ( SUCCEEDED( hr ) )
479 hr = MarshalIDataObjectInStream(pIDataObject.get(), ppStream);
480 OSL_ENSURE(SUCCEEDED(hr), "marshalling clipboard data object failed");
482 return hr;
485 // flush the ole-clipboard
487 HRESULT CMtaOleClipboard::onFlushClipboard( )
489 return sal::systools::RetryIfFailed(10, 100, [] { return OleFlushClipboard(); });
492 // handle clipboard update event
494 LRESULT CMtaOleClipboard::onClipboardUpdate()
496 // we don't send a notification if we are
497 // registering ourself as clipboard
498 if ( !m_bInRegisterClipViewer )
500 std::unique_lock aGuard( m_ClipboardChangedEventCountMutex );
502 m_ClipboardChangedEventCount++;
503 SetEvent( m_hClipboardChangedEvent );
506 return 0;
509 // SendMessage so we don't need to supply the HWND if we send
510 // something to our wrapped window
512 LRESULT CMtaOleClipboard::sendMessage( UINT msg, WPARAM wParam, LPARAM lParam )
514 return ::SendMessageW( m_hwndMtaOleReqWnd, msg, wParam, lParam );
517 // PostMessage so we don't need to supply the HWND if we send
518 // something to our wrapped window
520 bool CMtaOleClipboard::postMessage( UINT msg, WPARAM wParam, LPARAM lParam )
522 bool const ret = PostMessageW(m_hwndMtaOleReqWnd, msg, wParam, lParam);
523 SAL_WARN_IF(!ret, "vcl.win.dtrans", "ERROR: PostMessage() failed!");
524 return ret;
527 // the window proc
529 LRESULT CALLBACK CMtaOleClipboard::mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
531 LRESULT lResult = 0;
533 // get a connection to the class-instance via the static member
534 CMtaOleClipboard* pImpl = CMtaOleClipboard::s_theMtaOleClipboardInst;
535 OSL_ASSERT( nullptr != pImpl );
537 switch( uMsg )
539 case MSG_SETCLIPBOARD:
541 IDataObject* pIDataObject = reinterpret_cast< IDataObject* >( wParam );
542 CMtaOleClipboard::onSetClipboard( pIDataObject );
544 // in setClipboard we did acquire the
545 // interface pointer in order to prevent
546 // destruction of the object before the
547 // ole clipboard can acquire the interface
548 // now we release the interface so that
549 // our lostOwnership mechanism works
550 // remember: pIDataObject may be NULL
551 if ( pIDataObject )
552 pIDataObject->Release( );
554 break;
556 case MSG_GETCLIPBOARD:
558 MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
559 OSL_ASSERT( aMsgCtx );
561 aMsgCtx->hr = CMtaOleClipboard::onGetClipboard( reinterpret_cast< LPSTREAM* >(wParam) );
562 aMsgCtx->aCondition.set( );
564 break;
566 case MSG_FLUSHCLIPBOARD:
568 MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
569 OSL_ASSERT( aMsgCtx );
571 aMsgCtx->hr = CMtaOleClipboard::onFlushClipboard( );
572 aMsgCtx->aCondition.set( );
574 break;
576 case MSG_REGCLIPVIEWER:
578 MsgCtx* pMsgCtx = reinterpret_cast<MsgCtx*>(lParam);
579 SAL_WARN_IF(!pMsgCtx, "vcl.win.dtrans", "pMsgCtx is nullptr");
581 pImpl->onRegisterClipViewer(
582 reinterpret_cast<CMtaOleClipboard::LPFNC_CLIPVIEWER_CALLBACK_t>(wParam));
583 pMsgCtx->aCondition.set();
585 break;
587 case WM_CLIPBOARDUPDATE:
588 lResult = pImpl->onClipboardUpdate();
589 break;
591 case MSG_SHUTDOWN:
592 DestroyWindow( pImpl->m_hwndMtaOleReqWnd );
593 break;
595 // force the sta thread to end
596 case WM_DESTROY:
597 SetEvent(pImpl->m_hEvtWndDisposed); // stop waiting for conditions set by this wndproc
598 PostQuitMessage( 0 );
599 break;
601 default:
602 lResult = DefWindowProcW( hWnd, uMsg, wParam, lParam );
603 break;
606 return lResult;
609 void CMtaOleClipboard::createMtaOleReqWnd( )
611 WNDCLASSEXW wcex;
613 SalData* pSalData = GetSalData();
614 OSL_ASSERT(nullptr != pSalData->mhInst);
616 ZeroMemory( &wcex, sizeof(wcex) );
618 wcex.cbSize = sizeof(wcex);
619 wcex.style = 0;
620 wcex.lpfnWndProc = CMtaOleClipboard::mtaOleReqWndProc;
621 wcex.cbClsExtra = 0;
622 wcex.cbWndExtra = 0;
623 wcex.hInstance = pSalData->mhInst;
624 wcex.hIcon = nullptr;
625 wcex.hCursor = nullptr;
626 wcex.hbrBackground = nullptr;
627 wcex.lpszMenuName = nullptr;
628 wcex.lpszClassName = g_szWndClsName;
629 wcex.hIconSm = nullptr;
631 m_MtaOleReqWndClassAtom = RegisterClassExW( &wcex );
633 if ( 0 != m_MtaOleReqWndClassAtom )
634 m_hwndMtaOleReqWnd = CreateWindowW(
635 g_szWndClsName, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, pSalData->mhInst, nullptr );
638 unsigned int CMtaOleClipboard::run( )
640 HRESULT hr = OleInitialize( nullptr );
641 OSL_ASSERT( SUCCEEDED( hr ) );
643 createMtaOleReqWnd( );
645 unsigned int nRet = ~0U; // = error
647 if ( IsWindow( m_hwndMtaOleReqWnd ) )
649 if ( nullptr != m_hEvtThrdReady )
650 SetEvent( m_hEvtThrdReady );
652 nRet = 0;
654 // pumping messages
655 for (;;)
657 MSG msg;
658 int const bRet = GetMessageW(&msg, nullptr, 0, 0);
659 if (bRet == 0)
661 break;
663 if (-1 == bRet)
665 SAL_WARN("vcl.win.dtrans", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
666 nRet = ~0U;
667 break;
669 DispatchMessageW(&msg);
673 OleUninitialize( );
675 return nRet;
678 DWORD WINAPI CMtaOleClipboard::oleThreadProc( _In_ LPVOID pParam )
680 osl_setThreadName("CMtaOleClipboard::run()");
682 CMtaOleClipboard* pInst =
683 static_cast<CMtaOleClipboard*>( pParam );
684 OSL_ASSERT( nullptr != pInst );
686 return pInst->run( );
689 DWORD WINAPI CMtaOleClipboard::clipboardChangedNotifierThreadProc( _In_ LPVOID pParam )
691 osl_setThreadName("CMtaOleClipboard::clipboardChangedNotifierThreadProc()");
692 CMtaOleClipboard* pInst = static_cast< CMtaOleClipboard* >( pParam );
693 OSL_ASSERT( nullptr != pInst );
695 sal::systools::CoInitializeGuard aGuard(COINIT_APARTMENTTHREADED, false,
696 sal::systools::CoInitializeGuard::WhenFailed::NoThrow);
698 // assuming we don't need a lock for
699 // a boolean variable like m_bRun...
700 while ( pInst->m_bRunClipboardNotifierThread )
702 // process window messages because of CoInitializeEx
703 MSG Msg;
704 while (PeekMessageW(&Msg, nullptr, 0, 0, PM_REMOVE))
705 DispatchMessageW(&Msg);
707 // wait for clipboard changed or terminate event
708 MsgWaitForMultipleObjects(2, pInst->m_hClipboardChangedNotifierEvents, false, INFINITE,
709 QS_ALLINPUT | QS_ALLPOSTMESSAGE);
711 std::unique_lock aGuard2( pInst->m_ClipboardChangedEventCountMutex );
713 if ( pInst->m_ClipboardChangedEventCount > 0 )
715 pInst->m_ClipboardChangedEventCount--;
716 if ( 0 == pInst->m_ClipboardChangedEventCount )
717 ResetEvent( pInst->m_hClipboardChangedEvent );
719 aGuard2.unlock( );
721 // nobody should touch m_pfncClipViewerCallback while we do
722 std::unique_lock aClipViewerGuard( pInst->m_pfncClipViewerCallbackMutex );
724 // notify all clipboard listener
725 if ( pInst->m_pfncClipViewerCallback )
726 pInst->m_pfncClipViewerCallback( );
728 else
729 aGuard2.unlock( );
732 return 0;
735 bool CMtaOleClipboard::WaitForThreadReady( ) const
737 bool bRet = false;
739 if ( nullptr != m_hEvtThrdReady )
741 DWORD dwResult = WaitForSingleObject(
742 m_hEvtThrdReady, MAX_WAITTIME );
743 bRet = ( dwResult == WAIT_OBJECT_0 );
746 return bRet;
749 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */