VirtualBox

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

Last change on this file since 59368 was 59368, checked in by vboxsync, 9 years ago

VBoxCOM,VBoxSVC: Call VBoxProxyStub to gently update the MS COM registrations for the VBox components (requires VBOX_WITH_MIDL_PROXY_STUB=1).

  • 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 59368 2016-01-17 05:48:01Z vboxsync $ */
2
3/** @file
4 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
5 */
6
7/*
8 * Copyright (C) 2006-2014 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# include <nsIComponentRegistrar.h>
28# include <nsIServiceManager.h>
29# include <nsCOMPtr.h>
30# include <nsEventQueueUtils.h>
31# include <nsEmbedString.h>
32
33# include <nsILocalFile.h>
34# include <nsIDirectoryService.h>
35# include <nsDirectoryServiceDefs.h>
36
37#endif /* !defined(VBOX_WITH_XPCOM) */
38
39#include "VBox/com/com.h"
40#include "VBox/com/assert.h"
41#include "VBox/com/NativeEventQueue.h"
42#include "VBox/com/AutoLock.h"
43
44#include "../include/Logging.h"
45
46#include <iprt/asm.h>
47#include <iprt/env.h>
48#include <iprt/ldr.h>
49#include <iprt/param.h>
50#include <iprt/path.h>
51#include <iprt/string.h>
52#include <iprt/thread.h>
53
54#include <VBox/err.h>
55
56namespace com
57{
58
59#if defined(VBOX_WITH_XPCOM)
60
61class DirectoryServiceProvider : public nsIDirectoryServiceProvider
62{
63public:
64
65 NS_DECL_ISUPPORTS
66
67 DirectoryServiceProvider()
68 : mCompRegLocation(NULL), mXPTIDatLocation(NULL)
69 , mComponentDirLocation(NULL), mCurrProcDirLocation(NULL)
70 {}
71
72 virtual ~DirectoryServiceProvider();
73
74 HRESULT init(const char *aCompRegLocation,
75 const char *aXPTIDatLocation,
76 const char *aComponentDirLocation,
77 const char *aCurrProcDirLocation);
78
79 NS_DECL_NSIDIRECTORYSERVICEPROVIDER
80
81private:
82 /** @remarks This is not a UTF-8 string. */
83 char *mCompRegLocation;
84 /** @remarks This is not a UTF-8 string. */
85 char *mXPTIDatLocation;
86 /** @remarks This is not a UTF-8 string. */
87 char *mComponentDirLocation;
88 /** @remarks This is not a UTF-8 string. */
89 char *mCurrProcDirLocation;
90};
91
92NS_IMPL_ISUPPORTS1(DirectoryServiceProvider, nsIDirectoryServiceProvider)
93
94DirectoryServiceProvider::~DirectoryServiceProvider()
95{
96 if (mCompRegLocation)
97 {
98 RTStrFree(mCompRegLocation);
99 mCompRegLocation = NULL;
100 }
101 if (mXPTIDatLocation)
102 {
103 RTStrFree(mXPTIDatLocation);
104 mXPTIDatLocation = NULL;
105 }
106 if (mComponentDirLocation)
107 {
108 RTStrFree(mComponentDirLocation);
109 mComponentDirLocation = NULL;
110 }
111 if (mCurrProcDirLocation)
112 {
113 RTStrFree(mCurrProcDirLocation);
114 mCurrProcDirLocation = NULL;
115 }
116}
117
118/**
119 * @param aCompRegLocation Path to compreg.dat, in Utf8.
120 * @param aXPTIDatLocation Path to xpti.data, in Utf8.
121 */
122HRESULT
123DirectoryServiceProvider::init(const char *aCompRegLocation,
124 const char *aXPTIDatLocation,
125 const char *aComponentDirLocation,
126 const char *aCurrProcDirLocation)
127{
128 AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG);
129 AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG);
130
131/** @todo r=bird: Gotta check how this encoding stuff plays out on darwin!
132 * We get down to [VBoxNsxp]NS_NewNativeLocalFile and that file isn't
133 * nsLocalFileUnix.cpp on 32-bit darwin. On 64-bit darwin it's a question
134 * of what we're doing in IPRT and such... We should probably add a
135 * RTPathConvertToNative for use here. */
136 int vrc = RTStrUtf8ToCurrentCP(&mCompRegLocation, aCompRegLocation);
137 if (RT_SUCCESS(vrc))
138 vrc = RTStrUtf8ToCurrentCP(&mXPTIDatLocation, aXPTIDatLocation);
139 if (RT_SUCCESS(vrc) && aComponentDirLocation)
140 vrc = RTStrUtf8ToCurrentCP(&mComponentDirLocation, aComponentDirLocation);
141 if (RT_SUCCESS(vrc) && aCurrProcDirLocation)
142 vrc = RTStrUtf8ToCurrentCP(&mCurrProcDirLocation, aCurrProcDirLocation);
143
144 return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
145}
146
147NS_IMETHODIMP
148DirectoryServiceProvider::GetFile(const char *aProp,
149 PRBool *aPersistent,
150 nsIFile **aRetval)
151{
152 nsCOMPtr <nsILocalFile> localFile;
153 nsresult rv = NS_ERROR_FAILURE;
154
155 *aRetval = nsnull;
156 *aPersistent = PR_TRUE;
157
158 const char *fileLocation = NULL;
159
160 if (strcmp(aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
161 fileLocation = mCompRegLocation;
162 else if (strcmp(aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
163 fileLocation = mXPTIDatLocation;
164 else if (mComponentDirLocation && strcmp(aProp, NS_XPCOM_COMPONENT_DIR) == 0)
165 fileLocation = mComponentDirLocation;
166 else if (mCurrProcDirLocation && strcmp(aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
167 fileLocation = mCurrProcDirLocation;
168 else
169 return NS_ERROR_FAILURE;
170
171 rv = NS_NewNativeLocalFile(nsEmbedCString(fileLocation),
172 PR_TRUE, getter_AddRefs(localFile));
173 if (NS_FAILED(rv))
174 return rv;
175
176 return localFile->QueryInterface(NS_GET_IID(nsIFile), (void **)aRetval);
177}
178
179/**
180 * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
181 * doesn't provide such functionality)
182 */
183static bool volatile gIsXPCOMInitialized = false;
184
185/**
186 * Number of Initialize() calls on the main thread.
187 */
188static unsigned int gXPCOMInitCount = 0;
189
190#else /* !defined(VBOX_WITH_XPCOM) */
191
192/**
193 * The COM main thread handle. (The first caller of com::Initialize().)
194 */
195static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD;
196
197/**
198 * Number of Initialize() calls on the main thread.
199 */
200static uint32_t gCOMMainInitCount = 0;
201
202#endif /* !defined(VBOX_WITH_XPCOM) */
203
204
205/**
206 * Initializes the COM runtime.
207 *
208 * This method must be called on each thread of the client application that
209 * wants to access COM facilities. The initialization must be performed before
210 * calling any other COM method or attempting to instantiate COM objects.
211 *
212 * On platforms using XPCOM, this method uses the following scheme to search for
213 * XPCOM runtime:
214 *
215 * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies
216 * is used to search XPCOM libraries and components. If this method fails to
217 * initialize XPCOM runtime using this path, it will immediately return a
218 * failure and will NOT check for other paths as described below.
219 *
220 * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the
221 * given order:
222 *
223 * a) Compiled-in application data directory (as returned by
224 * RTPathAppPrivateArch())
225 * b) "/usr/lib/virtualbox" (Linux only)
226 * c) "/opt/VirtualBox" (Linux only)
227 *
228 * The first path for which the initialization succeeds will be used.
229 *
230 * On MS COM platforms, the COM runtime is provided by the system and does not
231 * need to be searched for.
232 *
233 * Once the COM subsystem is no longer necessary on a given thread, Shutdown()
234 * must be called to free resources allocated for it. Note that a thread may
235 * call Initialize() several times but for each of tese calls there must be a
236 * corresponding Shutdown() call.
237 *
238 * @return S_OK on success and a COM result code in case of failure.
239 */
240HRESULT Initialize(bool fGui /*= false*/, bool fAutoRegUpdate /*= true*/)
241{
242 HRESULT rc = E_FAIL;
243 NOREF(fAutoRegUpdate);
244
245#if !defined(VBOX_WITH_XPCOM)
246
247# ifdef VBOX_WITH_AUTO_COM_REG_UPDATE
248 /*
249 * First time we're called in a process, we refresh the VBox COM registrations.
250 */
251 if (fAutoRegUpdate && gCOMMainThread == NIL_RTTHREAD)
252 {
253 char szPath[RTPATH_MAX];
254 int vrc = RTPathAppPrivateArch(szPath, sizeof(szPath));
255 if (RT_SUCCESS(vrc))
256# ifndef VBOX_IN_32_ON_64_MAIN_API
257 rc = RTPathAppend(szPath, sizeof(szPath), "VBoxProxyStub.dll");
258# else
259 rc = RTPathAppend(szPath, sizeof(szPath), "x86\\VBoxProxyStub-x86.dll");
260# endif
261 if (RT_SUCCESS(vrc))
262 {
263 RTLDRMOD hMod;
264 vrc = RTLdrLoad(szPath, &hMod);
265 if (RT_SUCCESS(vrc))
266 {
267 union
268 {
269 void *pv;
270 DECLCALLBACKMEMBER(uint32_t, pfnRegUpdate)(void);
271 } u;
272 rc = RTLdrGetSymbol(hMod, "VbpsUpdateRegistrations", &u.pv);
273 if (RT_SUCCESS(rc))
274 u.pfnRegUpdate();
275 /* Just keep it loaded. */
276 }
277 }
278 }
279# endif
280
281 /*
282 * We initialize COM in GUI thread in STA, to be compliant with QT and
283 * OLE requirments (for example to allow D&D), while other threads
284 * initialized in regular MTA. To allow fast proxyless access from
285 * GUI thread to COM objects, we explicitly provide our COM objects
286 * with free threaded marshaller.
287 * !!!!! Please think twice before touching this code !!!!!
288 */
289 DWORD flags = fGui ?
290 COINIT_APARTMENTTHREADED
291 | COINIT_SPEED_OVER_MEMORY
292 :
293 COINIT_MULTITHREADED
294 | COINIT_DISABLE_OLE1DDE
295 | COINIT_SPEED_OVER_MEMORY;
296
297 rc = CoInitializeEx(NULL, flags);
298
299 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
300 * "already initialized using the same apartment model") */
301 AssertMsg(rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc));
302
303 /* To be flow compatible with the XPCOM case, we return here if this isn't
304 * the main thread or if it isn't its first initialization call.
305 * Note! CoInitializeEx and CoUninitialize does it's own reference
306 * counting, so this exercise is entirely for the EventQueue init. */
307 bool fRc;
308 RTTHREAD hSelf = RTThreadSelf();
309 if (hSelf != NIL_RTTHREAD)
310 ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
311 else
312 fRc = false;
313
314 if (fGui)
315 Assert(RTThreadIsMain(hSelf));
316
317 if (!fRc)
318 {
319 if ( gCOMMainThread == hSelf
320 && SUCCEEDED(rc))
321 gCOMMainInitCount++;
322
323 AssertComRC(rc);
324 return rc;
325 }
326 Assert(RTThreadIsMain(hSelf));
327
328 /* this is the first main thread initialization */
329 Assert(gCOMMainInitCount == 0);
330 if (SUCCEEDED(rc))
331 gCOMMainInitCount = 1;
332
333#else /* !defined(VBOX_WITH_XPCOM) */
334
335 /* Unused here */
336 NOREF(fGui);
337
338 if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true)
339 {
340 /* XPCOM is already initialized on the main thread, no special
341 * initialization is necessary on additional threads. Just increase
342 * the init counter if it's a main thread again (to correctly support
343 * nested calls to Initialize()/Shutdown() for compatibility with
344 * Win32). */
345
346 nsCOMPtr<nsIEventQueue> eventQ;
347 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
348
349 if (NS_SUCCEEDED(rc))
350 {
351 PRBool isOnMainThread = PR_FALSE;
352 rc = eventQ->IsOnCurrentThread(&isOnMainThread);
353 if (NS_SUCCEEDED(rc) && isOnMainThread)
354 ++gXPCOMInitCount;
355 }
356
357 AssertComRC(rc);
358 return rc;
359 }
360 Assert(RTThreadIsMain(RTThreadSelf()));
361
362 /* this is the first initialization */
363 gXPCOMInitCount = 1;
364
365 /* prepare paths for registry files */
366 char szCompReg[RTPATH_MAX];
367 char szXptiDat[RTPATH_MAX];
368
369 int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg));
370 if (vrc == VERR_ACCESS_DENIED)
371 return NS_ERROR_FILE_ACCESS_DENIED;
372 AssertRCReturn(vrc, NS_ERROR_FAILURE);
373 vrc = RTStrCopy(szXptiDat, sizeof(szXptiDat), szCompReg);
374 AssertRCReturn(vrc, NS_ERROR_FAILURE);
375#ifdef VBOX_IN_32_ON_64_MAIN_API
376 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg-x86.dat");
377 AssertRCReturn(vrc, NS_ERROR_FAILURE);
378 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti-x86.dat");
379 AssertRCReturn(vrc, NS_ERROR_FAILURE);
380#else
381 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat");
382 AssertRCReturn(vrc, NS_ERROR_FAILURE);
383 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat");
384 AssertRCReturn(vrc, NS_ERROR_FAILURE);
385#endif
386
387 LogFlowFunc(("component registry : \"%s\"\n", szCompReg));
388 LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat));
389
390 static const char *kAppPathsToProbe[] =
391 {
392 NULL, /* 0: will use VBOX_APP_HOME */
393 NULL, /* 1: will try RTPathAppPrivateArch(), correctly installed release builds will never go further */
394 NULL, /* 2: will try parent directory of RTPathAppPrivateArch(), only for testcases in non-hardened builds */
395 /* There used to be hard coded paths, but they only caused trouble
396 * because they often led to mixing of builds or even versions.
397 * If you feel tempted to add anything here, think again. They would
398 * only be used if option 1 would not work, which is a sign of a big
399 * problem, as it returns a fixed location defined at compile time.
400 * It is better to fail than blindly trying to cover the problem. */
401 };
402
403 /* Find out the directory where VirtualBox binaries are located */
404 for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i)
405 {
406 char szAppHomeDir[RTPATH_MAX];
407
408 if (i == 0)
409 {
410 /* Use VBOX_APP_HOME if present */
411 vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL);
412 if (vrc == VERR_ENV_VAR_NOT_FOUND)
413 continue;
414 AssertRC(vrc);
415 }
416 else if (i == 1)
417 {
418 /* Use RTPathAppPrivateArch() first */
419 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
420 AssertRC(vrc);
421 }
422 else if (i == 2)
423 {
424#ifdef VBOX_WITH_HARDENING
425 continue;
426#else /* !VBOX_WITH_HARDENING */
427 /* Use parent of RTPathAppPrivateArch() if ends with "testcase" */
428 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
429 AssertRC(vrc);
430 vrc = RTPathStripTrailingSlash(szAppHomeDir);
431 AssertRC(vrc);
432 char *filename = RTPathFilename(szAppHomeDir);
433 if (!filename || strcmp(filename, "testcase"))
434 continue;
435 RTPathStripFilename(szAppHomeDir);
436#endif /* !VBOX_WITH_HARDENING */
437 }
438 else
439 {
440 /* Iterate over all other paths */
441 RTStrCopy(szAppHomeDir, sizeof(szAppHomeDir), kAppPathsToProbe[i]);
442 vrc = VINF_SUCCESS;
443 }
444 if (RT_FAILURE(vrc))
445 {
446 rc = NS_ERROR_FAILURE;
447 continue;
448 }
449 char szCompDir[RTPATH_MAX];
450 vrc = RTStrCopy(szCompDir, sizeof(szCompDir), szAppHomeDir);
451 if (RT_FAILURE(vrc))
452 {
453 rc = NS_ERROR_FAILURE;
454 continue;
455 }
456 vrc = RTPathAppend(szCompDir, sizeof(szCompDir), "components");
457 if (RT_FAILURE(vrc))
458 {
459 rc = NS_ERROR_FAILURE;
460 continue;
461 }
462 LogFlowFunc(("component directory : \"%s\"\n", szCompDir));
463
464 nsCOMPtr<DirectoryServiceProvider> dsProv;
465 dsProv = new DirectoryServiceProvider();
466 if (dsProv)
467 rc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir);
468 else
469 rc = NS_ERROR_OUT_OF_MEMORY;
470 if (NS_FAILED(rc))
471 break;
472
473 /* Setup the application path for NS_InitXPCOM2. Note that we properly
474 * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
475 * service provider but it seems to be activated after the directory
476 * service is used for the first time (see the source NS_InitXPCOM2). So
477 * use the same value here to be on the safe side. */
478 nsCOMPtr <nsIFile> appDir;
479 {
480 char *appDirCP = NULL;
481 vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir);
482 if (RT_SUCCESS(vrc))
483 {
484 nsCOMPtr<nsILocalFile> file;
485 rc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP),
486 PR_FALSE, getter_AddRefs(file));
487 if (NS_SUCCEEDED(rc))
488 appDir = do_QueryInterface(file, &rc);
489
490 RTStrFree(appDirCP);
491 }
492 else
493 rc = NS_ERROR_FAILURE;
494 }
495 if (NS_FAILED(rc))
496 break;
497
498 /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
499 * still use it instead of the directory service happy */
500 vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir);
501 AssertRC(vrc);
502
503 /* Finally, initialize XPCOM */
504 {
505 nsCOMPtr<nsIServiceManager> serviceManager;
506 rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), appDir, dsProv);
507 if (NS_SUCCEEDED(rc))
508 {
509 nsCOMPtr<nsIComponentRegistrar> registrar =
510 do_QueryInterface(serviceManager, &rc);
511 if (NS_SUCCEEDED(rc))
512 {
513 rc = registrar->AutoRegister(nsnull);
514 if (NS_SUCCEEDED(rc))
515 {
516 /* We succeeded, stop probing paths */
517 LogFlowFunc(("Succeeded.\n"));
518 break;
519 }
520 }
521 }
522 }
523
524 /* clean up before the new try */
525 HRESULT rc2 = NS_ShutdownXPCOM(nsnull);
526 if (SUCCEEDED(rc))
527 rc = rc2;
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 AssertComRCReturnRC(rc);
539
540 // for both COM and XPCOM, we only get here if this is the main thread;
541 // only then initialize the autolock system (AutoLock.cpp)
542 Assert(RTThreadIsMain(RTThreadSelf()));
543 util::InitAutoLockSystem();
544
545 /*
546 * Init the main event queue (ASSUMES it cannot fail).
547 */
548 if (SUCCEEDED(rc))
549 NativeEventQueue::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 NativeEventQueue::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 = RTThreadIsMain(RTThreadSelf());
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 NativeEventQueue::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 }
616 }
617
618#endif /* !defined(VBOX_WITH_XPCOM) */
619
620 AssertComRC(rc);
621
622 return rc;
623}
624
625} /* 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