VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/win/svcmain.cpp@ 108303

最後變更 在這個檔案從108303是 108303,由 vboxsync 提交於 4 週 前

VBoxSVC/Win: Emphasize that this is a character and not an rc in WinMain (renaming).

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 41.6 KB
 
1/* $Id: svcmain.cpp 108303 2025-02-19 16:13:33Z vboxsync $ */
2/** @file
3 * SVCMAIN - COM out-of-proc server main entry
4 */
5
6/*
7 * Copyright (C) 2004-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_MAIN_VBOXSVC
33#include <iprt/win/windows.h>
34#ifdef DEBUG_bird
35# include <RpcAsync.h>
36#endif
37
38#include "VBox/com/defs.h"
39#include "VBox/com/com.h"
40#include "VBox/com/VirtualBox.h"
41
42#include "VirtualBoxImpl.h"
43#include "LoggingNew.h"
44
45#include "svchlp.h"
46
47#include <iprt/errcore.h>
48#include <iprt/buildconfig.h>
49#include <iprt/initterm.h>
50#include <iprt/string.h>
51#include <iprt/path.h>
52#include <iprt/getopt.h>
53#include <iprt/message.h>
54#include <iprt/asm.h>
55
56
57/*********************************************************************************************************************************
58* Defined Constants And Macros *
59*********************************************************************************************************************************/
60#define MAIN_WND_CLASS L"VirtualBox Interface"
61
62
63/*********************************************************************************************************************************
64* Structures and Typedefs *
65*********************************************************************************************************************************/
66class CExeModule : public ATL::CComModule
67{
68public:
69 LONG Unlock() throw();
70 DWORD dwThreadID;
71 HANDLE hEventShutdown;
72 void MonitorShutdown();
73 bool StartMonitor();
74 bool HasActiveConnection();
75 bool bActivity;
76 static bool isIdleLockCount(LONG cLocks);
77};
78
79
80/*********************************************************************************************************************************
81* Global Variables *
82*********************************************************************************************************************************/
83BEGIN_OBJECT_MAP(ObjectMap)
84 OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox)
85END_OBJECT_MAP()
86
87CExeModule *g_pModule = NULL;
88HWND g_hMainWindow = NULL;
89HINSTANCE g_hInstance = NULL;
90#ifdef VBOX_WITH_SDS
91/** This is set if we're connected to SDS.
92 *
93 * It means that we should discount a server lock that it is holding when
94 * deciding whether we're idle or not.
95 *
96 * Also, when set we deregister with SDS during class factory destruction. We
97 * exploit this to prevent attempts to deregister during or after COM shutdown.
98 */
99bool g_fRegisteredWithVBoxSDS = false;
100#endif
101
102/* Normal timeout usually used in Shutdown Monitor */
103const DWORD dwNormalTimeout = 5000;
104volatile uint32_t dwTimeOut = dwNormalTimeout; /* time for EXE to be idle before shutting down. Can be decreased at system shutdown phase. */
105
106
107
108/** Passed to CreateThread to monitor the shutdown event. */
109static DWORD WINAPI MonitorProc(void *pv) RT_NOTHROW_DEF
110{
111 CExeModule *p = (CExeModule *)pv;
112 p->MonitorShutdown();
113 return 0;
114}
115
116LONG CExeModule::Unlock() throw()
117{
118 LONG cLocks = ATL::CComModule::Unlock();
119 if (isIdleLockCount(cLocks))
120 {
121 bActivity = true;
122 SetEvent(hEventShutdown); /* tell monitor that we transitioned to zero */
123 }
124 return cLocks;
125}
126
127bool CExeModule::HasActiveConnection()
128{
129 return bActivity || !isIdleLockCount(GetLockCount());
130}
131
132/**
133 * Checks if @a cLocks signifies an IDLE server lock load.
134 *
135 * This takes VBoxSDS into account (i.e. ignores it).
136 */
137/*static*/ bool CExeModule::isIdleLockCount(LONG cLocks)
138{
139#ifdef VBOX_WITH_SDS
140 if (g_fRegisteredWithVBoxSDS)
141 return cLocks <= 1;
142#endif
143 return cLocks <= 0;
144}
145
146/* Monitors the shutdown event */
147void CExeModule::MonitorShutdown()
148{
149 while (1)
150 {
151 WaitForSingleObject(hEventShutdown, INFINITE);
152 DWORD dwWait;
153 do
154 {
155 bActivity = false;
156 dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
157 } while (dwWait == WAIT_OBJECT_0);
158 /* timed out */
159 if (!HasActiveConnection()) /* if no activity let's really bail */
160 {
161 /* Disable log rotation at this point, worst case a log file
162 * becomes slightly bigger than it should. Avoids quirks with
163 * log rotation: there might be another API service process
164 * running at this point which would rotate the logs concurrently,
165 * creating a mess. */
166 PRTLOGGER pReleaseLogger = RTLogRelGetDefaultInstance();
167 if (pReleaseLogger)
168 {
169 char szDest[1024];
170 int vrc = RTLogQueryDestinations(pReleaseLogger, szDest, sizeof(szDest));
171 if (RT_SUCCESS(vrc))
172 {
173 vrc = RTStrCat(szDest, sizeof(szDest), " nohistory");
174 if (RT_SUCCESS(vrc))
175 {
176 vrc = RTLogDestinations(pReleaseLogger, szDest);
177 AssertRC(vrc);
178 }
179 }
180 }
181#if _WIN32_WINNT >= 0x0400
182 CoSuspendClassObjects();
183 if (!HasActiveConnection())
184#endif
185 break;
186 }
187 }
188 CloseHandle(hEventShutdown);
189 PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
190}
191
192bool CExeModule::StartMonitor()
193{
194 hEventShutdown = CreateEvent(NULL, false, false, NULL);
195 if (hEventShutdown == NULL)
196 return false;
197 DWORD idThreadIgnored;
198 HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &idThreadIgnored);
199 return (h != NULL);
200}
201
202
203#ifdef VBOX_WITH_SDS
204
205class VBoxSVCRegistration;
206
207/**
208 * Custom class factory for the VirtualBox singleton.
209 *
210 * The implementation of CreateInstance is found in win/svcmain.cpp.
211 */
212class VirtualBoxClassFactory : public ATL::CComClassFactory
213{
214private:
215 /** Tri state: 0=uninitialized or initializing; 1=success; -1=failure.
216 * This will be updated after both m_hrcCreate and m_pObj have been set. */
217 volatile int32_t m_iState;
218 /** The result of the instantiation attempt. */
219 HRESULT m_hrcCreate;
220 /** The IUnknown of the VirtualBox object/interface we're working with. */
221 IUnknown *m_pObj;
222 /** Pointer to the IVBoxSVCRegistration implementation that VBoxSDS works with. */
223 VBoxSVCRegistration *m_pVBoxSVC;
224 /** The VBoxSDS interface. */
225 ComPtr<IVirtualBoxSDS> m_ptrVirtualBoxSDS;
226
227public:
228 VirtualBoxClassFactory() : m_iState(0), m_hrcCreate(S_OK), m_pObj(NULL), m_pVBoxSVC(NULL)
229 { }
230
231 virtual ~VirtualBoxClassFactory()
232 {
233 if (m_pObj)
234 {
235 m_pObj->Release();
236 m_pObj = NULL;
237 }
238
239 /* We usually get here during g_pModule->Term() via CoRevokeClassObjec, so COM
240 probably working well enough to talk to SDS when we get here. */
241 if (g_fRegisteredWithVBoxSDS)
242 i_deregisterWithSds();
243 }
244
245 // IClassFactory
246 STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj);
247
248 /** Worker for VBoxSVCRegistration::getVirtualBox. */
249 HRESULT i_getVirtualBox(IUnknown **ppResult);
250
251private:
252 HRESULT i_registerWithSds(IUnknown **ppOtherVirtualBox);
253 void i_deregisterWithSds(void);
254
255 friend VBoxSVCRegistration;
256};
257
258
259/**
260 * The VBoxSVC class is handed to VBoxSDS so it can call us back and ask for the
261 * VirtualBox object when the next VBoxSVC for this user registers itself.
262 */
263class VBoxSVCRegistration : public IVBoxSVCRegistration
264{
265private:
266 /** Number of references. */
267 uint32_t volatile m_cRefs;
268
269public:
270 /** Pointer to the factory. */
271 VirtualBoxClassFactory *m_pFactory;
272
273public:
274 VBoxSVCRegistration(VirtualBoxClassFactory *pFactory)
275 : m_cRefs(1), m_pFactory(pFactory)
276 { }
277 virtual ~VBoxSVCRegistration()
278 {
279 if (m_pFactory)
280 {
281 if (m_pFactory->m_pVBoxSVC)
282 m_pFactory->m_pVBoxSVC = NULL;
283 m_pFactory = NULL;
284 }
285 }
286 RTMEMEF_NEW_AND_DELETE_OPERATORS();
287
288 // IUnknown
289 STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
290 {
291 if (riid == __uuidof(IUnknown))
292 *ppvObject = (void *)(IUnknown *)this;
293 else if (riid == __uuidof(IVBoxSVCRegistration))
294 *ppvObject = (void *)(IVBoxSVCRegistration *)this;
295 else
296 {
297 return E_NOINTERFACE;
298 }
299 AddRef();
300 return S_OK;
301
302 }
303
304 STDMETHOD_(ULONG,AddRef)(void)
305 {
306 uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
307 return cRefs;
308 }
309
310 STDMETHOD_(ULONG,Release)(void)
311 {
312 uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
313 if (cRefs == 0)
314 delete this;
315 return cRefs;
316 }
317
318 // IVBoxSVCRegistration
319 STDMETHOD(GetVirtualBox)(IUnknown **ppResult)
320 {
321 if (m_pFactory)
322 return m_pFactory->i_getVirtualBox(ppResult);
323 return E_FAIL;
324 }
325};
326
327
328HRESULT VirtualBoxClassFactory::i_registerWithSds(IUnknown **ppOtherVirtualBox)
329{
330# ifdef DEBUG_bird
331 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
332 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
333 LogRel(("i_registerWithSds: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
334 rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
335 CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
336# endif
337
338 /*
339 * Connect to VBoxSDS.
340 */
341 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
342 (void **)m_ptrVirtualBoxSDS.asOutParam());
343 if (SUCCEEDED(hrc))
344 {
345 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
346 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
347 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
348 service to access the files. */
349 hrc = CoSetProxyBlanket(m_ptrVirtualBoxSDS,
350 RPC_C_AUTHN_DEFAULT,
351 RPC_C_AUTHZ_DEFAULT,
352 COLE_DEFAULT_PRINCIPAL,
353 RPC_C_AUTHN_LEVEL_DEFAULT,
354 RPC_C_IMP_LEVEL_IMPERSONATE,
355 NULL,
356 EOAC_DEFAULT);
357 if (SUCCEEDED(hrc))
358 {
359 /*
360 * Create VBoxSVCRegistration object and hand that to VBoxSDS.
361 */
362 m_pVBoxSVC = new VBoxSVCRegistration(this);
363 hrc = E_PENDING;
364 /* we try to register IVirtualBox 10 times */
365 for (int regTimes = 0; hrc == E_PENDING && regTimes < 10; --regTimes)
366 {
367 hrc = m_ptrVirtualBoxSDS->RegisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId(), ppOtherVirtualBox);
368 if (SUCCEEDED(hrc))
369 {
370 g_fRegisteredWithVBoxSDS = !*ppOtherVirtualBox;
371 return hrc;
372 }
373 /* sleep to give a time for windows session 0 registration */
374 if (hrc == E_PENDING)
375 RTThreadSleep(1000);
376 }
377 m_pVBoxSVC->Release();
378 }
379 }
380 m_ptrVirtualBoxSDS.setNull();
381 m_pVBoxSVC = NULL;
382 *ppOtherVirtualBox = NULL;
383 return hrc;
384}
385
386
387void VirtualBoxClassFactory::i_deregisterWithSds(void)
388{
389 Log(("VirtualBoxClassFactory::i_deregisterWithSds\n"));
390
391 if (m_ptrVirtualBoxSDS.isNotNull())
392 {
393 if (m_pVBoxSVC)
394 {
395 HRESULT hrc = m_ptrVirtualBoxSDS->DeregisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId());
396 NOREF(hrc);
397 }
398 m_ptrVirtualBoxSDS.setNull();
399 g_fRegisteredWithVBoxSDS = false;
400 }
401 if (m_pVBoxSVC)
402 {
403 m_pVBoxSVC->m_pFactory = NULL;
404 m_pVBoxSVC->Release();
405 m_pVBoxSVC = NULL;
406 }
407}
408
409
410HRESULT VirtualBoxClassFactory::i_getVirtualBox(IUnknown **ppResult)
411{
412# ifdef DEBUG_bird
413 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
414 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
415 LogRel(("i_getVirtualBox: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
416 rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
417 CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
418# endif
419 IUnknown *pObj = m_pObj;
420 if (pObj)
421 {
422 /** @todo Do we need to do something regarding server locking? Hopefully COM
423 * deals with that........... */
424 pObj->AddRef();
425 *ppResult = pObj;
426 Log(("VirtualBoxClassFactory::GetVirtualBox: S_OK - %p\n", pObj));
427 return S_OK;
428 }
429 *ppResult = NULL;
430 Log(("VirtualBoxClassFactory::GetVirtualBox: E_FAIL\n"));
431 return E_FAIL;
432}
433
434
435/**
436 * Custom instantiation of CComObjectCached.
437 *
438 * This catches certain QueryInterface callers for the purpose of watching for
439 * abnormal client process termination (@bugref{3300}).
440 *
441 * @todo just merge this into class VirtualBox VirtualBoxImpl.h
442 */
443class VirtualBoxObjectCached : public VirtualBox
444{
445public:
446 VirtualBoxObjectCached(void * = NULL)
447 : VirtualBox()
448 {
449 }
450
451 virtual ~VirtualBoxObjectCached()
452 {
453 m_iRef = LONG_MIN / 2; /* Catch refcount screwups by setting refcount something insane. */
454 FinalRelease();
455 }
456
457 /** @name IUnknown implementation for VirtualBox
458 * @{ */
459
460 STDMETHOD_(ULONG, AddRef)() throw()
461 {
462 ULONG cRefs = InternalAddRef();
463 if (cRefs == 2)
464 {
465 AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n"));
466 ATL::_pAtlModule->Lock();
467 }
468 return cRefs;
469 }
470
471 STDMETHOD_(ULONG, Release)() throw()
472 {
473 ULONG cRefs = InternalRelease();
474 if (cRefs == 0)
475 delete this;
476 else if (cRefs == 1)
477 {
478 AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n"));
479 ATL::_pAtlModule->Unlock();
480 }
481 return cRefs;
482 }
483
484 STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw()
485 {
486 HRESULT hrc = _InternalQueryInterface(iid, ppvObj);
487#ifdef VBOXSVC_WITH_CLIENT_WATCHER
488 i_logCaller("QueryInterface %RTuuid -> %Rhrc %p", &iid, hrc, *ppvObj);
489#endif
490 return hrc;
491 }
492
493 /** @} */
494
495 static HRESULT WINAPI CreateInstance(VirtualBoxObjectCached **ppObj) throw()
496 {
497 AssertReturn(ppObj, E_POINTER);
498 *ppObj = NULL;
499
500 HRESULT hrc = E_OUTOFMEMORY;
501 VirtualBoxObjectCached *p = new (std::nothrow) VirtualBoxObjectCached();
502 if (p)
503 {
504 p->SetVoid(NULL);
505 p->InternalFinalConstructAddRef();
506 hrc = p->_AtlInitialConstruct();
507 if (SUCCEEDED(hrc))
508 hrc = p->FinalConstruct();
509 p->InternalFinalConstructRelease();
510 if (FAILED(hrc))
511 delete p;
512 else
513 *ppObj = p;
514 }
515 return hrc;
516 }
517};
518
519
520/**
521 * Custom class factory impl for the VirtualBox singleton.
522 *
523 * This will consult with VBoxSDS on whether this VBoxSVC instance should
524 * provide the actual VirtualBox instance or just forward the instance from
525 * some other SVC instance.
526 *
527 * @param pUnkOuter This must be NULL.
528 * @param riid Reference to the interface ID to provide.
529 * @param ppvObj Where to return the pointer to the riid instance.
530 *
531 * @return COM status code.
532 */
533STDMETHODIMP VirtualBoxClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj)
534{
535# ifdef VBOXSVC_WITH_CLIENT_WATCHER
536 VirtualBox::i_logCaller("VirtualBoxClassFactory::CreateInstance: %RTuuid", riid);
537# endif
538 HRESULT hrc = E_POINTER;
539 if (ppvObj != NULL)
540 {
541 *ppvObj = NULL;
542 // no aggregation for singletons
543 AssertReturn(pUnkOuter == NULL, CLASS_E_NOAGGREGATION);
544
545 /*
546 * We must make sure there is only one instance around.
547 * So, we check without locking and then again after locking.
548 */
549 if (ASMAtomicReadS32(&m_iState) == 0)
550 {
551 Lock();
552 __try
553 {
554 if (ASMAtomicReadS32(&m_iState) == 0)
555 {
556 /*
557 * lock the module to indicate activity
558 * (necessary for the monitor shutdown thread to correctly
559 * terminate the module in case when CreateInstance() fails)
560 */
561 ATL::_pAtlModule->Lock();
562 __try
563 {
564 /*
565 * Now we need to connect to VBoxSDS to register ourselves.
566 */
567 IUnknown *pOtherVirtualBox = NULL;
568 m_hrcCreate = hrc = i_registerWithSds(&pOtherVirtualBox);
569 if (SUCCEEDED(hrc) && pOtherVirtualBox)
570 m_pObj = pOtherVirtualBox;
571 else if (SUCCEEDED(hrc))
572 {
573 ATL::_pAtlModule->Lock();
574 VirtualBoxObjectCached *p;
575 m_hrcCreate = hrc = VirtualBoxObjectCached::CreateInstance(&p);
576 if (SUCCEEDED(hrc))
577 {
578 m_hrcCreate = hrc = p->QueryInterface(IID_IUnknown, (void **)&m_pObj);
579 if (SUCCEEDED(hrc))
580 RTLogClearFileDelayFlag(RTLogRelGetDefaultInstance(), NULL);
581 else
582 {
583 delete p;
584 i_deregisterWithSds();
585 m_pObj = NULL;
586 }
587 }
588 }
589 ASMAtomicWriteS32(&m_iState, SUCCEEDED(hrc) ? 1 : -1);
590 }
591 __finally
592 {
593 ATL::_pAtlModule->Unlock();
594 }
595 }
596 }
597 __finally
598 {
599 if (ASMAtomicReadS32(&m_iState) == 0)
600 {
601 ASMAtomicWriteS32(&m_iState, -1);
602 if (SUCCEEDED(m_hrcCreate))
603 m_hrcCreate = E_FAIL;
604 }
605 Unlock();
606 }
607 }
608
609 /*
610 * Query the requested interface from the IUnknown one we're keeping around.
611 */
612 if (m_hrcCreate == S_OK)
613 hrc = m_pObj->QueryInterface(riid, ppvObj);
614 else
615 hrc = m_hrcCreate;
616 }
617 return hrc;
618}
619
620#endif // VBOX_WITH_SDS
621
622
623/*
624* Wrapper for Win API function ShutdownBlockReasonCreate
625* This function defined starting from Vista only.
626*/
627static BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
628{
629 typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONCREATE,(HWND hWnd, LPCWSTR pwszReason));
630
631 PFNSHUTDOWNBLOCKREASONCREATE pfn
632 = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
633 AssertPtr(pfn);
634
635 BOOL fResult = FALSE;
636 if (pfn)
637 fResult = pfn(hWnd, pwszReason);
638 return fResult;
639}
640
641/*
642* Wrapper for Win API function ShutdownBlockReasonDestroy
643* This function defined starting from Vista only.
644*/
645static BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd)
646{
647 typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONDESTROY,(HWND hWnd));
648 PFNSHUTDOWNBLOCKREASONDESTROY pfn
649 = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy");
650 AssertPtr(pfn);
651
652 BOOL fResult = FALSE;
653 if (pfn)
654 fResult = pfn(hWnd);
655 return fResult;
656}
657
658static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
659{
660 LRESULT lResult = 0;
661
662 switch (msg)
663 {
664 case WM_QUERYENDSESSION:
665 {
666 LogRel(("WM_QUERYENDSESSION:%s%s%s%s (0x%08lx)\n",
667 lParam == 0 ? " shutdown" : "",
668 lParam & ENDSESSION_CRITICAL ? " critical" : "",
669 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
670 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
671 (unsigned long)lParam));
672 if (g_pModule)
673 {
674 bool fActiveConnection = g_pModule->HasActiveConnection();
675 if (fActiveConnection)
676 {
677 lResult = FALSE;
678 LogRel(("VBoxSvc has active connections:"
679 " bActivity = %RTbool, lock count = %d\n",
680 g_pModule->bActivity, g_pModule->GetLockCount()));
681
682 /* place the VBoxSVC into system shutdown list */
683 ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
684 /* decrease a latency of MonitorShutdown loop */
685 ASMAtomicXchgU32(&dwTimeOut, 100);
686 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections."
687 " bActivity = %d. Lock count = %d\n",
688 g_pModule->bActivity, g_pModule->GetLockCount()));
689 }
690 else
691 {
692 LogRel(("No active connections:"
693 " bActivity = %RTbool, lock count = %d\n",
694 g_pModule->bActivity, g_pModule->GetLockCount()));
695 lResult = TRUE;
696 }
697 }
698 else
699 AssertMsgFailed(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL"));
700 break;
701 }
702 case WM_ENDSESSION:
703 {
704 LogRel(("WM_ENDSESSION:%s%s%s%s%s (%s/0x%08lx)\n",
705 lParam == 0 ? " shutdown" : "",
706 lParam & ENDSESSION_CRITICAL ? " critical" : "",
707 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
708 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
709 wParam == FALSE ? " cancelled" : "",
710 wParam ? "TRUE" : "FALSE",
711 (unsigned long)lParam));
712
713 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
714 if (wParam == FALSE)
715 {
716 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
717 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
718 ShutdownBlockReasonDestroyAPI(hwnd);
719 }
720 break;
721 }
722 case WM_DESTROY:
723 {
724 ShutdownBlockReasonDestroyAPI(hwnd);
725 PostQuitMessage(0);
726 break;
727 }
728
729 default:
730 {
731 lResult = DefWindowProc(hwnd, msg, wParam, lParam);
732 break;
733 }
734 }
735 return lResult;
736}
737
738static int CreateMainWindow()
739{
740 int vrc = VINF_SUCCESS;
741 Assert(g_hMainWindow == NULL);
742
743 LogFlow(("CreateMainWindow\n"));
744
745 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
746
747 /* Register the Window Class. */
748 WNDCLASS wc;
749 RT_ZERO(wc);
750
751 wc.style = CS_NOCLOSE;
752 wc.lpfnWndProc = WinMainWndProc;
753 wc.hInstance = g_hInstance;
754 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
755 wc.lpszClassName = MAIN_WND_CLASS;
756
757 ATOM atomWindowClass = RegisterClass(&wc);
758 if (atomWindowClass == 0)
759 {
760 LogRel(("Failed to register window class for session monitoring\n"));
761 vrc = VERR_NOT_SUPPORTED;
762 }
763 else
764 {
765 /* Create the window. */
766 g_hMainWindow = CreateWindowEx(0, MAIN_WND_CLASS, MAIN_WND_CLASS, 0,
767 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
768 if (g_hMainWindow == NULL)
769 {
770 LogRel(("Failed to create window for session monitoring\n"));
771 vrc = VERR_NOT_SUPPORTED;
772 }
773 }
774 return vrc;
775}
776
777
778static void DestroyMainWindow()
779{
780 Assert(g_hMainWindow != NULL);
781 Log(("SVCMain: DestroyMainWindow \n"));
782 if (g_hMainWindow != NULL)
783 {
784 DestroyWindow(g_hMainWindow);
785 g_hMainWindow = NULL;
786 if (g_hInstance != NULL)
787 {
788 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
789 g_hInstance = NULL;
790 }
791 }
792}
793
794
795static const char * const ctrl_event_names[] = {
796 "CTRL_C_EVENT",
797 "CTRL_BREAK_EVENT",
798 "CTRL_CLOSE_EVENT",
799 /* reserved, not used */
800 "<console control event 3>",
801 "<console control event 4>",
802 /* not sent to processes that load gdi32.dll or user32.dll */
803 "CTRL_LOGOFF_EVENT",
804 "CTRL_SHUTDOWN_EVENT",
805};
806
807/** @todo r=uwe placeholder */
808BOOL WINAPI
809ConsoleCtrlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF
810{
811 const char *signame;
812 char namebuf[48];
813 // int vrc;
814
815 if (dwCtrlType < RT_ELEMENTS(ctrl_event_names))
816 signame = ctrl_event_names[dwCtrlType];
817 else
818 {
819 /* should not happen, but be prepared */
820 RTStrPrintf(namebuf, sizeof(namebuf),
821 "<console control event %lu>", (unsigned long)dwCtrlType);
822 signame = namebuf;
823 }
824 LogRel(("Got %s\n", signame));
825
826 if (RT_UNLIKELY(g_pModule == NULL))
827 {
828 LogRel(("%s: g_pModule == NULL\n", __FUNCTION__));
829 return TRUE;
830 }
831
832 /* decrease latency of the MonitorShutdown loop */
833 ASMAtomicXchgU32(&dwTimeOut, 100);
834
835 bool fHasClients = g_pModule->HasActiveConnection();
836 if (!fHasClients)
837 {
838 LogRel(("No clients, closing the shop.\n"));
839 return TRUE;
840 }
841
842 LogRel(("VBoxSvc has clients: bActivity = %RTbool, lock count = %d\n",
843 g_pModule->bActivity, g_pModule->GetLockCount()));
844
845 /** @todo r=uwe wait for clients to disconnect */
846 return TRUE;
847}
848
849
850
851/** Special export that make VBoxProxyStub not register this process as one that
852 * VBoxSDS should be watching.
853 */
854extern "C" DECLEXPORT(void) VBOXCALL Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS(void)
855{
856 /* never called, just need to be here */
857}
858
859
860/* thread for registering the VBoxSVC started in session 0 */
861static DWORD WINAPI threadRegisterVirtualBox(LPVOID lpParam) throw()
862{
863 HANDLE hEvent = (HANDLE)lpParam;
864 HRESULT hrc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
865 if (SUCCEEDED(hrc))
866 {
867 /* create IVirtualBox instance */
868 ComPtr<IVirtualBox> pVirtualBox;
869 hrc = CoCreateInstance(CLSID_VirtualBox, NULL, CLSCTX_INPROC_SERVER /*CLSCTX_LOCAL_SERVER */, IID_IVirtualBox,
870 (void **)pVirtualBox.asOutParam());
871 if (SUCCEEDED(hrc))
872 {
873 /* wait a minute allowing clients to connect to the instance */
874 WaitForSingleObject(hEvent, 60 * 1000);
875 /* remove reference. If anybody connected to IVirtualBox it will stay alive. */
876 pVirtualBox.setNull();
877 }
878 CoUninitialize();
879 }
880 return 0L;
881}
882
883
884/////////////////////////////////////////////////////////////////////////////
885//
886int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
887{
888 int argc = __argc;
889 char **argv = __argv;
890
891 /*
892 * Need to parse the command line before initializing the VBox runtime so we can
893 * change to the user home directory before logs are being created.
894 */
895 for (int i = 1; i < argc; i++)
896 if ( (argv[i][0] == '/' || argv[i][0] == '-')
897 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
898 {
899 /* %HOMEDRIVE%%HOMEPATH% */
900 wchar_t wszHome[RTPATH_MAX];
901 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
902 if (cEnv && cEnv < RTPATH_MAX)
903 {
904 DWORD cwc = cEnv; /* doesn't include NUL */
905 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
906 if (cEnv && cEnv < RTPATH_MAX - cwc)
907 {
908 /* If this fails there is nothing we can do. Ignore. */
909 SetCurrentDirectory(wszHome);
910 }
911 }
912 }
913
914 /*
915 * Initialize the VBox runtime without loading
916 * the support driver.
917 */
918 RTR3InitExe(argc, &argv, 0);
919
920 static const RTGETOPTDEF s_aOptions[] =
921 {
922 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
923 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
924 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
925 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
926 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
927 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
928 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
929 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
930 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
931 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
932 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
933 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
934 { "--help", 'h', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
935 { "-help" , 'h', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
936 { "/help", 'h', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
937 { "/?", 'h', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
938 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
939 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
940 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
941 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
942 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
943 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
944 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
945 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
946 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
947 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
948 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
949 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
950 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
951 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
952 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
953 { "--registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
954 { "-registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
955 { "/registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
956 { "--version", 'V', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
957 { "-version", 'V', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
958 { "/version", 'V', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
959 };
960
961 bool fRun = true;
962 bool fRegister = false;
963 bool fUnregister = false;
964 const char *pszPipeName = NULL;
965 const char *pszLogFile = NULL;
966 uint32_t cHistory = 10; // enable log rotation, 10 files
967 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
968 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
969 bool fRegisterVBox = false;
970
971 RTGETOPTSTATE GetOptState;
972 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
973 AssertRC(vrc);
974
975 char ch;
976 RTGETOPTUNION ValueUnion;
977 while ((ch = RTGetOpt(&GetOptState, &ValueUnion)))
978 {
979 switch (ch)
980 {
981 case 'e':
982 /* already handled above */
983 break;
984
985 case 'u':
986 fUnregister = true;
987 fRun = false;
988 break;
989
990 case 'r':
991 fRegister = true;
992 fRun = false;
993 break;
994
995 case 'f':
996 fUnregister = true;
997 fRegister = true;
998 fRun = false;
999 break;
1000
1001 case 'H':
1002 pszPipeName = ValueUnion.psz;
1003 if (!pszPipeName)
1004 pszPipeName = "";
1005 fRun = false;
1006 break;
1007
1008 case 'F':
1009 pszLogFile = ValueUnion.psz;
1010 break;
1011
1012 case 'R':
1013 cHistory = ValueUnion.u32;
1014 break;
1015
1016 case 'S':
1017 uHistoryFileSize = ValueUnion.u64;
1018 break;
1019
1020 case 'I':
1021 uHistoryFileTime = ValueUnion.u32;
1022 break;
1023
1024 case 'h':
1025 {
1026 static const WCHAR s_wszText[] = L"Options:\n\n"
1027 L"/RegServer:\tregister COM out-of-proc server\n"
1028 L"/UnregServer:\tunregister COM out-of-proc server\n"
1029 L"/ReregServer:\tunregister and register COM server\n"
1030 L"no options:\trun the server";
1031 static const WCHAR s_wszTitle[] = L"Usage";
1032 fRun = false;
1033 MessageBoxW(NULL, s_wszText, s_wszTitle, MB_OK);
1034 return 0;
1035 }
1036
1037 case 'V':
1038 {
1039 static const WCHAR s_wszTitle[] = L"Version";
1040 char *pszText = NULL;
1041 RTStrAPrintf(&pszText, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1042 PRTUTF16 pwszText = NULL;
1043 RTStrToUtf16(pszText, &pwszText);
1044 RTStrFree(pszText);
1045 MessageBoxW(NULL, pwszText, s_wszTitle, MB_OK);
1046 RTUtf16Free(pwszText);
1047 fRun = false;
1048 return 0;
1049 }
1050
1051 case 'b':
1052 fRegisterVBox = true;
1053 break;
1054
1055 default:
1056 /** @todo this assumes that stderr is visible, which is not
1057 * true for standard Windows applications. */
1058 /* continue on command line errors... */
1059 RTGetOptPrintError(ch, &ValueUnion);
1060 }
1061 }
1062
1063 /* Only create the log file when running VBoxSVC normally, but not when
1064 * registering/unregistering or calling the helper functionality. */
1065 if (fRun)
1066 {
1067 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
1068 char szLogFile[RTPATH_MAX];
1069 if (!pszLogFile || !*pszLogFile)
1070 {
1071 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
1072 if (RT_SUCCESS(vrc))
1073 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
1074 if (RT_FAILURE(vrc))
1075 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to construct release log filename, vrc=%Rrc", vrc);
1076 pszLogFile = szLogFile;
1077 }
1078
1079 RTERRINFOSTATIC ErrInfo;
1080 vrc = com::VBoxLogRelCreate("COM Server", pszLogFile,
1081 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1082 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
1083#ifdef VBOX_WITH_SDS
1084 RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE,
1085#else
1086 RTLOGDEST_FILE,
1087#endif
1088 UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize,
1089 RTErrInfoInitStatic(&ErrInfo));
1090 if (RT_FAILURE(vrc))
1091 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
1092 }
1093
1094 /* Set up a build identifier so that it can be seen from core dumps what
1095 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
1096 static char saBuildID[48];
1097 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
1098 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
1099
1100 AssertCompile(VBOX_COM_INIT_F_DEFAULT == VBOX_COM_INIT_F_AUTO_REG_UPDATE);
1101 HRESULT hRes = com::Initialize(fRun ? VBOX_COM_INIT_F_AUTO_REG_UPDATE : 0);
1102 AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes));
1103
1104 g_pModule = new CExeModule();
1105 if(g_pModule == NULL)
1106 return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
1107 g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
1108 g_pModule->dwThreadID = GetCurrentThreadId();
1109
1110 int nRet = 0;
1111 if (!fRun)
1112 {
1113#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
1114 if (fUnregister)
1115 {
1116 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
1117 nRet = g_pModule->UnregisterServer(TRUE);
1118 }
1119 if (fRegister)
1120 {
1121 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
1122 nRet = g_pModule->RegisterServer(TRUE);
1123 }
1124#endif
1125 if (pszPipeName)
1126 {
1127 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
1128
1129 if (!*pszPipeName)
1130 vrc = VERR_INVALID_PARAMETER;
1131
1132 if (RT_SUCCESS(vrc))
1133 {
1134 /* do the helper job */
1135 SVCHlpServer server;
1136 vrc = server.open(pszPipeName);
1137 if (RT_SUCCESS(vrc))
1138 vrc = server.run();
1139 }
1140 if (RT_FAILURE(vrc))
1141 {
1142 Log(("SVCMAIN: Failed to process Helper request (%Rrc).\n", vrc));
1143 nRet = 1;
1144 }
1145 }
1146 }
1147 else
1148 {
1149
1150 g_pModule->StartMonitor();
1151#if _WIN32_WINNT >= 0x0400
1152 hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
1153 _ASSERTE(SUCCEEDED(hRes));
1154 hRes = CoResumeClassObjects();
1155#else
1156 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
1157#endif
1158 _ASSERTE(SUCCEEDED(hRes));
1159
1160 /*
1161 * Register windows console signal handler to react to Ctrl-C,
1162 * Ctrl-Break, Close; but more importantly - to get notified
1163 * about shutdown when we are running in the context of the
1164 * autostart service - we won't get WM_ENDSESSION in that
1165 * case.
1166 */
1167 ::SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
1168
1169
1170 if (RT_SUCCESS(CreateMainWindow()))
1171 Log(("SVCMain: Main window succesfully created\n"));
1172 else
1173 Log(("SVCMain: Failed to create main window\n"));
1174
1175 /* create thread to register IVirtualBox in VBoxSDS
1176 * It is used for starting the VBoxSVC in the windows
1177 * session 0. */
1178 HANDLE hWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1179 HANDLE hRegisterVBoxThread = NULL;
1180 if (fRegisterVBox)
1181 {
1182 DWORD dwThreadId = 0;
1183 hRegisterVBoxThread = CreateThread(NULL, 0, threadRegisterVirtualBox, (LPVOID)hWaitEvent,
1184 0, &dwThreadId);
1185 }
1186
1187 MSG msg;
1188 while (GetMessage(&msg, 0, 0, 0) > 0)
1189 {
1190 TranslateMessage(&msg);
1191 DispatchMessage(&msg);
1192 }
1193
1194 DestroyMainWindow();
1195
1196 if (fRegisterVBox)
1197 {
1198 SetEvent(hWaitEvent);
1199 WaitForSingleObject(hRegisterVBoxThread, INFINITE);
1200 CloseHandle(hRegisterVBoxThread);
1201 CloseHandle(hWaitEvent);
1202 }
1203
1204 g_pModule->RevokeClassObjects();
1205 }
1206
1207 g_pModule->Term();
1208
1209#ifdef VBOX_WITH_SDS
1210 g_fRegisteredWithVBoxSDS = false; /* Don't trust COM LPC to work right from now on. */
1211#endif
1212 com::Shutdown();
1213
1214 if(g_pModule)
1215 delete g_pModule;
1216 g_pModule = NULL;
1217
1218 Log(("SVCMAIN: Returning, COM server process ends.\n"));
1219 return nRet;
1220}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette