1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
21 #include <osl/thread.h>
22 #include <osl/conditn.hxx>
27 #include "../misc/resourceprovider.hxx"
29 #include <systools/win32/comtools.hxx>
33 const sal_uInt32 MSG_BROWSEFORFOLDER
= WM_USER
+ 1;
34 const sal_uInt32 MSG_SHUTDOWN
= WM_USER
+ 2;
36 const sal_uInt32 MAX_WAITTIME
= 2000; // msec
38 const sal_Bool MANUAL_RESET
= sal_True
;
39 const sal_Bool AUTO_RESET
= sal_False
;
40 const sal_Bool INIT_NONSIGNALED
= sal_False
;
42 typedef sal::systools::COMReference
<IMalloc
> IMallocPtr
;
43 typedef sal::systools::COMReference
<IShellFolder
> IShellFolderPtr
;
47 const char* FOLDERPICKER_SRV_DLL_NAME
= "fps.dll";
48 const char g_szWndClsName
[] = "FopStaReqWnd###";
49 const char* CURRENT_INSTANCE
= "CurrInst";
51 typedef struct _RequestContext
57 inline sal_Bool
InitializeRequestContext( RequestContext
* aRequestContext
)
59 OSL_ASSERT( aRequestContext
);
61 aRequestContext
->hEvent
= CreateEventA(
62 0, AUTO_RESET
, INIT_NONSIGNALED
, NULL
);
64 aRequestContext
->bRet
= sal_False
;
66 return ( 0 != aRequestContext
->hEvent
);
69 inline void DeinitializeRequestContext( RequestContext
* aRequestContext
)
71 OSL_ASSERT( aRequestContext
&& aRequestContext
->hEvent
);
72 CloseHandle( aRequestContext
->hEvent
);
76 // Determine if current thread is
77 // an MTA or STA thread
81 HRESULT hr
= CoInitialize(NULL
);
83 if (RPC_E_CHANGED_MODE
== hr
)
94 // static member initialization
97 ATOM
CMtaFolderPicker::s_ClassAtom
= 0;
98 osl::Mutex
CMtaFolderPicker::s_Mutex
;
99 sal_Int32
CMtaFolderPicker::s_StaRequestWndRegisterCount
= 0;
105 CMtaFolderPicker::CMtaFolderPicker( sal_uInt32 Flags
) :
106 m_hStaThread( NULL
),
108 m_hEvtThrdReady( NULL
),
109 m_hwndStaRequestWnd( NULL
)
111 m_hInstance
= GetModuleHandleA( FOLDERPICKER_SRV_DLL_NAME
);
112 OSL_ENSURE( m_hInstance
, "The name of the FolderPicker service dll must have changed" );
114 ZeroMemory( &m_bi
, sizeof( m_bi
) );
116 // !!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!
118 // Remember: This HACK prevents you from stepping
119 // through your code in the debugger because if you
120 // set a break point in the ctor here the debugger
121 // may become the owner of the FolderBrowse dialog
122 // and so it seems that the Visual Studio and the
123 // office are hanging
124 m_bi
.hwndOwner
= GetForegroundWindow( );
128 --------------------------------
129 BIF_EDITBOX Version 4.71
130 BIF_NEWDIALOGSTYLE Version 5.0
131 BIF_SHAREABLE Version 5.0
132 BIF_VALIDATE Version 4.71
134 Version 4.71 - Internet Explorer 4.0
135 Version 5.0 - Internet Explorer 5.0
138 m_bi
.ulFlags
= Flags
;
140 m_bi
.lpfn
= CMtaFolderPicker::FolderPickerCallback
;
141 m_bi
.lParam
= reinterpret_cast< LPARAM
>( this );
144 // read the default strings for title and
145 // description from a resource file
147 CResourceProvider ResProvider
;
149 m_dialogTitle
= ResProvider
.getResString( 500 );
150 m_Description
= ResProvider
.getResString( 501 );
152 // signals that the thread was successfully set up
153 m_hEvtThrdReady
= CreateEventA(
159 if ( m_hEvtThrdReady
)
161 // setup the sta thread
162 m_hStaThread
= (HANDLE
)_beginthreadex(
165 CMtaFolderPicker::StaThreadProc
,
170 OSL_ASSERT( m_hStaThread
);
173 OSL_ASSERT( m_hEvtThrdReady
);
180 CMtaFolderPicker::~CMtaFolderPicker( )
182 // only if the is a valid event handle
183 // there may also be a thread a hidden
184 // target request window and so on
186 if ( m_hEvtThrdReady
)
188 // block calling threads because we
189 // are about to shutdown
190 ResetEvent( m_hEvtThrdReady
);
192 // force the destruction of the sta thread request window
193 // and the end of the thread
194 // remember: DestroyWindow may only be called from within
195 // the thread that created the window
196 if ( IsWindow( m_hwndStaRequestWnd
) )
198 SendMessageA( m_hwndStaRequestWnd
, MSG_SHUTDOWN
, 0, 0 );
200 // we place unregister class here because
201 // if we have a valid window we must have
202 // successfully registered a window class
203 // if the creation of the window itself
204 // failed after registering the window
205 // class we have unregistered it immediately
206 // in createStaRequestWindow below
207 UnregisterStaRequestWindowClass( );
212 // wait for thread shutdown
213 sal_uInt32 dwResult
= WaitForSingleObject( m_hStaThread
, MAX_WAITTIME
);
214 OSL_ENSURE( dwResult
== WAIT_OBJECT_0
, "sta thread could not terminate" );
216 // terminate the thread if it
217 // doesn't shutdown itself
218 if ( WAIT_OBJECT_0
!= dwResult
)
220 m_hStaThread
, sal::static_int_cast
< DWORD
>(-1) );
222 CloseHandle( m_hStaThread
);
225 CloseHandle( m_hEvtThrdReady
);
233 sal_Bool
CMtaFolderPicker::browseForFolder( )
235 sal_Bool bRet
= sal_False
;
240 OSL_ASSERT( m_hEvtThrdReady
);
242 if ( WaitForSingleObject( m_hEvtThrdReady
, MAX_WAITTIME
) != WAIT_OBJECT_0
)
244 OSL_FAIL( "sta thread not ready" );
248 RequestContext aReqCtx
;
250 if ( !InitializeRequestContext( &aReqCtx
) )
252 OSL_ASSERT( sal_False
);
256 // marshall request into the sta thread
261 reinterpret_cast< LPARAM
>( &aReqCtx
) );
263 // waiting for the event to be signaled or
264 // window messages so that we don't block
267 sal_Bool bContinue
= sal_True
;
271 DWORD dwResult
= MsgWaitForMultipleObjects(
272 1, &aReqCtx
.hEvent
, sal_False
, INFINITE
, QS_ALLEVENTS
);
276 // the request context event is signaled
278 bContinue
= sal_False
;
281 // a window message has arrived
282 case WAIT_OBJECT_0
+ 1:
284 // dispatching all messages but we expect to
285 // receive only paint or timer messages that's
286 // why we don't need to call TranslateMessage or
287 // TranslateAccelerator, because keybord or
288 // mouse messages are for the FolderPicker which
289 // is in the foreground and should not arrive here
291 while ( PeekMessageA( &msg
, NULL
, 0, 0, PM_REMOVE
) )
292 DispatchMessageA(&msg
);
298 OSL_ASSERT( sal_False
);
302 /*sal_Bool*/ bRet
= aReqCtx
.bRet
;
303 DeinitializeRequestContext( &aReqCtx
);
307 bRet
= onBrowseForFolder();
317 void SAL_CALL
CMtaFolderPicker::setDisplayDirectory( const OUString
& aDirectory
)
319 m_displayDir
= aDirectory
;
326 OUString SAL_CALL
CMtaFolderPicker::getDisplayDirectory( )
335 OUString SAL_CALL
CMtaFolderPicker::getDirectory( )
337 return m_SelectedDir
;
344 void SAL_CALL
CMtaFolderPicker::setDescription( const OUString
& aDescription
)
346 m_Description
= aDescription
;
353 void SAL_CALL
CMtaFolderPicker::setTitle( const OUString
& aTitle
)
355 m_dialogTitle
= aTitle
;
362 OUString SAL_CALL
CMtaFolderPicker::getTitle( )
364 return m_dialogTitle
;
371 void SAL_CALL
CMtaFolderPicker::cancel( )
373 if ( IsWindow( m_hwnd
) )
375 // simulate a mouse click to the
380 MAKEWPARAM( IDCANCEL
, BN_CLICKED
),
381 (LPARAM
)GetDlgItem( m_hwnd
, IDCANCEL
) );
389 sal_Bool SAL_CALL
CMtaFolderPicker::onBrowseForFolder( )
394 // pre SHBrowseFroFolder
397 m_bi
.pszDisplayName
= reinterpret_cast<LPWSTR
>(m_pathBuff
.get());
399 if ( m_Description
.getLength( ) )
400 m_bi
.lpszTitle
= reinterpret_cast<LPCWSTR
>(m_Description
.getStr( ));
402 lpiid
= SHBrowseForFolderW( &m_bi
);
403 bRet
= ( NULL
!= lpiid
);
405 // post SHBrowseForFolder
407 m_SelectedDir
= getPathFromItemIdList( lpiid
);
408 releaseItemIdList( lpiid
);
417 void SAL_CALL
CMtaFolderPicker::releaseItemIdList( LPITEMIDLIST lpItemIdList
)
420 SHGetMalloc(&pIMalloc
);
423 pIMalloc
->Free(lpItemIdList
);
432 LPITEMIDLIST SAL_CALL
CMtaFolderPicker::getItemIdListFromPath( const OUString
& aDirectory
)
434 // parameter checking
435 if ( !aDirectory
.getLength( ) )
438 LPITEMIDLIST
lpItemIdList(NULL
);
440 IShellFolderPtr pIShellFolder
;
441 SHGetDesktopFolder(&pIShellFolder
);
443 if (pIShellFolder
.is())
445 pIShellFolder
->ParseDisplayName(
448 reinterpret_cast<LPWSTR
>(const_cast< sal_Unicode
* >( aDirectory
.getStr( ) )),
461 OUString SAL_CALL
CMtaFolderPicker::getPathFromItemIdList( LPCITEMIDLIST lpItemIdList
)
467 bool bRet
= SHGetPathFromIDListW( lpItemIdList
, reinterpret_cast<LPWSTR
>(m_pathBuff
.get()) );
469 path
= m_pathBuff
.get( );
479 void SAL_CALL
CMtaFolderPicker::enableOk( sal_Bool bEnable
)
481 OSL_ASSERT( IsWindow( m_hwnd
) );
486 static_cast< WPARAM
>( 0 ),
487 static_cast< LPARAM
>( bEnable
) );
494 void SAL_CALL
CMtaFolderPicker::setSelection( const OUString
& aDirectory
)
496 OSL_ASSERT( IsWindow( m_hwnd
) );
501 static_cast< WPARAM
>( sal_True
),
502 reinterpret_cast< LPARAM
>( aDirectory
.getStr( ) ) );
509 void SAL_CALL
CMtaFolderPicker::setStatusText( const OUString
& aStatusText
)
511 OSL_ASSERT( IsWindow( m_hwnd
) );
516 static_cast< WPARAM
>( 0 ),
517 reinterpret_cast< LPARAM
>( aStatusText
.getStr( ) ) );
524 void SAL_CALL
CMtaFolderPicker::onInitialized( )
526 LPITEMIDLIST lpiidDisplayDir
= getItemIdListFromPath( m_displayDir
);
528 if ( lpiidDisplayDir
)
534 (LPARAM
) lpiidDisplayDir
);
536 releaseItemIdList( lpiidDisplayDir
);
544 sal_uInt32
CMtaFolderPicker::onValidateFailed()
546 // to be overwritten by subclasses
554 int CALLBACK
CMtaFolderPicker::FolderPickerCallback( HWND hwnd
, UINT uMsg
, LPARAM lParam
, LPARAM lpData
)
556 CMtaFolderPicker
* pImpl
= reinterpret_cast< CMtaFolderPicker
* >( lpData
);
563 case BFFM_INITIALIZED
:
564 pImpl
->m_hwnd
= hwnd
;
565 pImpl
->onInitialized( );
566 SetWindowTextW( hwnd
, reinterpret_cast<LPCWSTR
>(pImpl
->m_dialogTitle
.getStr()) );
569 case BFFM_SELCHANGED
:
570 pImpl
->m_hwnd
= hwnd
;
572 pImpl
->getPathFromItemIdList(
573 reinterpret_cast< LPITEMIDLIST
>( lParam
) ) );
576 case BFFM_VALIDATEFAILEDW
:
577 nRC
= pImpl
->onValidateFailed();
581 OSL_ASSERT( sal_False
);
591 LRESULT CALLBACK
CMtaFolderPicker::StaWndProc( HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
594 CMtaFolderPicker
* pImpl
= NULL
;
597 we connect to the belonging class instance of this
598 window using SetProp, GetProp etc.
599 this may fail if somehow the class instance destroyed
607 LPCREATESTRUCT lpcs
=
608 reinterpret_cast< LPCREATESTRUCT
>( lParam
);
610 OSL_ASSERT( lpcs
->lpCreateParams
);
612 // connect the instance handle to the window
613 SetPropA( hWnd
, CURRENT_INSTANCE
, lpcs
->lpCreateParams
);
618 // RemoveProp returns the saved value on success
619 pImpl
= reinterpret_cast< CMtaFolderPicker
* >(
620 RemovePropA( hWnd
, CURRENT_INSTANCE
) );
622 OSL_ASSERT( pImpl
&& !IsBadReadPtr( pImpl
, sizeof( CMtaFolderPicker
) ) );
625 case MSG_BROWSEFORFOLDER
:
627 RequestContext
* aReqCtx
= reinterpret_cast< RequestContext
* >( lParam
);
628 OSL_ASSERT( aReqCtx
);
630 pImpl
= reinterpret_cast< CMtaFolderPicker
* >(
631 GetPropA( hWnd
, CURRENT_INSTANCE
) );
633 OSL_ASSERT( pImpl
&& !IsBadReadPtr( pImpl
, sizeof( CMtaFolderPicker
) ) );
635 aReqCtx
->bRet
= pImpl
->onBrowseForFolder( );
636 SetEvent( aReqCtx
->hEvent
);
641 pImpl
= reinterpret_cast< CMtaFolderPicker
* >(
642 GetPropA( hWnd
, CURRENT_INSTANCE
) );
644 OSL_ASSERT( pImpl
&& !IsBadReadPtr( pImpl
, sizeof( CMtaFolderPicker
) ) );
646 DestroyWindow( pImpl
->m_hwndStaRequestWnd
);
650 PostQuitMessage( 0 );
654 lResult
= DefWindowProcA( hWnd
, uMsg
, wParam
, lParam
);
665 sal_Bool SAL_CALL
CMtaFolderPicker::createStaRequestWindow( )
669 if ( RegisterStaRequestWindowClass( ) )
671 m_hwndStaRequestWnd
= CreateWindowA(
672 g_szWndClsName
, NULL
,
674 NULL
, NULL
, m_hInstance
,
675 (LPVOID
)this // provide the instance of the class
678 bIsWnd
= IsWindow( m_hwndStaRequestWnd
);
680 // we do immediately unregister the window class
681 // if the creation of the window fails because we
682 // don't want to spoil the register class counter
684 UnregisterStaRequestWindowClass( );
686 OSL_ENSURE( bIsWnd
, "sta request window creation failed" );
696 unsigned int CMtaFolderPicker::run( )
698 OSL_ASSERT( m_hEvtThrdReady
);
700 // setup an sta environment
701 HRESULT hr
= CoInitialize( NULL
);
703 // if we can't setup an sta environment
704 // we stop here and return
707 OSL_FAIL( "CoInitialize failed" );
708 return sal::static_int_cast
< unsigned int >(-1);
713 if ( createStaRequestWindow( ) )
715 SetEvent( m_hEvtThrdReady
);
719 while( GetMessageA( &msg
, NULL
, 0, 0 ) )
720 DispatchMessageA( &msg
);
726 OSL_FAIL( "failed to create sta thread" );
727 nRet
= sal::static_int_cast
< unsigned int >(-1);
730 // shutdown sta environment
740 unsigned int WINAPI
CMtaFolderPicker::StaThreadProc( LPVOID pParam
)
742 osl_setThreadName("fpicker CMtaFolderPicker::run()");
744 CMtaFolderPicker
* pInst
=
745 reinterpret_cast<CMtaFolderPicker
*>( pParam
);
749 HRESULT hr
= OleInitialize( NULL
);
751 unsigned int result
= pInst
->run( );
753 if ( SUCCEEDED( hr
) )
763 ATOM SAL_CALL
CMtaFolderPicker::RegisterStaRequestWindowClass( )
765 osl::MutexGuard
aGuard( s_Mutex
);
767 if ( 0 == s_ClassAtom
)
771 ZeroMemory( &wcex
, sizeof( WNDCLASSEXA
) );
773 wcex
.cbSize
= sizeof(WNDCLASSEXA
);
775 wcex
.lpfnWndProc
= static_cast< WNDPROC
>( CMtaFolderPicker::StaWndProc
);
778 wcex
.hInstance
= m_hInstance
;
781 wcex
.hbrBackground
= NULL
;
782 wcex
.lpszMenuName
= NULL
;
783 wcex
.lpszClassName
= g_szWndClsName
;
786 s_ClassAtom
= RegisterClassExA( &wcex
);
787 OSL_ASSERT( s_ClassAtom
);
790 // increment the register class counter
791 // so that we keep track of the number
792 // of class registrations
793 if ( 0 != s_ClassAtom
)
794 s_StaRequestWndRegisterCount
++;
803 void SAL_CALL
CMtaFolderPicker::UnregisterStaRequestWindowClass( )
805 osl::MutexGuard
aGuard( s_Mutex
);
807 OSL_ASSERT( 0 != s_ClassAtom
);
809 // update the register class counter
810 // and unregister the window class if
811 // counter drops to zero
812 if ( 0 != s_ClassAtom
)
814 s_StaRequestWndRegisterCount
--;
815 OSL_ASSERT( s_StaRequestWndRegisterCount
>= 0 );
818 if ( 0 == s_StaRequestWndRegisterCount
)
821 (LPCTSTR
)(DWORD_PTR
)MAKELONG( s_ClassAtom
, 0 ), m_hInstance
);
827 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */