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_vcl.hxx" 26 27 #include "rtl/logfile.hxx" 28 29 #include "osl/file.hxx" 30 31 #include "vos/signal.hxx" 32 #include "vos/process.hxx" 33 34 #include "tools/tools.h" 35 #include "tools/debug.hxx" 36 #include "tools/unqid.hxx" 37 #include "tools/resmgr.hxx" 38 39 #include "comphelper/processfactory.hxx" 40 41 #include "unotools/syslocaleoptions.hxx" 42 #include "unotools/fontcfg.hxx" 43 44 #include "vcl/svapp.hxx" 45 #include "vcl/wrkwin.hxx" 46 #include "vcl/cvtgrf.hxx" 47 #include "vcl/image.hxx" 48 #include "vcl/settings.hxx" 49 #include "vcl/unowrap.hxx" 50 #include "vcl/configsettings.hxx" 51 #include "vcl/lazydelete.hxx" 52 53 #ifdef WNT 54 #include <tools/prewin.h> 55 #include <process.h> // for _beginthreadex 56 #include <ole2.h> // for _beginthreadex 57 #include <tools/postwin.h> 58 //IAccessibility2 Implementation 2009----- 59 #include <com/sun/star/accessibility/XMSAAService.hpp> 60 #include <win/g_msaasvc.h> 61 using namespace com::sun::star::accessibility; 62 //-----IAccessibility2 Implementation 2009 63 #endif 64 65 // [ed 5/14/02 Add in explicit check for quartz graphics. OS X will define 66 // unx for both quartz and X11 graphics, but we include svunx.h only if we're 67 // building X11 graphics layers. 68 69 #if defined UNX && ! defined QUARTZ 70 //#include "svunx.h" 71 #endif 72 73 //#include "svsys.h" 74 75 #include "salinst.hxx" 76 #include "salwtype.hxx" 77 #include "svdata.hxx" 78 #include "dbggui.hxx" 79 #include "accmgr.hxx" 80 #include "idlemgr.hxx" 81 #include "outdev.h" 82 #include "outfont.hxx" 83 #include "print.h" 84 #include "salsys.hxx" 85 #include "saltimer.hxx" 86 #include "salimestatus.hxx" 87 #include "impimagetree.hxx" 88 #include "xconnection.hxx" 89 90 #include "com/sun/star/lang/XMultiServiceFactory.hpp" 91 #include "com/sun/star/lang/XComponent.hpp" 92 93 #include "cppuhelper/implbase1.hxx" 94 #include "uno/current_context.hxx" 95 96 #if OSL_DEBUG_LEVEL > 0 97 #include <typeinfo> 98 #include "rtl/strbuf.hxx" 99 #endif 100 101 namespace { 102 103 namespace css = com::sun::star; 104 105 } 106 107 using namespace ::rtl; 108 using namespace ::com::sun::star::uno; 109 using namespace ::com::sun::star::lang; 110 111 112 113 // ======================================================================= 114 115 class ImplVCLExceptionHandler : public ::vos::OSignalHandler 116 { 117 public: 118 virtual ::vos::OSignalHandler::TSignalAction SAL_CALL signal( ::vos::OSignalHandler::TSignalInfo* pInfo ); 119 }; 120 121 // ----------------------------------------------------------------------- 122 123 ::vos::OSignalHandler::TSignalAction SAL_CALL ImplVCLExceptionHandler::signal( ::vos::OSignalHandler::TSignalInfo* pInfo ) 124 { 125 static sal_Bool bIn = sal_False; 126 127 // Wenn wir nocheinmal abstuerzen, verabschieden wir uns gleich 128 if ( !bIn ) 129 { 130 sal_uInt16 nVCLException = 0; 131 132 // UAE 133 if ( (pInfo->Signal == osl_Signal_AccessViolation) || 134 (pInfo->Signal == osl_Signal_IntegerDivideByZero) || 135 (pInfo->Signal == osl_Signal_FloatDivideByZero) || 136 (pInfo->Signal == osl_Signal_DebugBreak) ) 137 nVCLException = EXC_SYSTEM; 138 139 // RC 140 if ((pInfo->Signal == osl_Signal_User) && 141 (pInfo->UserSignal == OSL_SIGNAL_USER_RESOURCEFAILURE) ) 142 nVCLException = EXC_RSCNOTLOADED; 143 144 // DISPLAY-Unix 145 if ((pInfo->Signal == osl_Signal_User) && 146 (pInfo->UserSignal == OSL_SIGNAL_USER_X11SUBSYSTEMERROR) ) 147 nVCLException = EXC_DISPLAY; 148 149 // Remote-Client 150 if ((pInfo->Signal == osl_Signal_User) && 151 (pInfo->UserSignal == OSL_SIGNAL_USER_RVPCONNECTIONERROR) ) 152 nVCLException = EXC_REMOTE; 153 154 if ( nVCLException ) 155 { 156 bIn = sal_True; 157 158 ::vos::OGuard aLock(&Application::GetSolarMutex()); 159 160 // Timer nicht mehr anhalten, da ansonsten die UAE-Box 161 // auch nicht mehr gepaintet wird 162 ImplSVData* pSVData = ImplGetSVData(); 163 if ( pSVData->mpApp ) 164 { 165 sal_uInt16 nOldMode = Application::GetSystemWindowMode(); 166 Application::SetSystemWindowMode( nOldMode & ~SYSTEMWINDOW_MODE_NOAUTOMODE ); 167 pSVData->mpApp->Exception( nVCLException ); 168 Application::SetSystemWindowMode( nOldMode ); 169 } 170 bIn = sal_False; 171 172 return vos::OSignalHandler::TAction_CallNextHandler; 173 } 174 } 175 176 return vos::OSignalHandler::TAction_CallNextHandler; 177 } 178 179 // ======================================================================= 180 sal_Bool ImplSVMain() 181 { 182 // The 'real' SVMain() 183 RTL_LOGFILE_CONTEXT( aLog, "vcl (ss112471) ::SVMain" ); 184 185 ImplSVData* pSVData = ImplGetSVData(); 186 187 DBG_ASSERT( pSVData->mpApp, "no instance of class Application" ); 188 189 css::uno::Reference<XMultiServiceFactory> xMS; 190 191 192 sal_Bool bInit = InitVCL( xMS ); 193 194 if( bInit ) 195 { 196 // Application-Main rufen 197 pSVData->maAppData.mbInAppMain = sal_True; 198 pSVData->mpApp->Main(); 199 pSVData->maAppData.mbInAppMain = sal_False; 200 } 201 202 if( pSVData->mxDisplayConnection.is() ) 203 { 204 pSVData->mxDisplayConnection->terminate(); 205 pSVData->mxDisplayConnection.clear(); 206 } 207 208 // This is a hack to work around the problem of the asynchronous nature 209 // of bridging accessibility through Java: on shutdown there might still 210 // be some events in the AWT EventQueue, which need the SolarMutex which 211 // - on the other hand - is destroyed in DeInitVCL(). So empty the queue 212 // here .. 213 css::uno::Reference< XComponent > xComponent(pSVData->mxAccessBridge, UNO_QUERY); 214 if( xComponent.is() ) 215 { 216 sal_uLong nCount = Application::ReleaseSolarMutex(); 217 xComponent->dispose(); 218 Application::AcquireSolarMutex(nCount); 219 pSVData->mxAccessBridge.clear(); 220 } 221 222 DeInitVCL(); 223 //IAccessibility2 Implementation 2009----- 224 #ifdef WNT 225 if( g_acc_manager1 ) 226 g_acc_manager1->release(); 227 #endif 228 //-----IAccessibility2 Implementation 2009 229 return bInit; 230 } 231 232 sal_Bool SVMain() 233 { 234 // #i47888# allow for alternative initialization as required for e.g. MacOSX 235 extern sal_Bool ImplSVMainHook( sal_Bool* ); 236 237 sal_Bool bInit; 238 if( ImplSVMainHook( &bInit ) ) 239 return bInit; 240 else 241 return ImplSVMain(); 242 } 243 // This variable is set, when no Application object is instantiated 244 // before SVInit is called 245 static Application * pOwnSvApp = NULL; 246 // Exception handler. pExceptionHandler != NULL => VCL already inited 247 ImplVCLExceptionHandler * pExceptionHandler = NULL; 248 249 class Application_Impl : public Application 250 { 251 public: 252 void Main(){}; 253 }; 254 255 class DesktopEnvironmentContext: public cppu::WeakImplHelper1< com::sun::star::uno::XCurrentContext > 256 { 257 public: 258 DesktopEnvironmentContext( const com::sun::star::uno::Reference< com::sun::star::uno::XCurrentContext > & ctx) 259 : m_xNextContext( ctx ) {} 260 261 // XCurrentContext 262 virtual com::sun::star::uno::Any SAL_CALL getValueByName( const rtl::OUString& Name ) 263 throw (com::sun::star::uno::RuntimeException); 264 265 private: 266 com::sun::star::uno::Reference< com::sun::star::uno::XCurrentContext > m_xNextContext; 267 }; 268 269 Any SAL_CALL DesktopEnvironmentContext::getValueByName( const rtl::OUString& Name) throw (RuntimeException) 270 { 271 Any retVal; 272 273 if ( 0 == Name.compareToAscii( "system.desktop-environment" ) ) 274 { 275 retVal = makeAny( Application::GetDesktopEnvironment() ); 276 } 277 else if( m_xNextContext.is() ) 278 { 279 // Call next context in chain if found 280 retVal = m_xNextContext->getValueByName( Name ); 281 } 282 return retVal; 283 } 284 285 sal_Bool InitVCL( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > & rSMgr ) 286 { 287 RTL_LOGFILE_CONTEXT( aLog, "vcl (ss112471) ::InitVCL" ); 288 289 if( pExceptionHandler != NULL ) 290 return sal_False; 291 292 if( ! ImplGetSVData() ) 293 ImplInitSVData(); 294 295 if( !ImplGetSVData()->mpApp ) 296 { 297 pOwnSvApp = new Application_Impl(); 298 } 299 InitSalMain(); 300 301 /*AllSettings aAS; 302 Application::SetSettings( aAS );// ??? 303 */ 304 ImplSVData* pSVData = ImplGetSVData(); 305 306 // SV bei den Tools anmelden 307 InitTools(); 308 309 DBG_ASSERT( !pSVData->maAppData.mxMSF.is(), "VCL service factory already set" ); 310 pSVData->maAppData.mxMSF = rSMgr; 311 312 // Main-Thread-Id merken 313 pSVData->mnMainThreadId = ::vos::OThread::getCurrentIdentifier(); 314 315 vos::OStartupInfo aStartInfo; 316 rtl::OUString aExeFileName; 317 318 319 // Sal initialisieren 320 RTL_LOGFILE_CONTEXT_TRACE( aLog, "{ ::CreateSalInstance" ); 321 pSVData->mpDefInst = CreateSalInstance(); 322 if ( !pSVData->mpDefInst ) 323 return sal_False; 324 RTL_LOGFILE_CONTEXT_TRACE( aLog, "} ::CreateSalInstance" ); 325 326 // Desktop Environment context (to be able to get value of "system.desktop-environment" as soon as possible) 327 com::sun::star::uno::setCurrentContext( 328 new DesktopEnvironmentContext( com::sun::star::uno::getCurrentContext() ) ); 329 330 // Initialize application instance (should be done after initialization of VCL SAL part) 331 if( pSVData->mpApp ) 332 // call init to initialize application class 333 // soffice/sfx implementation creates the global service manager 334 pSVData->mpApp->Init(); 335 336 // Den AppFileName gleich holen und absolut machen, bevor das 337 // WorkingDirectory sich aendert... 338 aStartInfo.getExecutableFile( aExeFileName ); 339 340 // convert path to native file format 341 rtl::OUString aNativeFileName; 342 osl::FileBase::getSystemPathFromFileURL( aExeFileName, aNativeFileName ); 343 pSVData->maAppData.mpAppFileName = new String( aNativeFileName ); 344 345 // Initialize global data 346 pSVData->maGDIData.mpScreenFontList = new ImplDevFontList; 347 pSVData->maGDIData.mpScreenFontCache = new ImplFontCache( sal_False ); 348 pSVData->maGDIData.mpGrfConverter = new GraphicConverter; 349 350 // Exception-Handler setzen 351 pExceptionHandler = new ImplVCLExceptionHandler(); 352 353 // Debug-Daten initialisieren 354 DBGGUI_INIT(); 355 356 return sal_True; 357 } 358 359 void DeInitVCL() 360 { 361 ImplSVData* pSVData = ImplGetSVData(); 362 pSVData->mbDeInit = sal_True; 363 364 vcl::DeleteOnDeinitBase::ImplDeleteOnDeInit(); 365 366 // give ime status a chance to destroy its own windows 367 delete pSVData->mpImeStatus; 368 pSVData->mpImeStatus = NULL; 369 370 #if OSL_DEBUG_LEVEL > 0 371 rtl::OStringBuffer aBuf( 256 ); 372 aBuf.append( "DeInitVCL: some top Windows are still alive\n" ); 373 long nTopWindowCount = Application::GetTopWindowCount(); 374 long nBadTopWindows = nTopWindowCount; 375 for( long i = 0; i < nTopWindowCount; i++ ) 376 { 377 Window* pWin = Application::GetTopWindow( i ); 378 // default window will be destroyed further down 379 // but may still be useful during deinit up to that point 380 if( pWin == pSVData->mpDefaultWin ) 381 nBadTopWindows--; 382 else 383 { 384 aBuf.append( "text = \"" ); 385 aBuf.append( rtl::OUStringToOString( pWin->GetText(), osl_getThreadTextEncoding() ) ); 386 aBuf.append( "\" type = \"" ); 387 aBuf.append( typeid(*pWin).name() ); 388 aBuf.append( "\", ptr = 0x" ); 389 aBuf.append( sal_Int64( pWin ), 16 ); 390 aBuf.append( "\n" ); 391 } 392 } 393 DBG_ASSERT( nBadTopWindows==0, aBuf.getStr() ); 394 #endif 395 396 ImplImageTreeSingletonRef()->shutDown(); 397 398 delete pExceptionHandler; 399 pExceptionHandler = NULL; 400 401 // Debug Daten zuruecksetzen 402 DBGGUI_DEINIT(); 403 404 // free global data 405 delete pSVData->maGDIData.mpGrfConverter; 406 407 if( pSVData->mpSettingsConfigItem ) 408 delete pSVData->mpSettingsConfigItem, pSVData->mpSettingsConfigItem = NULL; 409 if( pSVData->maGDIData.mpDefaultFontConfiguration ) 410 delete pSVData->maGDIData.mpDefaultFontConfiguration, pSVData->maGDIData.mpDefaultFontConfiguration = NULL; 411 if( pSVData->maGDIData.mpFontSubstConfiguration ) 412 delete pSVData->maGDIData.mpFontSubstConfiguration, pSVData->maGDIData.mpFontSubstConfiguration = NULL; 413 414 if ( pSVData->maAppData.mpIdleMgr ) 415 delete pSVData->maAppData.mpIdleMgr; 416 Timer::ImplDeInitTimer(); 417 418 if ( pSVData->maWinData.mpMsgBoxImgList ) 419 { 420 delete pSVData->maWinData.mpMsgBoxImgList; 421 pSVData->maWinData.mpMsgBoxImgList = NULL; 422 } 423 if ( pSVData->maWinData.mpMsgBoxHCImgList ) 424 { 425 delete pSVData->maWinData.mpMsgBoxHCImgList; 426 pSVData->maWinData.mpMsgBoxHCImgList = NULL; 427 } 428 if ( pSVData->maCtrlData.mpCheckImgList ) 429 { 430 delete pSVData->maCtrlData.mpCheckImgList; 431 pSVData->maCtrlData.mpCheckImgList = NULL; 432 } 433 if ( pSVData->maCtrlData.mpRadioImgList ) 434 { 435 delete pSVData->maCtrlData.mpRadioImgList; 436 pSVData->maCtrlData.mpRadioImgList = NULL; 437 } 438 if ( pSVData->maCtrlData.mpPinImgList ) 439 { 440 delete pSVData->maCtrlData.mpPinImgList; 441 pSVData->maCtrlData.mpPinImgList = NULL; 442 } 443 if ( pSVData->maCtrlData.mpSplitHPinImgList ) 444 { 445 delete pSVData->maCtrlData.mpSplitHPinImgList; 446 pSVData->maCtrlData.mpSplitHPinImgList = NULL; 447 } 448 if ( pSVData->maCtrlData.mpSplitVPinImgList ) 449 { 450 delete pSVData->maCtrlData.mpSplitVPinImgList; 451 pSVData->maCtrlData.mpSplitVPinImgList = NULL; 452 } 453 if ( pSVData->maCtrlData.mpSplitHArwImgList ) 454 { 455 delete pSVData->maCtrlData.mpSplitHArwImgList; 456 pSVData->maCtrlData.mpSplitHArwImgList = NULL; 457 } 458 if ( pSVData->maCtrlData.mpSplitVArwImgList ) 459 { 460 delete pSVData->maCtrlData.mpSplitVArwImgList; 461 pSVData->maCtrlData.mpSplitVArwImgList = NULL; 462 } 463 if ( pSVData->maCtrlData.mpDisclosurePlus ) 464 { 465 delete pSVData->maCtrlData.mpDisclosurePlus; 466 pSVData->maCtrlData.mpDisclosurePlus = NULL; 467 } 468 if ( pSVData->maCtrlData.mpDisclosurePlusHC ) 469 { 470 delete pSVData->maCtrlData.mpDisclosurePlusHC; 471 pSVData->maCtrlData.mpDisclosurePlusHC = NULL; 472 } 473 if ( pSVData->maCtrlData.mpDisclosureMinus ) 474 { 475 delete pSVData->maCtrlData.mpDisclosureMinus; 476 pSVData->maCtrlData.mpDisclosureMinus = NULL; 477 } 478 if ( pSVData->maCtrlData.mpDisclosureMinusHC ) 479 { 480 delete pSVData->maCtrlData.mpDisclosureMinusHC; 481 pSVData->maCtrlData.mpDisclosureMinusHC = NULL; 482 } 483 if ( pSVData->mpDefaultWin ) 484 { 485 delete pSVData->mpDefaultWin; 486 pSVData->mpDefaultWin = NULL; 487 } 488 489 // #114285# Moved here from ImplDeInitSVData... 490 if ( pSVData->mpUnoWrapper ) 491 { 492 pSVData->mpUnoWrapper->Destroy(); 493 pSVData->mpUnoWrapper = NULL; 494 } 495 496 pSVData->maAppData.mxMSF.clear(); 497 498 if( pSVData->mpApp ) 499 // call deinit to deinitialize application class 500 // soffice/sfx implementation disposes the global service manager 501 // Warning: After this call you can't call uno services 502 pSVData->mpApp->DeInit(); 503 504 if ( pSVData->maAppData.mpSettings ) 505 { 506 if ( pSVData->maAppData.mpCfgListener ) 507 { 508 pSVData->maAppData.mpSettings->GetSysLocale().GetOptions().RemoveListener( pSVData->maAppData.mpCfgListener ); 509 delete pSVData->maAppData.mpCfgListener; 510 } 511 512 delete pSVData->maAppData.mpSettings; 513 pSVData->maAppData.mpSettings = NULL; 514 } 515 if ( pSVData->maAppData.mpAccelMgr ) 516 { 517 delete pSVData->maAppData.mpAccelMgr; 518 pSVData->maAppData.mpAccelMgr = NULL; 519 } 520 if ( pSVData->maAppData.mpUniqueIdCont ) 521 { 522 delete pSVData->maAppData.mpUniqueIdCont; 523 pSVData->maAppData.mpUniqueIdCont = NULL; 524 } 525 if ( pSVData->maAppData.mpAppFileName ) 526 { 527 delete pSVData->maAppData.mpAppFileName; 528 pSVData->maAppData.mpAppFileName = NULL; 529 } 530 if ( pSVData->maAppData.mpAppName ) 531 { 532 delete pSVData->maAppData.mpAppName; 533 pSVData->maAppData.mpAppName = NULL; 534 } 535 if ( pSVData->maAppData.mpDisplayName ) 536 { 537 delete pSVData->maAppData.mpDisplayName; 538 pSVData->maAppData.mpDisplayName = NULL; 539 } 540 if ( pSVData->maAppData.mpEventListeners ) 541 { 542 delete pSVData->maAppData.mpEventListeners; 543 pSVData->maAppData.mpEventListeners = NULL; 544 } 545 if ( pSVData->maAppData.mpKeyListeners ) 546 { 547 delete pSVData->maAppData.mpKeyListeners; 548 pSVData->maAppData.mpKeyListeners = NULL; 549 } 550 551 if ( pSVData->maAppData.mpFirstHotKey ) 552 ImplFreeHotKeyData(); 553 if ( pSVData->maAppData.mpFirstEventHook ) 554 ImplFreeEventHookData(); 555 556 ImplDeletePrnQueueList(); 557 delete pSVData->maGDIData.mpScreenFontList; 558 pSVData->maGDIData.mpScreenFontList = NULL; 559 delete pSVData->maGDIData.mpScreenFontCache; 560 pSVData->maGDIData.mpScreenFontCache = NULL; 561 ImplFreeOutDevFontData(); 562 563 if ( pSVData->mpResMgr ) 564 { 565 delete pSVData->mpResMgr; 566 pSVData->mpResMgr = NULL; 567 } 568 569 ResMgr::DestroyAllResMgr(); 570 571 // destroy all Sal interfaces before destorying the instance 572 // and thereby unloading the plugin 573 delete pSVData->mpSalSystem; 574 pSVData->mpSalSystem = NULL; 575 delete pSVData->mpSalTimer; 576 pSVData->mpSalTimer = NULL; 577 578 // Sal deinitialisieren 579 DestroySalInstance( pSVData->mpDefInst ); 580 581 DeInitTools(); 582 583 DeInitSalMain(); 584 585 if( pOwnSvApp ) 586 { 587 delete pOwnSvApp; 588 pOwnSvApp = NULL; 589 } 590 } 591 592 // only one call is allowed 593 struct WorkerThreadData 594 { 595 oslWorkerFunction pWorker; 596 void * pThreadData; 597 WorkerThreadData( oslWorkerFunction pWorker_, void * pThreadData_ ) 598 : pWorker( pWorker_ ) 599 , pThreadData( pThreadData_ ) 600 { 601 } 602 }; 603 604 #ifdef WNT 605 static HANDLE hThreadID = 0; 606 static unsigned __stdcall _threadmain( void *pArgs ) 607 { 608 OleInitialize( NULL ); 609 ((WorkerThreadData*)pArgs)->pWorker( ((WorkerThreadData*)pArgs)->pThreadData ); 610 delete (WorkerThreadData*)pArgs; 611 OleUninitialize(); 612 hThreadID = 0; 613 return 0; 614 } 615 #else 616 static oslThread hThreadID = 0; 617 extern "C" 618 { 619 static void SAL_CALL MainWorkerFunction( void* pArgs ) 620 { 621 ((WorkerThreadData*)pArgs)->pWorker( ((WorkerThreadData*)pArgs)->pThreadData ); 622 delete (WorkerThreadData*)pArgs; 623 hThreadID = 0; 624 } 625 } // extern "C" 626 #endif 627 628 void CreateMainLoopThread( oslWorkerFunction pWorker, void * pThreadData ) 629 { 630 #ifdef WNT 631 // sal thread alway call CoInitializeEx, so a sysdepen implementation is necessary 632 633 unsigned uThreadID; 634 hThreadID = (HANDLE)_beginthreadex( 635 NULL, // no security handle 636 0, // stacksize 0 means default 637 _threadmain, // thread worker function 638 new WorkerThreadData( pWorker, pThreadData ), // arguments for worker function 639 0, // 0 means: create immediatly otherwise use CREATE_SUSPENDED 640 &uThreadID ); // thread id to fill 641 #else 642 hThreadID = osl_createThread( MainWorkerFunction, new WorkerThreadData( pWorker, pThreadData ) ); 643 #endif 644 } 645 646 void JoinMainLoopThread() 647 { 648 if( hThreadID ) 649 { 650 #ifdef WNT 651 WaitForSingleObject(hThreadID, INFINITE); 652 #else 653 osl_joinWithThread(hThreadID); 654 osl_destroyThread( hThreadID ); 655 #endif 656 } 657 } 658