VirtualBox

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

Last change on this file since 7936 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.0 KB
Line 
1/** @file
2 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
3 */
4
5/*
6 * Copyright (C) 2006-2007 innotek GmbH
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#if !defined (VBOX_WITH_XPCOM)
18
19#include <objbase.h>
20
21#else /* !defined (VBOX_WITH_XPCOM) */
22
23#include <stdlib.h>
24
25/* XPCOM_GLUE is defined when the client uses the standalone glue
26 * (i.e. dynamically picks up the existing XPCOM shared library installation).
27 * This is not the case for VirtualBox XPCOM clients (they are always
28 * distrubuted with the self-built XPCOM library, and therefore have a binary
29 * dependency on it) but left here for clarity.
30 */
31#if defined (XPCOM_GLUE)
32#include <nsXPCOMGlue.h>
33#endif
34
35#include <nsIComponentRegistrar.h>
36#include <nsIServiceManager.h>
37#include <nsCOMPtr.h>
38#include <nsEventQueueUtils.h>
39#include <nsEmbedString.h>
40
41#include <nsILocalFile.h>
42#include <nsIDirectoryService.h>
43#include <nsDirectoryServiceDefs.h>
44
45#endif /* !defined (VBOX_WITH_XPCOM) */
46
47#include <iprt/param.h>
48#include <iprt/path.h>
49#include <iprt/string.h>
50#include <iprt/env.h>
51#include <iprt/asm.h>
52
53#include <VBox/err.h>
54
55#include "VBox/com/com.h"
56#include "VBox/com/assert.h"
57
58#include "../include/Logging.h"
59
60namespace com
61{
62
63#if defined (VBOX_WITH_XPCOM)
64
65class DirectoryServiceProvider : public nsIDirectoryServiceProvider
66{
67public:
68
69 NS_DECL_ISUPPORTS
70
71 DirectoryServiceProvider()
72 : mCompRegLocation (NULL), mXPTIDatLocation (NULL)
73 , mComponentDirLocation (NULL), mCurrProcDirLocation (NULL)
74 {}
75
76 virtual ~DirectoryServiceProvider();
77
78 HRESULT init (const char *aCompRegLocation,
79 const char *aXPTIDatLocation,
80 const char *aComponentDirLocation,
81 const char *aCurrProcDirLocation);
82
83 NS_DECL_NSIDIRECTORYSERVICEPROVIDER
84
85private:
86
87 char *mCompRegLocation;
88 char *mXPTIDatLocation;
89 char *mComponentDirLocation;
90 char *mCurrProcDirLocation;
91};
92
93NS_IMPL_ISUPPORTS1 (DirectoryServiceProvider, nsIDirectoryServiceProvider)
94
95DirectoryServiceProvider::~DirectoryServiceProvider()
96{
97 if (mCompRegLocation)
98 {
99 RTStrFree (mCompRegLocation);
100 mCompRegLocation = NULL;
101 }
102 if (mXPTIDatLocation)
103 {
104 RTStrFree (mXPTIDatLocation);
105 mXPTIDatLocation = NULL;
106 }
107 if (mComponentDirLocation)
108 {
109 RTStrFree (mComponentDirLocation);
110 mComponentDirLocation = NULL;
111 }
112 if (mCurrProcDirLocation)
113 {
114 RTStrFree (mCurrProcDirLocation);
115 mCurrProcDirLocation = NULL;
116 }
117}
118
119/**
120 * @param aCompRegLocation Path to compreg.dat, in Utf8.
121 * @param aXPTIDatLocation Path to xpti.data, in Utf8.
122 */
123HRESULT
124DirectoryServiceProvider::init (const char *aCompRegLocation,
125 const char *aXPTIDatLocation,
126 const char *aComponentDirLocation,
127 const char *aCurrProcDirLocation)
128{
129 AssertReturn (aCompRegLocation, NS_ERROR_INVALID_ARG);
130 AssertReturn (aXPTIDatLocation, NS_ERROR_INVALID_ARG);
131
132 int vrc = RTStrUtf8ToCurrentCP (&mCompRegLocation, aCompRegLocation);
133 if (RT_SUCCESS (vrc))
134 vrc = RTStrUtf8ToCurrentCP (&mXPTIDatLocation, aXPTIDatLocation);
135 if (RT_SUCCESS (vrc) && aComponentDirLocation)
136 vrc = RTStrUtf8ToCurrentCP (&mComponentDirLocation, aComponentDirLocation);
137 if (RT_SUCCESS (vrc) && aCurrProcDirLocation)
138 vrc = RTStrUtf8ToCurrentCP (&mCurrProcDirLocation, aCurrProcDirLocation);
139
140 return RT_SUCCESS (vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
141}
142
143NS_IMETHODIMP
144DirectoryServiceProvider::GetFile (const char *aProp,
145 PRBool *aPersistent,
146 nsIFile **aRetval)
147{
148 nsCOMPtr <nsILocalFile> localFile;
149 nsresult rv = NS_ERROR_FAILURE;
150
151 *aRetval = nsnull;
152 *aPersistent = PR_TRUE;
153
154 const char *fileLocation = NULL;
155
156 if (strcmp (aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
157 fileLocation = mCompRegLocation;
158 else if (strcmp (aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
159 fileLocation = mXPTIDatLocation;
160 else if (mComponentDirLocation && strcmp (aProp, NS_XPCOM_COMPONENT_DIR) == 0)
161 fileLocation = mComponentDirLocation;
162 else if (mCurrProcDirLocation && strcmp (aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
163 fileLocation = mCurrProcDirLocation;
164 else
165 return NS_ERROR_FAILURE;
166
167 rv = NS_NewNativeLocalFile (nsEmbedCString (fileLocation),
168 PR_TRUE, getter_AddRefs (localFile));
169 if (NS_FAILED(rv))
170 return rv;
171
172 return localFile->QueryInterface (NS_GET_IID (nsIFile),
173 (void **) aRetval);
174}
175
176/**
177 * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
178 * doesn't provide such functionality)
179 */
180static bool gIsXPCOMInitialized = false;
181
182/**
183 * Number of Initialize() calls on the main thread.
184 */
185static unsigned int gXPCOMInitCount = 0;
186
187#endif /* defined (VBOX_WITH_XPCOM) */
188
189
190HRESULT Initialize()
191{
192 HRESULT rc = E_FAIL;
193
194#if !defined (VBOX_WITH_XPCOM)
195
196 DWORD flags = COINIT_MULTITHREADED |
197 COINIT_DISABLE_OLE1DDE |
198 COINIT_SPEED_OVER_MEMORY;
199
200 rc = CoInitializeEx (NULL, flags);
201
202 /// @todo the below rough method of changing the aparment type doesn't
203 /// work on some systems for unknown reason (CoUninitialize() simply does
204 /// nothing there, or at least all 10 000 of subsequent CoInitializeEx()
205 /// continue to return RPC_E_CHANGED_MODE there). The problem on those
206 /// systems is related to the "Extend support for advanced text services
207 /// to all programs" checkbox in the advanced language settings dialog,
208 /// i.e. the problem appears when this checkbox is checked and disappears
209 /// if you clear it. For this reason, we disable the code below and
210 /// instead initialize COM in MTA as early as possible, before 3rd party
211 /// libraries we use have done so (i.e. Qt3).
212#if 0
213 /* If we fail to set the necessary apartment model, it may mean that some
214 * DLL that was indirectly loaded by the process calling this function has
215 * already initialized COM on the given thread in an incompatible way
216 * which we can't leave with. Therefore, we try to fix this by using the
217 * brute force method: */
218
219 if (rc == RPC_E_CHANGED_MODE)
220 {
221 /* Before we use brute force, we need to check if we are in the
222 * neutral threaded apartment -- in this case there is no need to
223 * worry at all. */
224
225 rc = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
226 if (rc == RPC_E_CHANGED_MODE)
227 {
228 /* This is a neutral apartment, reset the error */
229 rc = S_OK;
230
231 LogFlowFunc (("COM is already initialized in neutral threaded "
232 "apartment mode,\nwill accept it.\n"));
233 }
234 else if (rc == S_FALSE)
235 {
236 /* balance the test CoInitializeEx above */
237 CoUninitialize();
238 rc = RPC_E_CHANGED_MODE;
239
240 LogFlowFunc (("COM is already initialized in single threaded "
241 "apartment mode,\nwill reinitialize as "
242 "multi threaded.\n"));
243
244 enum { MaxTries = 10000 };
245 int tries = MaxTries;
246 while (rc == RPC_E_CHANGED_MODE && tries --)
247 {
248 CoUninitialize();
249 rc = CoInitializeEx (NULL, flags);
250 if (rc == S_OK)
251 {
252 /* We've successfully reinitialized COM; restore the
253 * initialization reference counter */
254
255 LogFlowFunc (("Will call CoInitializeEx() %d times.\n",
256 MaxTries - tries));
257
258 while (tries ++ < MaxTries)
259 {
260 rc = CoInitializeEx (NULL, flags);
261 Assert (rc == S_FALSE);
262 }
263 }
264 }
265 }
266 else
267 AssertMsgFailed (("rc=%08X\n", rc));
268 }
269#endif
270
271 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
272 * "already initialized using the same apartment model") */
273 AssertMsg (rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc));
274
275#else /* !defined (VBOX_WITH_XPCOM) */
276
277 if (ASMAtomicXchgBool (&gIsXPCOMInitialized, true) == true)
278 {
279 /* XPCOM is already initialized on the main thread, no special
280 * initialization is necessary on additional threads. Just increase
281 * the init counter if it's a main thread again (to correctly support
282 * nested calls to Initialize()/Shutdown() for compatibility with
283 * Win32). */
284
285 nsCOMPtr <nsIEventQueue> eventQ;
286 rc = NS_GetMainEventQ (getter_AddRefs (eventQ));
287
288 if (NS_SUCCEEDED (rc))
289 {
290 PRBool isOnMainThread = PR_FALSE;
291 rc = eventQ->IsOnCurrentThread (&isOnMainThread);
292 if (NS_SUCCEEDED (rc) && isOnMainThread)
293 ++ gXPCOMInitCount;
294 }
295
296 AssertComRC (rc);
297 return rc;
298 }
299
300 /* this is the first initialization */
301 gXPCOMInitCount = 1;
302
303 /* Set VBOX_XPCOM_HOME if not present */
304 if (!RTEnvExist ("VBOX_XPCOM_HOME"))
305 {
306 /* get the executable path */
307 char pathProgram [RTPATH_MAX];
308 int vrc = RTPathProgram (pathProgram, sizeof (pathProgram));
309 if (RT_SUCCESS (vrc))
310 {
311 char *pathProgramCP = NULL;
312 vrc = RTStrUtf8ToCurrentCP (&pathProgramCP, pathProgram);
313 if (RT_SUCCESS (vrc))
314 {
315 vrc = RTEnvSet ("VBOX_XPCOM_HOME", pathProgramCP);
316 RTStrFree (pathProgramCP);
317 }
318 }
319 AssertRC (vrc);
320 }
321
322#if defined (XPCOM_GLUE)
323 XPCOMGlueStartup (nsnull);
324#endif
325
326 nsCOMPtr <DirectoryServiceProvider> dsProv;
327
328 /* prepare paths for registry files */
329 char homeDir [RTPATH_MAX];
330 char privateArchDir [RTPATH_MAX];
331 int vrc = GetVBoxUserHomeDirectory (homeDir, sizeof (homeDir));
332 if (RT_SUCCESS (vrc))
333 vrc = RTPathAppPrivateArch (privateArchDir, sizeof (privateArchDir));
334 if (RT_SUCCESS (vrc))
335 {
336 char compReg [RTPATH_MAX];
337 char xptiDat [RTPATH_MAX];
338 char compDir [RTPATH_MAX];
339
340 RTStrPrintf (compReg, sizeof (compReg), "%s%c%s",
341 homeDir, RTPATH_DELIMITER, "compreg.dat");
342 RTStrPrintf (xptiDat, sizeof (xptiDat), "%s%c%s",
343 homeDir, RTPATH_DELIMITER, "xpti.dat");
344 RTStrPrintf (compDir, sizeof (compDir), "%s%c/components",
345 privateArchDir, RTPATH_DELIMITER);
346
347 LogFlowFunc (("component registry : \"%s\"\n", compReg));
348 LogFlowFunc (("XPTI data file : \"%s\"\n", xptiDat));
349 LogFlowFunc (("component directory : \"%s\"\n", compDir));
350
351 dsProv = new DirectoryServiceProvider();
352 if (dsProv)
353 rc = dsProv->init (compReg, xptiDat, compDir, privateArchDir);
354 else
355 rc = NS_ERROR_OUT_OF_MEMORY;
356 }
357 else
358 rc = NS_ERROR_FAILURE;
359
360 if (NS_SUCCEEDED (rc))
361 {
362 /* get the path to the executable */
363 nsCOMPtr <nsIFile> appDir;
364 {
365 char path [RTPATH_MAX];
366 char *appDirCP = NULL;
367#if defined (DEBUG)
368 const char *env = RTEnvGet ("VIRTUALBOX_APP_HOME");
369 if (env)
370 {
371 char *appDirUtf8 = NULL;
372 vrc = RTStrCurrentCPToUtf8 (&appDirUtf8, env);
373 if (RT_SUCCESS (vrc))
374 {
375 vrc = RTPathReal (appDirUtf8, path, RTPATH_MAX);
376 if (RT_SUCCESS (vrc))
377 vrc = RTStrUtf8ToCurrentCP (&appDirCP, appDirUtf8);
378 RTStrFree (appDirUtf8);
379 }
380 }
381 else
382#endif
383 {
384 vrc = RTPathProgram (path, RTPATH_MAX);
385 if (RT_SUCCESS (vrc))
386 vrc = RTStrUtf8ToCurrentCP (&appDirCP, path);
387 }
388
389 if (RT_SUCCESS (vrc))
390 {
391 nsCOMPtr <nsILocalFile> file;
392 rc = NS_NewNativeLocalFile (nsEmbedCString (appDirCP),
393 PR_FALSE, getter_AddRefs (file));
394 if (NS_SUCCEEDED (rc))
395 appDir = do_QueryInterface (file, &rc);
396
397 RTStrFree (appDirCP);
398 }
399 else
400 rc = NS_ERROR_FAILURE;
401 }
402
403 /* Finally, initialize XPCOM */
404 if (NS_SUCCEEDED (rc))
405 {
406 nsCOMPtr <nsIServiceManager> serviceManager;
407 rc = NS_InitXPCOM2 (getter_AddRefs (serviceManager),
408 appDir, dsProv);
409
410 if (NS_SUCCEEDED (rc))
411 {
412 nsCOMPtr <nsIComponentRegistrar> registrar =
413 do_QueryInterface (serviceManager, &rc);
414 if (NS_SUCCEEDED (rc))
415 registrar->AutoRegister (nsnull);
416 }
417 }
418 }
419
420#endif /* !defined (VBOX_WITH_XPCOM) */
421
422 AssertComRC (rc);
423
424 return rc;
425}
426
427HRESULT Shutdown()
428{
429 HRESULT rc = S_OK;
430
431#if !defined (VBOX_WITH_XPCOM)
432
433 CoUninitialize();
434
435#else /* !defined (VBOX_WITH_XPCOM) */
436
437 nsCOMPtr <nsIEventQueue> eventQ;
438 rc = NS_GetMainEventQ (getter_AddRefs (eventQ));
439
440 if (NS_SUCCEEDED (rc) || rc == NS_ERROR_NOT_AVAILABLE)
441 {
442 /* NS_ERROR_NOT_AVAILABLE seems to mean that
443 * nsIEventQueue::StopAcceptingEvents() has been called (see
444 * nsEventQueueService.cpp). We hope that this error code always means
445 * just that in this case and assume that we're on the main thread
446 * (it's a kind of unexpected behavior if a non-main thread ever calls
447 * StopAcceptingEvents() on the main event queue). */
448
449 PRBool isOnMainThread = PR_FALSE;
450 if (NS_SUCCEEDED (rc))
451 {
452 rc = eventQ->IsOnCurrentThread (&isOnMainThread);
453 eventQ = nsnull; /* early release before shutdown */
454 }
455 else
456 {
457 isOnMainThread = PR_TRUE;
458 rc = NS_OK;
459 }
460
461 if (NS_SUCCEEDED (rc) && isOnMainThread)
462 {
463 /* only the main thread needs to uninitialize XPCOM and only if
464 * init counter drops to zero */
465 if (-- gXPCOMInitCount == 0)
466 {
467 rc = NS_ShutdownXPCOM (nsnull);
468
469 /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to
470 * true. Reset it back to false. */
471 bool wasInited = ASMAtomicXchgBool (&gIsXPCOMInitialized, false);
472 Assert (wasInited == true);
473 NOREF (wasInited);
474
475#if defined (XPCOM_GLUE)
476 XPCOMGlueShutdown();
477#endif
478 }
479 }
480 }
481
482#endif /* !defined (VBOX_WITH_XPCOM) */
483
484 AssertComRC (rc);
485
486 return rc;
487}
488
489} /* 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