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