VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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