VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp@ 95959

Last change on this file since 95959 was 95959, checked in by vboxsync, 2 years ago

Additions/VBoxTray: Enable (optional) release logging, handle log groups a bit more elegant. bugref:10267

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.5 KB
Line 
1/* $Id: VBoxTray.cpp 95959 2022-08-01 13:26:39Z vboxsync $ */
2/** @file
3 * VBoxTray - Guest Additions Tray Application
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 <package-generated.h>
23#include "product-generated.h"
24
25#include "VBoxTray.h"
26#include "VBoxTrayMsg.h"
27#include "VBoxHelpers.h"
28#include "VBoxSeamless.h"
29#include "VBoxClipboard.h"
30#include "VBoxDisplay.h"
31#include "VBoxVRDP.h"
32#include "VBoxHostVersion.h"
33#ifdef VBOX_WITH_DRAG_AND_DROP
34# include "VBoxDnD.h"
35#endif
36#include "VBoxIPC.h"
37#include "VBoxLA.h"
38#include <VBoxHook.h>
39
40#include <sddl.h>
41
42#include <iprt/asm.h>
43#include <iprt/buildconfig.h>
44#include <iprt/getopt.h>
45#include <iprt/ldr.h>
46#include <iprt/message.h>
47#include <iprt/path.h>
48#include <iprt/process.h>
49#include <iprt/system.h>
50#include <iprt/time.h>
51#include <iprt/utf16.h>
52
53#include <VBox/log.h>
54#include <VBox/err.h>
55
56/* Default desktop state tracking */
57#include <Wtsapi32.h>
58
59/*
60 * St (session [state] tracking) functionality API
61 *
62 * !!!NOTE: this API is NOT thread-safe!!!
63 * it is supposed to be called & used from within the window message handler thread
64 * of the window passed to vboxStInit */
65static int vboxStInit(HWND hWnd);
66static void vboxStTerm(void);
67/* @returns true on "IsActiveConsole" state change */
68static BOOL vboxStHandleEvent(WPARAM EventID);
69static BOOL vboxStIsActiveConsole();
70static BOOL vboxStCheckTimer(WPARAM wEvent);
71
72/*
73 * Dt (desktop [state] tracking) functionality API
74 *
75 * !!!NOTE: this API is NOT thread-safe!!!
76 * */
77static int vboxDtInit();
78static void vboxDtTerm();
79/* @returns true on "IsInputDesktop" state change */
80static BOOL vboxDtHandleEvent();
81/* @returns true iff the application (VBoxTray) desktop is input */
82static BOOL vboxDtIsInputDesktop();
83static HANDLE vboxDtGetNotifyEvent();
84static BOOL vboxDtCheckTimer(WPARAM wParam);
85
86/* caps API */
87#define VBOXCAPS_ENTRY_IDX_SEAMLESS 0
88#define VBOXCAPS_ENTRY_IDX_GRAPHICS 1
89#define VBOXCAPS_ENTRY_IDX_COUNT 2
90
91typedef enum VBOXCAPS_ENTRY_FUNCSTATE
92{
93 /* the cap is unsupported */
94 VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED = 0,
95 /* the cap is supported */
96 VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED,
97 /* the cap functionality is started, it can be disabled however if its AcState is not ACQUIRED */
98 VBOXCAPS_ENTRY_FUNCSTATE_STARTED,
99} VBOXCAPS_ENTRY_FUNCSTATE;
100
101
102static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState);
103static int VBoxCapsInit();
104static int VBoxCapsReleaseAll();
105static void VBoxCapsTerm();
106static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap);
107static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap);
108static BOOL VBoxCapsCheckTimer(WPARAM wParam);
109static int VBoxCapsEntryRelease(uint32_t iCap);
110static int VBoxCapsEntryAcquire(uint32_t iCap);
111static int VBoxCapsAcquireAllSupported();
112
113/* console-related caps API */
114static BOOL VBoxConsoleIsAllowed();
115static void VBoxConsoleEnable(BOOL fEnable);
116static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported);
117
118static void VBoxGrapicsSetSupported(BOOL fSupported);
119
120
121/*********************************************************************************************************************************
122* Internal Functions *
123*********************************************************************************************************************************/
124static int vboxTrayCreateTrayIcon(void);
125static LRESULT CALLBACK vboxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
126
127/* Global message handler prototypes. */
128static int vboxTrayGlMsgTaskbarCreated(WPARAM lParam, LPARAM wParam);
129/*static int vboxTrayGlMsgShowBalloonMsg(WPARAM lParam, LPARAM wParam);*/
130
131static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg);
132
133
134/*********************************************************************************************************************************
135* Global Variables *
136*********************************************************************************************************************************/
137int g_cVerbosity = 0;
138HANDLE g_hStopSem;
139HANDLE g_hSeamlessWtNotifyEvent = 0;
140HANDLE g_hSeamlessKmNotifyEvent = 0;
141HINSTANCE g_hInstance = NULL;
142HWND g_hwndToolWindow;
143NOTIFYICONDATA g_NotifyIconData;
144
145uint32_t g_fGuestDisplaysChanged = 0;
146
147static PRTLOGGER g_pLoggerRelease = NULL; /**< This is actually the debug logger in DEBUG builds! */
148static uint32_t g_cHistory = 10; /**< Enable log rotation, 10 files. */
149static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /**< Max 1 day per file. */
150static uint64_t g_uHistoryFileSize = 100 * _1M; /**< Max 100MB per file. */
151
152#ifdef DEBUG_andy
153static VBOXSERVICEINFO g_aServices[] =
154{
155 {&g_SvcDescDnD, NIL_RTTHREAD, NULL, false, false, false, false, true }
156};
157#else
158/**
159 * The details of the services that has been compiled in.
160 */
161static VBOXSERVICEINFO g_aServices[] =
162{
163 { &g_SvcDescDisplay, NIL_RTTHREAD, NULL, false, false, false, false, true },
164#ifdef VBOX_WITH_SHARED_CLIPBOARD
165 { &g_SvcDescClipboard, NIL_RTTHREAD, NULL, false, false, false, false, true },
166#endif
167 { &g_SvcDescSeamless, NIL_RTTHREAD, NULL, false, false, false, false, true },
168 { &g_SvcDescVRDP, NIL_RTTHREAD, NULL, false, false, false, false, true },
169 { &g_SvcDescIPC, NIL_RTTHREAD, NULL, false, false, false, false, true },
170 { &g_SvcDescLA, NIL_RTTHREAD, NULL, false, false, false, false, true },
171#ifdef VBOX_WITH_DRAG_AND_DROP
172 { &g_SvcDescDnD, NIL_RTTHREAD, NULL, false, false, false, false, true }
173#endif
174};
175#endif
176
177/* The global message table. */
178static VBOXGLOBALMESSAGE g_vboxGlobalMessageTable[] =
179{
180 /* Windows specific stuff. */
181 {
182 "TaskbarCreated",
183 vboxTrayGlMsgTaskbarCreated
184 },
185
186 /* VBoxTray specific stuff. */
187 /** @todo Add new messages here! */
188
189 {
190 NULL
191 }
192};
193
194/**
195 * Gets called whenever the Windows main taskbar
196 * get (re-)created. Nice to install our tray icon.
197 *
198 * @return IPRT status code.
199 * @param wParam
200 * @param lParam
201 */
202static int vboxTrayGlMsgTaskbarCreated(WPARAM wParam, LPARAM lParam)
203{
204 RT_NOREF(wParam, lParam);
205 return vboxTrayCreateTrayIcon();
206}
207
208static int vboxTrayCreateTrayIcon(void)
209{
210 HICON hIcon = LoadIcon(g_hInstance, "IDI_ICON1"); /* see Artwork/win/TemplateR3.rc */
211 if (hIcon == NULL)
212 {
213 DWORD dwErr = GetLastError();
214 LogFunc(("Could not load tray icon, error %08X\n", dwErr));
215 return RTErrConvertFromWin32(dwErr);
216 }
217
218 /* Prepare the system tray icon. */
219 RT_ZERO(g_NotifyIconData);
220 g_NotifyIconData.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA);
221 g_NotifyIconData.hWnd = g_hwndToolWindow;
222 g_NotifyIconData.uID = ID_TRAYICON;
223 g_NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
224 g_NotifyIconData.uCallbackMessage = WM_VBOXTRAY_TRAY_ICON;
225 g_NotifyIconData.hIcon = hIcon;
226
227 RTStrPrintf(g_NotifyIconData.szTip, sizeof(g_NotifyIconData.szTip), "%s Guest Additions %d.%d.%dr%d",
228 VBOX_PRODUCT, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
229
230 int rc = VINF_SUCCESS;
231 if (!Shell_NotifyIcon(NIM_ADD, &g_NotifyIconData))
232 {
233 DWORD dwErr = GetLastError();
234 LogFunc(("Could not create tray icon, error=%ld\n", dwErr));
235 rc = RTErrConvertFromWin32(dwErr);
236 RT_ZERO(g_NotifyIconData);
237 }
238
239 if (hIcon)
240 DestroyIcon(hIcon);
241 return rc;
242}
243
244static void vboxTrayRemoveTrayIcon(void)
245{
246 if (g_NotifyIconData.cbSize > 0)
247 {
248 /* Remove the system tray icon and refresh system tray. */
249 Shell_NotifyIcon(NIM_DELETE, &g_NotifyIconData);
250 HWND hTrayWnd = FindWindow("Shell_TrayWnd", NULL); /* We assume we only have one tray atm. */
251 if (hTrayWnd)
252 {
253 HWND hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", NULL);
254 if (hTrayNotifyWnd)
255 SendMessage(hTrayNotifyWnd, WM_PAINT, 0, NULL);
256 }
257 RT_ZERO(g_NotifyIconData);
258 }
259}
260
261/**
262 * The service thread.
263 *
264 * @returns Whatever the worker function returns.
265 * @param ThreadSelf My thread handle.
266 * @param pvUser The service index.
267 */
268static DECLCALLBACK(int) vboxTrayServiceThread(RTTHREAD ThreadSelf, void *pvUser)
269{
270 PVBOXSERVICEINFO pSvc = (PVBOXSERVICEINFO)pvUser;
271 AssertPtr(pSvc);
272
273#ifndef RT_OS_WINDOWS
274 /*
275 * Block all signals for this thread. Only the main thread will handle signals.
276 */
277 sigset_t signalMask;
278 sigfillset(&signalMask);
279 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
280#endif
281
282 int rc = pSvc->pDesc->pfnWorker(pSvc->pInstance, &pSvc->fShutdown);
283 ASMAtomicXchgBool(&pSvc->fShutdown, true);
284 RTThreadUserSignal(ThreadSelf);
285
286 LogFunc(("Worker for '%s' ended with %Rrc\n", pSvc->pDesc->pszName, rc));
287 return rc;
288}
289
290static int vboxTrayServicesStart(PVBOXSERVICEENV pEnv)
291{
292 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
293
294 LogRel(("Starting services ...\n"));
295
296 int rc = VINF_SUCCESS;
297
298 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
299 {
300 PVBOXSERVICEINFO pSvc = &g_aServices[i];
301 LogRel(("Starting service '%s' ...\n", pSvc->pDesc->pszName));
302
303 pSvc->hThread = NIL_RTTHREAD;
304 pSvc->pInstance = NULL;
305 pSvc->fStarted = false;
306 pSvc->fShutdown = false;
307
308 int rc2 = VINF_SUCCESS;
309
310 if (pSvc->pDesc->pfnInit)
311 rc2 = pSvc->pDesc->pfnInit(pEnv, &pSvc->pInstance);
312
313 if (RT_FAILURE(rc2))
314 {
315 switch (rc2)
316 {
317 case VERR_NOT_SUPPORTED:
318 LogRel(("Service '%s' is not supported on this system\n", pSvc->pDesc->pszName));
319 rc2 = VINF_SUCCESS; /* Keep going. */
320 break;
321
322 case VERR_HGCM_SERVICE_NOT_FOUND:
323 LogRel(("Service '%s' is not available on the host\n", pSvc->pDesc->pszName));
324 rc2 = VINF_SUCCESS; /* Keep going. */
325 break;
326
327 default:
328 LogRel(("Failed to initialize service '%s', rc=%Rrc\n", pSvc->pDesc->pszName, rc2));
329 break;
330 }
331 }
332 else
333 {
334 if (pSvc->pDesc->pfnWorker)
335 {
336 rc2 = RTThreadCreate(&pSvc->hThread, vboxTrayServiceThread, pSvc /* pvUser */,
337 0 /* Default stack size */, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, pSvc->pDesc->pszName);
338 if (RT_SUCCESS(rc2))
339 {
340 pSvc->fStarted = true;
341
342 RTThreadUserWait(pSvc->hThread, 30 * 1000 /* Timeout in ms */);
343 if (pSvc->fShutdown)
344 {
345 LogRel(("Service '%s' failed to start!\n", pSvc->pDesc->pszName));
346 rc = VERR_GENERAL_FAILURE;
347 }
348 else
349 LogRel(("Service '%s' started\n", pSvc->pDesc->pszName));
350 }
351 else
352 {
353 LogRel(("Failed to start thread for service '%s': %Rrc\n", rc2));
354 if (pSvc->pDesc->pfnDestroy)
355 pSvc->pDesc->pfnDestroy(pSvc->pInstance);
356 }
357 }
358 }
359
360 if (RT_SUCCESS(rc))
361 rc = rc2;
362 }
363
364 if (RT_SUCCESS(rc))
365 LogRel(("All services started\n"));
366 else
367 LogRel(("Services started, but some with errors\n"));
368
369 LogFlowFuncLeaveRC(rc);
370 return rc;
371}
372
373static int vboxTrayServicesStop(VBOXSERVICEENV *pEnv)
374{
375 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
376
377 LogRel2(("Stopping all services ...\n"));
378
379 /*
380 * Signal all the services.
381 */
382 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
383 ASMAtomicWriteBool(&g_aServices[i].fShutdown, true);
384
385 /*
386 * Do the pfnStop callback on all running services.
387 */
388 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
389 {
390 PVBOXSERVICEINFO pSvc = &g_aServices[i];
391 if ( pSvc->fStarted
392 && pSvc->pDesc->pfnStop)
393 {
394 LogRel2(("Calling stop function for service '%s' ...\n", pSvc->pDesc->pszName));
395 int rc2 = pSvc->pDesc->pfnStop(pSvc->pInstance);
396 if (RT_FAILURE(rc2))
397 LogRel(("Failed to stop service '%s': %Rrc\n", pSvc->pDesc->pszName, rc2));
398 }
399 }
400
401 LogRel2(("All stop functions for services called\n"));
402
403 int rc = VINF_SUCCESS;
404
405 /*
406 * Wait for all the service threads to complete.
407 */
408 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
409 {
410 PVBOXSERVICEINFO pSvc = &g_aServices[i];
411 if (!pSvc->fEnabled) /* Only stop services which were started before. */
412 continue;
413
414 if (pSvc->hThread != NIL_RTTHREAD)
415 {
416 LogRel2(("Waiting for service '%s' to stop ...\n", pSvc->pDesc->pszName));
417 int rc2 = VINF_SUCCESS;
418 for (int j = 0; j < 30; j++) /* Wait 30 seconds in total */
419 {
420 rc2 = RTThreadWait(pSvc->hThread, 1000 /* Wait 1 second */, NULL);
421 if (RT_SUCCESS(rc2))
422 break;
423 }
424 if (RT_FAILURE(rc2))
425 {
426 LogRel(("Service '%s' failed to stop (%Rrc)\n", pSvc->pDesc->pszName, rc2));
427 if (RT_SUCCESS(rc))
428 rc = rc2;
429 }
430 }
431
432 if ( pSvc->pDesc->pfnDestroy
433 && pSvc->pInstance) /* pInstance might be NULL if initialization of a service failed. */
434 {
435 LogRel2(("Terminating service '%s' ...\n", pSvc->pDesc->pszName));
436 pSvc->pDesc->pfnDestroy(pSvc->pInstance);
437 }
438 }
439
440 if (RT_SUCCESS(rc))
441 LogRel(("All services stopped\n"));
442
443 LogFlowFuncLeaveRC(rc);
444 return rc;
445}
446
447static int vboxTrayRegisterGlobalMessages(PVBOXGLOBALMESSAGE pTable)
448{
449 int rc = VINF_SUCCESS;
450 if (pTable == NULL) /* No table to register? Skip. */
451 return rc;
452 while ( pTable->pszName
453 && RT_SUCCESS(rc))
454 {
455 /* Register global accessible window messages. */
456 pTable->uMsgID = RegisterWindowMessage(TEXT(pTable->pszName));
457 if (!pTable->uMsgID)
458 {
459 DWORD dwErr = GetLastError();
460 Log(("Registering global message \"%s\" failed, error = %08X\n", dwErr));
461 rc = RTErrConvertFromWin32(dwErr);
462 }
463
464 /* Advance to next table element. */
465 pTable++;
466 }
467 return rc;
468}
469
470static bool vboxTrayHandleGlobalMessages(PVBOXGLOBALMESSAGE pTable, UINT uMsg,
471 WPARAM wParam, LPARAM lParam)
472{
473 if (pTable == NULL)
474 return false;
475 while (pTable && pTable->pszName)
476 {
477 if (pTable->uMsgID == uMsg)
478 {
479 if (pTable->pfnHandler)
480 pTable->pfnHandler(wParam, lParam);
481 return true;
482 }
483
484 /* Advance to next table element. */
485 pTable++;
486 }
487 return false;
488}
489
490/**
491 * Release logger callback.
492 *
493 * @return IPRT status code.
494 * @param pLoggerRelease
495 * @param enmPhase
496 * @param pfnLog
497 */
498static DECLCALLBACK(void) vboxTrayLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
499{
500 /* Some introductory information. */
501 static RTTIMESPEC s_TimeSpec;
502 char szTmp[256];
503 if (enmPhase == RTLOGPHASE_BEGIN)
504 RTTimeNow(&s_TimeSpec);
505 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
506
507 switch (enmPhase)
508 {
509 case RTLOGPHASE_BEGIN:
510 {
511 pfnLog(pLoggerRelease,
512 "VBoxTray %s r%s %s (%s %s) release log\n"
513 "Log opened %s\n",
514 RTBldCfgVersion(), RTBldCfgRevisionStr(), VBOX_BUILD_TARGET,
515 __DATE__, __TIME__, szTmp);
516
517 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
518 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
519 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
520 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
521 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
522 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
523 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
524 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
525 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
526 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
527 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
528
529 /* the package type is interesting for Linux distributions */
530 char szExecName[RTPATH_MAX];
531 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
532 pfnLog(pLoggerRelease,
533 "Executable: %s\n"
534 "Process ID: %u\n"
535 "Package type: %s"
536#ifdef VBOX_OSE
537 " (OSE)"
538#endif
539 "\n",
540 pszExecName ? pszExecName : "unknown",
541 RTProcSelf(),
542 VBOX_PACKAGE_STRING);
543 break;
544 }
545
546 case RTLOGPHASE_PREROTATE:
547 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
548 break;
549
550 case RTLOGPHASE_POSTROTATE:
551 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
552 break;
553
554 case RTLOGPHASE_END:
555 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
556 break;
557
558 default:
559 /* nothing */;
560 }
561}
562
563/**
564 * Creates the default release logger outputting to the specified file.
565 *
566 * @return IPRT status code.
567 * @param pszLogFile Path to log file to use.
568 */
569static int vboxTrayLogCreate(const char *pszLogFile)
570{
571 /* Create release (or debug) logger (stdout + file). */
572 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
573 static const char s_szEnvVarPfx[] = "VBOXTRAY_RELEASE_LOG";
574
575 RTERRINFOSTATIC ErrInfo;
576 int rc = RTLogCreateEx(&g_pLoggerRelease, s_szEnvVarPfx,
577 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_USECRLF,
578 "all.e", RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX,
579 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT,
580 vboxTrayLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
581 NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
582 RTErrInfoInitStatic(&ErrInfo), "%s", pszLogFile ? pszLogFile : "");
583 if (RT_SUCCESS(rc))
584 {
585 /* Register this logger as the release logger. */
586 RTLogRelSetDefaultInstance(g_pLoggerRelease);
587
588 /* Register this logger as the _debug_ logger. */
589 RTLogSetDefaultInstance(g_pLoggerRelease);
590
591 const char *apszGroups[] = { "all", "guest_dnd" }; /* All groups we want to enable logging for VBoxTray. */
592 char szGroupSettings[_1K];
593
594 szGroupSettings[0] = '\0';
595
596 for (size_t i = 0; i < RT_ELEMENTS(apszGroups); i++)
597 {
598 if (i > 0)
599 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), "+");
600 if (RT_SUCCESS(rc))
601 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), apszGroups[i]);
602 if (RT_FAILURE(rc))
603 break;
604
605 switch (g_cVerbosity)
606 {
607 case 1:
608 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), ".e.l");
609 break;
610
611 case 2:
612 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), ".e.l.l2");
613 break;
614
615 case 3:
616 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), ".e.l.l2.l3");
617 break;
618
619 case 4:
620 RT_FALL_THROUGH();
621 default:
622 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), ".e.l.l2.l3.f");
623 break;
624 }
625
626 if (RT_FAILURE(rc))
627 break;
628 }
629
630 LogRel(("Verbose log settings are: %s\n", szGroupSettings));
631
632 if (RT_SUCCESS(rc))
633 rc = RTLogGroupSettings(g_pLoggerRelease, szGroupSettings);
634 if (RT_FAILURE(rc))
635 RTMsgError("Setting log group settings failed, rc=%Rrc\n", rc);
636
637 /* Explicitly flush the log in case of VBOXTRAY_RELEASE_LOG=buffered. */
638 RTLogFlush(g_pLoggerRelease);
639 }
640 else
641 VBoxTrayShowError(ErrInfo.szMsg);
642
643 return rc;
644}
645
646static void vboxTrayLogDestroy(void)
647{
648 /* Only want to destroy the release logger before calling exit(). The debug
649 logger can be useful after that point... */
650 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
651}
652
653/**
654 * Displays an error message.
655 *
656 * @returns RTEXITCODE_FAILURE.
657 * @param pszFormat The message text.
658 * @param ... Format arguments.
659 */
660RTEXITCODE VBoxTrayShowError(const char *pszFormat, ...)
661{
662 va_list args;
663 va_start(args, pszFormat);
664 char *psz = NULL;
665 RTStrAPrintfV(&psz, pszFormat, args);
666 va_end(args);
667
668 AssertPtr(psz);
669 LogRel(("Error: %s", psz));
670
671 MessageBox(GetDesktopWindow(), psz, "VBoxTray - Error", MB_OK | MB_ICONERROR);
672
673 RTStrFree(psz);
674
675 return RTEXITCODE_FAILURE;
676}
677
678static void vboxTrayDestroyToolWindow(void)
679{
680 if (g_hwndToolWindow)
681 {
682 Log(("Destroying tool window ...\n"));
683
684 /* Destroy the tool window. */
685 DestroyWindow(g_hwndToolWindow);
686 g_hwndToolWindow = NULL;
687
688 UnregisterClass("VBoxTrayToolWndClass", g_hInstance);
689 }
690}
691
692static int vboxTrayCreateToolWindow(void)
693{
694 DWORD dwErr = ERROR_SUCCESS;
695
696 /* Create a custom window class. */
697 WNDCLASSEX wc = { 0 };
698 wc.cbSize = sizeof(WNDCLASSEX);
699 wc.style = CS_NOCLOSE;
700 wc.lpfnWndProc = (WNDPROC)vboxToolWndProc;
701 wc.hInstance = g_hInstance;
702 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
703 wc.lpszClassName = "VBoxTrayToolWndClass";
704
705 if (!RegisterClassEx(&wc))
706 {
707 dwErr = GetLastError();
708 Log(("Registering invisible tool window failed, error = %08X\n", dwErr));
709 }
710 else
711 {
712 /*
713 * Create our (invisible) tool window.
714 * Note: The window name ("VBoxTrayToolWnd") and class ("VBoxTrayToolWndClass") is
715 * needed for posting globally registered messages to VBoxTray and must not be
716 * changed! Otherwise things get broken!
717 *
718 */
719 g_hwndToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
720 "VBoxTrayToolWndClass", "VBoxTrayToolWnd",
721 WS_POPUPWINDOW,
722 -200, -200, 100, 100, NULL, NULL, g_hInstance, NULL);
723 if (!g_hwndToolWindow)
724 {
725 dwErr = GetLastError();
726 Log(("Creating invisible tool window failed, error = %08X\n", dwErr));
727 }
728 else
729 {
730 /* Reload the cursor(s). */
731 hlpReloadCursor();
732
733 Log(("Invisible tool window handle = %p\n", g_hwndToolWindow));
734 }
735 }
736
737 if (dwErr != ERROR_SUCCESS)
738 vboxTrayDestroyToolWindow();
739 return RTErrConvertFromWin32(dwErr);
740}
741
742static int vboxTraySetupSeamless(void)
743{
744 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore. */
745 SECURITY_ATTRIBUTES SecAttr;
746 DWORD dwErr = ERROR_SUCCESS;
747 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
748 BOOL fRC;
749
750 SecAttr.nLength = sizeof(SecAttr);
751 SecAttr.bInheritHandle = FALSE;
752 SecAttr.lpSecurityDescriptor = &secDesc;
753 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
754 fRC = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
755 if (!fRC)
756 {
757 dwErr = GetLastError();
758 Log(("SetSecurityDescriptorDacl failed with last error = %08X\n", dwErr));
759 }
760 else
761 {
762 /* For Vista and up we need to change the integrity of the security descriptor, too. */
763 uint64_t const uNtVersion = RTSystemGetNtVersion();
764 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
765 {
766 BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize);
767 *(void **)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA =
768 RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorA");
769 Log(("pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %p\n",
770 RT_CB_LOG_CAST(pfnConvertStringSecurityDescriptorToSecurityDescriptorA)));
771 if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA)
772 {
773 PSECURITY_DESCRIPTOR pSD;
774 PACL pSacl = NULL;
775 BOOL fSaclPresent = FALSE;
776 BOOL fSaclDefaulted = FALSE;
777
778 fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */
779 SDDL_REVISION_1, &pSD, NULL);
780 if (!fRC)
781 {
782 dwErr = GetLastError();
783 Log(("ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr));
784 }
785 else
786 {
787 fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted);
788 if (!fRC)
789 {
790 dwErr = GetLastError();
791 Log(("GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
792 }
793 else
794 {
795 fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE);
796 if (!fRC)
797 {
798 dwErr = GetLastError();
799 Log(("SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
800 }
801 }
802 }
803 }
804 }
805
806 if ( dwErr == ERROR_SUCCESS
807 && uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Only for W2K and up ... */
808 {
809 g_hSeamlessWtNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_WT_EVENT_NAME);
810 if (g_hSeamlessWtNotifyEvent == NULL)
811 {
812 dwErr = GetLastError();
813 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
814 }
815
816 g_hSeamlessKmNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
817 if (g_hSeamlessKmNotifyEvent == NULL)
818 {
819 dwErr = GetLastError();
820 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
821 }
822 }
823 }
824 return RTErrConvertFromWin32(dwErr);
825}
826
827static void vboxTrayShutdownSeamless(void)
828{
829 if (g_hSeamlessWtNotifyEvent)
830 {
831 CloseHandle(g_hSeamlessWtNotifyEvent);
832 g_hSeamlessWtNotifyEvent = NULL;
833 }
834
835 if (g_hSeamlessKmNotifyEvent)
836 {
837 CloseHandle(g_hSeamlessKmNotifyEvent);
838 g_hSeamlessKmNotifyEvent = NULL;
839 }
840}
841
842static void VBoxTrayCheckDt()
843{
844 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
845 if (vboxDtHandleEvent())
846 {
847 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
848 VBoxConsoleEnable(!fOldAllowedState);
849 }
850}
851
852static int vboxTrayServiceMain(void)
853{
854 int rc = VINF_SUCCESS;
855 LogFunc(("Entering vboxTrayServiceMain\n"));
856
857 g_hStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
858 if (g_hStopSem == NULL)
859 {
860 rc = RTErrConvertFromWin32(GetLastError());
861 LogFunc(("CreateEvent for stopping VBoxTray failed, rc=%Rrc\n", rc));
862 }
863 else
864 {
865 /*
866 * Start services listed in the vboxServiceTable.
867 */
868 VBOXSERVICEENV svcEnv;
869 svcEnv.hInstance = g_hInstance;
870
871 /* Initializes disp-if to default (XPDM) mode. */
872 VBoxDispIfInit(&svcEnv.dispIf); /* Cannot fail atm. */
873 #ifdef VBOX_WITH_WDDM
874 /*
875 * For now the display mode will be adjusted to WDDM mode if needed
876 * on display service initialization when it detects the display driver type.
877 */
878 #endif
879
880 /* Finally start all the built-in services! */
881 rc = vboxTrayServicesStart(&svcEnv);
882 if (RT_FAILURE(rc))
883 {
884 /* Terminate service if something went wrong. */
885 vboxTrayServicesStop(&svcEnv);
886 }
887 else
888 {
889 uint64_t const uNtVersion = RTSystemGetNtVersion();
890 rc = vboxTrayCreateTrayIcon();
891 if ( RT_SUCCESS(rc)
892 && uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Only for W2K and up ... */
893 {
894 /* We're ready to create the tooltip balloon.
895 Check in 10 seconds (@todo make seconds configurable) ... */
896 SetTimer(g_hwndToolWindow,
897 TIMERID_VBOXTRAY_CHECK_HOSTVERSION,
898 10 * 1000, /* 10 seconds */
899 NULL /* No timerproc */);
900 }
901
902 if (RT_SUCCESS(rc))
903 {
904 /* Report the host that we're up and running! */
905 hlpReportStatus(VBoxGuestFacilityStatus_Active);
906 }
907
908 if (RT_SUCCESS(rc))
909 {
910 /* Boost thread priority to make sure we wake up early for seamless window notifications
911 * (not sure if it actually makes any difference though). */
912 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
913
914 /*
915 * Main execution loop
916 * Wait for the stop semaphore to be posted or a window event to arrive
917 */
918
919 HANDLE hWaitEvent[4] = {0};
920 DWORD dwEventCount = 0;
921
922 hWaitEvent[dwEventCount++] = g_hStopSem;
923
924 /* Check if seamless mode is not active and add seamless event to the list */
925 if (0 != g_hSeamlessWtNotifyEvent)
926 {
927 hWaitEvent[dwEventCount++] = g_hSeamlessWtNotifyEvent;
928 }
929
930 if (0 != g_hSeamlessKmNotifyEvent)
931 {
932 hWaitEvent[dwEventCount++] = g_hSeamlessKmNotifyEvent;
933 }
934
935 if (0 != vboxDtGetNotifyEvent())
936 {
937 hWaitEvent[dwEventCount++] = vboxDtGetNotifyEvent();
938 }
939
940 LogFlowFunc(("Number of events to wait in main loop: %ld\n", dwEventCount));
941 while (true)
942 {
943 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
944 waitResult = waitResult - WAIT_OBJECT_0;
945
946 /* Only enable for message debugging, lots of traffic! */
947 //Log(("Wait result = %ld\n", waitResult));
948
949 if (waitResult == 0)
950 {
951 LogFunc(("Event 'Exit' triggered\n"));
952 /* exit */
953 break;
954 }
955 else
956 {
957 BOOL fHandled = FALSE;
958 if (waitResult < RT_ELEMENTS(hWaitEvent))
959 {
960 if (hWaitEvent[waitResult])
961 {
962 if (hWaitEvent[waitResult] == g_hSeamlessWtNotifyEvent)
963 {
964 LogFunc(("Event 'Seamless' triggered\n"));
965
966 /* seamless window notification */
967 VBoxSeamlessCheckWindows(false);
968 fHandled = TRUE;
969 }
970 else if (hWaitEvent[waitResult] == g_hSeamlessKmNotifyEvent)
971 {
972 LogFunc(("Event 'Km Seamless' triggered\n"));
973
974 /* seamless window notification */
975 VBoxSeamlessCheckWindows(true);
976 fHandled = TRUE;
977 }
978 else if (hWaitEvent[waitResult] == vboxDtGetNotifyEvent())
979 {
980 LogFunc(("Event 'Dt' triggered\n"));
981 VBoxTrayCheckDt();
982 fHandled = TRUE;
983 }
984 }
985 }
986
987 if (!fHandled)
988 {
989 /* timeout or a window message, handle it */
990 MSG msg;
991 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
992 {
993#ifdef DEBUG_andy
994 LogFlowFunc(("PeekMessage %u\n", msg.message));
995#endif
996 if (msg.message == WM_QUIT)
997 {
998 LogFunc(("Terminating ...\n"));
999 SetEvent(g_hStopSem);
1000 }
1001 TranslateMessage(&msg);
1002 DispatchMessage(&msg);
1003 }
1004 }
1005 }
1006 }
1007 LogFunc(("Returned from main loop, exiting ...\n"));
1008 }
1009 LogFunc(("Waiting for services to stop ...\n"));
1010 vboxTrayServicesStop(&svcEnv);
1011 } /* Services started */
1012 CloseHandle(g_hStopSem);
1013 } /* Stop event created */
1014
1015 vboxTrayRemoveTrayIcon();
1016
1017 LogFunc(("Leaving with rc=%Rrc\n", rc));
1018 return rc;
1019}
1020
1021/**
1022 * Main function
1023 */
1024int main(int cArgs, char **papszArgs)
1025{
1026 int rc = RTR3InitExe(cArgs, &papszArgs, RTR3INIT_FLAGS_STANDALONE_APP);
1027 if (RT_FAILURE(rc))
1028 return RTMsgInitFailure(rc);
1029
1030 /*
1031 * Parse the top level arguments until we find a command.
1032 */
1033 static const RTGETOPTDEF s_aOptions[] =
1034 {
1035 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1036 { "-help", 'h', RTGETOPT_REQ_NOTHING },
1037 { "/help", 'h', RTGETOPT_REQ_NOTHING },
1038 { "/?", 'h', RTGETOPT_REQ_NOTHING },
1039 { "--logfile", 'l', RTGETOPT_REQ_STRING },
1040 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1041 { "--version", 'V', RTGETOPT_REQ_NOTHING },
1042 };
1043
1044 char szLogFile[RTPATH_MAX] = {0};
1045
1046 RTGETOPTSTATE GetState;
1047 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
1048 if (RT_FAILURE(rc))
1049 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
1050
1051 int ch;
1052 RTGETOPTUNION ValueUnion;
1053 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
1054 {
1055 switch (ch)
1056 {
1057 case 'h':
1058 hlpShowMessageBox(VBOX_PRODUCT " - " VBOX_VBOXTRAY_TITLE,
1059 MB_ICONINFORMATION,
1060 "-- " VBOX_PRODUCT " %s v%u.%u.%ur%u --\n\n"
1061 "Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
1062 "All rights reserved.\n\n"
1063 "Command Line Parameters:\n\n"
1064 "-l, --logfile <file>\n"
1065 " Enables logging to a file\n"
1066 "-v, --verbose\n"
1067 " Increases verbosity\n"
1068 "-V, --version\n"
1069 " Displays version number and exit\n"
1070 "-?, -h, --help\n"
1071 " Displays this help text and exit\n"
1072 "\n"
1073 "Examples:\n"
1074 " %s -vvv\n",
1075 VBOX_VBOXTRAY_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV,
1076 papszArgs[0], papszArgs[0]);
1077 return RTEXITCODE_SUCCESS;
1078
1079 case 'l':
1080 if (*ValueUnion.psz == '\0')
1081 szLogFile[0] = '\0';
1082 else
1083 {
1084 rc = RTPathAbs(ValueUnion.psz, szLogFile, sizeof(szLogFile));
1085 if (RT_FAILURE(rc))
1086 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on log file path: %Rrc (%s)",
1087 rc, ValueUnion.psz);
1088 }
1089 break;
1090
1091 case 'v':
1092 g_cVerbosity++;
1093 break;
1094
1095 case 'V':
1096 hlpShowMessageBox(VBOX_VBOXTRAY_TITLE, MB_ICONINFORMATION,
1097 "Version: %u.%u.%ur%u",
1098 VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
1099 return RTEXITCODE_SUCCESS;
1100
1101 default:
1102 rc = RTGetOptPrintError(ch, &ValueUnion);
1103 break;
1104 }
1105 }
1106
1107 /* Note: Do not use a global namespace ("Global\\") for mutex name here,
1108 * will blow up NT4 compatibility! */
1109 HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, VBOX_VBOXTRAY_TITLE);
1110 if ( hMutexAppRunning != NULL
1111 && GetLastError() == ERROR_ALREADY_EXISTS)
1112 {
1113 /* VBoxTray already running? Bail out. */
1114 CloseHandle (hMutexAppRunning);
1115 hMutexAppRunning = NULL;
1116 return RTEXITCODE_SUCCESS;
1117 }
1118
1119 rc = vboxTrayLogCreate(szLogFile[0] ? szLogFile : NULL);
1120 if (RT_SUCCESS(rc))
1121 {
1122 LogRel(("Verbosity level: %d\n", g_cVerbosity));
1123
1124 rc = VbglR3Init();
1125 if (RT_SUCCESS(rc))
1126 {
1127 /* Log the major windows NT version: */
1128 uint64_t const uNtVersion = RTSystemGetNtVersion();
1129 LogRel(("Windows version %u.%u build %u (uNtVersion=%#RX64)\n", RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVersion),
1130 RTSYSTEM_NT_VERSION_GET_MINOR(uNtVersion), RTSYSTEM_NT_VERSION_GET_BUILD(uNtVersion), uNtVersion ));
1131
1132 /* Set the instance handle. */
1133#ifdef IPRT_NO_CRT
1134 Assert(g_hInstance == NULL); /* Make sure this isn't set before by WinMain(). */
1135 g_hInstance = GetModuleHandleW(NULL);
1136#endif
1137 hlpReportStatus(VBoxGuestFacilityStatus_Init);
1138 rc = vboxTrayCreateToolWindow();
1139 if (RT_SUCCESS(rc))
1140 {
1141 VBoxCapsInit();
1142
1143 rc = vboxStInit(g_hwndToolWindow);
1144 if (!RT_SUCCESS(rc))
1145 {
1146 LogFlowFunc(("vboxStInit failed, rc=%Rrc\n", rc));
1147 /* ignore the St Init failure. this can happen for < XP win that do not support WTS API
1148 * in that case the session is treated as active connected to the physical console
1149 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1150 Assert(vboxStIsActiveConsole());
1151 }
1152
1153 rc = vboxDtInit();
1154 if (!RT_SUCCESS(rc))
1155 {
1156 LogFlowFunc(("vboxDtInit failed, rc=%Rrc\n", rc));
1157 /* ignore the Dt Init failure. this can happen for < XP win that do not support WTS API
1158 * in that case the session is treated as active connected to the physical console
1159 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1160 Assert(vboxDtIsInputDesktop());
1161 }
1162
1163 rc = VBoxAcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, true);
1164 if (!RT_SUCCESS(rc))
1165 LogFlowFunc(("VBoxAcquireGuestCaps failed with rc=%Rrc, ignoring ...\n", rc));
1166
1167 rc = vboxTraySetupSeamless(); /** @todo r=andy Do we really want to be this critical for the whole application? */
1168 if (RT_SUCCESS(rc))
1169 {
1170 rc = vboxTrayServiceMain();
1171 if (RT_SUCCESS(rc))
1172 hlpReportStatus(VBoxGuestFacilityStatus_Terminating);
1173 vboxTrayShutdownSeamless();
1174 }
1175
1176 /* it should be safe to call vboxDtTerm even if vboxStInit above failed */
1177 vboxDtTerm();
1178
1179 /* it should be safe to call vboxStTerm even if vboxStInit above failed */
1180 vboxStTerm();
1181
1182 VBoxCapsTerm();
1183
1184 vboxTrayDestroyToolWindow();
1185 }
1186 if (RT_SUCCESS(rc))
1187 hlpReportStatus(VBoxGuestFacilityStatus_Terminated);
1188 else
1189 {
1190 LogRel(("Error while starting, rc=%Rrc\n", rc));
1191 hlpReportStatus(VBoxGuestFacilityStatus_Failed);
1192 }
1193
1194 LogRel(("Ended\n"));
1195 VbglR3Term();
1196 }
1197 else
1198 LogRel(("VbglR3Init failed: %Rrc\n", rc));
1199 }
1200
1201 /* Release instance mutex. */
1202 if (hMutexAppRunning != NULL)
1203 {
1204 CloseHandle(hMutexAppRunning);
1205 hMutexAppRunning = NULL;
1206 }
1207
1208 vboxTrayLogDestroy();
1209
1210 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1211}
1212
1213#ifndef IPRT_NO_CRT
1214int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
1215{
1216 RT_NOREF(hPrevInstance, lpCmdLine, nCmdShow);
1217
1218 g_hInstance = hInstance;
1219
1220 return main(__argc, __argv);
1221}
1222#endif /* IPRT_NO_CRT */
1223
1224/**
1225 * Window procedure for our main tool window.
1226 */
1227static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1228{
1229 LogFlowFunc(("hWnd=%p, uMsg=%u\n", hWnd, uMsg));
1230
1231 switch (uMsg)
1232 {
1233 case WM_CREATE:
1234 {
1235 LogFunc(("Tool window created\n"));
1236
1237 int rc = vboxTrayRegisterGlobalMessages(&g_vboxGlobalMessageTable[0]);
1238 if (RT_FAILURE(rc))
1239 LogFunc(("Error registering global window messages, rc=%Rrc\n", rc));
1240 return 0;
1241 }
1242
1243 case WM_CLOSE:
1244 return 0;
1245
1246 case WM_DESTROY:
1247 {
1248 LogFunc(("Tool window destroyed\n"));
1249 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1250 return 0;
1251 }
1252
1253 case WM_TIMER:
1254 {
1255 if (VBoxCapsCheckTimer(wParam))
1256 return 0;
1257 if (vboxDtCheckTimer(wParam))
1258 return 0;
1259 if (vboxStCheckTimer(wParam))
1260 return 0;
1261
1262 switch (wParam)
1263 {
1264 case TIMERID_VBOXTRAY_CHECK_HOSTVERSION:
1265 if (RT_SUCCESS(VBoxCheckHostVersion()))
1266 {
1267 /* After successful run we don't need to check again. */
1268 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1269 }
1270 return 0;
1271
1272 default:
1273 break;
1274 }
1275
1276 break; /* Make sure other timers get processed the usual way! */
1277 }
1278
1279 case WM_VBOXTRAY_TRAY_ICON:
1280 {
1281 switch (LOWORD(lParam))
1282 {
1283 case WM_LBUTTONDBLCLK:
1284 break;
1285 case WM_RBUTTONDOWN:
1286 {
1287 if (!g_cVerbosity) /* Don't show menu when running in non-verbose mode. */
1288 break;
1289
1290 POINT lpCursor;
1291 if (GetCursorPos(&lpCursor))
1292 {
1293 HMENU hContextMenu = CreatePopupMenu();
1294 if (hContextMenu)
1295 {
1296 UINT_PTR uMenuItem = 9999;
1297 UINT fMenuItem = MF_BYPOSITION | MF_STRING;
1298 if (InsertMenuW(hContextMenu, UINT_MAX, fMenuItem, uMenuItem, L"Exit"))
1299 {
1300 SetForegroundWindow(hWnd);
1301
1302 const bool fBlockWhileTracking = true;
1303
1304 UINT fTrack = TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN;
1305
1306 if (fBlockWhileTracking)
1307 fTrack |= TPM_RETURNCMD | TPM_NONOTIFY;
1308
1309 uMsg = TrackPopupMenu(hContextMenu, fTrack, lpCursor.x, lpCursor.y, 0, hWnd, NULL);
1310 if ( uMsg
1311 && fBlockWhileTracking)
1312 {
1313 if (uMsg == uMenuItem)
1314 PostMessage(g_hwndToolWindow, WM_QUIT, 0, 0);
1315 }
1316 else if (!uMsg)
1317 LogFlowFunc(("Tracking popup menu failed with %ld\n", GetLastError()));
1318 }
1319
1320 DestroyMenu(hContextMenu);
1321 }
1322 }
1323 break;
1324 }
1325 }
1326 return 0;
1327 }
1328
1329 case WM_VBOX_SEAMLESS_ENABLE:
1330 {
1331 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1332 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1333 VBoxSeamlessCheckWindows(true);
1334 return 0;
1335 }
1336
1337 case WM_VBOX_SEAMLESS_DISABLE:
1338 {
1339 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1340 return 0;
1341 }
1342
1343 case WM_DISPLAYCHANGE:
1344 ASMAtomicUoWriteU32(&g_fGuestDisplaysChanged, 1);
1345 // No break or return is intentional here.
1346 case WM_VBOX_SEAMLESS_UPDATE:
1347 {
1348 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1349 VBoxSeamlessCheckWindows(true);
1350 return 0;
1351 }
1352
1353 case WM_VBOX_GRAPHICS_SUPPORTED:
1354 {
1355 VBoxGrapicsSetSupported(TRUE);
1356 return 0;
1357 }
1358
1359 case WM_VBOX_GRAPHICS_UNSUPPORTED:
1360 {
1361 VBoxGrapicsSetSupported(FALSE);
1362 return 0;
1363 }
1364
1365 case WM_WTSSESSION_CHANGE:
1366 {
1367 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
1368 if (vboxStHandleEvent(wParam))
1369 {
1370 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
1371 VBoxConsoleEnable(!fOldAllowedState);
1372 }
1373 return 0;
1374 }
1375
1376 default:
1377 {
1378 /* Handle all globally registered window messages. */
1379 if (vboxTrayHandleGlobalMessages(&g_vboxGlobalMessageTable[0], uMsg,
1380 wParam, lParam))
1381 {
1382 return 0; /* We handled the message. @todo Add return value!*/
1383 }
1384 break; /* We did not handle the message, dispatch to DefWndProc. */
1385 }
1386 }
1387
1388 /* Only if message was *not* handled by our switch above, dispatch to DefWindowProc. */
1389 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1390}
1391
1392/* St (session [state] tracking) functionality API impl */
1393
1394typedef struct VBOXST
1395{
1396 HWND hWTSAPIWnd;
1397 RTLDRMOD hLdrModWTSAPI32;
1398 BOOL fIsConsole;
1399 WTS_CONNECTSTATE_CLASS enmConnectState;
1400 UINT_PTR idDelayedInitTimer;
1401 BOOL (WINAPI * pfnWTSRegisterSessionNotification)(HWND hWnd, DWORD dwFlags);
1402 BOOL (WINAPI * pfnWTSUnRegisterSessionNotification)(HWND hWnd);
1403 BOOL (WINAPI * pfnWTSQuerySessionInformationA)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR *ppBuffer, DWORD *pBytesReturned);
1404} VBOXST;
1405
1406static VBOXST gVBoxSt;
1407
1408static int vboxStCheckState()
1409{
1410 int rc = VINF_SUCCESS;
1411 WTS_CONNECTSTATE_CLASS *penmConnectState = NULL;
1412 USHORT *pProtocolType = NULL;
1413 DWORD cbBuf = 0;
1414 if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState,
1415 (LPTSTR *)&penmConnectState, &cbBuf))
1416 {
1417 if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientProtocolType,
1418 (LPTSTR *)&pProtocolType, &cbBuf))
1419 {
1420 gVBoxSt.fIsConsole = (*pProtocolType == 0);
1421 gVBoxSt.enmConnectState = *penmConnectState;
1422 return VINF_SUCCESS;
1423 }
1424
1425 DWORD dwErr = GetLastError();
1426 LogFlowFunc(("WTSQuerySessionInformationA WTSClientProtocolType failed, error = %08X\n", dwErr));
1427 rc = RTErrConvertFromWin32(dwErr);
1428 }
1429 else
1430 {
1431 DWORD dwErr = GetLastError();
1432 LogFlowFunc(("WTSQuerySessionInformationA WTSConnectState failed, error = %08X\n", dwErr));
1433 rc = RTErrConvertFromWin32(dwErr);
1434 }
1435
1436 /* failure branch, set to "console-active" state */
1437 gVBoxSt.fIsConsole = TRUE;
1438 gVBoxSt.enmConnectState = WTSActive;
1439
1440 return rc;
1441}
1442
1443static int vboxStInit(HWND hWnd)
1444{
1445 RT_ZERO(gVBoxSt);
1446 int rc = RTLdrLoadSystem("WTSAPI32.DLL", false /*fNoUnload*/, &gVBoxSt.hLdrModWTSAPI32);
1447 if (RT_SUCCESS(rc))
1448 {
1449 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSRegisterSessionNotification",
1450 (void **)&gVBoxSt.pfnWTSRegisterSessionNotification);
1451 if (RT_SUCCESS(rc))
1452 {
1453 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSUnRegisterSessionNotification",
1454 (void **)&gVBoxSt.pfnWTSUnRegisterSessionNotification);
1455 if (RT_SUCCESS(rc))
1456 {
1457 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSQuerySessionInformationA",
1458 (void **)&gVBoxSt.pfnWTSQuerySessionInformationA);
1459 if (RT_FAILURE(rc))
1460 LogFlowFunc(("WTSQuerySessionInformationA not found\n"));
1461 }
1462 else
1463 LogFlowFunc(("WTSUnRegisterSessionNotification not found\n"));
1464 }
1465 else
1466 LogFlowFunc(("WTSRegisterSessionNotification not found\n"));
1467 if (RT_SUCCESS(rc))
1468 {
1469 gVBoxSt.hWTSAPIWnd = hWnd;
1470 if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION))
1471 vboxStCheckState();
1472 else
1473 {
1474 DWORD dwErr = GetLastError();
1475 LogFlowFunc(("WTSRegisterSessionNotification failed, error = %08X\n", dwErr));
1476 if (dwErr == RPC_S_INVALID_BINDING)
1477 {
1478 gVBoxSt.idDelayedInitTimer = SetTimer(gVBoxSt.hWTSAPIWnd, TIMERID_VBOXTRAY_ST_DELAYED_INIT_TIMER,
1479 2000, (TIMERPROC)NULL);
1480 gVBoxSt.fIsConsole = TRUE;
1481 gVBoxSt.enmConnectState = WTSActive;
1482 rc = VINF_SUCCESS;
1483 }
1484 else
1485 rc = RTErrConvertFromWin32(dwErr);
1486 }
1487
1488 if (RT_SUCCESS(rc))
1489 return VINF_SUCCESS;
1490 }
1491
1492 RTLdrClose(gVBoxSt.hLdrModWTSAPI32);
1493 }
1494 else
1495 LogFlowFunc(("WTSAPI32 load failed, rc = %Rrc\n", rc));
1496
1497 RT_ZERO(gVBoxSt);
1498 gVBoxSt.fIsConsole = TRUE;
1499 gVBoxSt.enmConnectState = WTSActive;
1500 return rc;
1501}
1502
1503static void vboxStTerm(void)
1504{
1505 if (!gVBoxSt.hWTSAPIWnd)
1506 {
1507 LogFlowFunc(("vboxStTerm called for non-initialized St\n"));
1508 return;
1509 }
1510
1511 if (gVBoxSt.idDelayedInitTimer)
1512 {
1513 /* notification is not registered, just kill timer */
1514 KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer);
1515 gVBoxSt.idDelayedInitTimer = 0;
1516 }
1517 else
1518 {
1519 if (!gVBoxSt.pfnWTSUnRegisterSessionNotification(gVBoxSt.hWTSAPIWnd))
1520 {
1521 LogFlowFunc(("WTSAPI32 load failed, error = %08X\n", GetLastError()));
1522 }
1523 }
1524
1525 RTLdrClose(gVBoxSt.hLdrModWTSAPI32);
1526 RT_ZERO(gVBoxSt);
1527}
1528
1529#define VBOXST_DBG_MAKECASE(_val) case _val: return #_val;
1530
1531static const char* vboxStDbgGetString(DWORD val)
1532{
1533 switch (val)
1534 {
1535 VBOXST_DBG_MAKECASE(WTS_CONSOLE_CONNECT);
1536 VBOXST_DBG_MAKECASE(WTS_CONSOLE_DISCONNECT);
1537 VBOXST_DBG_MAKECASE(WTS_REMOTE_CONNECT);
1538 VBOXST_DBG_MAKECASE(WTS_REMOTE_DISCONNECT);
1539 VBOXST_DBG_MAKECASE(WTS_SESSION_LOGON);
1540 VBOXST_DBG_MAKECASE(WTS_SESSION_LOGOFF);
1541 VBOXST_DBG_MAKECASE(WTS_SESSION_LOCK);
1542 VBOXST_DBG_MAKECASE(WTS_SESSION_UNLOCK);
1543 VBOXST_DBG_MAKECASE(WTS_SESSION_REMOTE_CONTROL);
1544 default:
1545 LogFlowFunc(("invalid WTS state %d\n", val));
1546 return "Unknown";
1547 }
1548}
1549
1550static BOOL vboxStCheckTimer(WPARAM wEvent)
1551{
1552 if (wEvent != gVBoxSt.idDelayedInitTimer)
1553 return FALSE;
1554
1555 if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION))
1556 {
1557 KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer);
1558 gVBoxSt.idDelayedInitTimer = 0;
1559 vboxStCheckState();
1560 }
1561 else
1562 {
1563 LogFlowFunc(("timer WTSRegisterSessionNotification failed, error = %08X\n", GetLastError()));
1564 Assert(gVBoxSt.fIsConsole == TRUE);
1565 Assert(gVBoxSt.enmConnectState == WTSActive);
1566 }
1567
1568 return TRUE;
1569}
1570
1571
1572static BOOL vboxStHandleEvent(WPARAM wEvent)
1573{
1574 RT_NOREF(wEvent);
1575 LogFlowFunc(("WTS Event: %s\n", vboxStDbgGetString(wEvent)));
1576 BOOL fOldIsActiveConsole = vboxStIsActiveConsole();
1577
1578 vboxStCheckState();
1579
1580 return !vboxStIsActiveConsole() != !fOldIsActiveConsole;
1581}
1582
1583static BOOL vboxStIsActiveConsole(void)
1584{
1585 return (gVBoxSt.enmConnectState == WTSActive && gVBoxSt.fIsConsole);
1586}
1587
1588/*
1589 * Dt (desktop [state] tracking) functionality API impl
1590 *
1591 * !!!NOTE: this API is NOT thread-safe!!!
1592 * */
1593
1594typedef struct VBOXDT
1595{
1596 HANDLE hNotifyEvent;
1597 BOOL fIsInputDesktop;
1598 UINT_PTR idTimer;
1599 RTLDRMOD hLdrModHook;
1600 BOOL (* pfnVBoxHookInstallActiveDesktopTracker)(HMODULE hDll);
1601 BOOL (* pfnVBoxHookRemoveActiveDesktopTracker)();
1602 HDESK (WINAPI * pfnGetThreadDesktop)(DWORD dwThreadId);
1603 HDESK (WINAPI * pfnOpenInputDesktop)(DWORD dwFlags, BOOL fInherit, ACCESS_MASK dwDesiredAccess);
1604 BOOL (WINAPI * pfnCloseDesktop)(HDESK hDesktop);
1605} VBOXDT;
1606
1607static VBOXDT gVBoxDt;
1608
1609static BOOL vboxDtCalculateIsInputDesktop()
1610{
1611 BOOL fIsInputDt = FALSE;
1612 HDESK hInput = gVBoxDt.pfnOpenInputDesktop(0, FALSE, DESKTOP_CREATEWINDOW);
1613 if (hInput)
1614 {
1615// DWORD dwThreadId = GetCurrentThreadId();
1616// HDESK hThreadDt = gVBoxDt.pfnGetThreadDesktop(dwThreadId);
1617// if (hThreadDt)
1618// {
1619 fIsInputDt = TRUE;
1620// }
1621// else
1622// {
1623// DWORD dwErr = GetLastError();
1624// LogFlowFunc(("pfnGetThreadDesktop for Seamless failed, last error = %08X\n", dwErr));
1625// }
1626
1627 gVBoxDt.pfnCloseDesktop(hInput);
1628 }
1629 else
1630 {
1631// DWORD dwErr = GetLastError();
1632// LogFlowFunc(("pfnOpenInputDesktop for Seamless failed, last error = %08X\n", dwErr));
1633 }
1634 return fIsInputDt;
1635}
1636
1637static BOOL vboxDtCheckTimer(WPARAM wParam)
1638{
1639 if (wParam != gVBoxDt.idTimer)
1640 return FALSE;
1641
1642 VBoxTrayCheckDt();
1643
1644 return TRUE;
1645}
1646
1647static int vboxDtInit()
1648{
1649 RT_ZERO(gVBoxDt);
1650
1651 int rc;
1652 gVBoxDt.hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, VBOXHOOK_GLOBAL_DT_EVENT_NAME);
1653 if (gVBoxDt.hNotifyEvent != NULL)
1654 {
1655 /* Load the hook dll and resolve the necessary entry points. */
1656 rc = RTLdrLoadAppPriv(VBOXHOOK_DLL_NAME, &gVBoxDt.hLdrModHook);
1657 if (RT_SUCCESS(rc))
1658 {
1659 rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookInstallActiveDesktopTracker",
1660 (void **)&gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker);
1661 if (RT_SUCCESS(rc))
1662 {
1663 rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookRemoveActiveDesktopTracker",
1664 (void **)&gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker);
1665 if (RT_FAILURE(rc))
1666 LogFlowFunc(("VBoxHookRemoveActiveDesktopTracker not found\n"));
1667 }
1668 else
1669 LogFlowFunc(("VBoxHookInstallActiveDesktopTracker not found\n"));
1670 if (RT_SUCCESS(rc))
1671 {
1672 /* Try get the system APIs we need. */
1673 *(void **)&gVBoxDt.pfnGetThreadDesktop = RTLdrGetSystemSymbol("user32.dll", "GetThreadDesktop");
1674 if (!gVBoxDt.pfnGetThreadDesktop)
1675 {
1676 LogFlowFunc(("GetThreadDesktop not found\n"));
1677 rc = VERR_NOT_SUPPORTED;
1678 }
1679
1680 *(void **)&gVBoxDt.pfnOpenInputDesktop = RTLdrGetSystemSymbol("user32.dll", "OpenInputDesktop");
1681 if (!gVBoxDt.pfnOpenInputDesktop)
1682 {
1683 LogFlowFunc(("OpenInputDesktop not found\n"));
1684 rc = VERR_NOT_SUPPORTED;
1685 }
1686
1687 *(void **)&gVBoxDt.pfnCloseDesktop = RTLdrGetSystemSymbol("user32.dll", "CloseDesktop");
1688 if (!gVBoxDt.pfnCloseDesktop)
1689 {
1690 LogFlowFunc(("CloseDesktop not found\n"));
1691 rc = VERR_NOT_SUPPORTED;
1692 }
1693
1694 if (RT_SUCCESS(rc))
1695 {
1696 BOOL fRc = FALSE;
1697 /* For Vista and up we need to change the integrity of the security descriptor, too. */
1698 uint64_t const uNtVersion = RTSystemGetNtVersion();
1699 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
1700 {
1701 HMODULE hModHook = (HMODULE)RTLdrGetNativeHandle(gVBoxDt.hLdrModHook);
1702 Assert((uintptr_t)hModHook != ~(uintptr_t)0);
1703 fRc = gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker(hModHook);
1704 if (!fRc)
1705 LogFlowFunc(("pfnVBoxHookInstallActiveDesktopTracker failed, last error = %08X\n", GetLastError()));
1706 }
1707
1708 if (!fRc)
1709 {
1710 gVBoxDt.idTimer = SetTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_DT_TIMER, 500, (TIMERPROC)NULL);
1711 if (!gVBoxDt.idTimer)
1712 {
1713 DWORD dwErr = GetLastError();
1714 LogFlowFunc(("SetTimer error %08X\n", dwErr));
1715 rc = RTErrConvertFromWin32(dwErr);
1716 }
1717 }
1718
1719 if (RT_SUCCESS(rc))
1720 {
1721 gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop();
1722 return VINF_SUCCESS;
1723 }
1724 }
1725 }
1726
1727 RTLdrClose(gVBoxDt.hLdrModHook);
1728 }
1729 else
1730 {
1731 DWORD dwErr = GetLastError();
1732 LogFlowFunc(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
1733 rc = RTErrConvertFromWin32(dwErr);
1734 }
1735
1736 CloseHandle(gVBoxDt.hNotifyEvent);
1737 }
1738 else
1739 {
1740 DWORD dwErr = GetLastError();
1741 LogFlowFunc(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
1742 rc = RTErrConvertFromWin32(dwErr);
1743 }
1744
1745
1746 RT_ZERO(gVBoxDt);
1747 gVBoxDt.fIsInputDesktop = TRUE;
1748
1749 return rc;
1750}
1751
1752static void vboxDtTerm()
1753{
1754 if (!gVBoxDt.hLdrModHook)
1755 return;
1756
1757 gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker();
1758
1759 RTLdrClose(gVBoxDt.hLdrModHook);
1760 CloseHandle(gVBoxDt.hNotifyEvent);
1761
1762 RT_ZERO(gVBoxDt);
1763}
1764/* @returns true on "IsInputDesktop" state change */
1765static BOOL vboxDtHandleEvent()
1766{
1767 BOOL fIsInputDesktop = gVBoxDt.fIsInputDesktop;
1768 gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop();
1769 return !fIsInputDesktop != !gVBoxDt.fIsInputDesktop;
1770}
1771
1772static HANDLE vboxDtGetNotifyEvent()
1773{
1774 return gVBoxDt.hNotifyEvent;
1775}
1776
1777/* @returns true iff the application (VBoxTray) desktop is input */
1778static BOOL vboxDtIsInputDesktop()
1779{
1780 return gVBoxDt.fIsInputDesktop;
1781}
1782
1783/* we need to perform Acquire/Release using the file handled we use for rewuesting events from VBoxGuest
1784 * otherwise Acquisition mechanism will treat us as different client and will not propagate necessary requests
1785 * */
1786static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg)
1787{
1788 Log(("VBoxAcquireGuestCaps or(0x%x), not(0x%x), cfx(%d)\n", fOr, fNot, fCfg));
1789 int rc = VbglR3AcquireGuestCaps(fOr, fNot, fCfg);
1790 if (RT_FAILURE(rc))
1791 LogFlowFunc(("VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed: %Rrc\n", rc));
1792 return rc;
1793}
1794
1795typedef enum VBOXCAPS_ENTRY_ACSTATE
1796{
1797 /* the given cap is released */
1798 VBOXCAPS_ENTRY_ACSTATE_RELEASED = 0,
1799 /* the given cap acquisition is in progress */
1800 VBOXCAPS_ENTRY_ACSTATE_ACQUIRING,
1801 /* the given cap is acquired */
1802 VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
1803} VBOXCAPS_ENTRY_ACSTATE;
1804
1805
1806struct VBOXCAPS_ENTRY;
1807struct VBOXCAPS;
1808
1809typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE,(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled));
1810
1811typedef struct VBOXCAPS_ENTRY
1812{
1813 uint32_t fCap;
1814 uint32_t iCap;
1815 VBOXCAPS_ENTRY_FUNCSTATE enmFuncState;
1816 VBOXCAPS_ENTRY_ACSTATE enmAcState;
1817 PFNVBOXCAPS_ENTRY_ON_ENABLE pfnOnEnable;
1818} VBOXCAPS_ENTRY;
1819
1820
1821typedef struct VBOXCAPS
1822{
1823 UINT_PTR idTimer;
1824 VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT];
1825} VBOXCAPS;
1826
1827static VBOXCAPS gVBoxCaps;
1828
1829static DECLCALLBACK(void) vboxCapsOnEnableSeamless(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled)
1830{
1831 RT_NOREF(pConsole, pCap);
1832 if (fEnabled)
1833 {
1834 Log(("vboxCapsOnEnableSeamless: ENABLED\n"));
1835 Assert(pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
1836 Assert(pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1837 VBoxSeamlessEnable();
1838 }
1839 else
1840 {
1841 Log(("vboxCapsOnEnableSeamless: DISABLED\n"));
1842 Assert(pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRED || pCap->enmFuncState != VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1843 VBoxSeamlessDisable();
1844 }
1845}
1846
1847static void vboxCapsEntryAcStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_ACSTATE enmAcState)
1848{
1849 VBOXCAPS *pConsole = &gVBoxCaps;
1850
1851 Log(("vboxCapsEntryAcStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
1852 enmAcState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
1853
1854 if (pCap->enmAcState == enmAcState)
1855 return;
1856
1857 VBOXCAPS_ENTRY_ACSTATE enmOldAcState = pCap->enmAcState;
1858 pCap->enmAcState = enmAcState;
1859
1860 if (enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
1861 {
1862 if (pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1863 {
1864 if (pCap->pfnOnEnable)
1865 pCap->pfnOnEnable(pConsole, pCap, TRUE);
1866 }
1867 }
1868 else if (enmOldAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1869 {
1870 if (pCap->pfnOnEnable)
1871 pCap->pfnOnEnable(pConsole, pCap, FALSE);
1872 }
1873}
1874
1875static void vboxCapsEntryFuncStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
1876{
1877 VBOXCAPS *pConsole = &gVBoxCaps;
1878
1879 Log(("vboxCapsEntryFuncStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
1880 enmFuncState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
1881
1882 if (pCap->enmFuncState == enmFuncState)
1883 return;
1884
1885 VBOXCAPS_ENTRY_FUNCSTATE enmOldFuncState = pCap->enmFuncState;
1886
1887 pCap->enmFuncState = enmFuncState;
1888
1889 if (enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1890 {
1891 Assert(enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1892 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
1893 {
1894 if (pCap->pfnOnEnable)
1895 pCap->pfnOnEnable(pConsole, pCap, TRUE);
1896 }
1897 }
1898 else if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1899 {
1900 if (pCap->pfnOnEnable)
1901 pCap->pfnOnEnable(pConsole, pCap, FALSE);
1902 }
1903}
1904
1905static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
1906{
1907 VBOXCAPS *pConsole = &gVBoxCaps;
1908 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCup];
1909 vboxCapsEntryFuncStateSet(pCap, enmFuncState);
1910}
1911
1912static int VBoxCapsInit()
1913{
1914 VBOXCAPS *pConsole = &gVBoxCaps;
1915 memset(pConsole, 0, sizeof (*pConsole));
1916 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].fCap = VMMDEV_GUEST_SUPPORTS_SEAMLESS;
1917 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].iCap = VBOXCAPS_ENTRY_IDX_SEAMLESS;
1918 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].pfnOnEnable = vboxCapsOnEnableSeamless;
1919 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].fCap = VMMDEV_GUEST_SUPPORTS_GRAPHICS;
1920 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].iCap = VBOXCAPS_ENTRY_IDX_GRAPHICS;
1921 return VINF_SUCCESS;
1922}
1923
1924static int VBoxCapsReleaseAll()
1925{
1926 VBOXCAPS *pConsole = &gVBoxCaps;
1927 Log(("VBoxCapsReleaseAll\n"));
1928 int rc = VBoxAcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, false);
1929 if (!RT_SUCCESS(rc))
1930 {
1931 LogFlowFunc(("vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc));
1932 return rc;
1933 }
1934
1935 if (pConsole->idTimer)
1936 {
1937 Log(("killing console timer\n"));
1938 KillTimer(g_hwndToolWindow, pConsole->idTimer);
1939 pConsole->idTimer = 0;
1940 }
1941
1942 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
1943 {
1944 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_RELEASED);
1945 }
1946
1947 return rc;
1948}
1949
1950static void VBoxCapsTerm()
1951{
1952 VBOXCAPS *pConsole = &gVBoxCaps;
1953 VBoxCapsReleaseAll();
1954 memset(pConsole, 0, sizeof (*pConsole));
1955}
1956
1957static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap)
1958{
1959 VBOXCAPS *pConsole = &gVBoxCaps;
1960 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED;
1961}
1962
1963static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap)
1964{
1965 VBOXCAPS *pConsole = &gVBoxCaps;
1966 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
1967 && pConsole->aCaps[iCap].enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED;
1968}
1969
1970static BOOL VBoxCapsCheckTimer(WPARAM wParam)
1971{
1972 VBOXCAPS *pConsole = &gVBoxCaps;
1973 if (wParam != pConsole->idTimer)
1974 return FALSE;
1975
1976 uint32_t u32AcquiredCaps = 0;
1977 BOOL fNeedNewTimer = FALSE;
1978
1979 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
1980 {
1981 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[i];
1982 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRING)
1983 continue;
1984
1985 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
1986 if (RT_SUCCESS(rc))
1987 {
1988 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
1989 u32AcquiredCaps |= pCap->fCap;
1990 }
1991 else
1992 {
1993 Assert(rc == VERR_RESOURCE_BUSY);
1994 fNeedNewTimer = TRUE;
1995 }
1996 }
1997
1998 if (!fNeedNewTimer)
1999 {
2000 KillTimer(g_hwndToolWindow, pConsole->idTimer);
2001 /* cleanup timer data */
2002 pConsole->idTimer = 0;
2003 }
2004
2005 return TRUE;
2006}
2007
2008static int VBoxCapsEntryRelease(uint32_t iCap)
2009{
2010 VBOXCAPS *pConsole = &gVBoxCaps;
2011 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
2012 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_RELEASED)
2013 {
2014 LogFlowFunc(("invalid cap[%d] state[%d] on release\n", iCap, pCap->enmAcState));
2015 return VERR_INVALID_STATE;
2016 }
2017
2018 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
2019 {
2020 int rc = VBoxAcquireGuestCaps(0, pCap->fCap, false);
2021 AssertRC(rc);
2022 }
2023
2024 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_RELEASED);
2025
2026 return VINF_SUCCESS;
2027}
2028
2029static int VBoxCapsEntryAcquire(uint32_t iCap)
2030{
2031 VBOXCAPS *pConsole = &gVBoxCaps;
2032 Assert(VBoxConsoleIsAllowed());
2033 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
2034 Log(("VBoxCapsEntryAcquire %d\n", iCap));
2035 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_RELEASED)
2036 {
2037 LogFlowFunc(("invalid cap[%d] state[%d] on acquire\n", iCap, pCap->enmAcState));
2038 return VERR_INVALID_STATE;
2039 }
2040
2041 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRING);
2042 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
2043 if (RT_SUCCESS(rc))
2044 {
2045 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
2046 return VINF_SUCCESS;
2047 }
2048
2049 if (rc != VERR_RESOURCE_BUSY)
2050 {
2051 LogFlowFunc(("vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc));
2052 return rc;
2053 }
2054
2055 LogFlowFunc(("iCap %d is busy!\n", iCap));
2056
2057 /* the cap was busy, most likely it is still used by other VBoxTray instance running in another session,
2058 * queue the retry timer */
2059 if (!pConsole->idTimer)
2060 {
2061 pConsole->idTimer = SetTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CAPS_TIMER, 100, (TIMERPROC)NULL);
2062 if (!pConsole->idTimer)
2063 {
2064 DWORD dwErr = GetLastError();
2065 LogFlowFunc(("SetTimer error %08X\n", dwErr));
2066 return RTErrConvertFromWin32(dwErr);
2067 }
2068 }
2069
2070 return rc;
2071}
2072
2073static int VBoxCapsAcquireAllSupported()
2074{
2075 VBOXCAPS *pConsole = &gVBoxCaps;
2076 Log(("VBoxCapsAcquireAllSupported\n"));
2077 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
2078 {
2079 if (pConsole->aCaps[i].enmFuncState >= VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED)
2080 {
2081 Log(("VBoxCapsAcquireAllSupported acquiring cap %d, state %d\n", i, pConsole->aCaps[i].enmFuncState));
2082 VBoxCapsEntryAcquire(i);
2083 }
2084 else
2085 {
2086 LogFlowFunc(("VBoxCapsAcquireAllSupported: WARN: cap %d not supported, state %d\n", i, pConsole->aCaps[i].enmFuncState));
2087 }
2088 }
2089 return VINF_SUCCESS;
2090}
2091
2092static BOOL VBoxConsoleIsAllowed()
2093{
2094 return vboxDtIsInputDesktop() && vboxStIsActiveConsole();
2095}
2096
2097static void VBoxConsoleEnable(BOOL fEnable)
2098{
2099 if (fEnable)
2100 VBoxCapsAcquireAllSupported();
2101 else
2102 VBoxCapsReleaseAll();
2103}
2104
2105static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported)
2106{
2107 if (fSupported)
2108 {
2109 VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
2110
2111 if (VBoxConsoleIsAllowed())
2112 VBoxCapsEntryAcquire(iCap);
2113 }
2114 else
2115 {
2116 VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED);
2117
2118 VBoxCapsEntryRelease(iCap);
2119 }
2120}
2121
2122void VBoxSeamlessSetSupported(BOOL fSupported)
2123{
2124 VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_SEAMLESS, fSupported);
2125}
2126
2127static void VBoxGrapicsSetSupported(BOOL fSupported)
2128{
2129 VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_GRAPHICS, fSupported);
2130}
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