Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / dtrans / source / win32 / clipb / MtaOleClipb.cxx
bloba3e28806deed090030f1222904fdf8e290cb5ade
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"
37 #include <osl/thread.h>
39 #include <wchar.h>
40 #include <process.h>
42 #include <systools/win32/comtools.hxx>
44 // namespace directives
46 using osl::MutexGuard;
47 using osl::ClearableMutexGuard;
49 namespace /* private */
51 wchar_t CLIPSRV_DLL_NAME[] = L"sysdtrans.dll";
52 wchar_t g_szWndClsName[] = L"MtaOleReqWnd###";
54 // messages constants
56 const sal_uInt32 MSG_SETCLIPBOARD = WM_USER + 0x0001;
57 const sal_uInt32 MSG_GETCLIPBOARD = WM_USER + 0x0002;
58 const sal_uInt32 MSG_REGCLIPVIEWER = WM_USER + 0x0003;
59 const sal_uInt32 MSG_FLUSHCLIPBOARD = WM_USER + 0x0004;
60 const sal_uInt32 MSG_SHUTDOWN = WM_USER + 0x0005;
62 const sal_uInt32 MAX_WAITTIME = 10000; // msec
63 const sal_uInt32 MAX_WAIT_SHUTDOWN = 10000; // msec
65 const BOOL MANUAL_RESET = TRUE;
66 const BOOL INIT_NONSIGNALED = FALSE;
68 /* Cannot use osl conditions because they are blocking
69 without waking up on messages sent by another thread
70 this leads to deadlocks because we are blocking the
71 communication between inter-thread marshalled COM
72 pointers.
73 COM Proxy-Stub communication uses SendMessages for
74 synchronization purposes.
76 class Win32Condition
78 public:
79 Win32Condition() = default;
81 ~Win32Condition() { CloseHandle(m_hEvent); }
83 // wait infinite for own event (or abort event) be signaled
84 // leave messages sent through
85 bool wait(HANDLE hEvtAbort)
87 const HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort };
88 while (true)
90 DWORD dwResult
91 = MsgWaitForMultipleObjects(2, hWaitArray, FALSE, INFINITE, QS_SENDMESSAGE);
93 switch (dwResult)
95 case WAIT_OBJECT_0: // wait successful
96 return true;
98 case WAIT_OBJECT_0 + 1: // wait aborted
99 return false;
101 case WAIT_OBJECT_0 + 2:
103 /* PeekMessage processes all messages in the SendMessage
104 queue that's what we want, messages from the PostMessage
105 queue stay untouched */
106 MSG msg;
107 PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
109 break;
112 default: // WAIT_FAILED?
113 return false;
118 // set the event
119 void set() { SetEvent(m_hEvent); }
121 private:
122 HANDLE m_hEvent = CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr);
124 // prevent copy/assignment
125 Win32Condition(const Win32Condition&) = delete;
126 Win32Condition& operator=(const Win32Condition&) = delete;
129 // we use one condition for every request
131 struct MsgCtx
133 Win32Condition aCondition;
134 HRESULT hr;
137 } /* namespace private */
139 // static member initialization
141 CMtaOleClipboard* CMtaOleClipboard::s_theMtaOleClipboardInst = nullptr;
143 // marshal an IDataObject
145 //inline
146 static HRESULT MarshalIDataObjectInStream( IDataObject* pIDataObject, LPSTREAM* ppStream )
148 OSL_ASSERT( nullptr != pIDataObject );
149 OSL_ASSERT( nullptr != ppStream );
151 *ppStream = nullptr;
152 return CoMarshalInterThreadInterfaceInStream(
153 __uuidof(IDataObject), //The IID of interface to be marshalled
154 pIDataObject, //The interface pointer
155 ppStream //IStream pointer
159 // unmarshal an IDataObject
161 //inline
162 static HRESULT UnmarshalIDataObjectAndReleaseStream( LPSTREAM lpStream, IDataObject** ppIDataObject )
164 OSL_ASSERT( nullptr != lpStream );
165 OSL_ASSERT( nullptr != ppIDataObject );
167 *ppIDataObject = nullptr;
168 return CoGetInterfaceAndReleaseStream(
169 lpStream,
170 __uuidof(IDataObject),
171 reinterpret_cast<LPVOID*>(ppIDataObject));
174 // helper class to ensure that the calling thread has com initialized
176 class CAutoComInit
178 public:
179 CAutoComInit( )
182 to be safe we call CoInitialize
183 although it is not necessary if
184 the calling thread was created
185 using osl_CreateThread because
186 this function calls CoInitialize
187 for every thread it creates
189 m_hResult = CoInitialize( nullptr );
191 if ( S_OK == m_hResult )
192 OSL_FAIL(
193 "com was not yet initialized, the thread was not created using osl_createThread" );
194 else if ( FAILED( m_hResult ) && !( RPC_E_CHANGED_MODE == m_hResult ) )
195 OSL_FAIL(
196 "com could not be initialized, maybe the thread was not created using osl_createThread" );
199 ~CAutoComInit( )
202 we only call CoUninitialize when
203 CoInitialize returned S_FALSE, what
204 means that com was already initialize
205 for that thread so we keep the balance
206 if CoInitialize returned S_OK what means
207 com was not yet initialized we better
208 let com initialized or we may run into
209 the realm of undefined behaviour
211 if ( m_hResult == S_FALSE )
212 CoUninitialize( );
215 private:
216 HRESULT m_hResult;
219 // ctor
221 CMtaOleClipboard::CMtaOleClipboard( ) :
222 m_hOleThread( nullptr ),
223 m_uOleThreadId( 0 ),
224 m_hEvtThrdReady( nullptr ),
225 m_hwndMtaOleReqWnd( nullptr ),
226 // signals that the window is destroyed - to stop waiting any winproc result
227 m_hEvtWndDisposed(CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr)),
228 m_MtaOleReqWndClassAtom( 0 ),
229 m_pfncClipViewerCallback( nullptr ),
230 m_bRunClipboardNotifierThread( true ),
231 m_hClipboardChangedEvent( m_hClipboardChangedNotifierEvents[0] ),
232 m_hTerminateClipboardChangedNotifierEvent( m_hClipboardChangedNotifierEvents[1] ),
233 m_ClipboardChangedEventCount( 0 )
235 // signals that the thread was successfully setup
236 m_hEvtThrdReady = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
238 OSL_ASSERT( nullptr != m_hEvtThrdReady );
239 SAL_WARN_IF(!m_hEvtWndDisposed, "dtrans", "CreateEventW failed: m_hEvtWndDisposed is nullptr");
241 s_theMtaOleClipboardInst = this;
243 m_hOleThread = reinterpret_cast<HANDLE>(_beginthreadex(
244 nullptr, 0, CMtaOleClipboard::oleThreadProc, this, 0, &m_uOleThreadId ));
245 OSL_ASSERT( nullptr != m_hOleThread );
247 // setup the clipboard changed notifier thread
249 m_hClipboardChangedNotifierEvents[0] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
250 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[0] );
252 m_hClipboardChangedNotifierEvents[1] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
253 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[1] );
255 unsigned uThreadId;
256 m_hClipboardChangedNotifierThread = reinterpret_cast<HANDLE>(_beginthreadex(
257 nullptr, 0, CMtaOleClipboard::clipboardChangedNotifierThreadProc, this, 0, &uThreadId ));
259 OSL_ASSERT( nullptr != m_hClipboardChangedNotifierThread );
262 // dtor
264 CMtaOleClipboard::~CMtaOleClipboard( )
266 // block calling threads out
267 if ( nullptr != m_hEvtThrdReady )
268 ResetEvent( m_hEvtThrdReady );
270 // terminate the clipboard changed notifier thread
271 m_bRunClipboardNotifierThread = false;
272 SetEvent( m_hTerminateClipboardChangedNotifierEvent );
274 // unblock whoever could still wait for event processing
275 if (m_hEvtWndDisposed)
276 SetEvent(m_hEvtWndDisposed);
278 sal_uInt32 dwResult = WaitForSingleObject(
279 m_hClipboardChangedNotifierThread, MAX_WAIT_SHUTDOWN );
281 OSL_ENSURE( dwResult == WAIT_OBJECT_0, "clipboard notifier thread could not terminate" );
283 if ( nullptr != m_hClipboardChangedNotifierThread )
284 CloseHandle( m_hClipboardChangedNotifierThread );
286 if ( nullptr != m_hClipboardChangedNotifierEvents[0] )
287 CloseHandle( m_hClipboardChangedNotifierEvents[0] );
289 if ( nullptr != m_hClipboardChangedNotifierEvents[1] )
290 CloseHandle( m_hClipboardChangedNotifierEvents[1] );
292 // end the thread
293 // because DestroyWindow can only be called
294 // from within the thread that created the window
295 sendMessage( MSG_SHUTDOWN );
297 // wait for thread shutdown
298 dwResult = WaitForSingleObject( m_hOleThread, MAX_WAIT_SHUTDOWN );
299 OSL_ENSURE( dwResult == WAIT_OBJECT_0, "OleThread could not terminate" );
301 if ( nullptr != m_hOleThread )
302 CloseHandle( m_hOleThread );
304 if ( nullptr != m_hEvtThrdReady )
305 CloseHandle( m_hEvtThrdReady );
307 if (m_hEvtWndDisposed)
308 CloseHandle(m_hEvtWndDisposed);
310 if ( m_MtaOleReqWndClassAtom )
311 UnregisterClassW( g_szWndClsName, nullptr );
313 OSL_ENSURE( ( nullptr == m_pfncClipViewerCallback ),
314 "Clipboard viewer not properly unregistered" );
317 HRESULT CMtaOleClipboard::flushClipboard( )
319 if ( !WaitForThreadReady( ) )
321 OSL_FAIL( "clipboard sta thread not ready" );
322 return E_FAIL;
325 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId,
326 "flushClipboard from within clipboard sta thread called" );
328 MsgCtx aMsgCtx;
330 const bool bWaitSuccess = postMessage(MSG_FLUSHCLIPBOARD, 0, reinterpret_cast<LPARAM>(&aMsgCtx))
331 && aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
333 return bWaitSuccess ? aMsgCtx.hr : E_ABORT;
336 HRESULT CMtaOleClipboard::getClipboard( IDataObject** ppIDataObject )
338 OSL_PRECOND( nullptr != ppIDataObject, "invalid parameter" );
339 OSL_PRECOND( GetCurrentThreadId( ) != m_uOleThreadId, "getClipboard from within clipboard sta thread called" );
341 if ( !WaitForThreadReady( ) )
343 OSL_FAIL( "clipboard sta thread not ready" );
344 return E_FAIL;
347 CAutoComInit comAutoInit;
349 LPSTREAM lpStream;
351 *ppIDataObject = nullptr;
353 MsgCtx aMsgCtx;
355 const bool bWaitSuccess = postMessage(MSG_GETCLIPBOARD, reinterpret_cast<WPARAM>(&lpStream),
356 reinterpret_cast<LPARAM>(&aMsgCtx))
357 && aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
359 HRESULT hr = bWaitSuccess ? aMsgCtx.hr : E_ABORT;
361 if ( SUCCEEDED( hr ) )
363 hr = UnmarshalIDataObjectAndReleaseStream( lpStream, ppIDataObject );
364 OSL_ENSURE( SUCCEEDED( hr ), "unmarshalling clipboard data object failed" );
367 return hr;
370 // this is an asynchronous method that's why we don't wait until the
371 // request is completed
373 HRESULT CMtaOleClipboard::setClipboard( IDataObject* pIDataObject )
375 if ( !WaitForThreadReady( ) )
377 OSL_FAIL( "clipboard sta thread not ready" );
378 return E_FAIL;
381 CAutoComInit comAutoInit;
383 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "setClipboard from within the clipboard sta thread called" );
385 // because we marshall this request
386 // into the sta thread we better
387 // acquire the interface here so
388 // that the object will not be
389 // destroyed before the ole clipboard
390 // can acquire it
391 // remember: pIDataObject may be NULL
392 // which is a request to clear the
393 // current clipboard content
394 if ( pIDataObject )
395 pIDataObject->AddRef( );
397 postMessage(
398 MSG_SETCLIPBOARD,
399 reinterpret_cast< WPARAM >( pIDataObject ) );
401 // because this is an asynchronous function
402 // the return value is useless
403 return S_OK;
406 // register a clipboard viewer
408 bool CMtaOleClipboard::registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
410 if ( !WaitForThreadReady( ) )
412 OSL_FAIL( "clipboard sta thread not ready" );
413 return false;
416 OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "registerClipViewer from within the OleThread called" );
418 MsgCtx aMsgCtx;
420 if (postMessage(MSG_REGCLIPVIEWER, reinterpret_cast<WPARAM>(pfncClipViewerCallback),
421 reinterpret_cast<LPARAM>(&aMsgCtx)))
422 aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
424 return false;
427 // register a clipboard viewer
429 bool CMtaOleClipboard::onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
431 bool bRet = false;
433 // we need exclusive access because the clipboard changed notifier
434 // thread also accesses this variable
435 MutexGuard aGuard( m_pfncClipViewerCallbackMutex );
437 // register if not yet done
438 if ( ( nullptr != pfncClipViewerCallback ) && ( nullptr == m_pfncClipViewerCallback ) )
440 // SetClipboardViewer sends a WM_DRAWCLIPBOARD message we ignore
441 // this message if we register ourself as clip viewer
442 m_bInRegisterClipViewer = true;
443 bRet = AddClipboardFormatListener(m_hwndMtaOleReqWnd);
444 m_bInRegisterClipViewer = false;
446 // save the new callback function
447 m_pfncClipViewerCallback = pfncClipViewerCallback;
449 else if ( ( nullptr == pfncClipViewerCallback ) && ( nullptr != m_pfncClipViewerCallback ) )
451 m_pfncClipViewerCallback = nullptr;
453 // unregister if input parameter is NULL and we previously registered
454 // as clipboard viewer
455 bRet = RemoveClipboardFormatListener(m_hwndMtaOleReqWnd);
458 return bRet;
461 HRESULT CMtaOleClipboard::onSetClipboard( IDataObject* pIDataObject )
463 return OleSetClipboard( pIDataObject );
466 HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream )
468 OSL_ASSERT(nullptr != ppStream);
470 IDataObjectPtr pIDataObject;
472 // forward the request to the OleClipboard
473 HRESULT hr = OleGetClipboard( &pIDataObject );
474 if ( SUCCEEDED( hr ) )
476 hr = MarshalIDataObjectInStream(pIDataObject.get(), ppStream);
477 OSL_ENSURE(SUCCEEDED(hr), "marshalling cliboard data object failed");
479 return hr;
482 // flush the ole-clipboard
484 HRESULT CMtaOleClipboard::onFlushClipboard( )
486 return OleFlushClipboard();
489 // handle clipboard update event
491 LRESULT CMtaOleClipboard::onClipboardUpdate()
493 // we don't send a notification if we are
494 // registering ourself as clipboard
495 if ( !m_bInRegisterClipViewer )
497 MutexGuard aGuard( m_ClipboardChangedEventCountMutex );
499 m_ClipboardChangedEventCount++;
500 SetEvent( m_hClipboardChangedEvent );
503 return 0;
506 // SendMessage so we don't need to supply the HWND if we send
507 // something to our wrapped window
509 LRESULT CMtaOleClipboard::sendMessage( UINT msg, WPARAM wParam, LPARAM lParam )
511 return ::SendMessageW( m_hwndMtaOleReqWnd, msg, wParam, lParam );
514 // PostMessage so we don't need to supply the HWND if we send
515 // something to our wrapped window
517 bool CMtaOleClipboard::postMessage( UINT msg, WPARAM wParam, LPARAM lParam )
519 BOOL const ret = PostMessageW(m_hwndMtaOleReqWnd, msg, wParam, lParam);
520 SAL_WARN_IF(0 == ret, "dtrans", "ERROR: PostMessage() failed!");
521 return ret;
524 // the window proc
526 LRESULT CALLBACK CMtaOleClipboard::mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
528 LRESULT lResult = 0;
530 // get a connection to the class-instance via the static member
531 CMtaOleClipboard* pImpl = CMtaOleClipboard::s_theMtaOleClipboardInst;
532 OSL_ASSERT( nullptr != pImpl );
534 switch( uMsg )
536 case MSG_SETCLIPBOARD:
538 IDataObject* pIDataObject = reinterpret_cast< IDataObject* >( wParam );
539 CMtaOleClipboard::onSetClipboard( pIDataObject );
541 // in setClipboard we did acquire the
542 // interface pointer in order to prevent
543 // destruction of the object before the
544 // ole clipboard can acquire the interface
545 // now we release the interface so that
546 // our lostOwnership mechanism works
547 // remember: pIDataObject may be NULL
548 if ( pIDataObject )
549 pIDataObject->Release( );
551 break;
553 case MSG_GETCLIPBOARD:
555 MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
556 OSL_ASSERT( aMsgCtx );
558 aMsgCtx->hr = CMtaOleClipboard::onGetClipboard( reinterpret_cast< LPSTREAM* >(wParam) );
559 aMsgCtx->aCondition.set( );
561 break;
563 case MSG_FLUSHCLIPBOARD:
565 MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
566 OSL_ASSERT( aMsgCtx );
568 aMsgCtx->hr = CMtaOleClipboard::onFlushClipboard( );
569 aMsgCtx->aCondition.set( );
571 break;
573 case MSG_REGCLIPVIEWER:
575 MsgCtx* pMsgCtx = reinterpret_cast<MsgCtx*>(lParam);
576 SAL_WARN_IF(!pMsgCtx, "dtrans", "pMsgCtx is nullptr");
578 pImpl->onRegisterClipViewer(
579 reinterpret_cast<CMtaOleClipboard::LPFNC_CLIPVIEWER_CALLBACK_t>(wParam));
580 pMsgCtx->aCondition.set();
582 break;
584 case WM_CLIPBOARDUPDATE:
585 lResult = pImpl->onClipboardUpdate();
586 break;
588 case MSG_SHUTDOWN:
589 DestroyWindow( pImpl->m_hwndMtaOleReqWnd );
590 break;
592 // force the sta thread to end
593 case WM_DESTROY:
594 SetEvent(pImpl->m_hEvtWndDisposed); // stop waiting for conditions set by this wndproc
595 PostQuitMessage( 0 );
596 break;
598 default:
599 lResult = DefWindowProcW( hWnd, uMsg, wParam, lParam );
600 break;
603 return lResult;
606 void CMtaOleClipboard::createMtaOleReqWnd( )
608 WNDCLASSEXW wcex;
610 HINSTANCE hInst = GetModuleHandleW( CLIPSRV_DLL_NAME );
611 OSL_ENSURE( nullptr != hInst, "The name of the clipboard service dll must have changed" );
613 ZeroMemory( &wcex, sizeof(wcex) );
615 wcex.cbSize = sizeof(wcex);
616 wcex.style = 0;
617 wcex.lpfnWndProc = CMtaOleClipboard::mtaOleReqWndProc;
618 wcex.cbClsExtra = 0;
619 wcex.cbWndExtra = 0;
620 wcex.hInstance = hInst;
621 wcex.hIcon = nullptr;
622 wcex.hCursor = nullptr;
623 wcex.hbrBackground = nullptr;
624 wcex.lpszMenuName = nullptr;
625 wcex.lpszClassName = g_szWndClsName;
626 wcex.hIconSm = nullptr;
628 m_MtaOleReqWndClassAtom = RegisterClassExW( &wcex );
630 if ( 0 != m_MtaOleReqWndClassAtom )
631 m_hwndMtaOleReqWnd = CreateWindowW(
632 g_szWndClsName, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, hInst, nullptr );
635 unsigned int CMtaOleClipboard::run( )
637 HRESULT hr = OleInitialize( nullptr );
638 OSL_ASSERT( SUCCEEDED( hr ) );
640 createMtaOleReqWnd( );
642 unsigned int nRet;
644 if ( IsWindow( m_hwndMtaOleReqWnd ) )
646 if ( nullptr != m_hEvtThrdReady )
647 SetEvent( m_hEvtThrdReady );
649 // pumping messages
650 MSG msg;
651 while( GetMessageW( &msg, nullptr, 0, 0 ) )
652 DispatchMessageW( &msg );
654 nRet = 0;
656 else
657 nRet = ~0U;
659 OleUninitialize( );
661 return nRet;
664 unsigned int WINAPI CMtaOleClipboard::oleThreadProc( LPVOID pParam )
666 osl_setThreadName("CMtaOleClipboard::run()");
668 CMtaOleClipboard* pInst =
669 static_cast<CMtaOleClipboard*>( pParam );
670 OSL_ASSERT( nullptr != pInst );
672 return pInst->run( );
675 unsigned int WINAPI CMtaOleClipboard::clipboardChangedNotifierThreadProc( LPVOID pParam )
677 osl_setThreadName("CMtaOleClipboard::clipboardChangedNotifierThreadProc()");
678 CMtaOleClipboard* pInst = static_cast< CMtaOleClipboard* >( pParam );
679 OSL_ASSERT( nullptr != pInst );
681 CoInitialize( nullptr );
683 // assuming we don't need a lock for
684 // a boolean variable like m_bRun...
685 while ( pInst->m_bRunClipboardNotifierThread )
687 // wait for clipboard changed or terminate event
688 WaitForMultipleObjects( 2, pInst->m_hClipboardChangedNotifierEvents, false, INFINITE );
690 ClearableMutexGuard aGuard( pInst->m_ClipboardChangedEventCountMutex );
692 if ( pInst->m_ClipboardChangedEventCount > 0 )
694 pInst->m_ClipboardChangedEventCount--;
695 if ( 0 == pInst->m_ClipboardChangedEventCount )
696 ResetEvent( pInst->m_hClipboardChangedEvent );
698 aGuard.clear( );
700 // nobody should touch m_pfncClipViewerCallback while we do
701 MutexGuard aClipViewerGuard( pInst->m_pfncClipViewerCallbackMutex );
703 // notify all clipboard listener
704 if ( pInst->m_pfncClipViewerCallback )
705 pInst->m_pfncClipViewerCallback( );
707 else
708 aGuard.clear( );
711 CoUninitialize( );
713 return 0;
716 bool CMtaOleClipboard::WaitForThreadReady( ) const
718 bool bRet = false;
720 if ( nullptr != m_hEvtThrdReady )
722 DWORD dwResult = WaitForSingleObject(
723 m_hEvtThrdReady, MAX_WAITTIME );
724 bRet = ( dwResult == WAIT_OBJECT_0 );
727 return bRet;
730 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */