nss: upgrade to release 3.73
[LibreOffice.git] / vcl / win / dtrans / MtaOleClipb.cxx
blobd5c73ef4e4cd6e26b38380106875ac201300ae3c
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 // namespace directives
51 using osl::MutexGuard;
52 using osl::ClearableMutexGuard;
54 namespace /* private */
56 const wchar_t g_szWndClsName[] = L"MtaOleReqWnd###";
58 // messages constants
60 const sal_uInt32 MSG_SETCLIPBOARD = WM_USER + 0x0001;
61 const sal_uInt32 MSG_GETCLIPBOARD = WM_USER + 0x0002;
62 const sal_uInt32 MSG_REGCLIPVIEWER = WM_USER + 0x0003;
63 const sal_uInt32 MSG_FLUSHCLIPBOARD = WM_USER + 0x0004;
64 const sal_uInt32 MSG_SHUTDOWN = WM_USER + 0x0005;
66 const sal_uInt32 MAX_WAITTIME = 10000; // msec
67 const sal_uInt32 MAX_WAIT_SHUTDOWN = 10000; // msec
69 const bool MANUAL_RESET = true;
70 const bool INIT_NONSIGNALED = false;
72 /* Cannot use osl conditions because they are blocking
73 without waking up on messages sent by another thread
74 this leads to deadlocks because we are blocking the
75 communication between inter-thread marshalled COM
76 pointers.
77 COM Proxy-Stub communication uses SendMessages for
78 synchronization purposes.
80 class Win32Condition
82 public:
83 Win32Condition() = default;
85 ~Win32Condition() { CloseHandle(m_hEvent); }
87 // wait infinite for own event (or abort event) be signaled
88 // leave messages sent through
89 bool wait(HANDLE hEvtAbort)
91 const HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort };
92 while (true)
94 DWORD dwResult
95 = MsgWaitForMultipleObjects(2, hWaitArray, FALSE, INFINITE, QS_SENDMESSAGE);
97 switch (dwResult)
99 case WAIT_OBJECT_0: // wait successful
100 return true;
102 case WAIT_OBJECT_0 + 1: // wait aborted
103 return false;
105 case WAIT_OBJECT_0 + 2:
107 /* PeekMessage processes all messages in the SendMessage
108 queue that's what we want, messages from the PostMessage
109 queue stay untouched */
110 MSG msg;
111 PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
113 break;
116 default: // WAIT_FAILED?
117 return false;
122 // set the event
123 void set() { SetEvent(m_hEvent); }
125 private:
126 HANDLE m_hEvent = CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr);
128 // prevent copy/assignment
129 Win32Condition(const Win32Condition&) = delete;
130 Win32Condition& operator=(const Win32Condition&) = delete;
133 // we use one condition for every request
135 struct MsgCtx
137 Win32Condition aCondition;
138 HRESULT hr;
141 } /* namespace private */
143 // static member initialization
145 CMtaOleClipboard* CMtaOleClipboard::s_theMtaOleClipboardInst = nullptr;
147 // marshal an IDataObject
149 //inline
150 static HRESULT MarshalIDataObjectInStream( IDataObject* pIDataObject, LPSTREAM* ppStream )
152 OSL_ASSERT( nullptr != pIDataObject );
153 OSL_ASSERT( nullptr != ppStream );
155 *ppStream = nullptr;
156 return CoMarshalInterThreadInterfaceInStream(
157 __uuidof(IDataObject), //The IID of interface to be marshalled
158 pIDataObject, //The interface pointer
159 ppStream //IStream pointer
163 // unmarshal an IDataObject
165 //inline
166 static HRESULT UnmarshalIDataObjectAndReleaseStream( LPSTREAM lpStream, IDataObject** ppIDataObject )
168 OSL_ASSERT( nullptr != lpStream );
169 OSL_ASSERT( nullptr != ppIDataObject );
171 *ppIDataObject = nullptr;
172 return CoGetInterfaceAndReleaseStream(
173 lpStream,
174 __uuidof(IDataObject),
175 reinterpret_cast<LPVOID*>(ppIDataObject));
178 // helper class to ensure that the calling thread has com initialized
180 namespace {
182 class CAutoComInit
184 public:
186 to be safe we call CoInitializeEx
187 although it is not necessary if
188 the calling thread was created
189 using osl_CreateThread because
190 this function calls CoInitializeEx
191 for every thread it creates
193 CAutoComInit( ) : m_hResult( CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ) )
195 if ( S_OK == m_hResult )
196 OSL_FAIL(
197 "com was not yet initialized, the thread was not created using osl_createThread" );
198 else if ( FAILED( m_hResult ) && !( RPC_E_CHANGED_MODE == m_hResult ) )
199 OSL_FAIL(
200 "com could not be initialized, maybe the thread was not created using osl_createThread" );
203 ~CAutoComInit( )
206 we only call CoUninitialize when
207 CoInitializeEx returned S_FALSE, what
208 means that com was already initialize
209 for that thread so we keep the balance
210 if CoInitializeEx returned S_OK what means
211 com was not yet initialized we better
212 let com initialized or we may run into
213 the realm of undefined behaviour
215 if ( m_hResult == S_FALSE )
216 CoUninitialize( );
219 private:
220 HRESULT m_hResult;
225 // ctor
227 CMtaOleClipboard::CMtaOleClipboard( ) :
228 m_hOleThread( nullptr ),
229 m_uOleThreadId( 0 ),
230 // signals that the thread was successfully setup
231 m_hEvtThrdReady(CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr )),
232 m_hwndMtaOleReqWnd( nullptr ),
233 // signals that the window is destroyed - to stop waiting any winproc result
234 m_hEvtWndDisposed(CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr)),
235 m_MtaOleReqWndClassAtom( 0 ),
236 m_pfncClipViewerCallback( nullptr ),
237 m_bRunClipboardNotifierThread( true ),
238 m_hClipboardChangedEvent( m_hClipboardChangedNotifierEvents[0] ),
239 m_hTerminateClipboardChangedNotifierEvent( m_hClipboardChangedNotifierEvents[1] ),
240 m_ClipboardChangedEventCount( 0 )
242 OSL_ASSERT( nullptr != m_hEvtThrdReady );
243 SAL_WARN_IF(!m_hEvtWndDisposed, "dtrans", "CreateEventW failed: m_hEvtWndDisposed is nullptr");
245 s_theMtaOleClipboardInst = this;
247 m_hOleThread = reinterpret_cast<HANDLE>(_beginthreadex(
248 nullptr, 0, CMtaOleClipboard::oleThreadProc, this, 0, &m_uOleThreadId ));
249 OSL_ASSERT( nullptr != m_hOleThread );
251 // setup the clipboard changed notifier thread
253 m_hClipboardChangedNotifierEvents[0] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
254 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[0] );
256 m_hClipboardChangedNotifierEvents[1] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
257 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[1] );
259 unsigned uThreadId;
260 m_hClipboardChangedNotifierThread = reinterpret_cast<HANDLE>(_beginthreadex(
261 nullptr, 0, CMtaOleClipboard::clipboardChangedNotifierThreadProc, this, 0, &uThreadId ));
263 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierThread );
266 // dtor
268 CMtaOleClipboard::~CMtaOleClipboard( )
270 // block calling threads out
271 if ( nullptr != m_hEvtThrdReady )
272 ResetEvent( m_hEvtThrdReady );
274 // terminate the clipboard changed notifier thread
275 m_bRunClipboardNotifierThread = false;
276 SetEvent( m_hTerminateClipboardChangedNotifierEvent );
278 // unblock whoever could still wait for event processing
279 if (m_hEvtWndDisposed)
280 SetEvent(m_hEvtWndDisposed);
282 sal_uInt32 dwResult = WaitForSingleObject(
283 m_hClipboardChangedNotifierThread, MAX_WAIT_SHUTDOWN );
285 OSL_ENSURE( dwResult == WAIT_OBJECT_0, "clipboard notifier thread could not terminate" );
287 if ( nullptr != m_hClipboardChangedNotifierThread )
288 CloseHandle( m_hClipboardChangedNotifierThread );
290 if ( nullptr != m_hClipboardChangedNotifierEvents[0] )
291 CloseHandle( m_hClipboardChangedNotifierEvents[0] );
293 if ( nullptr != m_hClipboardChangedNotifierEvents[1] )
294 CloseHandle( m_hClipboardChangedNotifierEvents[1] );
296 // end the thread
297 // because DestroyWindow can only be called
298 // from within the thread that created the window
299 sendMessage( MSG_SHUTDOWN );
301 // wait for thread shutdown
302 dwResult = WaitForSingleObject( m_hOleThread, MAX_WAIT_SHUTDOWN );
303 OSL_ENSURE( dwResult == WAIT_OBJECT_0, "OleThread could not terminate" );
305 if ( nullptr != m_hOleThread )
306 CloseHandle( m_hOleThread );
308 if ( nullptr != m_hEvtThrdReady )
309 CloseHandle( m_hEvtThrdReady );
311 if (m_hEvtWndDisposed)
312 CloseHandle(m_hEvtWndDisposed);
314 if ( m_MtaOleReqWndClassAtom )
315 UnregisterClassW( g_szWndClsName, nullptr );
317 OSL_ENSURE( ( nullptr == m_pfncClipViewerCallback ),
318 "Clipboard viewer not properly unregistered" );
321 HRESULT CMtaOleClipboard::flushClipboard( )
323 if ( !WaitForThreadReady( ) )
325 OSL_FAIL( "clipboard sta thread not ready" );
326 return E_FAIL;
329 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId,
330 "flushClipboard from within clipboard sta thread called" );
332 MsgCtx aMsgCtx;
334 const bool bWaitSuccess = postMessage(MSG_FLUSHCLIPBOARD, 0, reinterpret_cast<LPARAM>(&aMsgCtx))
335 && aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
337 return bWaitSuccess ? aMsgCtx.hr : E_ABORT;
340 HRESULT CMtaOleClipboard::getClipboard( IDataObject** ppIDataObject )
342 OSL_PRECOND( nullptr != ppIDataObject, "invalid parameter" );
343 OSL_PRECOND( GetCurrentThreadId( ) != m_uOleThreadId, "getClipboard from within clipboard sta thread called" );
345 if ( !WaitForThreadReady( ) )
347 OSL_FAIL( "clipboard sta thread not ready" );
348 return E_FAIL;
351 CAutoComInit comAutoInit;
353 LPSTREAM lpStream;
355 *ppIDataObject = nullptr;
357 MsgCtx aMsgCtx;
359 const bool bWaitSuccess = postMessage(MSG_GETCLIPBOARD, reinterpret_cast<WPARAM>(&lpStream),
360 reinterpret_cast<LPARAM>(&aMsgCtx))
361 && aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
363 HRESULT hr = bWaitSuccess ? aMsgCtx.hr : E_ABORT;
365 if ( SUCCEEDED( hr ) )
367 hr = UnmarshalIDataObjectAndReleaseStream( lpStream, ppIDataObject );
368 OSL_ENSURE( SUCCEEDED( hr ), "unmarshalling clipboard data object failed" );
371 return hr;
374 // this is an asynchronous method that's why we don't wait until the
375 // request is completed
377 HRESULT CMtaOleClipboard::setClipboard( IDataObject* pIDataObject )
379 if ( !WaitForThreadReady( ) )
381 OSL_FAIL( "clipboard sta thread not ready" );
382 return E_FAIL;
385 CAutoComInit comAutoInit;
387 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "setClipboard from within the clipboard sta thread called" );
389 // because we marshall this request
390 // into the sta thread we better
391 // acquire the interface here so
392 // that the object will not be
393 // destroyed before the ole clipboard
394 // can acquire it
395 // remember: pIDataObject may be NULL
396 // which is a request to clear the
397 // current clipboard content
398 if ( pIDataObject )
399 pIDataObject->AddRef( );
401 postMessage(
402 MSG_SETCLIPBOARD,
403 reinterpret_cast< WPARAM >( pIDataObject ) );
405 // because this is an asynchronous function
406 // the return value is useless
407 return S_OK;
410 // register a clipboard viewer
412 bool CMtaOleClipboard::registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
414 if ( !WaitForThreadReady( ) )
416 OSL_FAIL( "clipboard sta thread not ready" );
417 return false;
420 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "registerClipViewer from within the OleThread called" );
422 MsgCtx aMsgCtx;
424 if (postMessage(MSG_REGCLIPVIEWER, reinterpret_cast<WPARAM>(pfncClipViewerCallback),
425 reinterpret_cast<LPARAM>(&aMsgCtx)))
426 aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
428 return false;
431 // register a clipboard viewer
433 bool CMtaOleClipboard::onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
435 bool bRet = false;
437 // we need exclusive access because the clipboard changed notifier
438 // thread also accesses this variable
439 MutexGuard aGuard( m_pfncClipViewerCallbackMutex );
441 // register if not yet done
442 if ( ( nullptr != pfncClipViewerCallback ) && ( nullptr == m_pfncClipViewerCallback ) )
444 // SetClipboardViewer sends a WM_DRAWCLIPBOARD message we ignore
445 // this message if we register ourself as clip viewer
446 m_bInRegisterClipViewer = true;
447 bRet = AddClipboardFormatListener(m_hwndMtaOleReqWnd);
448 m_bInRegisterClipViewer = false;
450 // save the new callback function
451 m_pfncClipViewerCallback = pfncClipViewerCallback;
453 else if ( ( nullptr == pfncClipViewerCallback ) && ( nullptr != m_pfncClipViewerCallback ) )
455 m_pfncClipViewerCallback = nullptr;
457 // unregister if input parameter is NULL and we previously registered
458 // as clipboard viewer
459 bRet = RemoveClipboardFormatListener(m_hwndMtaOleReqWnd);
462 return bRet;
465 HRESULT CMtaOleClipboard::onSetClipboard( IDataObject* pIDataObject )
467 return sal::systools::RetryIfFailed(10, 100,
468 [pIDataObject] { return OleSetClipboard(pIDataObject); });
471 HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream )
473 OSL_ASSERT(nullptr != ppStream);
475 IDataObjectPtr pIDataObject;
477 // forward the request to the OleClipboard
478 HRESULT hr
479 = sal::systools::RetryIfFailed(10, 100, [p = &pIDataObject] { return OleGetClipboard(p); });
480 if ( SUCCEEDED( hr ) )
482 hr = MarshalIDataObjectInStream(pIDataObject.get(), ppStream);
483 OSL_ENSURE(SUCCEEDED(hr), "marshalling cliboard data object failed");
485 return hr;
488 // flush the ole-clipboard
490 HRESULT CMtaOleClipboard::onFlushClipboard( )
492 return sal::systools::RetryIfFailed(10, 100, [] { return OleFlushClipboard(); });
495 // handle clipboard update event
497 LRESULT CMtaOleClipboard::onClipboardUpdate()
499 // we don't send a notification if we are
500 // registering ourself as clipboard
501 if ( !m_bInRegisterClipViewer )
503 MutexGuard aGuard( m_ClipboardChangedEventCountMutex );
505 m_ClipboardChangedEventCount++;
506 SetEvent( m_hClipboardChangedEvent );
509 return 0;
512 // SendMessage so we don't need to supply the HWND if we send
513 // something to our wrapped window
515 LRESULT CMtaOleClipboard::sendMessage( UINT msg, WPARAM wParam, LPARAM lParam )
517 return ::SendMessageW( m_hwndMtaOleReqWnd, msg, wParam, lParam );
520 // PostMessage so we don't need to supply the HWND if we send
521 // something to our wrapped window
523 bool CMtaOleClipboard::postMessage( UINT msg, WPARAM wParam, LPARAM lParam )
525 bool const ret = PostMessageW(m_hwndMtaOleReqWnd, msg, wParam, lParam);
526 SAL_WARN_IF(!ret, "dtrans", "ERROR: PostMessage() failed!");
527 return ret;
530 // the window proc
532 LRESULT CALLBACK CMtaOleClipboard::mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
534 LRESULT lResult = 0;
536 // get a connection to the class-instance via the static member
537 CMtaOleClipboard* pImpl = CMtaOleClipboard::s_theMtaOleClipboardInst;
538 OSL_ASSERT( nullptr != pImpl );
540 switch( uMsg )
542 case MSG_SETCLIPBOARD:
544 IDataObject* pIDataObject = reinterpret_cast< IDataObject* >( wParam );
545 CMtaOleClipboard::onSetClipboard( pIDataObject );
547 // in setClipboard we did acquire the
548 // interface pointer in order to prevent
549 // destruction of the object before the
550 // ole clipboard can acquire the interface
551 // now we release the interface so that
552 // our lostOwnership mechanism works
553 // remember: pIDataObject may be NULL
554 if ( pIDataObject )
555 pIDataObject->Release( );
557 break;
559 case MSG_GETCLIPBOARD:
561 MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
562 OSL_ASSERT( aMsgCtx );
564 aMsgCtx->hr = CMtaOleClipboard::onGetClipboard( reinterpret_cast< LPSTREAM* >(wParam) );
565 aMsgCtx->aCondition.set( );
567 break;
569 case MSG_FLUSHCLIPBOARD:
571 MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
572 OSL_ASSERT( aMsgCtx );
574 aMsgCtx->hr = CMtaOleClipboard::onFlushClipboard( );
575 aMsgCtx->aCondition.set( );
577 break;
579 case MSG_REGCLIPVIEWER:
581 MsgCtx* pMsgCtx = reinterpret_cast<MsgCtx*>(lParam);
582 SAL_WARN_IF(!pMsgCtx, "dtrans", "pMsgCtx is nullptr");
584 pImpl->onRegisterClipViewer(
585 reinterpret_cast<CMtaOleClipboard::LPFNC_CLIPVIEWER_CALLBACK_t>(wParam));
586 pMsgCtx->aCondition.set();
588 break;
590 case WM_CLIPBOARDUPDATE:
591 lResult = pImpl->onClipboardUpdate();
592 break;
594 case MSG_SHUTDOWN:
595 DestroyWindow( pImpl->m_hwndMtaOleReqWnd );
596 break;
598 // force the sta thread to end
599 case WM_DESTROY:
600 SetEvent(pImpl->m_hEvtWndDisposed); // stop waiting for conditions set by this wndproc
601 PostQuitMessage( 0 );
602 break;
604 default:
605 lResult = DefWindowProcW( hWnd, uMsg, wParam, lParam );
606 break;
609 return lResult;
612 void CMtaOleClipboard::createMtaOleReqWnd( )
614 WNDCLASSEXW wcex;
616 SalData* pSalData = GetSalData();
617 OSL_ASSERT(nullptr != pSalData->mhInst);
619 ZeroMemory( &wcex, sizeof(wcex) );
621 wcex.cbSize = sizeof(wcex);
622 wcex.style = 0;
623 wcex.lpfnWndProc = CMtaOleClipboard::mtaOleReqWndProc;
624 wcex.cbClsExtra = 0;
625 wcex.cbWndExtra = 0;
626 wcex.hInstance = pSalData->mhInst;
627 wcex.hIcon = nullptr;
628 wcex.hCursor = nullptr;
629 wcex.hbrBackground = nullptr;
630 wcex.lpszMenuName = nullptr;
631 wcex.lpszClassName = g_szWndClsName;
632 wcex.hIconSm = nullptr;
634 m_MtaOleReqWndClassAtom = RegisterClassExW( &wcex );
636 if ( 0 != m_MtaOleReqWndClassAtom )
637 m_hwndMtaOleReqWnd = CreateWindowW(
638 g_szWndClsName, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, pSalData->mhInst, nullptr );
641 unsigned int CMtaOleClipboard::run( )
643 HRESULT hr = OleInitialize( nullptr );
644 OSL_ASSERT( SUCCEEDED( hr ) );
646 createMtaOleReqWnd( );
648 unsigned int nRet;
650 if ( IsWindow( m_hwndMtaOleReqWnd ) )
652 if ( nullptr != m_hEvtThrdReady )
653 SetEvent( m_hEvtThrdReady );
655 // pumping messages
656 MSG msg;
657 while( GetMessageW( &msg, nullptr, 0, 0 ) )
658 DispatchMessageW( &msg );
660 nRet = 0;
662 else
663 nRet = ~0U;
665 OleUninitialize( );
667 return nRet;
670 unsigned int WINAPI CMtaOleClipboard::oleThreadProc( LPVOID pParam )
672 osl_setThreadName("CMtaOleClipboard::run()");
674 CMtaOleClipboard* pInst =
675 static_cast<CMtaOleClipboard*>( pParam );
676 OSL_ASSERT( nullptr != pInst );
678 return pInst->run( );
681 unsigned int WINAPI CMtaOleClipboard::clipboardChangedNotifierThreadProc( LPVOID pParam )
683 osl_setThreadName("CMtaOleClipboard::clipboardChangedNotifierThreadProc()");
684 CMtaOleClipboard* pInst = static_cast< CMtaOleClipboard* >( pParam );
685 OSL_ASSERT( nullptr != pInst );
687 CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED );
689 // assuming we don't need a lock for
690 // a boolean variable like m_bRun...
691 while ( pInst->m_bRunClipboardNotifierThread )
693 // process window messages because of CoInitializeEx
694 MSG Msg;
695 while (PeekMessageW(&Msg, nullptr, 0, 0, PM_REMOVE))
696 DispatchMessageW(&Msg);
698 // wait for clipboard changed or terminate event
699 MsgWaitForMultipleObjects(2, pInst->m_hClipboardChangedNotifierEvents, false, INFINITE,
700 QS_ALLINPUT | QS_ALLPOSTMESSAGE);
702 ClearableMutexGuard aGuard( pInst->m_ClipboardChangedEventCountMutex );
704 if ( pInst->m_ClipboardChangedEventCount > 0 )
706 pInst->m_ClipboardChangedEventCount--;
707 if ( 0 == pInst->m_ClipboardChangedEventCount )
708 ResetEvent( pInst->m_hClipboardChangedEvent );
710 aGuard.clear( );
712 // nobody should touch m_pfncClipViewerCallback while we do
713 MutexGuard aClipViewerGuard( pInst->m_pfncClipViewerCallbackMutex );
715 // notify all clipboard listener
716 if ( pInst->m_pfncClipViewerCallback )
717 pInst->m_pfncClipViewerCallback( );
719 else
720 aGuard.clear( );
723 CoUninitialize( );
725 return 0;
728 bool CMtaOleClipboard::WaitForThreadReady( ) const
730 bool bRet = false;
732 if ( nullptr != m_hEvtThrdReady )
734 DWORD dwResult = WaitForSingleObject(
735 m_hEvtThrdReady, MAX_WAITTIME );
736 bRet = ( dwResult == WAIT_OBJECT_0 );
739 return bRet;
742 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */