/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: MtaFop.cxx,v $ * $Revision: 1.17 $ * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_fpicker.hxx" #include #include #include "MtaFop.hxx" #include #include #include "..\misc\resourceprovider.hxx" #include using rtl::OUString; using osl::Condition; const sal_uInt32 MSG_BROWSEFORFOLDER = WM_USER + 1; const sal_uInt32 MSG_SHUTDOWN = WM_USER + 2; const sal_uInt32 MAX_WAITTIME = 2000; // msec const sal_Bool MANUAL_RESET = sal_True; const sal_Bool AUTO_RESET = sal_False; const sal_Bool INIT_NONSIGNALED = sal_False; typedef sal::systools::COMReference IMallocPtr; typedef sal::systools::COMReference IShellFolderPtr; namespace { const char* FOLDERPICKER_SRV_DLL_NAME = "fop.dll"; const char g_szWndClsName[] = "FopStaReqWnd###"; const char* CURRENT_INSTANCE = "CurrInst"; typedef struct _RequestContext { HANDLE hEvent; sal_Bool bRet; } RequestContext; inline sal_Bool InitializeRequestContext( RequestContext* aRequestContext ) { OSL_ASSERT( aRequestContext ); aRequestContext->hEvent = CreateEventA( 0, AUTO_RESET, INIT_NONSIGNALED, NULL ); aRequestContext->bRet = sal_False; return ( 0 != aRequestContext->hEvent ); } inline void DeinitializeRequestContext( RequestContext* aRequestContext ) { OSL_ASSERT( aRequestContext && aRequestContext->hEvent ); CloseHandle( aRequestContext->hEvent ); } //------------------------------- // Determine if current thread is // an MTA or STA thread //------------------------------- bool IsMTA() { HRESULT hr = CoInitialize(NULL); if (RPC_E_CHANGED_MODE == hr) return true; if(SUCCEEDED(hr)) CoUninitialize(); return false; } } //---------------------------------------------------------------- // static member initialization //---------------------------------------------------------------- ATOM CMtaFolderPicker::s_ClassAtom = 0; osl::Mutex CMtaFolderPicker::s_Mutex; sal_Int32 CMtaFolderPicker::s_StaRequestWndRegisterCount = 0; //-------------------------------------------------------------------- // ctor //-------------------------------------------------------------------- CMtaFolderPicker::CMtaFolderPicker( sal_uInt32 Flags ) : m_hStaThread( NULL ), m_uStaThreadId( 0 ), m_hEvtThrdReady( NULL ), m_hwndStaRequestWnd( NULL ) { m_hInstance = GetModuleHandleA( FOLDERPICKER_SRV_DLL_NAME ); OSL_ENSURE( m_hInstance, "The name of the FolderPicker service dll must have changed" ); ZeroMemory( &m_bi, sizeof( m_bi ) ); // !!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!! // // Remember: This HACK prevents you from stepping // through your code in the debugger because if you // set a break point in the ctor here the debugger // may become the owner of the FolderBrowse dialog // and so it seems that the Visual Studio and the // office are hanging m_bi.hwndOwner = GetForegroundWindow( ); /* Flag Available -------------------------------- BIF_EDITBOX Version 4.71 BIF_NEWDIALOGSTYLE Version 5.0 BIF_SHAREABLE Version 5.0 BIF_VALIDATE Version 4.71 Version 4.71 - Internet Explorer 4.0 Version 5.0 - Internet Explorer 5.0 Windows 2000 */ m_bi.ulFlags = Flags; m_bi.lpfn = CMtaFolderPicker::FolderPickerCallback; m_bi.lParam = reinterpret_cast< LPARAM >( this ); //--------------------------------------- // read the default strings for title and // description from a resource file CResourceProvider ResProvider; m_dialogTitle = ResProvider.getResString( 500 ); m_Description = ResProvider.getResString( 501 ); // signals that the thread was successfully set up m_hEvtThrdReady = CreateEventA( 0, MANUAL_RESET, INIT_NONSIGNALED, NULL ); if ( m_hEvtThrdReady ) { // setup the sta thread m_hStaThread = (HANDLE)_beginthreadex( NULL, 0, CMtaFolderPicker::StaThreadProc, this, 0, &m_uStaThreadId ); OSL_ASSERT( m_hStaThread ); } OSL_ASSERT( m_hEvtThrdReady ); } //-------------------------------------------------------------------- // dtor //-------------------------------------------------------------------- CMtaFolderPicker::~CMtaFolderPicker( ) { // only if the is a valid event handle // there may also be a thread a hidden // target request window and so on // see ctor if ( m_hEvtThrdReady ) { // block calling threads because we // are about to shutdown ResetEvent( m_hEvtThrdReady ); // force the destruction of the sta thread request window // and the end of the thread // remeber: DestroyWindow may only be called from within // the thread that created the window if ( IsWindow( m_hwndStaRequestWnd ) ) { SendMessageA( m_hwndStaRequestWnd, MSG_SHUTDOWN, 0, 0 ); // we place unregister class here because // if we have a valid window we must have // sucessfully registered a window class // if the creation of the window itself // failed after registering the window // class we have unregistered it immediately // in createStaRequestWindow below UnregisterStaRequestWindowClass( ); } if ( m_hStaThread ) { // wait for thread shutdown sal_uInt32 dwResult = WaitForSingleObject( m_hStaThread, MAX_WAITTIME ); OSL_ENSURE( dwResult == WAIT_OBJECT_0, "sta thread could not terminate" ); // terminate the thread if it // doesn't shutdown itself if ( WAIT_OBJECT_0 != dwResult ) TerminateThread( m_hStaThread, sal::static_int_cast< DWORD >(-1) ); CloseHandle( m_hStaThread ); } CloseHandle( m_hEvtThrdReady ); } } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- sal_Bool CMtaFolderPicker::browseForFolder( ) { sal_Bool bRet = sal_False; if (IsMTA()) { OSL_ASSERT( m_hEvtThrdReady ); if ( WaitForSingleObject( m_hEvtThrdReady, MAX_WAITTIME ) != WAIT_OBJECT_0 ) { OSL_ENSURE( sal_False, "sta thread not ready" ); return sal_False; } RequestContext aReqCtx; if ( !InitializeRequestContext( &aReqCtx ) ) { OSL_ASSERT( sal_False ); return sal_False; } // marshall request into the sta thread PostMessageA( m_hwndStaRequestWnd, MSG_BROWSEFORFOLDER, 0, reinterpret_cast< LPARAM >( &aReqCtx ) ); // waiting for the event to be signaled or // window messages so that we don't block // our parent window sal_Bool bContinue = sal_True; while ( bContinue ) { DWORD dwResult = MsgWaitForMultipleObjects( 1, &aReqCtx.hEvent, FALSE, INFINITE, QS_ALLEVENTS ); switch ( dwResult ) { // the request context event is signaled case WAIT_OBJECT_0: bContinue = sal_False; break; // a window message has arrived case WAIT_OBJECT_0 + 1: { // dispatching all messages but we expect to // receive only paint or timer messages that's // why we don't need to call TranslateMessage or // TranslateAccelerator, because keybord or // mouse messages are for the FolderPicker which // is in the foreground and should not arrive here MSG msg; while ( PeekMessageA( &msg, NULL, 0, 0, PM_REMOVE ) ) DispatchMessageA(&msg); } break; // should not happen default: OSL_ASSERT( sal_False ); } } /*sal_Bool*/ bRet = aReqCtx.bRet; DeinitializeRequestContext( &aReqCtx ); } else { bRet = onBrowseForFolder(); } return bRet; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- void SAL_CALL CMtaFolderPicker::setDisplayDirectory( const OUString& aDirectory ) { m_displayDir = aDirectory; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- OUString SAL_CALL CMtaFolderPicker::getDisplayDirectory( ) { return m_displayDir; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- OUString SAL_CALL CMtaFolderPicker::getDirectory( ) { return m_SelectedDir; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- void SAL_CALL CMtaFolderPicker::setDescription( const rtl::OUString& aDescription ) { m_Description = aDescription; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- void SAL_CALL CMtaFolderPicker::setTitle( const OUString& aTitle ) { m_dialogTitle = aTitle; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- OUString SAL_CALL CMtaFolderPicker::getTitle( ) { return m_dialogTitle; } //----------------------------------------------------- // XCancellable //----------------------------------------------------- void SAL_CALL CMtaFolderPicker::cancel( ) { if ( IsWindow( m_hwnd ) ) { // simulate a mouse click to the // cancel button PostMessageA( m_hwnd, WM_COMMAND, MAKEWPARAM( IDCANCEL, BN_CLICKED ), (LPARAM)GetDlgItem( m_hwnd, IDCANCEL ) ); } } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- sal_Bool SAL_CALL CMtaFolderPicker::onBrowseForFolder( ) { sal_Bool bRet; LPITEMIDLIST lpiid; // pre SHBrowseFroFolder m_bi.pidlRoot = 0; m_bi.pszDisplayName = reinterpret_cast(m_pathBuff.get()); if ( m_Description.getLength( ) ) m_bi.lpszTitle = reinterpret_cast(m_Description.getStr( )); lpiid = SHBrowseForFolderW( &m_bi ); bRet = ( NULL != lpiid ); // post SHBrowseForFolder m_SelectedDir = getPathFromItemIdList( lpiid ); releaseItemIdList( lpiid ); return bRet; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- void SAL_CALL CMtaFolderPicker::releaseItemIdList( LPITEMIDLIST lpItemIdList ) { IMallocPtr pIMalloc; SHGetMalloc(&pIMalloc); if (pIMalloc.is()) { pIMalloc->Free(lpItemIdList); lpItemIdList = NULL; } } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- LPITEMIDLIST SAL_CALL CMtaFolderPicker::getItemIdListFromPath( const rtl::OUString& aDirectory ) { // parameter checking if ( !aDirectory.getLength( ) ) return NULL; IMallocPtr pIMalloc; SHGetMalloc(&pIMalloc); LPITEMIDLIST lpItemIdList = static_cast( pIMalloc->Alloc(sizeof(ITEMIDLIST))); if (lpItemIdList) { IShellFolderPtr pIShellFolder; SHGetDesktopFolder(&pIShellFolder); if (pIShellFolder.is()) { pIShellFolder->ParseDisplayName( NULL, NULL, reinterpret_cast(const_cast< sal_Unicode* >( aDirectory.getStr( ) )), NULL, &lpItemIdList, NULL ); } } if (pIMalloc.is()) pIMalloc->Free(lpItemIdList); lpItemIdList = NULL; return lpItemIdList; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- OUString SAL_CALL CMtaFolderPicker::getPathFromItemIdList( LPCITEMIDLIST lpItemIdList ) { OUString path; if ( lpItemIdList ) { bool bRet = SHGetPathFromIDListW( lpItemIdList, reinterpret_cast(m_pathBuff.get()) ); if ( bRet ) path = m_pathBuff.get( ); } return path; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- void SAL_CALL CMtaFolderPicker::enableOk( sal_Bool bEnable ) { OSL_ASSERT( IsWindow( m_hwnd ) ); SendMessageW( m_hwnd, BFFM_ENABLEOK, static_cast< WPARAM >( 0 ), static_cast< LPARAM >( bEnable ) ); } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- void SAL_CALL CMtaFolderPicker::setSelection( const rtl::OUString& aDirectory ) { OSL_ASSERT( IsWindow( m_hwnd ) ); #ifdef _MSC_VER #pragma message( "#######################################" ) #pragma message( "SendMessageW wrapper has to be extended" ) #pragma message( "#######################################" ) #endif SendMessageW( m_hwnd, BFFM_SETSELECTIONW, static_cast< WPARAM >( sal_True ), reinterpret_cast< LPARAM >( aDirectory.getStr( ) ) ); } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- void SAL_CALL CMtaFolderPicker::setStatusText( const rtl::OUString& aStatusText ) { OSL_ASSERT( IsWindow( m_hwnd ) ); SendMessageW( m_hwnd, BFFM_SETSTATUSTEXTW, static_cast< WPARAM >( 0 ), reinterpret_cast< LPARAM >( aStatusText.getStr( ) ) ); } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- void SAL_CALL CMtaFolderPicker::onInitialized( ) { LPITEMIDLIST lpiidDisplayDir = getItemIdListFromPath( m_displayDir ); if ( lpiidDisplayDir ) { SendMessageA( m_hwnd, BFFM_SETSELECTION, (WPARAM)FALSE, (LPARAM) lpiidDisplayDir ); releaseItemIdList( lpiidDisplayDir ); } } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- sal_uInt32 CMtaFolderPicker::onValidateFailed() { // to be overwritten by subclasses return 1; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- int CALLBACK CMtaFolderPicker::FolderPickerCallback( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData ) { CMtaFolderPicker* pImpl = reinterpret_cast< CMtaFolderPicker* >( lpData ); OSL_ASSERT( pImpl ); int nRC = 0; switch( uMsg ) { case BFFM_INITIALIZED: pImpl->m_hwnd = hwnd; pImpl->onInitialized( ); SetWindowTextW( hwnd, reinterpret_cast(pImpl->m_dialogTitle.getStr()) ); break; case BFFM_SELCHANGED: pImpl->m_hwnd = hwnd; pImpl->onSelChanged( pImpl->getPathFromItemIdList( reinterpret_cast< LPITEMIDLIST >( lParam ) ) ); break; case BFFM_VALIDATEFAILEDW: nRC = pImpl->onValidateFailed(); break; default: OSL_ASSERT( sal_False ); } return nRC; } //-------------------------------------------------------------------- // the window proc //-------------------------------------------------------------------- LRESULT CALLBACK CMtaFolderPicker::StaWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { LRESULT lResult = 0; CMtaFolderPicker* pImpl = NULL; /* we connect to the belonging class instance of this window using SetProp, GetProp etc. this may fail if somehow the class instance destroyed before the window */ switch( uMsg ) { case WM_CREATE: { LPCREATESTRUCT lpcs = reinterpret_cast< LPCREATESTRUCT >( lParam ); OSL_ASSERT( lpcs->lpCreateParams ); // connect the instance handle to the window SetPropA( hWnd, CURRENT_INSTANCE, lpcs->lpCreateParams ); } break; case WM_NCDESTROY: // RemoveProp returns the saved value on success pImpl = reinterpret_cast< CMtaFolderPicker* >( RemovePropA( hWnd, CURRENT_INSTANCE ) ); OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) ); break; case MSG_BROWSEFORFOLDER: { RequestContext* aReqCtx = reinterpret_cast< RequestContext* >( lParam ); OSL_ASSERT( aReqCtx ); pImpl = reinterpret_cast< CMtaFolderPicker* >( GetPropA( hWnd, CURRENT_INSTANCE ) ); OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) ); aReqCtx->bRet = pImpl->onBrowseForFolder( ); SetEvent( aReqCtx->hEvent ); } break; case MSG_SHUTDOWN: pImpl = reinterpret_cast< CMtaFolderPicker* >( GetPropA( hWnd, CURRENT_INSTANCE ) ); OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) ); DestroyWindow( pImpl->m_hwndStaRequestWnd ); break; case WM_DESTROY: PostQuitMessage( 0 ); break; default: lResult = DefWindowProcA( hWnd, uMsg, wParam, lParam ); break; } return lResult; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- sal_Bool SAL_CALL CMtaFolderPicker::createStaRequestWindow( ) { bool bIsWnd = false; if ( RegisterStaRequestWindowClass( ) ) { m_hwndStaRequestWnd = CreateWindowA( g_szWndClsName, NULL, 0, 0, 0, 0, 0, NULL, NULL, m_hInstance, (LPVOID)this // provide the instance of the class ); bIsWnd = IsWindow( m_hwndStaRequestWnd ); // we do immediately unregister the window class // if the creation of the window fails because we // don't want to spoil the register class counter if ( !bIsWnd ) UnregisterStaRequestWindowClass( ); OSL_ENSURE( bIsWnd, "sta request window creation failed" ); } return bIsWnd; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- unsigned int CMtaFolderPicker::run( ) { OSL_ASSERT( m_hEvtThrdReady ); // setup an sta environment HRESULT hr = CoInitialize( NULL ); // if we can't setup an sta environment // we stop here and return if ( FAILED( hr ) ) { OSL_ENSURE( sal_False, "CoInitialize failed" ); return sal::static_int_cast< unsigned int >(-1); } unsigned int nRet; if ( createStaRequestWindow( ) ) { SetEvent( m_hEvtThrdReady ); // pumping messages MSG msg; while( GetMessageA( &msg, NULL, 0, 0 ) ) DispatchMessageA( &msg ); nRet = 0; } else { OSL_ENSURE( sal_False, "failed to create sta thread" ); nRet = sal::static_int_cast< unsigned int >(-1); } // shutdown sta environment CoUninitialize( ); return nRet; } //-------------------------------------------------------------------- // //-------------------------------------------------------------------- unsigned int WINAPI CMtaFolderPicker::StaThreadProc( LPVOID pParam ) { CMtaFolderPicker* pInst = reinterpret_cast( pParam ); OSL_ASSERT( pInst ); HRESULT hr = OleInitialize( NULL ); unsigned int result = pInst->run( ); if ( SUCCEEDED( hr ) ) OleUninitialize(); return result; } //--------------------------------------------------- // //--------------------------------------------------- ATOM SAL_CALL CMtaFolderPicker::RegisterStaRequestWindowClass( ) { osl::MutexGuard aGuard( s_Mutex ); if ( 0 == s_ClassAtom ) { WNDCLASSEXA wcex; ZeroMemory( &wcex, sizeof( WNDCLASSEXA ) ); wcex.cbSize = sizeof(WNDCLASSEXA); wcex.style = 0; wcex.lpfnWndProc = static_cast< WNDPROC >( CMtaFolderPicker::StaWndProc ); wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = m_hInstance; wcex.hIcon = NULL; wcex.hCursor = NULL; wcex.hbrBackground = NULL; wcex.lpszMenuName = NULL; wcex.lpszClassName = g_szWndClsName; wcex.hIconSm = NULL; s_ClassAtom = RegisterClassExA( &wcex ); OSL_ASSERT( s_ClassAtom ); } // increment the register class counter // so that we keep track of the number // of class registrations if ( 0 != s_ClassAtom ) s_StaRequestWndRegisterCount++; return s_ClassAtom; } //--------------------------------------------------- // //--------------------------------------------------- void SAL_CALL CMtaFolderPicker::UnregisterStaRequestWindowClass( ) { osl::MutexGuard aGuard( s_Mutex ); OSL_ASSERT( 0 != s_ClassAtom ); // update the register class counter // and unregister the window class if // counter drops to zero if ( 0 != s_ClassAtom ) { s_StaRequestWndRegisterCount--; OSL_ASSERT( s_StaRequestWndRegisterCount >= 0 ); } if ( 0 == s_StaRequestWndRegisterCount ) { UnregisterClass( (LPCTSTR)MAKELONG( s_ClassAtom, 0 ), m_hInstance ); s_ClassAtom = 0; } }