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