VirtualBox

source: vbox/trunk/src/VBox/Main/glue/initterm.cpp@ 25877

Last change on this file since 25877 was 25813, checked in by vboxsync, 15 years ago

Main: move AutoLock classes to glue

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
Line 
1/* $Id: initterm.cpp 25813 2010-01-13 17:24:35Z vboxsync $ */
2
3/** @file
4 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#if !defined (VBOX_WITH_XPCOM)
24
25#include <objbase.h>
26
27#else /* !defined (VBOX_WITH_XPCOM) */
28
29#include <stdlib.h>
30
31/* XPCOM_GLUE is defined when the client uses the standalone glue
32 * (i.e. dynamically picks up the existing XPCOM shared library installation).
33 * This is not the case for VirtualBox XPCOM clients (they are always
34 * distrubuted with the self-built XPCOM library, and therefore have a binary
35 * dependency on it) but left here for clarity.
36 */
37#if defined (XPCOM_GLUE)
38#include <nsXPCOMGlue.h>
39#endif
40
41#include <nsIComponentRegistrar.h>
42#include <nsIServiceManager.h>
43#include <nsCOMPtr.h>
44#include <nsEventQueueUtils.h>
45#include <nsEmbedString.h>
46
47#include <nsILocalFile.h>
48#include <nsIDirectoryService.h>
49#include <nsDirectoryServiceDefs.h>
50
51#endif /* !defined (VBOX_WITH_XPCOM) */
52
53#include "VBox/com/com.h"
54#include "VBox/com/assert.h"
55#include "VBox/com/EventQueue.h"
56#include "VBox/com/AutoLock.h"
57
58#include "../include/Logging.h"
59
60#include <iprt/asm.h>
61#include <iprt/env.h>
62#include <iprt/param.h>
63#include <iprt/path.h>
64#include <iprt/string.h>
65#include <iprt/thread.h>
66
67#include <VBox/err.h>
68
69namespace com
70{
71
72#if defined (VBOX_WITH_XPCOM)
73
74class DirectoryServiceProvider : public nsIDirectoryServiceProvider
75{
76public:
77
78 NS_DECL_ISUPPORTS
79
80 DirectoryServiceProvider()
81 : mCompRegLocation (NULL), mXPTIDatLocation (NULL)
82 , mComponentDirLocation (NULL), mCurrProcDirLocation (NULL)
83 {}
84
85 virtual ~DirectoryServiceProvider();
86
87 HRESULT init (const char *aCompRegLocation,
88 const char *aXPTIDatLocation,
89 const char *aComponentDirLocation,
90 const char *aCurrProcDirLocation);
91
92 NS_DECL_NSIDIRECTORYSERVICEPROVIDER
93
94private:
95
96 char *mCompRegLocation;
97 char *mXPTIDatLocation;
98 char *mComponentDirLocation;
99 char *mCurrProcDirLocation;
100};
101
102NS_IMPL_ISUPPORTS1 (DirectoryServiceProvider, nsIDirectoryServiceProvider)
103
104DirectoryServiceProvider::~DirectoryServiceProvider()
105{
106 if (mCompRegLocation)
107 {
108 RTStrFree (mCompRegLocation);
109 mCompRegLocation = NULL;
110 }
111 if (mXPTIDatLocation)
112 {
113 RTStrFree (mXPTIDatLocation);
114 mXPTIDatLocation = NULL;
115 }
116 if (mComponentDirLocation)
117 {
118 RTStrFree (mComponentDirLocation);
119 mComponentDirLocation = NULL;
120 }
121 if (mCurrProcDirLocation)
122 {
123 RTStrFree (mCurrProcDirLocation);
124 mCurrProcDirLocation = NULL;
125 }
126}
127
128/**
129 * @param aCompRegLocation Path to compreg.dat, in Utf8.
130 * @param aXPTIDatLocation Path to xpti.data, in Utf8.
131 */
132HRESULT
133DirectoryServiceProvider::init (const char *aCompRegLocation,
134 const char *aXPTIDatLocation,
135 const char *aComponentDirLocation,
136 const char *aCurrProcDirLocation)
137{
138 AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG);
139 AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG);
140
141 int vrc = RTStrUtf8ToCurrentCP (&mCompRegLocation, aCompRegLocation);
142 if (RT_SUCCESS(vrc))
143 vrc = RTStrUtf8ToCurrentCP (&mXPTIDatLocation, aXPTIDatLocation);
144 if (RT_SUCCESS(vrc) && aComponentDirLocation)
145 vrc = RTStrUtf8ToCurrentCP (&mComponentDirLocation, aComponentDirLocation);
146 if (RT_SUCCESS(vrc) && aCurrProcDirLocation)
147 vrc = RTStrUtf8ToCurrentCP (&mCurrProcDirLocation, aCurrProcDirLocation);
148
149 return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
150}
151
152NS_IMETHODIMP
153DirectoryServiceProvider::GetFile (const char *aProp,
154 PRBool *aPersistent,
155 nsIFile **aRetval)
156{
157 nsCOMPtr <nsILocalFile> localFile;
158 nsresult rv = NS_ERROR_FAILURE;
159
160 *aRetval = nsnull;
161 *aPersistent = PR_TRUE;
162
163 const char *fileLocation = NULL;
164
165 if (strcmp (aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
166 fileLocation = mCompRegLocation;
167 else if (strcmp (aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
168 fileLocation = mXPTIDatLocation;
169 else if (mComponentDirLocation && strcmp (aProp, NS_XPCOM_COMPONENT_DIR) == 0)
170 fileLocation = mComponentDirLocation;
171 else if (mCurrProcDirLocation && strcmp (aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
172 fileLocation = mCurrProcDirLocation;
173 else
174 return NS_ERROR_FAILURE;
175
176 rv = NS_NewNativeLocalFile (nsEmbedCString (fileLocation),
177 PR_TRUE, getter_AddRefs (localFile));
178 if (NS_FAILED(rv))
179 return rv;
180
181 return localFile->QueryInterface (NS_GET_IID (nsIFile),
182 (void **) aRetval);
183}
184
185/**
186 * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
187 * doesn't provide such functionality)
188 */
189static bool volatile gIsXPCOMInitialized = false;
190
191/**
192 * Number of Initialize() calls on the main thread.
193 */
194static unsigned int gXPCOMInitCount = 0;
195
196#else /* !defined (VBOX_WITH_XPCOM) */
197
198/**
199 * The COM main thread handle. (The first caller of com::Initialize().)
200 */
201static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD;
202
203/**
204 * Number of Initialize() calls on the main thread.
205 */
206static uint32_t gCOMMainInitCount = 0;
207
208#endif /* !defined (VBOX_WITH_XPCOM) */
209
210
211/**
212 * Initializes the COM runtime.
213 *
214 * This method must be called on each thread of the client application that
215 * wants to access COM facilities. The initialization must be performed before
216 * calling any other COM method or attempting to instantiate COM objects.
217 *
218 * On platforms using XPCOM, this method uses the following scheme to search for
219 * XPCOM runtime:
220 *
221 * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies
222 * is used to search XPCOM libraries and components. If this method fails to
223 * initialize XPCOM runtime using this path, it will immediately return a
224 * failure and will NOT check for other paths as described below.
225 *
226 * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the
227 * given order:
228 *
229 * a) Compiled-in application data directory (as returned by
230 * RTPathAppPrivateArch())
231 * b) "/usr/lib/virtualbox" (Linux only)
232 * c) "/opt/VirtualBox" (Linux only)
233 *
234 * The first path for which the initialization succeeds will be used.
235 *
236 * On MS COM platforms, the COM runtime is provided by the system and does not
237 * need to be searched for.
238 *
239 * Once the COM subsystem is no longer necessary on a given thread, Shutdown()
240 * must be called to free resources allocated for it. Note that a thread may
241 * call Initialize() several times but for each of tese calls there must be a
242 * corresponding Shutdown() call.
243 *
244 * @return S_OK on success and a COM result code in case of failure.
245 */
246HRESULT Initialize()
247{
248 HRESULT rc = E_FAIL;
249
250#if !defined (VBOX_WITH_XPCOM)
251
252 DWORD flags = COINIT_MULTITHREADED |
253 COINIT_DISABLE_OLE1DDE |
254 COINIT_SPEED_OVER_MEMORY;
255
256 rc = CoInitializeEx (NULL, flags);
257
258 /// @todo the below rough method of changing the aparment type doesn't
259 /// work on some systems for unknown reason (CoUninitialize() simply does
260 /// nothing there, or at least all 10 000 of subsequent CoInitializeEx()
261 /// continue to return RPC_E_CHANGED_MODE there). The problem on those
262 /// systems is related to the "Extend support for advanced text services
263 /// to all programs" checkbox in the advanced language settings dialog,
264 /// i.e. the problem appears when this checkbox is checked and disappears
265 /// if you clear it. For this reason, we disable the code below and
266 /// instead initialize COM in MTA as early as possible, before 3rd party
267 /// libraries we use have done so (i.e. Qt).
268# if 0
269 /* If we fail to set the necessary apartment model, it may mean that some
270 * DLL that was indirectly loaded by the process calling this function has
271 * already initialized COM on the given thread in an incompatible way
272 * which we can't leave with. Therefore, we try to fix this by using the
273 * brute force method: */
274
275 if (rc == RPC_E_CHANGED_MODE)
276 {
277 /* Before we use brute force, we need to check if we are in the
278 * neutral threaded apartment -- in this case there is no need to
279 * worry at all. */
280
281 rc = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
282 if (rc == RPC_E_CHANGED_MODE)
283 {
284 /* This is a neutral apartment, reset the error */
285 rc = S_OK;
286
287 LogFlowFunc (("COM is already initialized in neutral threaded "
288 "apartment mode,\nwill accept it.\n"));
289 }
290 else if (rc == S_FALSE)
291 {
292 /* balance the test CoInitializeEx above */
293 CoUninitialize();
294 rc = RPC_E_CHANGED_MODE;
295
296 LogFlowFunc (("COM is already initialized in single threaded "
297 "apartment mode,\nwill reinitialize as "
298 "multi threaded.\n"));
299
300 enum { MaxTries = 10000 };
301 int tries = MaxTries;
302 while (rc == RPC_E_CHANGED_MODE && tries --)
303 {
304 CoUninitialize();
305 rc = CoInitializeEx (NULL, flags);
306 if (rc == S_OK)
307 {
308 /* We've successfully reinitialized COM; restore the
309 * initialization reference counter */
310
311 LogFlowFunc (("Will call CoInitializeEx() %d times.\n",
312 MaxTries - tries));
313
314 while (tries ++ < MaxTries)
315 {
316 rc = CoInitializeEx (NULL, flags);
317 Assert (rc == S_FALSE);
318 }
319 }
320 }
321 }
322 else
323 AssertMsgFailed (("rc=%08X\n", rc));
324 }
325# endif
326
327 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
328 * "already initialized using the same apartment model") */
329 AssertMsg (rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc));
330
331 /* To be flow compatible with the XPCOM case, we return here if this isn't
332 * the main thread or if it isn't its first initialization call.
333 * Note! CoInitializeEx and CoUninitialize does it's own reference
334 * counting, so this exercise is entirely for the EventQueue init. */
335 bool fRc;
336 RTTHREAD hSelf = RTThreadSelf ();
337 if (hSelf != NIL_RTTHREAD)
338 ASMAtomicCmpXchgHandle (&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
339 else
340 fRc = false;
341 if (!fRc)
342 {
343 if ( gCOMMainThread == hSelf
344 && SUCCEEDED (rc))
345 gCOMMainInitCount++;
346
347 AssertComRC (rc);
348 return rc;
349 }
350 Assert (RTThreadIsMain (hSelf));
351
352 /* this is the first main thread initialization */
353 Assert (gCOMMainInitCount == 0);
354 if (SUCCEEDED (rc))
355 gCOMMainInitCount = 1;
356
357#else /* !defined (VBOX_WITH_XPCOM) */
358
359 if (ASMAtomicXchgBool (&gIsXPCOMInitialized, true) == true)
360 {
361 /* XPCOM is already initialized on the main thread, no special
362 * initialization is necessary on additional threads. Just increase
363 * the init counter if it's a main thread again (to correctly support
364 * nested calls to Initialize()/Shutdown() for compatibility with
365 * Win32). */
366
367 nsCOMPtr <nsIEventQueue> eventQ;
368 rc = NS_GetMainEventQ (getter_AddRefs (eventQ));
369
370 if (NS_SUCCEEDED(rc))
371 {
372 PRBool isOnMainThread = PR_FALSE;
373 rc = eventQ->IsOnCurrentThread (&isOnMainThread);
374 if (NS_SUCCEEDED(rc) && isOnMainThread)
375 ++ gXPCOMInitCount;
376 }
377
378 AssertComRC (rc);
379 return rc;
380 }
381 Assert (RTThreadIsMain (RTThreadSelf()));
382
383 /* this is the first initialization */
384 gXPCOMInitCount = 1;
385 bool const fInitEventQueues = true;
386
387 /* prepare paths for registry files */
388 char userHomeDir [RTPATH_MAX];
389 int vrc = GetVBoxUserHomeDirectory (userHomeDir, sizeof (userHomeDir));
390 AssertRCReturn (vrc, NS_ERROR_FAILURE);
391
392 char compReg [RTPATH_MAX];
393 char xptiDat [RTPATH_MAX];
394
395 /** @todo use RTPathAppend */
396 RTStrPrintf (compReg, sizeof (compReg), "%s%c%s",
397 userHomeDir, RTPATH_DELIMITER, "compreg.dat");
398 RTStrPrintf (xptiDat, sizeof (xptiDat), "%s%c%s",
399 userHomeDir, RTPATH_DELIMITER, "xpti.dat");
400
401 LogFlowFunc (("component registry : \"%s\"\n", compReg));
402 LogFlowFunc (("XPTI data file : \"%s\"\n", xptiDat));
403
404#if defined (XPCOM_GLUE)
405 XPCOMGlueStartup (nsnull);
406#endif
407
408 static const char *kAppPathsToProbe[] =
409 {
410 NULL, /* 0: will use VBOX_APP_HOME */
411 NULL, /* 1: will try RTPathAppPrivateArch() */
412#ifdef RT_OS_LINUX
413 "/usr/lib/virtualbox",
414 "/opt/VirtualBox",
415#elif RT_OS_SOLARIS
416 "/opt/VirtualBox/amd64",
417 "/opt/VirtualBox/i386",
418#elif RT_OS_DARWIN
419 "/Application/VirtualBox.app/Contents/MacOS",
420#endif
421 };
422
423 /* Find out the directory where VirtualBox binaries are located */
424 for (size_t i = 0; i < RT_ELEMENTS (kAppPathsToProbe); ++ i)
425 {
426 char appHomeDir [RTPATH_MAX];
427 appHomeDir [RTPATH_MAX - 1] = '\0';
428
429 if (i == 0)
430 {
431 /* Use VBOX_APP_HOME if present */
432 if (!RTEnvExist ("VBOX_APP_HOME"))
433 continue;
434
435 strncpy (appHomeDir, RTEnvGet ("VBOX_APP_HOME"), RTPATH_MAX - 1); /** @todo r=bird: Use RTEnvGetEx. */
436 }
437 else if (i == 1)
438 {
439 /* Use RTPathAppPrivateArch() first */
440 vrc = RTPathAppPrivateArch (appHomeDir, sizeof (appHomeDir));
441 AssertRC (vrc);
442 if (RT_FAILURE(vrc))
443 {
444 rc = NS_ERROR_FAILURE;
445 continue;
446 }
447 }
448 else
449 {
450 /* Iterate over all other paths */
451 strncpy (appHomeDir, kAppPathsToProbe [i], RTPATH_MAX - 1);
452 }
453
454 nsCOMPtr <DirectoryServiceProvider> dsProv;
455
456 char compDir [RTPATH_MAX];
457 RTStrPrintf (compDir, sizeof (compDir), "%s%c%s",
458 appHomeDir, RTPATH_DELIMITER, "components");
459 LogFlowFunc (("component directory : \"%s\"\n", compDir));
460
461 dsProv = new DirectoryServiceProvider();
462 if (dsProv)
463 rc = dsProv->init (compReg, xptiDat, compDir, appHomeDir);
464 else
465 rc = NS_ERROR_OUT_OF_MEMORY;
466 if (NS_FAILED (rc))
467 break;
468
469 /* Setup the application path for NS_InitXPCOM2. Note that we properly
470 * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
471 * service provider but it seems to be activated after the directory
472 * service is used for the first time (see the source NS_InitXPCOM2). So
473 * use the same value here to be on the safe side. */
474 nsCOMPtr <nsIFile> appDir;
475 {
476 char *appDirCP = NULL;
477 vrc = RTStrUtf8ToCurrentCP (&appDirCP, appHomeDir);
478 if (RT_SUCCESS(vrc))
479 {
480 nsCOMPtr <nsILocalFile> file;
481 rc = NS_NewNativeLocalFile (nsEmbedCString (appDirCP),
482 PR_FALSE, getter_AddRefs (file));
483 if (NS_SUCCEEDED(rc))
484 appDir = do_QueryInterface (file, &rc);
485
486 RTStrFree (appDirCP);
487 }
488 else
489 rc = NS_ERROR_FAILURE;
490 }
491 if (NS_FAILED (rc))
492 break;
493
494 /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
495 * still use it instead of the directory service happy */
496 {
497 char *pathCP = NULL;
498 vrc = RTStrUtf8ToCurrentCP (&pathCP, appHomeDir);
499 if (RT_SUCCESS(vrc))
500 {
501 vrc = RTEnvSet ("VBOX_XPCOM_HOME", pathCP);
502 RTStrFree (pathCP);
503 }
504 AssertRC (vrc);
505 }
506
507 /* Finally, initialize XPCOM */
508 {
509 nsCOMPtr <nsIServiceManager> serviceManager;
510 rc = NS_InitXPCOM2 (getter_AddRefs (serviceManager),
511 appDir, dsProv);
512
513 if (NS_SUCCEEDED(rc))
514 {
515 nsCOMPtr <nsIComponentRegistrar> registrar =
516 do_QueryInterface (serviceManager, &rc);
517 if (NS_SUCCEEDED(rc))
518 {
519 rc = registrar->AutoRegister (nsnull);
520 if (NS_SUCCEEDED(rc))
521 {
522 /* We succeeded, stop probing paths */
523 LogFlowFunc (("Succeeded.\n"));
524 break;
525 }
526 }
527 }
528 }
529
530 /* clean up before the new try */
531 rc = NS_ShutdownXPCOM (nsnull);
532
533 if (i == 0)
534 {
535 /* We failed with VBOX_APP_HOME, don't probe other paths */
536 break;
537 }
538 }
539
540#endif /* !defined (VBOX_WITH_XPCOM) */
541
542 // for both COM and XPCOM, we only get here if this is the main thread;
543 // only then initialize the autolock system (AutoLock.cpp)
544 Assert(RTThreadIsMain(RTThreadSelf()));
545 util::InitAutoLockSystem();
546
547 AssertComRC (rc);
548
549 /*
550 * Init the main event queue (ASSUMES it cannot fail).
551 */
552 if (SUCCEEDED (rc))
553 EventQueue::init();
554
555 return rc;
556}
557
558HRESULT Shutdown()
559{
560 HRESULT rc = S_OK;
561
562#if !defined (VBOX_WITH_XPCOM)
563
564 /* EventQueue::uninit reference counting fun. */
565 RTTHREAD hSelf = RTThreadSelf();
566 if ( hSelf == gCOMMainThread
567 && hSelf != NIL_RTTHREAD)
568 {
569 if (-- gCOMMainInitCount == 0)
570 {
571 EventQueue::uninit();
572 ASMAtomicWriteHandle (&gCOMMainThread, NIL_RTTHREAD);
573 }
574 }
575
576 CoUninitialize();
577
578#else /* !defined (VBOX_WITH_XPCOM) */
579
580 nsCOMPtr <nsIEventQueue> eventQ;
581 rc = NS_GetMainEventQ (getter_AddRefs (eventQ));
582
583 if (NS_SUCCEEDED(rc) || rc == NS_ERROR_NOT_AVAILABLE)
584 {
585 /* NS_ERROR_NOT_AVAILABLE seems to mean that
586 * nsIEventQueue::StopAcceptingEvents() has been called (see
587 * nsEventQueueService.cpp). We hope that this error code always means
588 * just that in this case and assume that we're on the main thread
589 * (it's a kind of unexpected behavior if a non-main thread ever calls
590 * StopAcceptingEvents() on the main event queue). */
591
592 PRBool isOnMainThread = PR_FALSE;
593 if (NS_SUCCEEDED(rc))
594 {
595 rc = eventQ->IsOnCurrentThread (&isOnMainThread);
596 eventQ = nsnull; /* early release before shutdown */
597 }
598 else
599 {
600 isOnMainThread = PR_TRUE;
601 rc = NS_OK;
602 }
603
604 if (NS_SUCCEEDED(rc) && isOnMainThread)
605 {
606 /* only the main thread needs to uninitialize XPCOM and only if
607 * init counter drops to zero */
608 if (-- gXPCOMInitCount == 0)
609 {
610 EventQueue::uninit();
611 rc = NS_ShutdownXPCOM (nsnull);
612
613 /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to
614 * true. Reset it back to false. */
615 bool wasInited = ASMAtomicXchgBool (&gIsXPCOMInitialized, false);
616 Assert (wasInited == true);
617 NOREF (wasInited);
618
619#if defined (XPCOM_GLUE)
620 XPCOMGlueShutdown();
621#endif
622 }
623 }
624 }
625
626#endif /* !defined (VBOX_WITH_XPCOM) */
627
628 AssertComRC (rc);
629
630 return rc;
631}
632
633} /* namespace com */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette