VirtualBox

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

Last change on this file since 75262 was 73919, checked in by vboxsync, 6 years ago

Main: On Windows, fix compiling with VBOX_WITH_SDS=

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