VirtualBox

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

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

*: RTEnv usage cleanup - avoid RTEnvGet() as it doesn't necessarily return UTF-8 encoded strings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 KB
Line 
1/* $Id: initterm.cpp 25942 2010-01-20 17:26:22Z vboxsync $ */
2
3/** @file
4 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
5 */
6
7/*
8 * Copyright (C) 2006-2010 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 /** @remarks This is not a UTF-8 string. */
96 char *mCompRegLocation;
97 /** @remarks This is not a UTF-8 string. */
98 char *mXPTIDatLocation;
99 /** @remarks This is not a UTF-8 string. */
100 char *mComponentDirLocation;
101 /** @remarks This is not a UTF-8 string. */
102 char *mCurrProcDirLocation;
103};
104
105NS_IMPL_ISUPPORTS1(DirectoryServiceProvider, nsIDirectoryServiceProvider)
106
107DirectoryServiceProvider::~DirectoryServiceProvider()
108{
109 if (mCompRegLocation)
110 {
111 RTStrFree(mCompRegLocation);
112 mCompRegLocation = NULL;
113 }
114 if (mXPTIDatLocation)
115 {
116 RTStrFree(mXPTIDatLocation);
117 mXPTIDatLocation = NULL;
118 }
119 if (mComponentDirLocation)
120 {
121 RTStrFree(mComponentDirLocation);
122 mComponentDirLocation = NULL;
123 }
124 if (mCurrProcDirLocation)
125 {
126 RTStrFree(mCurrProcDirLocation);
127 mCurrProcDirLocation = NULL;
128 }
129}
130
131/**
132 * @param aCompRegLocation Path to compreg.dat, in Utf8.
133 * @param aXPTIDatLocation Path to xpti.data, in Utf8.
134 */
135HRESULT
136DirectoryServiceProvider::init(const char *aCompRegLocation,
137 const char *aXPTIDatLocation,
138 const char *aComponentDirLocation,
139 const char *aCurrProcDirLocation)
140{
141 AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG);
142 AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG);
143
144/** @todo r=bird: Gotta check how this encoding stuff plays out on darwin!
145 * We get down to [VBoxNsxp]NS_NewNativeLocalFile and that file isn't
146 * nsLocalFileUnix.cpp on 32-bit darwin. On 64-bit darwin it's a question
147 * of what we're doing in IPRT and such... We should probably add a
148 * RTPathConvertToNative for use here. */
149 int vrc = RTStrUtf8ToCurrentCP(&mCompRegLocation, aCompRegLocation);
150 if (RT_SUCCESS(vrc))
151 vrc = RTStrUtf8ToCurrentCP(&mXPTIDatLocation, aXPTIDatLocation);
152 if (RT_SUCCESS(vrc) && aComponentDirLocation)
153 vrc = RTStrUtf8ToCurrentCP(&mComponentDirLocation, aComponentDirLocation);
154 if (RT_SUCCESS(vrc) && aCurrProcDirLocation)
155 vrc = RTStrUtf8ToCurrentCP(&mCurrProcDirLocation, aCurrProcDirLocation);
156
157 return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
158}
159
160NS_IMETHODIMP
161DirectoryServiceProvider::GetFile(const char *aProp,
162 PRBool *aPersistent,
163 nsIFile **aRetval)
164{
165 nsCOMPtr <nsILocalFile> localFile;
166 nsresult rv = NS_ERROR_FAILURE;
167
168 *aRetval = nsnull;
169 *aPersistent = PR_TRUE;
170
171 const char *fileLocation = NULL;
172
173 if (strcmp(aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
174 fileLocation = mCompRegLocation;
175 else if (strcmp(aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
176 fileLocation = mXPTIDatLocation;
177 else if (mComponentDirLocation && strcmp(aProp, NS_XPCOM_COMPONENT_DIR) == 0)
178 fileLocation = mComponentDirLocation;
179 else if (mCurrProcDirLocation && strcmp(aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
180 fileLocation = mCurrProcDirLocation;
181 else
182 return NS_ERROR_FAILURE;
183
184 rv = NS_NewNativeLocalFile(nsEmbedCString(fileLocation),
185 PR_TRUE, getter_AddRefs(localFile));
186 if (NS_FAILED(rv))
187 return rv;
188
189 return localFile->QueryInterface(NS_GET_IID (nsIFile), (void **)aRetval);
190}
191
192/**
193 * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
194 * doesn't provide such functionality)
195 */
196static bool volatile gIsXPCOMInitialized = false;
197
198/**
199 * Number of Initialize() calls on the main thread.
200 */
201static unsigned int gXPCOMInitCount = 0;
202
203#else /* !defined (VBOX_WITH_XPCOM) */
204
205/**
206 * The COM main thread handle. (The first caller of com::Initialize().)
207 */
208static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD;
209
210/**
211 * Number of Initialize() calls on the main thread.
212 */
213static uint32_t gCOMMainInitCount = 0;
214
215#endif /* !defined (VBOX_WITH_XPCOM) */
216
217
218/**
219 * Initializes the COM runtime.
220 *
221 * This method must be called on each thread of the client application that
222 * wants to access COM facilities. The initialization must be performed before
223 * calling any other COM method or attempting to instantiate COM objects.
224 *
225 * On platforms using XPCOM, this method uses the following scheme to search for
226 * XPCOM runtime:
227 *
228 * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies
229 * is used to search XPCOM libraries and components. If this method fails to
230 * initialize XPCOM runtime using this path, it will immediately return a
231 * failure and will NOT check for other paths as described below.
232 *
233 * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the
234 * given order:
235 *
236 * a) Compiled-in application data directory (as returned by
237 * RTPathAppPrivateArch())
238 * b) "/usr/lib/virtualbox" (Linux only)
239 * c) "/opt/VirtualBox" (Linux only)
240 *
241 * The first path for which the initialization succeeds will be used.
242 *
243 * On MS COM platforms, the COM runtime is provided by the system and does not
244 * need to be searched for.
245 *
246 * Once the COM subsystem is no longer necessary on a given thread, Shutdown()
247 * must be called to free resources allocated for it. Note that a thread may
248 * call Initialize() several times but for each of tese calls there must be a
249 * corresponding Shutdown() call.
250 *
251 * @return S_OK on success and a COM result code in case of failure.
252 */
253HRESULT Initialize()
254{
255 HRESULT rc = E_FAIL;
256
257#if !defined(VBOX_WITH_XPCOM)
258
259 DWORD flags = COINIT_MULTITHREADED
260 | COINIT_DISABLE_OLE1DDE
261 | COINIT_SPEED_OVER_MEMORY;
262
263 rc = CoInitializeEx(NULL, flags);
264
265 /// @todo the below rough method of changing the aparment type doesn't
266 /// work on some systems for unknown reason (CoUninitialize() simply does
267 /// nothing there, or at least all 10 000 of subsequent CoInitializeEx()
268 /// continue to return RPC_E_CHANGED_MODE there). The problem on those
269 /// systems is related to the "Extend support for advanced text services
270 /// to all programs" checkbox in the advanced language settings dialog,
271 /// i.e. the problem appears when this checkbox is checked and disappears
272 /// if you clear it. For this reason, we disable the code below and
273 /// instead initialize COM in MTA as early as possible, before 3rd party
274 /// libraries we use have done so (i.e. Qt).
275# if 0
276 /* If we fail to set the necessary apartment model, it may mean that some
277 * DLL that was indirectly loaded by the process calling this function has
278 * already initialized COM on the given thread in an incompatible way
279 * which we can't leave with. Therefore, we try to fix this by using the
280 * brute force method: */
281
282 if (rc == RPC_E_CHANGED_MODE)
283 {
284 /* Before we use brute force, we need to check if we are in the
285 * neutral threaded apartment -- in this case there is no need to
286 * worry at all. */
287
288 rc = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
289 if (rc == RPC_E_CHANGED_MODE)
290 {
291 /* This is a neutral apartment, reset the error */
292 rc = S_OK;
293
294 LogFlowFunc(("COM is already initialized in neutral threaded "
295 "apartment mode,\nwill accept it.\n"));
296 }
297 else if (rc == S_FALSE)
298 {
299 /* balance the test CoInitializeEx above */
300 CoUninitialize();
301 rc = RPC_E_CHANGED_MODE;
302
303 LogFlowFunc(("COM is already initialized in single threaded "
304 "apartment mode,\nwill reinitialize as "
305 "multi threaded.\n"));
306
307 enum { MaxTries = 10000 };
308 int tries = MaxTries;
309 while (rc == RPC_E_CHANGED_MODE && tries --)
310 {
311 CoUninitialize();
312 rc = CoInitializeEx(NULL, flags);
313 if (rc == S_OK)
314 {
315 /* We've successfully reinitialized COM; restore the
316 * initialization reference counter */
317
318 LogFlowFunc(("Will call CoInitializeEx() %d times.\n",
319 MaxTries - tries));
320
321 while (tries ++ < MaxTries)
322 {
323 rc = CoInitializeEx(NULL, flags);
324 Assert(rc == S_FALSE);
325 }
326 }
327 }
328 }
329 else
330 AssertMsgFailed(("rc=%08X\n", rc));
331 }
332# endif
333
334 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
335 * "already initialized using the same apartment model") */
336 AssertMsg(rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc));
337
338 /* To be flow compatible with the XPCOM case, we return here if this isn't
339 * the main thread or if it isn't its first initialization call.
340 * Note! CoInitializeEx and CoUninitialize does it's own reference
341 * counting, so this exercise is entirely for the EventQueue init. */
342 bool fRc;
343 RTTHREAD hSelf = RTThreadSelf();
344 if (hSelf != NIL_RTTHREAD)
345 ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
346 else
347 fRc = false;
348 if (!fRc)
349 {
350 if ( gCOMMainThread == hSelf
351 && SUCCEEDED(rc))
352 gCOMMainInitCount++;
353
354 AssertComRC(rc);
355 return rc;
356 }
357 Assert(RTThreadIsMain(hSelf));
358
359 /* this is the first main thread initialization */
360 Assert(gCOMMainInitCount == 0);
361 if (SUCCEEDED(rc))
362 gCOMMainInitCount = 1;
363
364#else /* !defined (VBOX_WITH_XPCOM) */
365
366 if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true)
367 {
368 /* XPCOM is already initialized on the main thread, no special
369 * initialization is necessary on additional threads. Just increase
370 * the init counter if it's a main thread again (to correctly support
371 * nested calls to Initialize()/Shutdown() for compatibility with
372 * Win32). */
373
374 nsCOMPtr<nsIEventQueue> eventQ;
375 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
376
377 if (NS_SUCCEEDED(rc))
378 {
379 PRBool isOnMainThread = PR_FALSE;
380 rc = eventQ->IsOnCurrentThread(&isOnMainThread);
381 if (NS_SUCCEEDED(rc) && isOnMainThread)
382 ++gXPCOMInitCount;
383 }
384
385 AssertComRC(rc);
386 return rc;
387 }
388 Assert(RTThreadIsMain(RTThreadSelf()));
389
390 /* this is the first initialization */
391 gXPCOMInitCount = 1;
392 bool const fInitEventQueues = true;
393
394 /* prepare paths for registry files */
395 char szCompReg[RTPATH_MAX];
396 char szXptiDat[RTPATH_MAX];
397
398 int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg));
399 AssertRCReturn(vrc, NS_ERROR_FAILURE);
400 strcpy(szXptiDat, szCompReg);
401
402 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat");
403 AssertRCReturn(vrc, NS_ERROR_FAILURE);
404 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat");
405 AssertRCReturn(vrc, NS_ERROR_FAILURE);
406
407 LogFlowFunc(("component registry : \"%s\"\n", szCompReg));
408 LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat));
409
410#if defined (XPCOM_GLUE)
411 XPCOMGlueStartup(nsnull);
412#endif
413
414 static const char *kAppPathsToProbe[] =
415 {
416 NULL, /* 0: will use VBOX_APP_HOME */
417 NULL, /* 1: will try RTPathAppPrivateArch() */
418#ifdef RT_OS_LINUX
419 "/usr/lib/virtualbox",
420 "/opt/VirtualBox",
421#elif RT_OS_SOLARIS
422 "/opt/VirtualBox/amd64",
423 "/opt/VirtualBox/i386",
424#elif RT_OS_DARWIN
425 "/Application/VirtualBox.app/Contents/MacOS",
426#endif
427 };
428
429 /* Find out the directory where VirtualBox binaries are located */
430 for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i)
431 {
432 char szAppHomeDir[RTPATH_MAX];
433
434 if (i == 0)
435 {
436 /* Use VBOX_APP_HOME if present */
437 vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL);
438 if (vrc == VERR_ENV_VAR_NOT_FOUND)
439 continue;
440 AssertRC(vrc);
441 }
442 else if (i == 1)
443 {
444 /* Use RTPathAppPrivateArch() first */
445 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
446 AssertRC (vrc);
447 }
448 else
449 {
450 /* Iterate over all other paths */
451 szAppHomeDir[RTPATH_MAX - 1] = '\0';
452 strncpy(szAppHomeDir, kAppPathsToProbe [i], RTPATH_MAX - 1);
453 vrc = VINF_SUCCESS;
454 }
455 if (RT_FAILURE(vrc))
456 {
457 rc = NS_ERROR_FAILURE;
458 continue;
459 }
460
461 char szCompDir[RTPATH_MAX];
462 vrc = RTPathAppend(strcpy(szCompDir, szAppHomeDir), sizeof(szCompDir), "components");
463 if (RT_FAILURE(vrc))
464 {
465 rc = NS_ERROR_FAILURE;
466 continue;
467 }
468 LogFlowFunc(("component directory : \"%s\"\n", szCompDir));
469
470 nsCOMPtr<DirectoryServiceProvider> dsProv;
471 dsProv = new DirectoryServiceProvider();
472 if (dsProv)
473 rc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir);
474 else
475 rc = NS_ERROR_OUT_OF_MEMORY;
476 if (NS_FAILED(rc))
477 break;
478
479 /* Setup the application path for NS_InitXPCOM2. Note that we properly
480 * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
481 * service provider but it seems to be activated after the directory
482 * service is used for the first time (see the source NS_InitXPCOM2). So
483 * use the same value here to be on the safe side. */
484 nsCOMPtr <nsIFile> appDir;
485 {
486 char *appDirCP = NULL;
487 vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir);
488 if (RT_SUCCESS(vrc))
489 {
490 nsCOMPtr<nsILocalFile> file;
491 rc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP),
492 PR_FALSE, getter_AddRefs(file));
493 if (NS_SUCCEEDED(rc))
494 appDir = do_QueryInterface(file, &rc);
495
496 RTStrFree(appDirCP);
497 }
498 else
499 rc = NS_ERROR_FAILURE;
500 }
501 if (NS_FAILED(rc))
502 break;
503
504 /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
505 * still use it instead of the directory service happy */
506 vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir);
507 AssertRC(vrc);
508
509 /* Finally, initialize XPCOM */
510 {
511 nsCOMPtr<nsIServiceManager> serviceManager;
512 rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), appDir, dsProv);
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