xref: /trunk/main/dtrans/source/win32/mtaole/MtaOleClipb.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_dtrans.hxx"
30 
31 /*
32     MtaOleClipb.cxx - documentation
33 
34     This class setup a single threaded apartment (sta) thread to deal with
35     the ole clipboard, which runs only in an sta thread.
36     The consequence is that callback from the ole clipboard are in the
37     context of this sta thread. In the soffice applications this may lead
38     to problems because they all use the one and only mutex called
39     SolarMutex.
40     In order to transfer clipboard requests to our sta thread we use a
41     hidden window an forward these requests via window messages.
42 */
43 
44 #ifdef _MSC_VER
45 #pragma warning( disable : 4786 ) // identifier was truncated to 'number'
46                                   // characters in the debug information
47 #endif
48 
49 //#define UNICODE
50 #include <osl/diagnose.h>
51 
52 #include "..\..\inc\MtaOleClipb.hxx"
53 #include <osl/conditn.hxx>
54 
55 #include <wchar.h>
56 #include <process.h>
57 
58 #include <systools/win32/comtools.hxx>
59 #ifdef __MINGW32__
60 #define __uuidof(I) IID_##I
61 #endif
62 
63 //----------------------------------------------------------------
64 //  namespace directives
65 //----------------------------------------------------------------
66 
67 using osl::Condition;
68 using osl::Mutex;
69 using osl::MutexGuard;
70 using osl::ClearableMutexGuard;
71 
72 //----------------------------------------------------------------
73 //  defines
74 //----------------------------------------------------------------
75 
76 namespace /* private */
77 {
78     char CLIPSRV_DLL_NAME[] = "sysdtrans.dll";
79     char g_szWndClsName[]   = "MtaOleReqWnd###";
80 
81     //--------------------------------------------------------
82     // messages constants
83     //--------------------------------------------------------
84 
85     const sal_uInt32 MSG_SETCLIPBOARD               = WM_USER + 0x0001;
86     const sal_uInt32 MSG_GETCLIPBOARD               = WM_USER + 0x0002;
87     const sal_uInt32 MSG_REGCLIPVIEWER              = WM_USER + 0x0003;
88     const sal_uInt32 MSG_FLUSHCLIPBOARD             = WM_USER + 0x0004;
89     const sal_uInt32 MSG_SHUTDOWN                   = WM_USER + 0x0005;
90 
91     const sal_uInt32 MAX_WAITTIME                   = 10000;  // msec
92     const sal_uInt32 MAX_WAIT_SHUTDOWN              = 10000; // msec
93     const sal_uInt32 MAX_CLIPEVENT_PROCESSING_TIME  = 5000;  // msec
94 
95     const sal_Bool MANUAL_RESET                     = sal_True;
96     const sal_Bool AUTO_RESET                       = sal_False;
97     const sal_Bool INIT_NONSIGNALED                 = sal_False;
98 
99     //------------------------------------------------------
100     /*  Cannot use osl conditions because they are blocking
101         without waking up on messages sent by another thread
102         this leads to deadlocks because we are blocking the
103         communication between inter-thread marshalled COM
104         pointers.
105         COM Proxy-Stub communication uses SendMessages for
106         synchronization purposes.
107     */
108     class Win32Condition
109     {
110         public:
111             // ctor
112             Win32Condition()
113             {
114                 m_hEvent = CreateEvent(
115                     0,      /* no security */
116                     true,   /* manual reset */
117                     false,  /* initial state not signaled */
118                     0);     /* automatic name */
119             }
120 
121             // dtor
122             ~Win32Condition()
123             {
124                 CloseHandle(m_hEvent);
125             }
126 
127             // wait infinite for event be signaled
128             // leave messages sent through
129             void wait()
130             {
131                 while(1)
132                 {
133                     DWORD dwResult =
134                         MsgWaitForMultipleObjects(1, &m_hEvent, FALSE, INFINITE, QS_SENDMESSAGE);
135 
136                     switch (dwResult)
137                     {
138                         case WAIT_OBJECT_0:
139                             return;
140 
141                         case WAIT_OBJECT_0 + 1:
142                         {
143                             /* PeekMessage processes all messages in the SendMessage
144                                queue that's what we want, messages from the PostMessage
145                                queue stay untouched */
146                             MSG msg;
147                             PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
148 
149                             break;
150                         }
151                     }
152                 }
153             }
154 
155             // reset the event
156             void set()
157             {
158                 SetEvent(m_hEvent);
159             }
160 
161         private:
162             HANDLE m_hEvent;
163 
164         // prevent copy/assignment
165         private:
166             Win32Condition(const Win32Condition&);
167             Win32Condition& operator=(const Win32Condition&);
168     };
169 
170     //------------------------------------------
171     // we use one condition for every request
172     //------------------------------------------
173 
174     struct MsgCtx
175     {
176         Win32Condition  aCondition;
177         HRESULT         hr;
178     };
179 
180 } /* namespace private */
181 
182 //----------------------------------------------------------------
183 //  static member initialization
184 //----------------------------------------------------------------
185 
186 CMtaOleClipboard* CMtaOleClipboard::s_theMtaOleClipboardInst = NULL;
187 
188 //--------------------------------------------------------------------
189 // marshal an IDataObject
190 //--------------------------------------------------------------------
191 
192 //inline
193 HRESULT MarshalIDataObjectInStream( IDataObject* pIDataObject, LPSTREAM* ppStream )
194 {
195     OSL_ASSERT( NULL != pIDataObject );
196     OSL_ASSERT( NULL != ppStream );
197 
198     *ppStream = NULL;
199     return CoMarshalInterThreadInterfaceInStream(
200         __uuidof(IDataObject),  //The IID of inteface to be marshaled
201         pIDataObject,           //The interface pointer
202         ppStream                //IStream pointer
203         );
204 }
205 
206 //--------------------------------------------------------------------
207 // unmarshal an IDataObject
208 //--------------------------------------------------------------------
209 
210 //inline
211 HRESULT UnmarshalIDataObjectAndReleaseStream( LPSTREAM lpStream, IDataObject** ppIDataObject )
212 {
213     OSL_ASSERT( NULL != lpStream );
214     OSL_ASSERT( NULL != ppIDataObject );
215 
216     *ppIDataObject = NULL;
217     return CoGetInterfaceAndReleaseStream(
218         lpStream,
219         __uuidof(IDataObject),
220         reinterpret_cast<LPVOID*>(ppIDataObject));
221 }
222 
223 //--------------------------------------------------------------------
224 // helper class to ensure that the calling thread has com initialized
225 //--------------------------------------------------------------------
226 
227 class CAutoComInit
228 {
229 public:
230     CAutoComInit( )
231     {
232         /*
233             to be safe we call CoInitialize
234             although it is not necessary if
235             the calling thread was created
236             using osl_CreateThread because
237             this function calls CoInitialize
238             for every thread it creates
239         */
240         m_hResult = CoInitialize( NULL );
241 
242         if ( S_OK == m_hResult )
243             OSL_ENSURE( sal_False, \
244             "com was not yet initialzed, the thread was not created using osl_createThread" );
245         else if ( FAILED( m_hResult ) && !( RPC_E_CHANGED_MODE == m_hResult ) )
246             OSL_ENSURE( sal_False, \
247             "com could not be initialized, maybe the thread was not created using osl_createThread" );
248     }
249 
250     ~CAutoComInit( )
251     {
252         /*
253             we only call CoUninitialize when
254             CoInitailize returned S_FALSE, what
255             means that com was already initialize
256             for that thread so we keep the balance
257             if CoInitialize returned S_OK what means
258             com was not yet initialized we better
259             let com initialized or we may run into
260             the realm of undefined behaviour
261         */
262         if ( m_hResult == S_FALSE )
263             CoUninitialize( );
264     }
265 
266 private:
267     HRESULT m_hResult;
268 };
269 
270 //--------------------------------------------------------------------
271 // ctor
272 //--------------------------------------------------------------------
273 
274 CMtaOleClipboard::CMtaOleClipboard( ) :
275     m_hOleThread( NULL ),
276     m_uOleThreadId( 0 ),
277     m_hEvtThrdReady( NULL ),
278     m_hwndMtaOleReqWnd( NULL ),
279     m_MtaOleReqWndClassAtom( 0 ),
280     m_hwndNextClipViewer( NULL ),
281     m_pfncClipViewerCallback( NULL ),
282     m_bRunClipboardNotifierThread( sal_True ),
283     m_hClipboardChangedEvent( m_hClipboardChangedNotifierEvents[0] ),
284     m_hTerminateClipboardChangedNotifierEvent( m_hClipboardChangedNotifierEvents[1] ),
285     m_ClipboardChangedEventCount( 0 )
286 {
287     // signals that the thread was successfully setup
288     m_hEvtThrdReady  = CreateEventA( 0, MANUAL_RESET, INIT_NONSIGNALED, NULL );
289 
290     OSL_ASSERT( NULL != m_hEvtThrdReady );
291 
292     s_theMtaOleClipboardInst = this;
293 
294     m_hOleThread = (HANDLE)_beginthreadex(
295         NULL, 0, CMtaOleClipboard::oleThreadProc, this, 0, &m_uOleThreadId );
296     OSL_ASSERT( NULL != m_hOleThread );
297 
298     //----------------------------------------------
299     // setup the clipboard changed notifier thread
300     //----------------------------------------------
301 
302     m_hClipboardChangedNotifierEvents[0] = CreateEventA( 0, MANUAL_RESET, INIT_NONSIGNALED, NULL );
303     OSL_ASSERT( NULL != m_hClipboardChangedNotifierEvents[0] );
304 
305     m_hClipboardChangedNotifierEvents[1] = CreateEventA( 0, MANUAL_RESET, INIT_NONSIGNALED, NULL );
306     OSL_ASSERT( NULL != m_hClipboardChangedNotifierEvents[1] );
307 
308     unsigned uThreadId;
309     m_hClipboardChangedNotifierThread = (HANDLE)_beginthreadex(
310         NULL, 0, CMtaOleClipboard::clipboardChangedNotifierThreadProc, this, 0, &uThreadId );
311 
312     OSL_ASSERT( NULL != m_hClipboardChangedNotifierThread );
313 }
314 
315 //--------------------------------------------------------------------
316 // dtor
317 //--------------------------------------------------------------------
318 
319 CMtaOleClipboard::~CMtaOleClipboard( )
320 {
321     // block calling threads out
322     if ( NULL != m_hEvtThrdReady )
323         ResetEvent( m_hEvtThrdReady );
324 
325     // terminate the clipboard changed notifier thread
326     m_bRunClipboardNotifierThread = sal_False;
327     SetEvent( m_hTerminateClipboardChangedNotifierEvent );
328 
329     sal_uInt32 dwResult = WaitForSingleObject(
330         m_hClipboardChangedNotifierThread, MAX_WAIT_SHUTDOWN );
331 
332     OSL_ENSURE( dwResult == WAIT_OBJECT_0, "clipboard notifier thread could not terminate" );
333 
334     if ( NULL != m_hClipboardChangedNotifierThread )
335         CloseHandle( m_hClipboardChangedNotifierThread );
336 
337     if ( NULL != m_hClipboardChangedNotifierEvents[0] )
338         CloseHandle( m_hClipboardChangedNotifierEvents[0] );
339 
340     if ( NULL != m_hClipboardChangedNotifierEvents[1] )
341         CloseHandle( m_hClipboardChangedNotifierEvents[1] );
342 
343     // end the thread
344     // because DestroyWindow can only be called
345     // from within the thread that created the window
346     sendMessage( MSG_SHUTDOWN,
347                  static_cast< WPARAM >( 0 ),
348                  static_cast< LPARAM >( 0 ) );
349 
350     // wait for thread shutdown
351     dwResult = WaitForSingleObject( m_hOleThread, MAX_WAIT_SHUTDOWN );
352     OSL_ENSURE( dwResult == WAIT_OBJECT_0, "OleThread could not terminate" );
353 
354     if ( NULL != m_hOleThread )
355         CloseHandle( m_hOleThread );
356 
357     if ( NULL != m_hEvtThrdReady )
358         CloseHandle( m_hEvtThrdReady );
359 
360     if ( m_MtaOleReqWndClassAtom )
361         UnregisterClassA( g_szWndClsName, NULL );
362 
363     OSL_ENSURE( ( NULL == m_pfncClipViewerCallback ) &&
364                 !IsWindow( m_hwndNextClipViewer ), \
365                 "Clipboard viewer not properly unregistered" );
366 }
367 
368 
369 //--------------------------------------------------------------------
370 //
371 //--------------------------------------------------------------------
372 
373 HRESULT CMtaOleClipboard::flushClipboard( )
374 {
375     if ( !WaitForThreadReady( ) )
376     {
377         OSL_ENSURE( sal_False, "clipboard sta thread not ready" );
378         return E_FAIL;
379     }
380 
381     OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, \
382         "flushClipboard from within clipboard sta thread called" );
383 
384     MsgCtx  aMsgCtx;
385 
386     postMessage( MSG_FLUSHCLIPBOARD,
387                  static_cast< WPARAM >( 0 ),
388                  reinterpret_cast< LPARAM >( &aMsgCtx ) );
389 
390     aMsgCtx.aCondition.wait( /* infinite */ );
391 
392     return aMsgCtx.hr;
393 }
394 
395 //--------------------------------------------------------------------
396 //
397 //--------------------------------------------------------------------
398 
399 HRESULT CMtaOleClipboard::getClipboard( IDataObject** ppIDataObject )
400 {
401     OSL_PRECOND( NULL != ppIDataObject, "invalid parameter" );
402     OSL_PRECOND( GetCurrentThreadId( ) != m_uOleThreadId, "getClipboard from within clipboard sta thread called" );
403 
404     if ( !WaitForThreadReady( ) )
405     {
406         OSL_ENSURE( sal_False, "clipboard sta thread not ready" );
407         return E_FAIL;
408     }
409 
410     CAutoComInit comAutoInit;
411 
412     LPSTREAM lpStream;
413     HRESULT  hr = E_FAIL;
414 
415     *ppIDataObject = NULL;
416 
417     MsgCtx    aMsgCtx;
418 
419     postMessage( MSG_GETCLIPBOARD,
420                  reinterpret_cast< WPARAM >( &lpStream ),
421                  reinterpret_cast< LPARAM >( &aMsgCtx ) );
422 
423     aMsgCtx.aCondition.wait( /* infinite */ );
424 
425     hr = aMsgCtx.hr;
426 
427     if ( SUCCEEDED( hr ) )
428     {
429         hr = UnmarshalIDataObjectAndReleaseStream( lpStream, ppIDataObject );
430         OSL_ENSURE( SUCCEEDED( hr ), "unmarshalling clipboard data object failed" );
431     }
432 
433     return hr;
434 }
435 
436 //--------------------------------------------------------------------
437 // this is an asynchronous method that's why we don't wait until the
438 // request is completed
439 //--------------------------------------------------------------------
440 
441 HRESULT CMtaOleClipboard::setClipboard( IDataObject* pIDataObject )
442 {
443     if ( !WaitForThreadReady( ) )
444     {
445         OSL_ENSURE( sal_False, "clipboard sta thread not ready" );
446         return E_FAIL;
447     }
448 
449     CAutoComInit comAutoInit;
450 
451     OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "setClipboard from within the clipboard sta thread called" );
452 
453     // because we marshall this request
454     // into the sta thread we better
455     // acquire the interface here so
456     // that the object will not be
457     // destroyed before the ole clipboard
458     // can acquire it
459     // remember: pIDataObject may be NULL
460     // which is an request to clear the
461     // current clipboard content
462     if ( pIDataObject )
463         pIDataObject->AddRef( );
464 
465     postMessage(
466         MSG_SETCLIPBOARD,
467         reinterpret_cast< WPARAM >( pIDataObject ),
468         0 );
469 
470     // because this is an asynchronous function
471     // the return value is useless
472     return S_OK;
473 }
474 
475 //--------------------------------------------------------------------
476 // register a clipboard viewer
477 //--------------------------------------------------------------------
478 
479 sal_Bool CMtaOleClipboard::registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
480 {
481     if ( !WaitForThreadReady( ) )
482     {
483         OSL_ENSURE( sal_False, "clipboard sta thread not ready" );
484         return sal_False;
485     }
486 
487     sal_Bool bRet = sal_False;
488 
489     OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "registerClipViewer from within the OleThread called" );
490 
491     MsgCtx  aMsgCtx;
492 
493     postMessage( MSG_REGCLIPVIEWER,
494                  reinterpret_cast<WPARAM>( pfncClipViewerCallback ),
495                  reinterpret_cast<LPARAM>( &aMsgCtx ) );
496 
497     aMsgCtx.aCondition.wait( /* infinite */ );
498 
499     return bRet;
500 }
501 
502 //--------------------------------------------------------------------
503 // register a clipboard viewer
504 //--------------------------------------------------------------------
505 
506 sal_Bool CMtaOleClipboard::onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
507 {
508     sal_Bool bRet = sal_True;
509 
510     // we need exclusive access because the clipboard changed notifier
511     // thread also accesses this variable
512     MutexGuard aGuard( m_pfncClipViewerCallbackMutex );
513 
514     // register if not yet done
515     if ( ( NULL != pfncClipViewerCallback ) && ( NULL == m_pfncClipViewerCallback ) )
516     {
517         // SetClipboardViewer sends a WM_DRAWCLIPBOARD message we ignore
518         // this message if we register ourself as clip viewer
519         m_bInRegisterClipViewer = sal_True;
520         m_hwndNextClipViewer = SetClipboardViewer( m_hwndMtaOleReqWnd );
521         m_bInRegisterClipViewer = sal_False;
522 
523         // if there is no other cb-viewer the
524         // return value is NULL!!!
525         bRet = IsWindow( m_hwndNextClipViewer ) ? sal_True : sal_False;
526 
527         // save the new callback function
528         m_pfncClipViewerCallback = pfncClipViewerCallback;
529     }
530     else if ( ( NULL == pfncClipViewerCallback ) && ( NULL != m_pfncClipViewerCallback ) )
531     {
532         m_pfncClipViewerCallback = NULL;
533 
534         // unregister if input parameter is NULL and we previously registered
535         // as clipboard viewer
536         ChangeClipboardChain( m_hwndMtaOleReqWnd, m_hwndNextClipViewer );
537         m_hwndNextClipViewer = NULL;
538     }
539 
540     return bRet;
541 }
542 
543 //--------------------------------------------------------------------
544 //
545 //--------------------------------------------------------------------
546 
547 LRESULT CMtaOleClipboard::onSetClipboard( IDataObject* pIDataObject )
548 {
549     return static_cast<LRESULT>( OleSetClipboard( pIDataObject ) );
550 }
551 
552 //--------------------------------------------------------------------
553 //
554 //--------------------------------------------------------------------
555 
556 LRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream )
557 {
558     OSL_ASSERT(NULL != ppStream);
559 
560     IDataObjectPtr pIDataObject;
561 
562     // forward the request to the OleClipboard
563     HRESULT hr = OleGetClipboard( &pIDataObject );
564     if ( SUCCEEDED( hr ) )
565     {
566         hr = MarshalIDataObjectInStream(pIDataObject.get(), ppStream);
567         OSL_ENSURE(SUCCEEDED(hr), "marshalling cliboard data object failed");
568     }
569     return static_cast<LRESULT>(hr);
570 }
571 
572 //--------------------------------------------------------------------
573 // flush the ole-clipboard
574 //--------------------------------------------------------------------
575 
576 LRESULT CMtaOleClipboard::onFlushClipboard( )
577 {
578     return static_cast<LRESULT>( OleFlushClipboard( ) );
579 }
580 
581 //--------------------------------------------------------------------
582 // handle clipboard chain change event
583 //--------------------------------------------------------------------
584 
585 LRESULT CMtaOleClipboard::onChangeCBChain( HWND hWndRemove, HWND hWndNext )
586 {
587     if ( hWndRemove == m_hwndNextClipViewer )
588         m_hwndNextClipViewer = hWndNext;
589     else if ( IsWindow( m_hwndNextClipViewer ) )
590     {
591         // forward the message to the next one
592         DWORD dwResult;
593         SendMessageTimeoutA(
594             m_hwndNextClipViewer,
595             WM_CHANGECBCHAIN,
596             reinterpret_cast<WPARAM>(hWndRemove),
597             reinterpret_cast<LPARAM>(hWndNext),
598             SMTO_BLOCK,
599             MAX_CLIPEVENT_PROCESSING_TIME,
600             &dwResult );
601     }
602 
603     return 0;
604 }
605 
606 //--------------------------------------------------------------------
607 // handle draw clipboard event
608 //--------------------------------------------------------------------
609 
610 LRESULT CMtaOleClipboard::onDrawClipboard( )
611 {
612     // we don't send a notification if we are
613     // registering ourself as clipboard
614     if ( !m_bInRegisterClipViewer )
615     {
616         ClearableMutexGuard aGuard( m_ClipboardChangedEventCountMutex );
617 
618         m_ClipboardChangedEventCount++;
619         SetEvent( m_hClipboardChangedEvent );
620 
621         aGuard.clear( );
622     }
623 
624     // foward the message to the next viewer in the chain
625     if ( IsWindow( m_hwndNextClipViewer ) )
626     {
627         DWORD dwResult;
628         SendMessageTimeoutA(
629             m_hwndNextClipViewer,
630             WM_DRAWCLIPBOARD,
631             static_cast< WPARAM >( 0 ),
632             static_cast< LPARAM >( 0 ),
633             SMTO_BLOCK,
634             MAX_CLIPEVENT_PROCESSING_TIME,
635             &dwResult );
636     }
637 
638     return 0;
639 }
640 
641 //--------------------------------------------------------------------
642 // SendMessage so we don't need to supply the HWND if we send
643 // something to our wrapped window
644 //--------------------------------------------------------------------
645 
646 LRESULT CMtaOleClipboard::sendMessage( UINT msg, WPARAM wParam, LPARAM lParam )
647 {
648     return ::SendMessageA( m_hwndMtaOleReqWnd, msg, wParam, lParam );
649 }
650 
651 //--------------------------------------------------------------------
652 // PostMessage so we don't need to supply the HWND if we send
653 // something to our wrapped window
654 //--------------------------------------------------------------------
655 
656 sal_Bool CMtaOleClipboard::postMessage( UINT msg, WPARAM wParam, LPARAM lParam )
657 {
658     return PostMessageA( m_hwndMtaOleReqWnd, msg, wParam, lParam ) ? sal_True : sal_False;
659 }
660 
661 
662 //--------------------------------------------------------------------
663 // the window proc
664 //--------------------------------------------------------------------
665 
666 LRESULT CALLBACK CMtaOleClipboard::mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
667 {
668     LRESULT lResult = 0;
669 
670     // get a connection to the class-instance via the static member
671     CMtaOleClipboard* pImpl = CMtaOleClipboard::s_theMtaOleClipboardInst;
672     OSL_ASSERT( NULL != pImpl );
673 
674     switch( uMsg )
675     {
676     case MSG_SETCLIPBOARD:
677         {
678             IDataObject* pIDataObject = reinterpret_cast< IDataObject* >( wParam );
679             pImpl->onSetClipboard( pIDataObject );
680 
681             // in setClipboard we did acquire the
682             // interface pointer in order to prevent
683             // destruction of the object before the
684             // ole clipboard can acquire the interface
685             // now we release the interface so that
686             // our lostOwnership mechanism works
687             // remember: pIDataObject may be NULL
688             if ( pIDataObject )
689                 pIDataObject->Release( );
690         }
691         break;
692 
693     case MSG_GETCLIPBOARD:
694         {
695             MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
696             OSL_ASSERT( aMsgCtx );
697 
698             aMsgCtx->hr = pImpl->onGetClipboard( reinterpret_cast< LPSTREAM* >(wParam) );
699             aMsgCtx->aCondition.set( );
700         }
701         break;
702 
703     case MSG_FLUSHCLIPBOARD:
704         {
705             MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
706             OSL_ASSERT( aMsgCtx );
707 
708             aMsgCtx->hr = pImpl->onFlushClipboard( );
709             aMsgCtx->aCondition.set( );
710         }
711         break;
712 
713     case MSG_REGCLIPVIEWER:
714         {
715             MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
716             OSL_ASSERT( aMsgCtx );
717 
718             pImpl->onRegisterClipViewer( reinterpret_cast<CMtaOleClipboard::LPFNC_CLIPVIEWER_CALLBACK_t>(wParam) );
719             aMsgCtx->aCondition.set( );
720         }
721         break;
722 
723     case WM_CHANGECBCHAIN:
724         lResult = pImpl->onChangeCBChain(
725             reinterpret_cast< HWND >( wParam ), reinterpret_cast< HWND >( lParam ) );
726         break;
727 
728     case WM_DRAWCLIPBOARD:
729         lResult = pImpl->onDrawClipboard( );
730         break;
731 
732     case MSG_SHUTDOWN:
733         DestroyWindow( pImpl->m_hwndMtaOleReqWnd );
734         break;
735 
736     // force the sta thread to end
737     case WM_DESTROY:
738         PostQuitMessage( 0 );
739         break;
740 
741     default:
742         lResult = DefWindowProcA( hWnd, uMsg, wParam, lParam );
743         break;
744     }
745 
746     return lResult;
747 }
748 
749 //--------------------------------------------------------------------
750 //
751 //--------------------------------------------------------------------
752 
753 void CMtaOleClipboard::createMtaOleReqWnd( )
754 {
755     WNDCLASSEXA  wcex;
756 
757     HINSTANCE hInst = GetModuleHandleA( CLIPSRV_DLL_NAME );
758     OSL_ENSURE( NULL != hInst, "The name of the clipboard service dll must have changed" );
759 
760     ZeroMemory( &wcex, sizeof( WNDCLASSEXA ) );
761 
762     wcex.cbSize         = sizeof(WNDCLASSEXA);
763     wcex.style          = 0;
764     wcex.lpfnWndProc    = static_cast< WNDPROC >( CMtaOleClipboard::mtaOleReqWndProc );
765     wcex.cbClsExtra     = 0;
766     wcex.cbWndExtra     = 0;
767     wcex.hInstance      = hInst;
768     wcex.hIcon          = NULL;
769     wcex.hCursor        = NULL;
770     wcex.hbrBackground  = NULL;
771     wcex.lpszMenuName   = NULL;
772     wcex.lpszClassName  = g_szWndClsName;
773     wcex.hIconSm        = NULL;
774 
775     m_MtaOleReqWndClassAtom = RegisterClassExA( &wcex );
776 
777     if ( 0 != m_MtaOleReqWndClassAtom )
778         m_hwndMtaOleReqWnd = CreateWindowA(
779             g_szWndClsName, NULL, 0, 0, 0, 0, 0, NULL, NULL, hInst, NULL );
780 }
781 
782 //--------------------------------------------------------------------
783 //
784 //--------------------------------------------------------------------
785 
786 unsigned int CMtaOleClipboard::run( )
787 {
788     #if OSL_DEBUG_LEVEL > 0
789     HRESULT hr =
790     #endif
791         OleInitialize( NULL );
792     OSL_ASSERT( SUCCEEDED( hr ) );
793 
794     createMtaOleReqWnd( );
795 
796     unsigned int nRet;
797 
798     if ( IsWindow( m_hwndMtaOleReqWnd ) )
799     {
800         if ( NULL != m_hEvtThrdReady )
801             SetEvent( m_hEvtThrdReady );
802 
803         // pumping messages
804         MSG msg;
805         while( GetMessageA( &msg, NULL, 0, 0 ) )
806             DispatchMessageA( &msg );
807 
808         nRet = 0;
809     }
810     else
811         nRet = ~0U;
812 
813     OleUninitialize( );
814 
815     return nRet;
816 }
817 
818 //--------------------------------------------------------------------
819 //
820 //--------------------------------------------------------------------
821 
822 unsigned int WINAPI CMtaOleClipboard::oleThreadProc( LPVOID pParam )
823 {
824     CMtaOleClipboard* pInst =
825         reinterpret_cast<CMtaOleClipboard*>( pParam );
826     OSL_ASSERT( NULL != pInst );
827 
828     return pInst->run( );
829 }
830 
831 //--------------------------------------------------------------------
832 //
833 //--------------------------------------------------------------------
834 
835 unsigned int WINAPI CMtaOleClipboard::clipboardChangedNotifierThreadProc( LPVOID pParam )
836 {
837     CMtaOleClipboard* pInst = reinterpret_cast< CMtaOleClipboard* >( pParam );
838     OSL_ASSERT( NULL != pInst );
839 
840     CoInitialize( NULL );
841 
842     // assuming we don't need a lock for
843     // a boolean variable like m_bRun...
844     while ( pInst->m_bRunClipboardNotifierThread )
845     {
846         // wait for clipboard changed or terminate event
847         WaitForMultipleObjects( 2, pInst->m_hClipboardChangedNotifierEvents, false, INFINITE );
848 
849         ClearableMutexGuard aGuard( pInst->m_ClipboardChangedEventCountMutex );
850 
851         if ( pInst->m_ClipboardChangedEventCount > 0 )
852         {
853             pInst->m_ClipboardChangedEventCount--;
854             if ( 0 == pInst->m_ClipboardChangedEventCount )
855                 ResetEvent( pInst->m_hClipboardChangedEvent );
856 
857             aGuard.clear( );
858 
859             // nobody should touch m_pfncClipViewerCallback while we do
860             MutexGuard aClipViewerGuard( pInst->m_pfncClipViewerCallbackMutex );
861 
862             // notify all clipboard listener
863             if ( pInst->m_pfncClipViewerCallback )
864                 pInst->m_pfncClipViewerCallback( );
865         }
866         else
867             aGuard.clear( );
868     }
869 
870     CoUninitialize( );
871 
872     return ( 0 );
873 }
874 
875 //--------------------------------------------------------------------
876 //
877 //--------------------------------------------------------------------
878 
879 inline
880 sal_Bool CMtaOleClipboard::WaitForThreadReady( ) const
881 {
882     sal_Bool bRet = sal_False;
883 
884     if ( NULL != m_hEvtThrdReady )
885     {
886         DWORD dwResult = WaitForSingleObject(
887             m_hEvtThrdReady, MAX_WAITTIME );
888         bRet = ( dwResult == WAIT_OBJECT_0 );
889     }
890 
891     return bRet;
892 }
893 
894