VirtualBox

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

Last change on this file since 62701 was 62701, checked in by vboxsync, 9 years ago

VBoxMain/svcmain : fix for ​​​​​​​bugref:8161: added processing of shutdown messages in Windows

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/* $Id: svcmain.cpp 62701 2016-07-29 18:30:43Z 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 result = 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 result = pfn(hWnd, pwszReason);
165 return result;
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 result = 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 result = pfn(hWnd);
182 return result;
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: VBoxSvc has active connections. bActivity = %d. Loc count = %d\n",
201 _Module.bActivity, _Module.GetLockCount()));
202 }
203 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION msg: %d rc= %d\n", msg, rc));
204 } break;
205 case WM_ENDSESSION:
206 {
207 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
208 if (wParam == FALSE)
209 {
210 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
211 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
212 }
213 Log(("VBoxSVCWinMain: WM_ENDSESSION msg: %d. wParam: %d. lParam: %d\n", msg, wParam, lParam));
214 } break;
215 case WM_DESTROY:
216 {
217 Log(("VBoxSVCWinMain: WM_DESTROY \n"));
218 ShutdownBlockReasonDestroyAPI(hwnd);
219 PostQuitMessage(0);
220 } break;
221
222 default:
223 {
224 Log(("VBoxSVCWinMain: msg %p\n", msg));
225 rc = DefWindowProc(hwnd, msg, wParam, lParam);
226 }
227 }
228 return rc;
229}
230
231
232int CreateMainWindow()
233{
234 int rc = VINF_SUCCESS;
235 _ASSERTE(g_hMainWindow == NULL);
236
237 LogFlow(("CreateMainWindow\n"));
238
239 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
240
241 /* Register the Window Class. */
242 WNDCLASS wc;
243 RT_ZERO(wc);
244
245 wc.style = CS_NOCLOSE;
246 wc.lpfnWndProc = WinMainWndProc;
247 wc.hInstance = g_hInstance;
248 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
249 wc.lpszClassName = MAIN_WND_CLASS;
250
251
252 ATOM atomWindowClass = RegisterClass(&wc);
253
254 if (atomWindowClass == 0)
255 {
256 Log(("Failed to register main window class\n"));
257 rc = VERR_NOT_SUPPORTED;
258 }
259 else
260 {
261 /* Create the window. */
262 g_hMainWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
263 MAIN_WND_CLASS, MAIN_WND_CLASS,
264 WS_POPUPWINDOW,
265 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
266
267 if (g_hMainWindow == NULL)
268 {
269 Log(("Failed to create main window\n"));
270 rc = VERR_NOT_SUPPORTED;
271 }
272 else
273 {
274 SetWindowPos(g_hMainWindow, HWND_TOPMOST, -200, -200, 0, 0,
275 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
276
277 }
278 }
279 return 0;
280}
281
282
283void DestroyMainWindow()
284{
285 _ASSERTE(g_hMainWindow != NULL);
286 Log(("SVCMain: DestroyMainWindow \n"));
287 if (g_hMainWindow != NULL)
288 {
289 DestroyWindow(g_hMainWindow);
290 g_hMainWindow = NULL;
291
292 if (g_hInstance != NULL)
293 {
294 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
295 g_hInstance = NULL;
296 }
297 }
298}
299
300
301/////////////////////////////////////////////////////////////////////////////
302//
303int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
304{
305 int argc = __argc;
306 char **argv = __argv;
307
308 /*
309 * Need to parse the command line before initializing the VBox runtime so we can
310 * change to the user home directory before logs are being created.
311 */
312 for (int i = 1; i < argc; i++)
313 if ( (argv[i][0] == '/' || argv[i][0] == '-')
314 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
315 {
316 /* %HOMEDRIVE%%HOMEPATH% */
317 wchar_t wszHome[RTPATH_MAX];
318 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
319 if (cEnv && cEnv < RTPATH_MAX)
320 {
321 DWORD cwc = cEnv; /* doesn't include NUL */
322 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
323 if (cEnv && cEnv < RTPATH_MAX - cwc)
324 {
325 /* If this fails there is nothing we can do. Ignore. */
326 SetCurrentDirectory(wszHome);
327 }
328 }
329 }
330
331 /*
332 * Initialize the VBox runtime without loading
333 * the support driver.
334 */
335 RTR3InitExe(argc, &argv, 0);
336
337
338 /* Note that all options are given lowercase/camel case/uppercase to
339 * approximate case insensitive matching, which RTGetOpt doesn't offer. */
340 static const RTGETOPTDEF s_aOptions[] =
341 {
342 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
343 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
344 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
345 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
346 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
347 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
348 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
349 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
350 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
351 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
352 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
353 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
354 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
355 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
356 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
357 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
358 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
359 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
360 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
361 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
362 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
363 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
364 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
365 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
366 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
367 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
368 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
369 };
370
371 bool fRun = true;
372 bool fRegister = false;
373 bool fUnregister = false;
374 const char *pszPipeName = NULL;
375 const char *pszLogFile = NULL;
376 uint32_t cHistory = 10; // enable log rotation, 10 files
377 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
378 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
379
380 RTGETOPTSTATE GetOptState;
381 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
382 AssertRC(vrc);
383
384 RTGETOPTUNION ValueUnion;
385 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
386 {
387 switch (vrc)
388 {
389 case 'e':
390 /* already handled above */
391 break;
392
393 case 'u':
394 fUnregister = true;
395 fRun = false;
396 break;
397
398 case 'r':
399 fRegister = true;
400 fRun = false;
401 break;
402
403 case 'f':
404 fUnregister = true;
405 fRegister = true;
406 fRun = false;
407 break;
408
409 case 'H':
410 pszPipeName = ValueUnion.psz;
411 if (!pszPipeName)
412 pszPipeName = "";
413 fRun = false;
414 break;
415
416 case 'F':
417 pszLogFile = ValueUnion.psz;
418 break;
419
420 case 'R':
421 cHistory = ValueUnion.u32;
422 break;
423
424 case 'S':
425 uHistoryFileSize = ValueUnion.u64;
426 break;
427
428 case 'I':
429 uHistoryFileTime = ValueUnion.u32;
430 break;
431
432 case 'h':
433 {
434 TCHAR txt[]= L"Options:\n\n"
435 L"/RegServer:\tregister COM out-of-proc server\n"
436 L"/UnregServer:\tunregister COM out-of-proc server\n"
437 L"/ReregServer:\tunregister and register COM server\n"
438 L"no options:\trun the server";
439 TCHAR title[]=_T("Usage");
440 fRun = false;
441 MessageBox(NULL, txt, title, MB_OK);
442 return 0;
443 }
444
445 case 'V':
446 {
447 char *psz = NULL;
448 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
449 PRTUTF16 txt = NULL;
450 RTStrToUtf16(psz, &txt);
451 TCHAR title[]=_T("Version");
452 fRun = false;
453 MessageBox(NULL, txt, title, MB_OK);
454 RTStrFree(psz);
455 RTUtf16Free(txt);
456 return 0;
457 }
458
459 default:
460 /** @todo this assumes that stderr is visible, which is not
461 * true for standard Windows applications. */
462 /* continue on command line errors... */
463 RTGetOptPrintError(vrc, &ValueUnion);
464 }
465 }
466
467 /* Only create the log file when running VBoxSVC normally, but not when
468 * registering/unregistering or calling the helper functionality. */
469 if (fRun)
470 {
471 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
472 char szLogFile[RTPATH_MAX];
473 if (!pszLogFile)
474 {
475 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
476 if (RT_SUCCESS(vrc))
477 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
478 }
479 else
480 {
481 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
482 vrc = VERR_NO_MEMORY;
483 }
484 if (RT_FAILURE(vrc))
485 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
486
487 char szError[RTPATH_MAX + 128];
488 vrc = com::VBoxLogRelCreate("COM Server", szLogFile,
489 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
490 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
491 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
492 cHistory, uHistoryFileTime, uHistoryFileSize,
493 szError, sizeof(szError));
494 if (RT_FAILURE(vrc))
495 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, vrc);
496 }
497
498 /* Set up a build identifier so that it can be seen from core dumps what
499 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
500 static char saBuildID[48];
501 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
502 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
503
504 int nRet = 0;
505 HRESULT hRes = com::Initialize(false /*fGui*/, fRun /*fAutoRegUpdate*/);
506
507 _ASSERTE(SUCCEEDED(hRes));
508 _Module.Init(ObjectMap, hInstance, &LIBID_VirtualBox);
509 _Module.dwThreadID = GetCurrentThreadId();
510
511 if (!fRun)
512 {
513#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
514 if (fUnregister)
515 {
516 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
517 nRet = _Module.UnregisterServer(TRUE);
518 }
519 if (fRegister)
520 {
521 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
522 nRet = _Module.RegisterServer(TRUE);
523 }
524#endif
525 if (pszPipeName)
526 {
527 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
528
529 if (!*pszPipeName)
530 vrc = VERR_INVALID_PARAMETER;
531
532 if (RT_SUCCESS(vrc))
533 {
534 /* do the helper job */
535 SVCHlpServer server;
536 vrc = server.open(pszPipeName);
537 if (RT_SUCCESS(vrc))
538 vrc = server.run();
539 }
540 if (RT_FAILURE(vrc))
541 {
542 Log(("SVCMAIN: Failed to process Helper request (%Rrc).", vrc));
543 nRet = 1;
544 }
545 }
546 }
547 else
548 {
549 _Module.StartMonitor();
550#if _WIN32_WINNT >= 0x0400
551 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
552 _ASSERTE(SUCCEEDED(hRes));
553 hRes = CoResumeClassObjects();
554#else
555 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
556#endif
557 _ASSERTE(SUCCEEDED(hRes));
558
559 if (RT_SUCCESS(CreateMainWindow()))
560 {
561 Log(("SVCMain: Main window succesfully created\n"));
562 }
563 else
564 {
565 Log(("SVCMain: Failed to create main window\n"));
566 }
567
568 MSG msg;
569 while (GetMessage(&msg, 0, 0, 0) > 0)
570 {
571 DispatchMessage(&msg);
572 TranslateMessage(&msg);
573 }
574
575 DestroyMainWindow();
576
577 _Module.RevokeClassObjects();
578 }
579
580 _Module.Term();
581
582 com::Shutdown();
583
584 Log(("SVCMAIN: Returning, COM server process ends.\n"));
585 return nRet;
586}
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