VirtualBox

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

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

VBoxSDS,VBoxSVC,VBoxProxyStub: Kicked out the failed attempt at centralized client watching. bugref:3300

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.2 KB
Line 
1/* $Id: svcmain.cpp 76067 2018-12-08 00:54:36Z 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.215389.xyz. 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#if 0 //def 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 LONG cLocks = g_pModule->Unlock();
451 LogRelFunc(("Unlock -> %d\n", cLocks));
452 while (cLocks > 0)
453 cLocks = g_pModule->Unlock();
454
455 LogRelFunc(("returns\n"));
456 return S_OK;
457 }
458};
459
460
461HRESULT VirtualBoxClassFactory::i_registerWithSds(IUnknown **ppOtherVirtualBox)
462{
463 /*
464 * Connect to VBoxSDS.
465 */
466 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
467 (void **)m_ptrVirtualBoxSDS.asOutParam());
468 if (SUCCEEDED(hrc))
469 {
470 /*
471 * Create VBoxSVCRegistration object and hand that to VBoxSDS.
472 */
473 m_pVBoxSVC = new VBoxSVCRegistration(this);
474 hrc = m_ptrVirtualBoxSDS->RegisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId(), ppOtherVirtualBox);
475 if (SUCCEEDED(hrc))
476 {
477 g_fRegisteredWithVBoxSDS = !*ppOtherVirtualBox;
478 return hrc;
479 }
480 m_pVBoxSVC->Release();
481 }
482 m_ptrVirtualBoxSDS.setNull();
483 m_pVBoxSVC = NULL;
484 *ppOtherVirtualBox = NULL;
485 return hrc;
486}
487
488
489void VirtualBoxClassFactory::i_deregisterWithSds(void)
490{
491 Log(("VirtualBoxClassFactory::i_deregisterWithSds\n"));
492
493 if (m_ptrVirtualBoxSDS.isNotNull())
494 {
495 if (m_pVBoxSVC)
496 {
497 HRESULT hrc = m_ptrVirtualBoxSDS->DeregisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId());
498 NOREF(hrc);
499 }
500 }
501 i_finishVBoxSvc();
502}
503
504
505HRESULT VirtualBoxClassFactory::i_getVirtualBox(IUnknown **ppResult)
506{
507 IUnknown *pObj = m_pObj;
508 if (pObj)
509 {
510 /** @todo Do we need to do something regarding server locking? Hopefully COM
511 * deals with that........... */
512 pObj->AddRef();
513 *ppResult = pObj;
514 Log(("VirtualBoxClassFactory::GetVirtualBox: S_OK - %p\n", pObj));
515 return S_OK;
516 }
517 *ppResult = NULL;
518 Log(("VirtualBoxClassFactory::GetVirtualBox: E_FAIL\n"));
519 return E_FAIL;
520}
521
522
523void VirtualBoxClassFactory::i_finishVBoxSvc()
524{
525 LogRelFunc(("Finish work of VBoxSVC and VBoxSDS\n"));
526 if (m_ptrVirtualBoxSDS.isNotNull())
527 {
528 m_ptrVirtualBoxSDS.setNull();
529 g_fRegisteredWithVBoxSDS = false;
530 }
531 if (m_pVBoxSVC)
532 {
533 m_pVBoxSVC->m_pFactory = NULL;
534 m_pVBoxSVC->Release();
535 m_pVBoxSVC = NULL;
536 }
537}
538
539
540/**
541 * Custom class factory impl for the VirtualBox singleton.
542 *
543 * This will consult with VBoxSDS on whether this VBoxSVC instance should
544 * provide the actual VirtualBox instance or just forward the instance from
545 * some other SVC instance.
546 *
547 * @param pUnkOuter This must be NULL.
548 * @param riid Reference to the interface ID to provide.
549 * @param ppvObj Where to return the pointer to the riid instance.
550 *
551 * @return COM status code.
552 */
553STDMETHODIMP VirtualBoxClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj)
554{
555 HRESULT hrc = E_POINTER;
556 if (ppvObj != NULL)
557 {
558 *ppvObj = NULL;
559 // no aggregation for singletons
560 AssertReturn(pUnkOuter == NULL, CLASS_E_NOAGGREGATION);
561
562 /*
563 * We must make sure there is only one instance around.
564 * So, we check without locking and then again after locking.
565 */
566 if (ASMAtomicReadS32(&m_iState) == 0)
567 {
568 Lock();
569 __try
570 {
571 if (ASMAtomicReadS32(&m_iState) == 0)
572 {
573 /*
574 * lock the module to indicate activity
575 * (necessary for the monitor shutdown thread to correctly
576 * terminate the module in case when CreateInstance() fails)
577 */
578 ATL::_pAtlModule->Lock();
579 __try
580 {
581 /*
582 * Now we need to connect to VBoxSDS to register ourselves.
583 */
584 IUnknown *pOtherVirtualBox = NULL;
585 m_hrcCreate = hrc = i_registerWithSds(&pOtherVirtualBox);
586 if (SUCCEEDED(hrc) && pOtherVirtualBox)
587 m_pObj = pOtherVirtualBox;
588 else if (SUCCEEDED(hrc))
589 {
590 ATL::_pAtlModule->Lock();
591 ATL::CComObjectCached<VirtualBox> *p;
592 m_hrcCreate = hrc = ATL::CComObjectCached<VirtualBox>::CreateInstance(&p);
593 if (SUCCEEDED(hrc))
594 {
595 m_hrcCreate = hrc = p->QueryInterface(IID_IUnknown, (void **)&m_pObj);
596 if (SUCCEEDED(hrc))
597 RTLogClearFileDelayFlag(RTLogRelGetDefaultInstance(), NULL);
598 else
599 {
600 delete p;
601 i_deregisterWithSds();
602 m_pObj = NULL;
603 }
604 }
605 }
606 ASMAtomicWriteS32(&m_iState, SUCCEEDED(hrc) ? 1 : -1);
607 }
608 __finally
609 {
610 ATL::_pAtlModule->Unlock();
611 }
612 }
613 }
614 __finally
615 {
616 if (ASMAtomicReadS32(&m_iState) == 0)
617 {
618 ASMAtomicWriteS32(&m_iState, -1);
619 if (SUCCEEDED(m_hrcCreate))
620 m_hrcCreate = E_FAIL;
621 }
622 Unlock();
623 }
624 }
625
626 /*
627 * Query the requested interface from the IUnknown one we're keeping around.
628 */
629 if (m_hrcCreate == S_OK)
630 hrc = m_pObj->QueryInterface(riid, ppvObj);
631 else
632 hrc = m_hrcCreate;
633 }
634 return hrc;
635}
636
637#endif // VBOX_WITH_SDS
638
639
640/*
641* Wrapper for Win API function ShutdownBlockReasonCreate
642* This function defined starting from Vista only.
643*/
644static BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
645{
646 BOOL fResult = FALSE;
647 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONCREATE)(HWND hWnd, LPCWSTR pwszReason);
648
649 PFNSHUTDOWNBLOCKREASONCREATE pfn = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(
650 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
651 AssertPtr(pfn);
652 if (pfn)
653 fResult = pfn(hWnd, pwszReason);
654 return fResult;
655}
656
657/*
658* Wrapper for Win API function ShutdownBlockReasonDestroy
659* This function defined starting from Vista only.
660*/
661static BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd)
662{
663 BOOL fResult = FALSE;
664 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONDESTROY)(HWND hWnd);
665
666 PFNSHUTDOWNBLOCKREASONDESTROY pfn = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(
667 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy");
668 AssertPtr(pfn);
669 if (pfn)
670 fResult = pfn(hWnd);
671 return fResult;
672}
673
674static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
675{
676 LRESULT rc = 0;
677 switch (msg)
678 {
679 case WM_QUERYENDSESSION:
680 {
681 if (g_pModule)
682 {
683 bool fActiveConnection = g_pModule->HasActiveConnection();
684 if (fActiveConnection)
685 {
686 /* place the VBoxSVC into system shutdown list */
687 ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
688 /* decrease a latency of MonitorShutdown loop */
689 ASMAtomicXchgU32(&dwTimeOut, 100);
690 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections. bActivity = %d. Loc count = %d\n",
691 g_pModule->bActivity, g_pModule->GetLockCount()));
692
693#if 0 //def VBOX_WITH_SDS
694 // On Windows 7 our clients doesn't receive right sequence of Session End events
695 // So we send them all WM_QUIT to forcible close them.
696 // Windows 10 sends end session events correctly
697 // Note: the IsWindows8Point1() and IsWindows10OrGreater() doesnt work in
698 // application without manifest so I use old compatible functions for detection of Win 7
699 if(!IsWindows8OrGreaterWrap())
700 CloseActiveClients();
701#endif
702 }
703 rc = !fActiveConnection;
704 }
705 else
706 AssertMsgFailed(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL"));
707 break;
708 }
709 case WM_ENDSESSION:
710 {
711 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
712 if (wParam == FALSE)
713 {
714 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
715 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
716 }
717 break;
718 }
719 case WM_DESTROY:
720 {
721 ShutdownBlockReasonDestroyAPI(hwnd);
722 PostQuitMessage(0);
723 break;
724 }
725 default:
726 {
727 rc = DefWindowProc(hwnd, msg, wParam, lParam);
728 }
729 }
730 return rc;
731}
732
733static int CreateMainWindow()
734{
735 int rc = VINF_SUCCESS;
736 Assert(g_hMainWindow == NULL);
737
738 LogFlow(("CreateMainWindow\n"));
739
740 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
741
742 /* Register the Window Class. */
743 WNDCLASS wc;
744 RT_ZERO(wc);
745
746 wc.style = CS_NOCLOSE;
747 wc.lpfnWndProc = WinMainWndProc;
748 wc.hInstance = g_hInstance;
749 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
750 wc.lpszClassName = MAIN_WND_CLASS;
751
752 ATOM atomWindowClass = RegisterClass(&wc);
753 if (atomWindowClass == 0)
754 {
755 Log(("Failed to register main window class\n"));
756 rc = VERR_NOT_SUPPORTED;
757 }
758 else
759 {
760 /* Create the window. */
761 g_hMainWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
762 MAIN_WND_CLASS, MAIN_WND_CLASS,
763 WS_POPUPWINDOW,
764 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
765 if (g_hMainWindow == NULL)
766 {
767 Log(("Failed to create main window\n"));
768 rc = VERR_NOT_SUPPORTED;
769 }
770 else
771 {
772 SetWindowPos(g_hMainWindow, HWND_TOPMOST, -200, -200, 0, 0,
773 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
774
775 }
776 }
777 return 0;
778}
779
780
781static void DestroyMainWindow()
782{
783 Assert(g_hMainWindow != NULL);
784 Log(("SVCMain: DestroyMainWindow \n"));
785 if (g_hMainWindow != NULL)
786 {
787 DestroyWindow(g_hMainWindow);
788 g_hMainWindow = NULL;
789 if (g_hInstance != NULL)
790 {
791 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
792 g_hInstance = NULL;
793 }
794 }
795}
796
797
798/** Special export that make VBoxProxyStub not register this process as one that
799 * VBoxSDS should be watching.
800 */
801extern "C" DECLEXPORT(void) VBOXCALL Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS(void)
802{
803 /* never called, just need to be here */
804}
805
806
807/////////////////////////////////////////////////////////////////////////////
808//
809int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
810{
811 int argc = __argc;
812 char **argv = __argv;
813
814 /*
815 * Need to parse the command line before initializing the VBox runtime so we can
816 * change to the user home directory before logs are being created.
817 */
818 for (int i = 1; i < argc; i++)
819 if ( (argv[i][0] == '/' || argv[i][0] == '-')
820 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
821 {
822 /* %HOMEDRIVE%%HOMEPATH% */
823 wchar_t wszHome[RTPATH_MAX];
824 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
825 if (cEnv && cEnv < RTPATH_MAX)
826 {
827 DWORD cwc = cEnv; /* doesn't include NUL */
828 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
829 if (cEnv && cEnv < RTPATH_MAX - cwc)
830 {
831 /* If this fails there is nothing we can do. Ignore. */
832 SetCurrentDirectory(wszHome);
833 }
834 }
835 }
836
837 /*
838 * Initialize the VBox runtime without loading
839 * the support driver.
840 */
841 RTR3InitExe(argc, &argv, 0);
842
843 static const RTGETOPTDEF s_aOptions[] =
844 {
845 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
846 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
847 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
848 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
849 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
850 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
851 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
852 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
853 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
854 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
855 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
856 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
857 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
858 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
859 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
860 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
861 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
862 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
863 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
864 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
865 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
866 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
867 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
868 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
869 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
870 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
871 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
872 };
873
874 bool fRun = true;
875 bool fRegister = false;
876 bool fUnregister = false;
877 const char *pszPipeName = NULL;
878 const char *pszLogFile = NULL;
879 uint32_t cHistory = 10; // enable log rotation, 10 files
880 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
881 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
882
883 RTGETOPTSTATE GetOptState;
884 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
885 AssertRC(vrc);
886
887 RTGETOPTUNION ValueUnion;
888 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
889 {
890 switch (vrc)
891 {
892 case 'e':
893 /* already handled above */
894 break;
895
896 case 'u':
897 fUnregister = true;
898 fRun = false;
899 break;
900
901 case 'r':
902 fRegister = true;
903 fRun = false;
904 break;
905
906 case 'f':
907 fUnregister = true;
908 fRegister = true;
909 fRun = false;
910 break;
911
912 case 'H':
913 pszPipeName = ValueUnion.psz;
914 if (!pszPipeName)
915 pszPipeName = "";
916 fRun = false;
917 break;
918
919 case 'F':
920 pszLogFile = ValueUnion.psz;
921 break;
922
923 case 'R':
924 cHistory = ValueUnion.u32;
925 break;
926
927 case 'S':
928 uHistoryFileSize = ValueUnion.u64;
929 break;
930
931 case 'I':
932 uHistoryFileTime = ValueUnion.u32;
933 break;
934
935 case 'h':
936 {
937 TCHAR txt[]= L"Options:\n\n"
938 L"/RegServer:\tregister COM out-of-proc server\n"
939 L"/UnregServer:\tunregister COM out-of-proc server\n"
940 L"/ReregServer:\tunregister and register COM server\n"
941 L"no options:\trun the server";
942 TCHAR title[]=_T("Usage");
943 fRun = false;
944 MessageBox(NULL, txt, title, MB_OK);
945 return 0;
946 }
947
948 case 'V':
949 {
950 char *psz = NULL;
951 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
952 PRTUTF16 txt = NULL;
953 RTStrToUtf16(psz, &txt);
954 TCHAR title[]=_T("Version");
955 fRun = false;
956 MessageBox(NULL, txt, title, MB_OK);
957 RTStrFree(psz);
958 RTUtf16Free(txt);
959 return 0;
960 }
961
962 default:
963 /** @todo this assumes that stderr is visible, which is not
964 * true for standard Windows applications. */
965 /* continue on command line errors... */
966 RTGetOptPrintError(vrc, &ValueUnion);
967 }
968 }
969
970 /* Only create the log file when running VBoxSVC normally, but not when
971 * registering/unregistering or calling the helper functionality. */
972 if (fRun)
973 {
974 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
975 char szLogFile[RTPATH_MAX];
976 if (!pszLogFile || !*pszLogFile)
977 {
978 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
979 if (RT_SUCCESS(vrc))
980 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
981 if (RT_FAILURE(vrc))
982 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to construct release log filename, rc=%Rrc", vrc);
983 pszLogFile = szLogFile;
984 }
985
986 RTERRINFOSTATIC ErrInfo;
987 vrc = com::VBoxLogRelCreate("COM Server", pszLogFile,
988 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
989 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
990#ifdef VBOX_WITH_SDS
991 RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE,
992#else
993 RTLOGDEST_FILE,
994#endif
995 UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize,
996 RTErrInfoInitStatic(&ErrInfo));
997 if (RT_FAILURE(vrc))
998 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
999 }
1000
1001 /* Set up a build identifier so that it can be seen from core dumps what
1002 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
1003 static char saBuildID[48];
1004 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
1005 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
1006
1007 int nRet = 0;
1008 HRESULT hRes = com::Initialize(false /*fGui*/, fRun /*fAutoRegUpdate*/);
1009 AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes));
1010
1011 g_pModule = new CExeModule();
1012 if(g_pModule == NULL)
1013 return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
1014 g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
1015 g_pModule->dwThreadID = GetCurrentThreadId();
1016
1017 if (!fRun)
1018 {
1019#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
1020 if (fUnregister)
1021 {
1022 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
1023 nRet = g_pModule->UnregisterServer(TRUE);
1024 }
1025 if (fRegister)
1026 {
1027 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
1028 nRet = g_pModule->RegisterServer(TRUE);
1029 }
1030#endif
1031 if (pszPipeName)
1032 {
1033 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
1034
1035 if (!*pszPipeName)
1036 vrc = VERR_INVALID_PARAMETER;
1037
1038 if (RT_SUCCESS(vrc))
1039 {
1040 /* do the helper job */
1041 SVCHlpServer server;
1042 vrc = server.open(pszPipeName);
1043 if (RT_SUCCESS(vrc))
1044 vrc = server.run();
1045 }
1046 if (RT_FAILURE(vrc))
1047 {
1048 Log(("SVCMAIN: Failed to process Helper request (%Rrc).\n", vrc));
1049 nRet = 1;
1050 }
1051 }
1052 }
1053 else
1054 {
1055 g_pModule->StartMonitor();
1056#if _WIN32_WINNT >= 0x0400
1057 hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
1058 _ASSERTE(SUCCEEDED(hRes));
1059 hRes = CoResumeClassObjects();
1060#else
1061 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
1062#endif
1063 _ASSERTE(SUCCEEDED(hRes));
1064
1065 if (RT_SUCCESS(CreateMainWindow()))
1066 Log(("SVCMain: Main window succesfully created\n"));
1067 else
1068 Log(("SVCMain: Failed to create main window\n"));
1069
1070 MSG msg;
1071 while (GetMessage(&msg, 0, 0, 0) > 0)
1072 {
1073 DispatchMessage(&msg);
1074 TranslateMessage(&msg);
1075 }
1076
1077 DestroyMainWindow();
1078
1079 g_pModule->RevokeClassObjects();
1080 }
1081
1082 g_pModule->Term();
1083
1084#ifdef VBOX_WITH_SDS
1085 g_fRegisteredWithVBoxSDS = false; /* Don't trust COM LPC to work right from now on. */
1086#endif
1087 com::Shutdown();
1088
1089 if(g_pModule)
1090 delete g_pModule;
1091 g_pModule = NULL;
1092
1093 Log(("SVCMAIN: Returning, COM server process ends.\n"));
1094 return nRet;
1095}
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