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