VirtualBox

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

Last change on this file since 65103 was 64160, checked in by vboxsync, 8 years ago

VBoxFrontends: fix for bugref:8161: added shutdown events handling to VirtualBox UI

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.5 KB
Line 
1/* $Id: svcmain.cpp 64160 2016-10-06 12:03:52Z vboxsync $ */
2/** @file
3 *
4 * SVCMAIN - COM out-of-proc server main entry
5 */
6
7/*
8 * Copyright (C) 2004-2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/win/windows.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <tchar.h>
23
24#include "VBox/com/defs.h"
25
26#include "VBox/com/com.h"
27
28#include "VBox/com/VirtualBox.h"
29
30#include "VirtualBoxImpl.h"
31#include "Logging.h"
32
33#include "svchlp.h"
34
35#include <VBox/err.h>
36#include <iprt/buildconfig.h>
37#include <iprt/initterm.h>
38#include <iprt/string.h>
39#include <iprt/uni.h>
40#include <iprt/path.h>
41#include <iprt/getopt.h>
42#include <iprt/message.h>
43#include <iprt\asm.h>
44
45class CExeModule : public ATL::CComModule
46{
47public:
48 LONG Unlock();
49 DWORD dwThreadID;
50 HANDLE hEventShutdown;
51 void MonitorShutdown();
52 bool StartMonitor();
53 bool HasActiveConnection();
54 bool bActivity;
55};
56
57/* Normal timeout usually used in Shutdown Monitor */
58const DWORD dwNormalTimeout = 5000;
59volatile uint32_t dwTimeOut = dwNormalTimeout; /* time for EXE to be idle before shutting down. Can be decreased at system shutdown phase. */
60
61/* Passed to CreateThread to monitor the shutdown event */
62static DWORD WINAPI MonitorProc(void* pv)
63{
64 CExeModule* p = (CExeModule*)pv;
65 p->MonitorShutdown();
66 return 0;
67}
68
69LONG CExeModule::Unlock()
70{
71 LONG l = ATL::CComModule::Unlock();
72 if (l == 0)
73 {
74 bActivity = true;
75 SetEvent(hEventShutdown); /* tell monitor that we transitioned to zero */
76 }
77 return l;
78}
79
80bool CExeModule::HasActiveConnection()
81{
82 return bActivity || GetLockCount() > 0;
83}
84
85/* Monitors the shutdown event */
86void CExeModule::MonitorShutdown()
87{
88 while (1)
89 {
90 WaitForSingleObject(hEventShutdown, INFINITE);
91 DWORD dwWait=0;
92 do
93 {
94 bActivity = false;
95 dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
96 } while (dwWait == WAIT_OBJECT_0);
97 /* timed out */
98 if (!HasActiveConnection()) /* if no activity let's really bail */
99 {
100 /* Disable log rotation at this point, worst case a log file
101 * becomes slightly bigger than it should. Avoids quirks with
102 * log rotation: there might be another API service process
103 * running at this point which would rotate the logs concurrently,
104 * creating a mess. */
105 PRTLOGGER pReleaseLogger = RTLogRelGetDefaultInstance();
106 if (pReleaseLogger)
107 {
108 char szDest[1024];
109 int rc = RTLogGetDestinations(pReleaseLogger, szDest, sizeof(szDest));
110 if (RT_SUCCESS(rc))
111 {
112 rc = RTStrCat(szDest, sizeof(szDest), " nohistory");
113 if (RT_SUCCESS(rc))
114 {
115 rc = RTLogDestinations(pReleaseLogger, szDest);
116 AssertRC(rc);
117 }
118 }
119 }
120#if _WIN32_WINNT >= 0x0400
121 CoSuspendClassObjects();
122 if (!HasActiveConnection())
123#endif
124 break;
125 }
126 }
127 CloseHandle(hEventShutdown);
128 PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
129}
130
131bool CExeModule::StartMonitor()
132{
133 hEventShutdown = CreateEvent(NULL, false, false, NULL);
134 if (hEventShutdown == NULL)
135 return false;
136 DWORD dwThreadID;
137 HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &dwThreadID);
138 return (h != NULL);
139}
140
141
142BEGIN_OBJECT_MAP(ObjectMap)
143 OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox)
144END_OBJECT_MAP()
145
146CExeModule _Module;
147HWND g_hMainWindow = NULL;
148HINSTANCE g_hInstance = NULL;
149#define MAIN_WND_CLASS L"VirtualBox Interface"
150
151/*
152* Wrapper for Win API function ShutdownBlockReasonCreate
153* This function defined starting from Vista only.
154*/
155BOOL ShutdownBlockReasonCreateAPI(HWND hWnd,LPCWSTR pwszReason)
156{
157 BOOL fResult = FALSE;
158 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONCREATE)(HWND hWnd, LPCWSTR pwszReason);
159
160 PFNSHUTDOWNBLOCKREASONCREATE pfn = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(
161 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
162 _ASSERTE(pfn);
163 if (pfn)
164 fResult = pfn(hWnd, pwszReason);
165 return fResult;
166}
167
168/*
169* Wrapper for Win API function ShutdownBlockReasonDestroy
170* This function defined starting from Vista only.
171*/
172BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd)
173{
174 BOOL fResult = FALSE;
175 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONDESTROY)(HWND hWnd);
176
177 PFNSHUTDOWNBLOCKREASONDESTROY pfn = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(
178 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy");
179 _ASSERTE(pfn);
180 if (pfn)
181 fResult = pfn(hWnd);
182 return fResult;
183}
184
185
186LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
187{
188 LRESULT rc = 0;
189 switch (msg)
190 {
191 case WM_QUERYENDSESSION:
192 {
193 rc = !_Module.HasActiveConnection();
194 if (!rc)
195 {
196 /* place the VBoxSVC into system shutdown list */
197 ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
198 /* decrease a latency of MonitorShutdown loop */
199 ASMAtomicXchgU32(&dwTimeOut, 100);
200 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections. bActivity = %d. Loc count = %d\n",
201 _Module.bActivity, _Module.GetLockCount()));
202 }
203 } break;
204 case WM_ENDSESSION:
205 {
206 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
207 if (wParam == FALSE)
208 {
209 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
210 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
211 }
212 } break;
213 case WM_DESTROY:
214 {
215 ShutdownBlockReasonDestroyAPI(hwnd);
216 PostQuitMessage(0);
217 } break;
218
219 default:
220 {
221 rc = DefWindowProc(hwnd, msg, wParam, lParam);
222 }
223 }
224 return rc;
225}
226
227
228int CreateMainWindow()
229{
230 int rc = VINF_SUCCESS;
231 _ASSERTE(g_hMainWindow == NULL);
232
233 LogFlow(("CreateMainWindow\n"));
234
235 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
236
237 /* Register the Window Class. */
238 WNDCLASS wc;
239 RT_ZERO(wc);
240
241 wc.style = CS_NOCLOSE;
242 wc.lpfnWndProc = WinMainWndProc;
243 wc.hInstance = g_hInstance;
244 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
245 wc.lpszClassName = MAIN_WND_CLASS;
246
247
248 ATOM atomWindowClass = RegisterClass(&wc);
249
250 if (atomWindowClass == 0)
251 {
252 Log(("Failed to register main window class\n"));
253 rc = VERR_NOT_SUPPORTED;
254 }
255 else
256 {
257 /* Create the window. */
258 g_hMainWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
259 MAIN_WND_CLASS, MAIN_WND_CLASS,
260 WS_POPUPWINDOW,
261 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
262
263 if (g_hMainWindow == NULL)
264 {
265 Log(("Failed to create main window\n"));
266 rc = VERR_NOT_SUPPORTED;
267 }
268 else
269 {
270 SetWindowPos(g_hMainWindow, HWND_TOPMOST, -200, -200, 0, 0,
271 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
272
273 }
274 }
275 return 0;
276}
277
278
279void DestroyMainWindow()
280{
281 _ASSERTE(g_hMainWindow != NULL);
282 Log(("SVCMain: DestroyMainWindow \n"));
283 if (g_hMainWindow != NULL)
284 {
285 DestroyWindow(g_hMainWindow);
286 g_hMainWindow = NULL;
287
288 if (g_hInstance != NULL)
289 {
290 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
291 g_hInstance = NULL;
292 }
293 }
294}
295
296
297/////////////////////////////////////////////////////////////////////////////
298//
299int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
300{
301 int argc = __argc;
302 char **argv = __argv;
303
304 /*
305 * Need to parse the command line before initializing the VBox runtime so we can
306 * change to the user home directory before logs are being created.
307 */
308 for (int i = 1; i < argc; i++)
309 if ( (argv[i][0] == '/' || argv[i][0] == '-')
310 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
311 {
312 /* %HOMEDRIVE%%HOMEPATH% */
313 wchar_t wszHome[RTPATH_MAX];
314 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
315 if (cEnv && cEnv < RTPATH_MAX)
316 {
317 DWORD cwc = cEnv; /* doesn't include NUL */
318 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
319 if (cEnv && cEnv < RTPATH_MAX - cwc)
320 {
321 /* If this fails there is nothing we can do. Ignore. */
322 SetCurrentDirectory(wszHome);
323 }
324 }
325 }
326
327 /*
328 * Initialize the VBox runtime without loading
329 * the support driver.
330 */
331 RTR3InitExe(argc, &argv, 0);
332
333
334 /* Note that all options are given lowercase/camel case/uppercase to
335 * approximate case insensitive matching, which RTGetOpt doesn't offer. */
336 static const RTGETOPTDEF s_aOptions[] =
337 {
338 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
339 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
340 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
341 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
342 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
343 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
344 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
345 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
346 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
347 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
348 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
349 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
350 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
351 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
352 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
353 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
354 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
355 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
356 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
357 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
358 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
359 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
360 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
361 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
362 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
363 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
364 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
365 };
366
367 bool fRun = true;
368 bool fRegister = false;
369 bool fUnregister = false;
370 const char *pszPipeName = NULL;
371 const char *pszLogFile = NULL;
372 uint32_t cHistory = 10; // enable log rotation, 10 files
373 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
374 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
375
376 RTGETOPTSTATE GetOptState;
377 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
378 AssertRC(vrc);
379
380 RTGETOPTUNION ValueUnion;
381 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
382 {
383 switch (vrc)
384 {
385 case 'e':
386 /* already handled above */
387 break;
388
389 case 'u':
390 fUnregister = true;
391 fRun = false;
392 break;
393
394 case 'r':
395 fRegister = true;
396 fRun = false;
397 break;
398
399 case 'f':
400 fUnregister = true;
401 fRegister = true;
402 fRun = false;
403 break;
404
405 case 'H':
406 pszPipeName = ValueUnion.psz;
407 if (!pszPipeName)
408 pszPipeName = "";
409 fRun = false;
410 break;
411
412 case 'F':
413 pszLogFile = ValueUnion.psz;
414 break;
415
416 case 'R':
417 cHistory = ValueUnion.u32;
418 break;
419
420 case 'S':
421 uHistoryFileSize = ValueUnion.u64;
422 break;
423
424 case 'I':
425 uHistoryFileTime = ValueUnion.u32;
426 break;
427
428 case 'h':
429 {
430 TCHAR txt[]= L"Options:\n\n"
431 L"/RegServer:\tregister COM out-of-proc server\n"
432 L"/UnregServer:\tunregister COM out-of-proc server\n"
433 L"/ReregServer:\tunregister and register COM server\n"
434 L"no options:\trun the server";
435 TCHAR title[]=_T("Usage");
436 fRun = false;
437 MessageBox(NULL, txt, title, MB_OK);
438 return 0;
439 }
440
441 case 'V':
442 {
443 char *psz = NULL;
444 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
445 PRTUTF16 txt = NULL;
446 RTStrToUtf16(psz, &txt);
447 TCHAR title[]=_T("Version");
448 fRun = false;
449 MessageBox(NULL, txt, title, MB_OK);
450 RTStrFree(psz);
451 RTUtf16Free(txt);
452 return 0;
453 }
454
455 default:
456 /** @todo this assumes that stderr is visible, which is not
457 * true for standard Windows applications. */
458 /* continue on command line errors... */
459 RTGetOptPrintError(vrc, &ValueUnion);
460 }
461 }
462
463 /* Only create the log file when running VBoxSVC normally, but not when
464 * registering/unregistering or calling the helper functionality. */
465 if (fRun)
466 {
467 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
468 char szLogFile[RTPATH_MAX];
469 if (!pszLogFile)
470 {
471 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
472 if (RT_SUCCESS(vrc))
473 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
474 }
475 else
476 {
477 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
478 vrc = VERR_NO_MEMORY;
479 }
480 if (RT_FAILURE(vrc))
481 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
482
483 char szError[RTPATH_MAX + 128];
484 vrc = com::VBoxLogRelCreate("COM Server", szLogFile,
485 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
486 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
487 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
488 cHistory, uHistoryFileTime, uHistoryFileSize,
489 szError, sizeof(szError));
490 if (RT_FAILURE(vrc))
491 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, vrc);
492 }
493
494 /* Set up a build identifier so that it can be seen from core dumps what
495 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
496 static char saBuildID[48];
497 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
498 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
499
500 int nRet = 0;
501 HRESULT hRes = com::Initialize(false /*fGui*/, fRun /*fAutoRegUpdate*/);
502
503 _ASSERTE(SUCCEEDED(hRes));
504 _Module.Init(ObjectMap, hInstance, &LIBID_VirtualBox);
505 _Module.dwThreadID = GetCurrentThreadId();
506
507 if (!fRun)
508 {
509#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
510 if (fUnregister)
511 {
512 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
513 nRet = _Module.UnregisterServer(TRUE);
514 }
515 if (fRegister)
516 {
517 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
518 nRet = _Module.RegisterServer(TRUE);
519 }
520#endif
521 if (pszPipeName)
522 {
523 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
524
525 if (!*pszPipeName)
526 vrc = VERR_INVALID_PARAMETER;
527
528 if (RT_SUCCESS(vrc))
529 {
530 /* do the helper job */
531 SVCHlpServer server;
532 vrc = server.open(pszPipeName);
533 if (RT_SUCCESS(vrc))
534 vrc = server.run();
535 }
536 if (RT_FAILURE(vrc))
537 {
538 Log(("SVCMAIN: Failed to process Helper request (%Rrc).", vrc));
539 nRet = 1;
540 }
541 }
542 }
543 else
544 {
545 _Module.StartMonitor();
546#if _WIN32_WINNT >= 0x0400
547 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
548 _ASSERTE(SUCCEEDED(hRes));
549 hRes = CoResumeClassObjects();
550#else
551 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
552#endif
553 _ASSERTE(SUCCEEDED(hRes));
554
555 if (RT_SUCCESS(CreateMainWindow()))
556 {
557 Log(("SVCMain: Main window succesfully created\n"));
558 }
559 else
560 {
561 Log(("SVCMain: Failed to create main window\n"));
562 }
563
564 MSG msg;
565 while (GetMessage(&msg, 0, 0, 0) > 0)
566 {
567 DispatchMessage(&msg);
568 TranslateMessage(&msg);
569 }
570
571 DestroyMainWindow();
572
573 _Module.RevokeClassObjects();
574 }
575
576 _Module.Term();
577
578 com::Shutdown();
579
580 Log(("SVCMAIN: Returning, COM server process ends.\n"));
581 return nRet;
582}
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