VirtualBox

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

Last change on this file since 71142 was 71142, checked in by vboxsync, 7 years ago

Main/VBoxSVC,VBoxSDS: fix for ​​​​​bugref:8161: added and updated copyright

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.0 KB
Line 
1/* $Id: svcmain.cpp 71142 2018-02-27 20:12:26Z vboxsync $ */
2/** @file
3 * SVCMAIN - COM out-of-proc server main entry
4 */
5
6/*
7 * Copyright (C) 2004-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <iprt/win/windows.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <tchar.h>
26
27#include "VBox/com/defs.h"
28#include "VBox/com/com.h"
29#include "VBox/com/VirtualBox.h"
30#include "VBox/com/array.h"
31
32#include "VirtualBoxImpl.h"
33#include "Logging.h"
34
35#include "svchlp.h"
36
37#include <VBox/err.h>
38#include <iprt/buildconfig.h>
39#include <iprt/initterm.h>
40#include <iprt/string.h>
41#include <iprt/uni.h>
42#include <iprt/path.h>
43#include <iprt/getopt.h>
44#include <iprt/message.h>
45#include <iprt/asm.h>
46
47#include <TlHelp32.h>
48
49/*********************************************************************************************************************************
50* Defined Constants And Macros *
51*********************************************************************************************************************************/
52#define MAIN_WND_CLASS L"VirtualBox Interface"
53
54
55/*********************************************************************************************************************************
56* Structures and Typedefs *
57*********************************************************************************************************************************/
58class CExeModule : public ATL::CComModule
59{
60public:
61 LONG Unlock();
62 DWORD dwThreadID;
63 HANDLE hEventShutdown;
64 void MonitorShutdown();
65 bool StartMonitor();
66 bool HasActiveConnection();
67 bool bActivity;
68 static bool isIdleLockCount(LONG cLocks);
69};
70
71
72/*********************************************************************************************************************************
73* Global Variables *
74*********************************************************************************************************************************/
75BEGIN_OBJECT_MAP(ObjectMap)
76 OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox)
77END_OBJECT_MAP()
78
79CExeModule *g_pModule = NULL;
80HWND g_hMainWindow = NULL;
81HINSTANCE g_hInstance = NULL;
82#ifdef VBOX_WITH_SDS
83/** This is set if we're connected to SDS.
84 *
85 * It means that we should discount a server lock that it is holding when
86 * deciding whether we're idle or not.
87 *
88 * Also, when set we deregister with SDS during class factory destruction. We
89 * exploit this to prevent attempts to deregister during or after COM shutdown.
90 */
91bool g_fRegisteredWithVBoxSDS = false;
92#endif
93
94/* Normal timeout usually used in Shutdown Monitor */
95const DWORD dwNormalTimeout = 5000;
96volatile uint32_t dwTimeOut = dwNormalTimeout; /* time for EXE to be idle before shutting down. Can be decreased at system shutdown phase. */
97
98
99BOOL CALLBACK CloseWindowProc(_In_ HWND hWnd, _In_ LPARAM /* lParam */)
100{
101 _ASSERTE(hWnd);
102 DWORD_PTR dwResult;
103 // Close topmost windows in the thread
104 LRESULT lResult = SendMessageTimeout(hWnd, WM_CLOSE, NULL, NULL,
105 SMTO_ABORTIFHUNG | SMTO_BLOCK, 0, &dwResult);
106 if (lResult != 0)
107 {
108 LogRel(("EnumThreadWndProc: Close message sent to window %x successfully \n", hWnd));
109 }
110 else
111 {
112 LogRel(("EnumThreadWndProc: Cannot send event to window %x. result: %d, last error: %x\n",
113 hWnd, dwResult, GetLastError()));
114 }
115 return TRUE;
116}
117
118void SendCloseToAllThreads(DWORD dwTargetPid)
119{
120 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
121 if (hSnapshot == NULL)
122 {
123 LogRel(("SendCloseToAllThreads: cannot get threads snapshot. error: 0x%x \n",
124 GetLastError()));
125 return;
126 }
127
128 THREADENTRY32 threadEntry;
129 ZeroMemory(&threadEntry, sizeof(threadEntry));
130 threadEntry.dwSize = sizeof(threadEntry);
131
132 if (Thread32First(hSnapshot, &threadEntry))
133 {
134 do
135 {
136 LogRel(("SendCloseToAllThreads: process: %d thread: %x \n",
137 threadEntry.th32OwnerProcessID, threadEntry.th32ThreadID));
138 if (threadEntry.th32OwnerProcessID == dwTargetPid)
139 {
140 BOOL bRes = EnumThreadWindows(threadEntry.th32ThreadID, CloseWindowProc, NULL);
141 if (!bRes)
142 {
143 LogRel(("SendCloseToAllThreads: EnumThreadWindows() failed to enumerate threads. error: %x \n",
144 GetLastError()));
145 }
146 else
147 {
148 LogRel(("SendCloseToAllThreads: about to close window in thread %x of process d\n",
149 threadEntry.th32ThreadID, dwTargetPid));
150 }
151 }
152 } while (Thread32Next(hSnapshot, &threadEntry));
153 }
154 CloseHandle(hSnapshot);
155}
156
157static int CloseActiveClients()
158{
159 ComPtr<IVirtualBoxClientList> ptrClientList;
160 /*
161 * Connect to VBoxSDS.
162 */
163 // TODO: here we close all API client processes: our own and customers
164 LogRelFunc(("Forcibly close API clients during system shutdown on Windows 7:\n"));
165 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxClientList, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxClientList,
166 (void **)ptrClientList.asOutParam());
167 if (SUCCEEDED(hrc))
168 {
169 com::SafeArray<LONG> aCllients;
170 hrc = ptrClientList->get_Clients(aCllients.__asOutParam());
171 RTCList<LONG> clientsList = aCllients.toList();
172 LogRel(("==========Client list begin ========\n"));
173 for (int i = 0; i < clientsList.size(); i++)
174 {
175 LogRel(("About to close client pid: %d\n", clientsList[i]));
176 SendCloseToAllThreads(clientsList[i]);
177 }
178 LogRel(("==========Client list end ========\n"));
179 }
180 else
181 {
182 LogFunc(("Error to connect to VBoxSDS: hr=%Rhrf\n", hrc));
183 }
184 return 0;
185}
186
187// These are copies of functions defined in VersionHelpers.h
188bool IsWindowsVersionOrGreaterWrap(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
189{
190 OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0,{ 0 }, 0, 0 };
191 DWORDLONG const dwlConditionMask = VerSetConditionMask(
192 VerSetConditionMask(
193 VerSetConditionMask(
194 0, VER_MAJORVERSION, VER_GREATER_EQUAL),
195 VER_MINORVERSION, VER_GREATER_EQUAL),
196 VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
197
198 osvi.dwMajorVersion = wMajorVersion;
199 osvi.dwMinorVersion = wMinorVersion;
200 osvi.wServicePackMajor = wServicePackMajor;
201
202 return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
203}
204
205
206#if !defined _WIN32_WINNT_WIN8
207
208#define _WIN32_WINNT_WIN8 0x0602
209
210#endif // #if !defined _WIN32_WINNT_WIN8
211
212bool IsWindows8OrGreaterWrap()
213{
214 return IsWindowsVersionOrGreaterWrap(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
215}
216
217
218/* Passed to CreateThread to monitor the shutdown event */
219static DWORD WINAPI MonitorProc(void* pv)
220{
221 CExeModule* p = (CExeModule*)pv;
222 p->MonitorShutdown();
223 return 0;
224}
225
226LONG CExeModule::Unlock()
227{
228 LONG cLocks = ATL::CComModule::Unlock();
229 if (isIdleLockCount(cLocks))
230 {
231 bActivity = true;
232 SetEvent(hEventShutdown); /* tell monitor that we transitioned to zero */
233 }
234 return cLocks;
235}
236
237bool CExeModule::HasActiveConnection()
238{
239 return bActivity || !isIdleLockCount(GetLockCount());
240}
241
242/**
243 * Checks if @a cLocks signifies an IDLE server lock load.
244 *
245 * This takes VBoxSDS into account (i.e. ignores it).
246 */
247/*static*/ bool CExeModule::isIdleLockCount(LONG cLocks)
248{
249#ifdef VBOX_WITH_SDS
250 if (g_fRegisteredWithVBoxSDS)
251 return cLocks <= 1;
252#endif
253 return cLocks <= 0;
254}
255
256/* Monitors the shutdown event */
257void CExeModule::MonitorShutdown()
258{
259 while (1)
260 {
261 WaitForSingleObject(hEventShutdown, INFINITE);
262 DWORD dwWait;
263 do
264 {
265 bActivity = false;
266 dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
267 } while (dwWait == WAIT_OBJECT_0);
268 /* timed out */
269 if (!HasActiveConnection()) /* if no activity let's really bail */
270 {
271 /* Disable log rotation at this point, worst case a log file
272 * becomes slightly bigger than it should. Avoids quirks with
273 * log rotation: there might be another API service process
274 * running at this point which would rotate the logs concurrently,
275 * creating a mess. */
276 PRTLOGGER pReleaseLogger = RTLogRelGetDefaultInstance();
277 if (pReleaseLogger)
278 {
279 char szDest[1024];
280 int rc = RTLogGetDestinations(pReleaseLogger, szDest, sizeof(szDest));
281 if (RT_SUCCESS(rc))
282 {
283 rc = RTStrCat(szDest, sizeof(szDest), " nohistory");
284 if (RT_SUCCESS(rc))
285 {
286 rc = RTLogDestinations(pReleaseLogger, szDest);
287 AssertRC(rc);
288 }
289 }
290 }
291#if _WIN32_WINNT >= 0x0400
292 CoSuspendClassObjects();
293 if (!HasActiveConnection())
294#endif
295 break;
296 }
297 }
298 CloseHandle(hEventShutdown);
299 PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
300}
301
302bool CExeModule::StartMonitor()
303{
304 hEventShutdown = CreateEvent(NULL, false, false, NULL);
305 if (hEventShutdown == NULL)
306 return false;
307 DWORD dwThreadID;
308 HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &dwThreadID);
309 return (h != NULL);
310}
311
312
313#ifdef VBOX_WITH_SDS
314class VBoxSVCRegistration;
315
316/**
317 * Custom class factory for the VirtualBox singleton.
318 *
319 * The implementation of CreateInstance is found in win/svcmain.cpp.
320 */
321class VirtualBoxClassFactory : public ATL::CComClassFactory
322{
323private:
324 /** Tri state: 0=uninitialized or initializing; 1=success; -1=failure.
325 * This will be updated after both m_hrcCreate and m_pObj have been set. */
326 volatile int32_t m_iState;
327 /** The result of the instantiation attempt. */
328 HRESULT m_hrcCreate;
329 /** The IUnknown of the VirtualBox object/interface we're working with. */
330 IUnknown *m_pObj;
331 /** Pointer to the IVBoxSVCRegistration implementation that VBoxSDS works with. */
332 VBoxSVCRegistration *m_pVBoxSVC;
333 /** The VBoxSDS interface. */
334 ComPtr<IVirtualBoxSDS> m_ptrVirtualBoxSDS;
335
336public:
337 VirtualBoxClassFactory() : m_iState(0), m_hrcCreate(S_OK), m_pObj(NULL), m_pVBoxSVC(NULL)
338 { }
339
340 virtual ~VirtualBoxClassFactory()
341 {
342 if (m_pObj)
343 {
344 m_pObj->Release();
345 m_pObj = NULL;
346 }
347
348 /* We usually get here during g_pModule->Term() via CoRevokeClassObjec, so COM
349 probably working well enough to talk to SDS when we get here. */
350 if (g_fRegisteredWithVBoxSDS)
351 i_deregisterWithSds();
352 }
353
354 // IClassFactory
355 STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj);
356
357 /** Worker for VBoxSVCRegistration::getVirtualBox. */
358 HRESULT i_getVirtualBox(IUnknown **ppResult);
359
360private:
361 HRESULT VirtualBoxClassFactory::i_registerWithSds(IUnknown **ppOtherVirtualBox);
362 void VirtualBoxClassFactory::i_deregisterWithSds(void);
363 void VirtualBoxClassFactory::i_finishVBoxSvc();
364
365 friend VBoxSVCRegistration;
366};
367
368
369/**
370 * The VBoxSVC class is handed to VBoxSDS so it can call us back and ask for the
371 * VirtualBox object when the next VBoxSVC for this user registers itself.
372 */
373class VBoxSVCRegistration : public IVBoxSVCRegistration
374{
375private:
376 /** Number of references. */
377 uint32_t volatile m_cRefs;
378
379public:
380 /** Pointer to the factory. */
381 VirtualBoxClassFactory *m_pFactory;
382
383public:
384 VBoxSVCRegistration(VirtualBoxClassFactory *pFactory)
385 : m_cRefs(1), m_pFactory(pFactory)
386 { }
387 virtual ~VBoxSVCRegistration()
388 {
389 if (m_pFactory)
390 {
391 if (m_pFactory->m_pVBoxSVC)
392 m_pFactory->m_pVBoxSVC = NULL;
393 m_pFactory = NULL;
394 }
395 }
396 RTMEMEF_NEW_AND_DELETE_OPERATORS();
397
398 // IUnknown
399 STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
400 {
401 if (riid == __uuidof(IUnknown))
402 *ppvObject = (void *)(IUnknown *)this;
403 else if (riid == __uuidof(IVBoxSVCRegistration))
404 *ppvObject = (void *)(IVBoxSVCRegistration *)this;
405 else
406 {
407 return E_NOINTERFACE;
408 }
409 AddRef();
410 return S_OK;
411
412 }
413
414 STDMETHOD_(ULONG,AddRef)(void)
415 {
416 uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
417 return cRefs;
418 }
419
420 STDMETHOD_(ULONG,Release)(void)
421 {
422 uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
423 if (cRefs == 0)
424 delete this;
425 return cRefs;
426 }
427
428 // IVBoxSVCRegistration
429 STDMETHOD(GetVirtualBox)(IUnknown **ppResult)
430 {
431 if (m_pFactory)
432 return m_pFactory->i_getVirtualBox(ppResult);
433 return E_FAIL;
434 }
435
436 // IVBoxSVCRegistration: called from
437 STDMETHOD(NotifyClientsFinished)()
438 {
439 LogRelFunc(("All clients gone - shutdown sequence initiated\n"));
440
441 m_pFactory->i_finishVBoxSvc();
442
443 // This is not enough to finish VBoxSvc such as reference to crashed client still is in action
444 // So I forcebly shutdown VBoxSvc
445 while (g_pModule->Unlock() > 0)
446 {};
447
448 return S_OK;
449 }
450};
451
452
453HRESULT VirtualBoxClassFactory::i_registerWithSds(IUnknown **ppOtherVirtualBox)
454{
455 /*
456 * Connect to VBoxSDS.
457 */
458 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
459 (void **)m_ptrVirtualBoxSDS.asOutParam());
460 if (SUCCEEDED(hrc))
461 {
462 /*
463 * Create VBoxSVCRegistration object and hand that to VBoxSDS.
464 */
465 m_pVBoxSVC = new VBoxSVCRegistration(this);
466 hrc = m_ptrVirtualBoxSDS->RegisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId(), ppOtherVirtualBox);
467 if (SUCCEEDED(hrc))
468 {
469 g_fRegisteredWithVBoxSDS = !*ppOtherVirtualBox;
470 return hrc;
471 }
472 m_pVBoxSVC->Release();
473 }
474 m_ptrVirtualBoxSDS.setNull();
475 m_pVBoxSVC = NULL;
476 *ppOtherVirtualBox = NULL;
477 return hrc;
478}
479
480
481void VirtualBoxClassFactory::i_deregisterWithSds(void)
482{
483 Log(("VirtualBoxClassFactory::i_deregisterWithSds\n"));
484
485 if (m_ptrVirtualBoxSDS.isNotNull())
486 {
487 if (m_pVBoxSVC)
488 {
489 HRESULT hrc = m_ptrVirtualBoxSDS->DeregisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId());
490 NOREF(hrc);
491 }
492 }
493 i_finishVBoxSvc();
494}
495
496
497HRESULT VirtualBoxClassFactory::i_getVirtualBox(IUnknown **ppResult)
498{
499 IUnknown *pObj = m_pObj;
500 if (pObj)
501 {
502 /** @todo Do we need to do something regarding server locking? Hopefully COM
503 * deals with that........... */
504 pObj->AddRef();
505 *ppResult = pObj;
506 Log(("VirtualBoxClassFactory::GetVirtualBox: S_OK - %p\n", pObj));
507 return S_OK;
508 }
509 *ppResult = NULL;
510 Log(("VirtualBoxClassFactory::GetVirtualBox: E_FAIL\n"));
511 return E_FAIL;
512}
513
514
515void VirtualBoxClassFactory::i_finishVBoxSvc()
516{
517 LogRelFunc(("Finish work of VBoxSVc and VBoxSDS\n"));
518 if (m_ptrVirtualBoxSDS.isNotNull())
519 {
520 m_ptrVirtualBoxSDS.setNull();
521 g_fRegisteredWithVBoxSDS = false;
522 }
523 if (m_pVBoxSVC)
524 {
525 m_pVBoxSVC->m_pFactory = NULL;
526 m_pVBoxSVC->Release();
527 m_pVBoxSVC = NULL;
528 }
529}
530
531
532/**
533 * Custom class factory impl for the VirtualBox singleton.
534 *
535 * This will consult with VBoxSDS on whether this VBoxSVC instance should
536 * provide the actual VirtualBox instance or just forward the instance from
537 * some other SVC instance.
538 *
539 * @param pUnkOuter This must be NULL.
540 * @param riid Reference to the interface ID to provide.
541 * @param ppvObj Where to return the pointer to the riid instance.
542 *
543 * @return COM status code.
544 */
545STDMETHODIMP VirtualBoxClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj)
546{
547 HRESULT hrc = E_POINTER;
548 if (ppvObj != NULL)
549 {
550 *ppvObj = NULL;
551 // no aggregation for singletons
552 AssertReturn(pUnkOuter == NULL, CLASS_E_NOAGGREGATION);
553
554 /*
555 * We must make sure there is only one instance around.
556 * So, we check without locking and then again after locking.
557 */
558 if (ASMAtomicReadS32(&m_iState) == 0)
559 {
560 Lock();
561 __try
562 {
563 if (ASMAtomicReadS32(&m_iState) == 0)
564 {
565 /*
566 * lock the module to indicate activity
567 * (necessary for the monitor shutdown thread to correctly
568 * terminate the module in case when CreateInstance() fails)
569 */
570 ATL::_pAtlModule->Lock();
571 __try
572 {
573 /*
574 * Now we need to connect to VBoxSDS to register ourselves.
575 */
576 IUnknown *pOtherVirtualBox = NULL;
577 m_hrcCreate = hrc = i_registerWithSds(&pOtherVirtualBox);
578 if (SUCCEEDED(hrc) && pOtherVirtualBox)
579 m_pObj = pOtherVirtualBox;
580 else if (SUCCEEDED(hrc))
581 {
582 ATL::_pAtlModule->Lock();
583 ATL::CComObjectCached<VirtualBox> *p;
584 m_hrcCreate = hrc = ATL::CComObjectCached<VirtualBox>::CreateInstance(&p);
585 if (SUCCEEDED(hrc))
586 {
587 m_hrcCreate = hrc = p->QueryInterface(IID_IUnknown, (void **)&m_pObj);
588 if (SUCCEEDED(hrc))
589 RTLogClearFileDelayFlag(RTLogRelGetDefaultInstance(), NULL);
590 else
591 {
592 delete p;
593 i_deregisterWithSds();
594 m_pObj = NULL;
595 }
596 }
597 }
598 ASMAtomicWriteS32(&m_iState, SUCCEEDED(hrc) ? 1 : -1);
599 }
600 __finally
601 {
602 ATL::_pAtlModule->Unlock();
603 }
604 }
605 }
606 __finally
607 {
608 if (ASMAtomicReadS32(&m_iState) == 0)
609 {
610 ASMAtomicWriteS32(&m_iState, -1);
611 if (SUCCEEDED(m_hrcCreate))
612 m_hrcCreate = E_FAIL;
613 }
614 Unlock();
615 }
616 }
617
618 /*
619 * Query the requested interface from the IUnknown one we're keeping around.
620 */
621 if (m_hrcCreate == S_OK)
622 hrc = m_pObj->QueryInterface(riid, ppvObj);
623 else
624 hrc = m_hrcCreate;
625 }
626 return hrc;
627}
628
629#endif /* VBOX_WITH_SDS */
630
631
632/*
633* Wrapper for Win API function ShutdownBlockReasonCreate
634* This function defined starting from Vista only.
635*/
636static BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
637{
638 BOOL fResult = FALSE;
639 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONCREATE)(HWND hWnd, LPCWSTR pwszReason);
640
641 PFNSHUTDOWNBLOCKREASONCREATE pfn = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(
642 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
643 AssertPtr(pfn);
644 if (pfn)
645 fResult = pfn(hWnd, pwszReason);
646 return fResult;
647}
648
649/*
650* Wrapper for Win API function ShutdownBlockReasonDestroy
651* This function defined starting from Vista only.
652*/
653static BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd)
654{
655 BOOL fResult = FALSE;
656 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONDESTROY)(HWND hWnd);
657
658 PFNSHUTDOWNBLOCKREASONDESTROY pfn = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(
659 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy");
660 AssertPtr(pfn);
661 if (pfn)
662 fResult = pfn(hWnd);
663 return fResult;
664}
665
666static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
667{
668 LRESULT rc = 0;
669 switch (msg)
670 {
671 case WM_QUERYENDSESSION:
672 {
673 if (g_pModule)
674 {
675 bool fActiveConnection = g_pModule->HasActiveConnection();
676 if (fActiveConnection)
677 {
678 /* place the VBoxSVC into system shutdown list */
679 ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
680 /* decrease a latency of MonitorShutdown loop */
681 ASMAtomicXchgU32(&dwTimeOut, 100);
682 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections. bActivity = %d. Loc count = %d\n",
683 g_pModule->bActivity, g_pModule->GetLockCount()));
684
685 // On Windows 7 our clients doesn't receive right sequence of Session End events
686 // So we send them all WM_QUIT to forcible close them.
687 // Windows 10 sends end session events correctly
688 // Note: the IsWindows8Point1() and IsWindows10OrGreater() doesnt work in
689 // application without manifest so I use old compatible functions for detection of Win 7
690 if(!IsWindows8OrGreaterWrap())
691 CloseActiveClients();
692 }
693 rc = !fActiveConnection;
694 }
695 else
696 AssertMsgFailed(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL"));
697 break;
698 }
699 case WM_ENDSESSION:
700 {
701 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
702 if (wParam == FALSE)
703 {
704 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
705 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
706 }
707 break;
708 }
709 case WM_DESTROY:
710 {
711 ShutdownBlockReasonDestroyAPI(hwnd);
712 PostQuitMessage(0);
713 break;
714 }
715 default:
716 {
717 rc = DefWindowProc(hwnd, msg, wParam, lParam);
718 }
719 }
720 return rc;
721}
722
723static int CreateMainWindow()
724{
725 int rc = VINF_SUCCESS;
726 Assert(g_hMainWindow == NULL);
727
728 LogFlow(("CreateMainWindow\n"));
729
730 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
731
732 /* Register the Window Class. */
733 WNDCLASS wc;
734 RT_ZERO(wc);
735
736 wc.style = CS_NOCLOSE;
737 wc.lpfnWndProc = WinMainWndProc;
738 wc.hInstance = g_hInstance;
739 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
740 wc.lpszClassName = MAIN_WND_CLASS;
741
742 ATOM atomWindowClass = RegisterClass(&wc);
743 if (atomWindowClass == 0)
744 {
745 Log(("Failed to register main window class\n"));
746 rc = VERR_NOT_SUPPORTED;
747 }
748 else
749 {
750 /* Create the window. */
751 g_hMainWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
752 MAIN_WND_CLASS, MAIN_WND_CLASS,
753 WS_POPUPWINDOW,
754 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
755 if (g_hMainWindow == NULL)
756 {
757 Log(("Failed to create main window\n"));
758 rc = VERR_NOT_SUPPORTED;
759 }
760 else
761 {
762 SetWindowPos(g_hMainWindow, HWND_TOPMOST, -200, -200, 0, 0,
763 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
764
765 }
766 }
767 return 0;
768}
769
770
771static void DestroyMainWindow()
772{
773 Assert(g_hMainWindow != NULL);
774 Log(("SVCMain: DestroyMainWindow \n"));
775 if (g_hMainWindow != NULL)
776 {
777 DestroyWindow(g_hMainWindow);
778 g_hMainWindow = NULL;
779 if (g_hInstance != NULL)
780 {
781 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
782 g_hInstance = NULL;
783 }
784 }
785}
786
787
788int SetServiceEnvFlag()
789{
790 int rc = VINF_SUCCESS;
791 if (!SetEnvironmentVariable(L"VBOX_SERVICE_PROCESS", L""))
792 {
793 rc = RTErrConvertFromWin32(GetLastError());
794 LogRel(("Error: cannot set service environment flag: %Rrs\n", rc));
795 }
796 return rc;
797}
798
799
800/////////////////////////////////////////////////////////////////////////////
801//
802int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
803{
804 int argc = __argc;
805 char **argv = __argv;
806
807 /*
808 * Need to parse the command line before initializing the VBox runtime so we can
809 * change to the user home directory before logs are being created.
810 */
811 for (int i = 1; i < argc; i++)
812 if ( (argv[i][0] == '/' || argv[i][0] == '-')
813 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
814 {
815 /* %HOMEDRIVE%%HOMEPATH% */
816 wchar_t wszHome[RTPATH_MAX];
817 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
818 if (cEnv && cEnv < RTPATH_MAX)
819 {
820 DWORD cwc = cEnv; /* doesn't include NUL */
821 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
822 if (cEnv && cEnv < RTPATH_MAX - cwc)
823 {
824 /* If this fails there is nothing we can do. Ignore. */
825 SetCurrentDirectory(wszHome);
826 }
827 }
828 }
829
830 /*
831 * Initialize the VBox runtime without loading
832 * the support driver.
833 */
834 RTR3InitExe(argc, &argv, 0);
835
836 SetServiceEnvFlag();
837
838 static const RTGETOPTDEF s_aOptions[] =
839 {
840 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
841 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
842 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
843 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
844 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
845 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
846 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
847 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
848 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
849 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
850 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
851 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
852 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
853 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
854 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
855 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
856 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
857 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
858 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
859 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
860 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
861 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
862 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
863 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
864 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
865 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
866 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
867 };
868
869 bool fRun = true;
870 bool fRegister = false;
871 bool fUnregister = false;
872 const char *pszPipeName = NULL;
873 const char *pszLogFile = NULL;
874 uint32_t cHistory = 10; // enable log rotation, 10 files
875 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
876 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
877
878 RTGETOPTSTATE GetOptState;
879 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
880 AssertRC(vrc);
881
882 RTGETOPTUNION ValueUnion;
883 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
884 {
885 switch (vrc)
886 {
887 case 'e':
888 /* already handled above */
889 break;
890
891 case 'u':
892 fUnregister = true;
893 fRun = false;
894 break;
895
896 case 'r':
897 fRegister = true;
898 fRun = false;
899 break;
900
901 case 'f':
902 fUnregister = true;
903 fRegister = true;
904 fRun = false;
905 break;
906
907 case 'H':
908 pszPipeName = ValueUnion.psz;
909 if (!pszPipeName)
910 pszPipeName = "";
911 fRun = false;
912 break;
913
914 case 'F':
915 pszLogFile = ValueUnion.psz;
916 break;
917
918 case 'R':
919 cHistory = ValueUnion.u32;
920 break;
921
922 case 'S':
923 uHistoryFileSize = ValueUnion.u64;
924 break;
925
926 case 'I':
927 uHistoryFileTime = ValueUnion.u32;
928 break;
929
930 case 'h':
931 {
932 TCHAR txt[]= L"Options:\n\n"
933 L"/RegServer:\tregister COM out-of-proc server\n"
934 L"/UnregServer:\tunregister COM out-of-proc server\n"
935 L"/ReregServer:\tunregister and register COM server\n"
936 L"no options:\trun the server";
937 TCHAR title[]=_T("Usage");
938 fRun = false;
939 MessageBox(NULL, txt, title, MB_OK);
940 return 0;
941 }
942
943 case 'V':
944 {
945 char *psz = NULL;
946 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
947 PRTUTF16 txt = NULL;
948 RTStrToUtf16(psz, &txt);
949 TCHAR title[]=_T("Version");
950 fRun = false;
951 MessageBox(NULL, txt, title, MB_OK);
952 RTStrFree(psz);
953 RTUtf16Free(txt);
954 return 0;
955 }
956
957 default:
958 /** @todo this assumes that stderr is visible, which is not
959 * true for standard Windows applications. */
960 /* continue on command line errors... */
961 RTGetOptPrintError(vrc, &ValueUnion);
962 }
963 }
964
965 /* Only create the log file when running VBoxSVC normally, but not when
966 * registering/unregistering or calling the helper functionality. */
967 if (fRun)
968 {
969 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
970 char szLogFile[RTPATH_MAX];
971 if (!pszLogFile || !*pszLogFile)
972 {
973 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
974 if (RT_SUCCESS(vrc))
975 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
976 if (RT_FAILURE(vrc))
977 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to construct release log filename, rc=%Rrc", vrc);
978 pszLogFile = szLogFile;
979 }
980
981 RTERRINFOSTATIC ErrInfo;
982 vrc = com::VBoxLogRelCreate("COM Server", pszLogFile,
983 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
984 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
985#ifdef VBOX_WITH_SDS
986 RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE,
987#else
988 RTLOGDEST_FILE,
989#endif
990 UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize,
991 RTErrInfoInitStatic(&ErrInfo));
992 if (RT_FAILURE(vrc))
993 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
994 }
995
996 /* Set up a build identifier so that it can be seen from core dumps what
997 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
998 static char saBuildID[48];
999 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
1000 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
1001
1002 int nRet = 0;
1003 HRESULT hRes = com::Initialize(false /*fGui*/, fRun /*fAutoRegUpdate*/);
1004 AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes));
1005
1006 g_pModule = new CExeModule();
1007 if(g_pModule == NULL)
1008 return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
1009 g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
1010 g_pModule->dwThreadID = GetCurrentThreadId();
1011
1012 if (!fRun)
1013 {
1014#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
1015 if (fUnregister)
1016 {
1017 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
1018 nRet = g_pModule->UnregisterServer(TRUE);
1019 }
1020 if (fRegister)
1021 {
1022 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
1023 nRet = g_pModule->RegisterServer(TRUE);
1024 }
1025#endif
1026 if (pszPipeName)
1027 {
1028 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
1029
1030 if (!*pszPipeName)
1031 vrc = VERR_INVALID_PARAMETER;
1032
1033 if (RT_SUCCESS(vrc))
1034 {
1035 /* do the helper job */
1036 SVCHlpServer server;
1037 vrc = server.open(pszPipeName);
1038 if (RT_SUCCESS(vrc))
1039 vrc = server.run();
1040 }
1041 if (RT_FAILURE(vrc))
1042 {
1043 Log(("SVCMAIN: Failed to process Helper request (%Rrc).\n", vrc));
1044 nRet = 1;
1045 }
1046 }
1047 }
1048 else
1049 {
1050 g_pModule->StartMonitor();
1051#if _WIN32_WINNT >= 0x0400
1052 hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
1053 _ASSERTE(SUCCEEDED(hRes));
1054 hRes = CoResumeClassObjects();
1055#else
1056 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
1057#endif
1058 _ASSERTE(SUCCEEDED(hRes));
1059
1060 if (RT_SUCCESS(CreateMainWindow()))
1061 Log(("SVCMain: Main window succesfully created\n"));
1062 else
1063 Log(("SVCMain: Failed to create main window\n"));
1064
1065 MSG msg;
1066 while (GetMessage(&msg, 0, 0, 0) > 0)
1067 {
1068 DispatchMessage(&msg);
1069 TranslateMessage(&msg);
1070 }
1071
1072 DestroyMainWindow();
1073
1074 g_pModule->RevokeClassObjects();
1075 }
1076
1077 g_pModule->Term();
1078
1079#ifdef VBOX_WITH_SDS
1080 g_fRegisteredWithVBoxSDS = false; /* Don't trust COM LPC to work right from now on. */
1081#endif
1082 com::Shutdown();
1083
1084 if(g_pModule)
1085 delete g_pModule;
1086 g_pModule = NULL;
1087
1088 Log(("SVCMAIN: Returning, COM server process ends.\n"));
1089 return nRet;
1090}
Note: See TracBrowser for help on using the repository browser.

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