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