VirtualBox

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

Last change on this file since 46649 was 46649, checked in by vboxsync, 12 years ago

Forward ported r85941 and required build fixes (Main: Implemented new event queue to separate system's native event queue and our own. Also, XPCOM is not needed for handling our own events. On Windows this also fixes the system's queue quota limitation).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.9 KB
Line 
1/* $Id: initterm.cpp 46649 2013-06-19 11:47:32Z vboxsync $ */
2
3/** @file
4 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
5 */
6
7/*
8 * Copyright (C) 2006-2013 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 * distributed 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/NativeEventQueue.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(bool fGui)
250{
251 HRESULT rc = E_FAIL;
252
253#if !defined(VBOX_WITH_XPCOM)
254
255 /*
256 * We initialize COM in GUI thread in STA, to be compliant with QT and
257 * OLE requirments (for example to allow D&D), while other threads
258 * initialized in regular MTA. To allow fast proxyless access from
259 * GUI thread to COM objects, we explicitly provide our COM objects
260 * with free threaded marshaller.
261 * !!!!! Please think twice before touching this code !!!!!
262 */
263 DWORD flags = fGui ?
264 COINIT_APARTMENTTHREADED
265 | COINIT_SPEED_OVER_MEMORY
266 :
267 COINIT_MULTITHREADED
268 | COINIT_DISABLE_OLE1DDE
269 | COINIT_SPEED_OVER_MEMORY;
270
271 rc = CoInitializeEx(NULL, flags);
272
273 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
274 * "already initialized using the same apartment model") */
275 AssertMsg(rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc));
276
277 /* To be flow compatible with the XPCOM case, we return here if this isn't
278 * the main thread or if it isn't its first initialization call.
279 * Note! CoInitializeEx and CoUninitialize does it's own reference
280 * counting, so this exercise is entirely for the EventQueue init. */
281 bool fRc;
282 RTTHREAD hSelf = RTThreadSelf();
283 if (hSelf != NIL_RTTHREAD)
284 ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
285 else
286 fRc = false;
287
288 if (fGui)
289 Assert(RTThreadIsMain(hSelf));
290
291 if (!fRc)
292 {
293 if ( gCOMMainThread == hSelf
294 && SUCCEEDED(rc))
295 gCOMMainInitCount++;
296
297 AssertComRC(rc);
298 return rc;
299 }
300 Assert(RTThreadIsMain(hSelf));
301
302 /* this is the first main thread initialization */
303 Assert(gCOMMainInitCount == 0);
304 if (SUCCEEDED(rc))
305 gCOMMainInitCount = 1;
306
307#else /* !defined (VBOX_WITH_XPCOM) */
308
309 /* Unused here */
310 NOREF(fGui);
311
312 if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true)
313 {
314 /* XPCOM is already initialized on the main thread, no special
315 * initialization is necessary on additional threads. Just increase
316 * the init counter if it's a main thread again (to correctly support
317 * nested calls to Initialize()/Shutdown() for compatibility with
318 * Win32). */
319
320 nsCOMPtr<nsIEventQueue> eventQ;
321 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
322
323 if (NS_SUCCEEDED(rc))
324 {
325 PRBool isOnMainThread = PR_FALSE;
326 rc = eventQ->IsOnCurrentThread(&isOnMainThread);
327 if (NS_SUCCEEDED(rc) && isOnMainThread)
328 ++gXPCOMInitCount;
329 }
330
331 AssertComRC(rc);
332 return rc;
333 }
334 Assert(RTThreadIsMain(RTThreadSelf()));
335
336 /* this is the first initialization */
337 gXPCOMInitCount = 1;
338 bool const fInitEventQueues = true;
339
340 /* prepare paths for registry files */
341 char szCompReg[RTPATH_MAX];
342 char szXptiDat[RTPATH_MAX];
343
344 int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg));
345 if (vrc == VERR_ACCESS_DENIED)
346 return NS_ERROR_FILE_ACCESS_DENIED;
347 AssertRCReturn(vrc, NS_ERROR_FAILURE);
348 strcpy(szXptiDat, szCompReg);
349
350 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat");
351 AssertRCReturn(vrc, NS_ERROR_FAILURE);
352 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat");
353 AssertRCReturn(vrc, NS_ERROR_FAILURE);
354
355 LogFlowFunc(("component registry : \"%s\"\n", szCompReg));
356 LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat));
357
358#if defined (XPCOM_GLUE)
359 XPCOMGlueStartup(nsnull);
360#endif
361
362 static const char *kAppPathsToProbe[] =
363 {
364 NULL, /* 0: will use VBOX_APP_HOME */
365 NULL, /* 1: will try RTPathAppPrivateArch() */
366#ifdef RT_OS_LINUX
367 "/usr/lib/virtualbox",
368 "/opt/VirtualBox",
369#elif RT_OS_SOLARIS
370 "/opt/VirtualBox/amd64",
371 "/opt/VirtualBox/i386",
372#elif RT_OS_DARWIN
373 "/Application/VirtualBox.app/Contents/MacOS",
374#endif
375 };
376
377 /* Find out the directory where VirtualBox binaries are located */
378 for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i)
379 {
380 char szAppHomeDir[RTPATH_MAX];
381
382 if (i == 0)
383 {
384 /* Use VBOX_APP_HOME if present */
385 vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL);
386 if (vrc == VERR_ENV_VAR_NOT_FOUND)
387 continue;
388 AssertRC(vrc);
389 }
390 else if (i == 1)
391 {
392 /* Use RTPathAppPrivateArch() first */
393 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
394 AssertRC(vrc);
395 }
396 else
397 {
398 /* Iterate over all other paths */
399 szAppHomeDir[RTPATH_MAX - 1] = '\0';
400 strncpy(szAppHomeDir, kAppPathsToProbe[i], RTPATH_MAX - 1);
401 vrc = VINF_SUCCESS;
402 }
403 if (RT_FAILURE(vrc))
404 {
405 rc = NS_ERROR_FAILURE;
406 continue;
407 }
408
409 char szCompDir[RTPATH_MAX];
410 vrc = RTPathAppend(strcpy(szCompDir, szAppHomeDir), sizeof(szCompDir), "components");
411 if (RT_FAILURE(vrc))
412 {
413 rc = NS_ERROR_FAILURE;
414 continue;
415 }
416 LogFlowFunc(("component directory : \"%s\"\n", szCompDir));
417
418 nsCOMPtr<DirectoryServiceProvider> dsProv;
419 dsProv = new DirectoryServiceProvider();
420 if (dsProv)
421 rc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir);
422 else
423 rc = NS_ERROR_OUT_OF_MEMORY;
424 if (NS_FAILED(rc))
425 break;
426
427 /* Setup the application path for NS_InitXPCOM2. Note that we properly
428 * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
429 * service provider but it seems to be activated after the directory
430 * service is used for the first time (see the source NS_InitXPCOM2). So
431 * use the same value here to be on the safe side. */
432 nsCOMPtr <nsIFile> appDir;
433 {
434 char *appDirCP = NULL;
435 vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir);
436 if (RT_SUCCESS(vrc))
437 {
438 nsCOMPtr<nsILocalFile> file;
439 rc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP),
440 PR_FALSE, getter_AddRefs(file));
441 if (NS_SUCCEEDED(rc))
442 appDir = do_QueryInterface(file, &rc);
443
444 RTStrFree(appDirCP);
445 }
446 else
447 rc = NS_ERROR_FAILURE;
448 }
449 if (NS_FAILED(rc))
450 break;
451
452 /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
453 * still use it instead of the directory service happy */
454 vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir);
455 AssertRC(vrc);
456
457 /* Finally, initialize XPCOM */
458 {
459 nsCOMPtr<nsIServiceManager> serviceManager;
460 rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), appDir, dsProv);
461 if (NS_SUCCEEDED(rc))
462 {
463 nsCOMPtr<nsIComponentRegistrar> registrar =
464 do_QueryInterface(serviceManager, &rc);
465 if (NS_SUCCEEDED(rc))
466 {
467 rc = registrar->AutoRegister(nsnull);
468 if (NS_SUCCEEDED(rc))
469 {
470 /* We succeeded, stop probing paths */
471 LogFlowFunc(("Succeeded.\n"));
472 break;
473 }
474 }
475 }
476 }
477
478 /* clean up before the new try */
479 rc = NS_ShutdownXPCOM(nsnull);
480
481 if (i == 0)
482 {
483 /* We failed with VBOX_APP_HOME, don't probe other paths */
484 break;
485 }
486 }
487
488#endif /* !defined (VBOX_WITH_XPCOM) */
489
490 // for both COM and XPCOM, we only get here if this is the main thread;
491 // only then initialize the autolock system (AutoLock.cpp)
492 Assert(RTThreadIsMain(RTThreadSelf()));
493 util::InitAutoLockSystem();
494
495 AssertComRC(rc);
496
497 /*
498 * Init the main event queue (ASSUMES it cannot fail).
499 */
500 if (SUCCEEDED(rc))
501 NativeEventQueue::init();
502
503 return rc;
504}
505
506HRESULT Shutdown()
507{
508 HRESULT rc = S_OK;
509
510#if !defined(VBOX_WITH_XPCOM)
511
512 /* EventQueue::uninit reference counting fun. */
513 RTTHREAD hSelf = RTThreadSelf();
514 if ( hSelf == gCOMMainThread
515 && hSelf != NIL_RTTHREAD)
516 {
517 if (-- gCOMMainInitCount == 0)
518 {
519 NativeEventQueue::uninit();
520 ASMAtomicWriteHandle(&gCOMMainThread, NIL_RTTHREAD);
521 }
522 }
523
524 CoUninitialize();
525
526#else /* !defined (VBOX_WITH_XPCOM) */
527
528 nsCOMPtr<nsIEventQueue> eventQ;
529 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
530
531 if (NS_SUCCEEDED(rc) || rc == NS_ERROR_NOT_AVAILABLE)
532 {
533 /* NS_ERROR_NOT_AVAILABLE seems to mean that
534 * nsIEventQueue::StopAcceptingEvents() has been called (see
535 * nsEventQueueService.cpp). We hope that this error code always means
536 * just that in this case and assume that we're on the main thread
537 * (it's a kind of unexpected behavior if a non-main thread ever calls
538 * StopAcceptingEvents() on the main event queue). */
539
540 PRBool isOnMainThread = PR_FALSE;
541 if (NS_SUCCEEDED(rc))
542 {
543 rc = eventQ->IsOnCurrentThread(&isOnMainThread);
544 eventQ = nsnull; /* early release before shutdown */
545 }
546 else
547 {
548 isOnMainThread = PR_TRUE;
549 rc = NS_OK;
550 }
551
552 if (NS_SUCCEEDED(rc) && isOnMainThread)
553 {
554 /* only the main thread needs to uninitialize XPCOM and only if
555 * init counter drops to zero */
556 if (--gXPCOMInitCount == 0)
557 {
558 MainEventQueue::uninit();
559 rc = NS_ShutdownXPCOM(nsnull);
560
561 /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to
562 * true. Reset it back to false. */
563 bool wasInited = ASMAtomicXchgBool(&gIsXPCOMInitialized, false);
564 Assert(wasInited == true);
565 NOREF(wasInited);
566
567# if defined (XPCOM_GLUE)
568 XPCOMGlueShutdown();
569# endif
570 }
571 }
572 }
573
574#endif /* !defined(VBOX_WITH_XPCOM) */
575
576 AssertComRC(rc);
577
578 return rc;
579}
580
581} /* 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