VirtualBox

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

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

VBoxSDS,VBoxSVC: Replaced the enviornment variable hack (VBOX_SERVICE_PROCESS) for preventing VBoxSDS and VBoxSVC from getting watched as clients, with a special export in the executable (Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS). Some cleanups here and there.

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

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