VirtualBox

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

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

VBoxSVC/Win: Fixed yet another ancient bug: Actually handle the version command -- was completely ignored by RTGetOpt.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 41.6 KB
 
1/* $Id: svcmain.cpp 108301 2025-02-19 15:52:39Z 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 RTGETOPTUNION ValueUnion;
976 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
977 {
978 switch (vrc)
979 {
980 case 'e':
981 /* already handled above */
982 break;
983
984 case 'u':
985 fUnregister = true;
986 fRun = false;
987 break;
988
989 case 'r':
990 fRegister = true;
991 fRun = false;
992 break;
993
994 case 'f':
995 fUnregister = true;
996 fRegister = true;
997 fRun = false;
998 break;
999
1000 case 'H':
1001 pszPipeName = ValueUnion.psz;
1002 if (!pszPipeName)
1003 pszPipeName = "";
1004 fRun = false;
1005 break;
1006
1007 case 'F':
1008 pszLogFile = ValueUnion.psz;
1009 break;
1010
1011 case 'R':
1012 cHistory = ValueUnion.u32;
1013 break;
1014
1015 case 'S':
1016 uHistoryFileSize = ValueUnion.u64;
1017 break;
1018
1019 case 'I':
1020 uHistoryFileTime = ValueUnion.u32;
1021 break;
1022
1023 case 'h':
1024 {
1025 static const WCHAR s_wszText[] = L"Options:\n\n"
1026 L"/RegServer:\tregister COM out-of-proc server\n"
1027 L"/UnregServer:\tunregister COM out-of-proc server\n"
1028 L"/ReregServer:\tunregister and register COM server\n"
1029 L"no options:\trun the server";
1030 static const WCHAR s_wszTitle[] = L"Usage";
1031 fRun = false;
1032 MessageBoxW(NULL, s_wszText, s_wszTitle, MB_OK);
1033 return 0;
1034 }
1035
1036 case 'V':
1037 {
1038 static const WCHAR s_wszTitle[] = L"Version";
1039 char *pszText = NULL;
1040 RTStrAPrintf(&pszText, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1041 PRTUTF16 pwszText = NULL;
1042 RTStrToUtf16(pszText, &pwszText);
1043 RTStrFree(pszText);
1044 MessageBoxW(NULL, pwszText, s_wszTitle, MB_OK);
1045 RTUtf16Free(pwszText);
1046 fRun = false;
1047 return 0;
1048 }
1049
1050 case 'b':
1051 fRegisterVBox = true;
1052 break;
1053
1054 default:
1055 /** @todo this assumes that stderr is visible, which is not
1056 * true for standard Windows applications. */
1057 /* continue on command line errors... */
1058 RTGetOptPrintError(vrc, &ValueUnion);
1059 }
1060 }
1061
1062 /* Only create the log file when running VBoxSVC normally, but not when
1063 * registering/unregistering or calling the helper functionality. */
1064 if (fRun)
1065 {
1066 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
1067 char szLogFile[RTPATH_MAX];
1068 if (!pszLogFile || !*pszLogFile)
1069 {
1070 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
1071 if (RT_SUCCESS(vrc))
1072 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
1073 if (RT_FAILURE(vrc))
1074 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to construct release log filename, vrc=%Rrc", vrc);
1075 pszLogFile = szLogFile;
1076 }
1077
1078 RTERRINFOSTATIC ErrInfo;
1079 vrc = com::VBoxLogRelCreate("COM Server", pszLogFile,
1080 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1081 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
1082#ifdef VBOX_WITH_SDS
1083 RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE,
1084#else
1085 RTLOGDEST_FILE,
1086#endif
1087 UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize,
1088 RTErrInfoInitStatic(&ErrInfo));
1089 if (RT_FAILURE(vrc))
1090 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
1091 }
1092
1093 /* Set up a build identifier so that it can be seen from core dumps what
1094 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
1095 static char saBuildID[48];
1096 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
1097 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
1098
1099 AssertCompile(VBOX_COM_INIT_F_DEFAULT == VBOX_COM_INIT_F_AUTO_REG_UPDATE);
1100 HRESULT hRes = com::Initialize(fRun ? VBOX_COM_INIT_F_AUTO_REG_UPDATE : 0);
1101 AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes));
1102
1103 g_pModule = new CExeModule();
1104 if(g_pModule == NULL)
1105 return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
1106 g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
1107 g_pModule->dwThreadID = GetCurrentThreadId();
1108
1109 int nRet = 0;
1110 if (!fRun)
1111 {
1112#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
1113 if (fUnregister)
1114 {
1115 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
1116 nRet = g_pModule->UnregisterServer(TRUE);
1117 }
1118 if (fRegister)
1119 {
1120 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
1121 nRet = g_pModule->RegisterServer(TRUE);
1122 }
1123#endif
1124 if (pszPipeName)
1125 {
1126 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
1127
1128 if (!*pszPipeName)
1129 vrc = VERR_INVALID_PARAMETER;
1130
1131 if (RT_SUCCESS(vrc))
1132 {
1133 /* do the helper job */
1134 SVCHlpServer server;
1135 vrc = server.open(pszPipeName);
1136 if (RT_SUCCESS(vrc))
1137 vrc = server.run();
1138 }
1139 if (RT_FAILURE(vrc))
1140 {
1141 Log(("SVCMAIN: Failed to process Helper request (%Rrc).\n", vrc));
1142 nRet = 1;
1143 }
1144 }
1145 }
1146 else
1147 {
1148
1149 g_pModule->StartMonitor();
1150#if _WIN32_WINNT >= 0x0400
1151 hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
1152 _ASSERTE(SUCCEEDED(hRes));
1153 hRes = CoResumeClassObjects();
1154#else
1155 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
1156#endif
1157 _ASSERTE(SUCCEEDED(hRes));
1158
1159 /*
1160 * Register windows console signal handler to react to Ctrl-C,
1161 * Ctrl-Break, Close; but more importantly - to get notified
1162 * about shutdown when we are running in the context of the
1163 * autostart service - we won't get WM_ENDSESSION in that
1164 * case.
1165 */
1166 ::SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
1167
1168
1169 if (RT_SUCCESS(CreateMainWindow()))
1170 Log(("SVCMain: Main window succesfully created\n"));
1171 else
1172 Log(("SVCMain: Failed to create main window\n"));
1173
1174 /* create thread to register IVirtualBox in VBoxSDS
1175 * It is used for starting the VBoxSVC in the windows
1176 * session 0. */
1177 HANDLE hWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1178 HANDLE hRegisterVBoxThread = NULL;
1179 if (fRegisterVBox)
1180 {
1181 DWORD dwThreadId = 0;
1182 hRegisterVBoxThread = CreateThread(NULL, 0, threadRegisterVirtualBox, (LPVOID)hWaitEvent,
1183 0, &dwThreadId);
1184 }
1185
1186 MSG msg;
1187 while (GetMessage(&msg, 0, 0, 0) > 0)
1188 {
1189 TranslateMessage(&msg);
1190 DispatchMessage(&msg);
1191 }
1192
1193 DestroyMainWindow();
1194
1195 if (fRegisterVBox)
1196 {
1197 SetEvent(hWaitEvent);
1198 WaitForSingleObject(hRegisterVBoxThread, INFINITE);
1199 CloseHandle(hRegisterVBoxThread);
1200 CloseHandle(hWaitEvent);
1201 }
1202
1203 g_pModule->RevokeClassObjects();
1204 }
1205
1206 g_pModule->Term();
1207
1208#ifdef VBOX_WITH_SDS
1209 g_fRegisteredWithVBoxSDS = false; /* Don't trust COM LPC to work right from now on. */
1210#endif
1211 com::Shutdown();
1212
1213 if(g_pModule)
1214 delete g_pModule;
1215 g_pModule = NULL;
1216
1217 Log(("SVCMAIN: Returning, COM server process ends.\n"));
1218 return nRet;
1219}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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