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