VirtualBox

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

Last change on this file since 95863 was 95863, checked in by vboxsync, 3 years ago

VBoxTray/VBoxTray.cpp: Use RTStrPrintf instead of sprintf; in VBOX_WITH_NOCRT_STATIC we have main() function instead of WinMain. Left angry @todo about non-sensical argv handling. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 72.3 KB
Line 
1/* $Id: VBoxTray.cpp 95863 2022-07-27 01:10:09Z 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;
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 switch (g_cVerbosity) /* Not very elegant, but has to do it for now. */
592 {
593 case 1:
594 rc = RTLogGroupSettings(g_pLoggerRelease, "all.e.l");
595 break;
596
597 case 2:
598 rc = RTLogGroupSettings(g_pLoggerRelease, "all.e.l.l2");
599 break;
600
601 case 3:
602 rc = RTLogGroupSettings(g_pLoggerRelease, "all.e.l.l2.l3");
603 break;
604
605 case 4:
606 RT_FALL_THROUGH();
607 default:
608 rc = RTLogGroupSettings(g_pLoggerRelease, "all.e.l.l2.l3.f");
609 break;
610 }
611 if (RT_FAILURE(rc))
612 RTMsgError("Setting debug logging failed, rc=%Rrc\n", rc);
613
614 /* Explicitly flush the log in case of VBOXTRAY_RELEASE_LOG=buffered. */
615 RTLogFlush(g_pLoggerRelease);
616 }
617 else
618 VBoxTrayShowError(ErrInfo.szMsg);
619
620 return rc;
621}
622
623static void vboxTrayLogDestroy(void)
624{
625 /* Only want to destroy the release logger before calling exit(). The debug
626 logger can be useful after that point... */
627 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
628}
629
630/**
631 * Displays an error message.
632 *
633 * @returns RTEXITCODE_FAILURE.
634 * @param pszFormat The message text.
635 * @param ... Format arguments.
636 */
637RTEXITCODE VBoxTrayShowError(const char *pszFormat, ...)
638{
639 va_list args;
640 va_start(args, pszFormat);
641 char *psz = NULL;
642 RTStrAPrintfV(&psz, pszFormat, args);
643 va_end(args);
644
645 AssertPtr(psz);
646 LogRel(("Error: %s", psz));
647
648 MessageBox(GetDesktopWindow(), psz, "VBoxTray - Error", MB_OK | MB_ICONERROR);
649
650 RTStrFree(psz);
651
652 return RTEXITCODE_FAILURE;
653}
654
655static void vboxTrayDestroyToolWindow(void)
656{
657 if (g_hwndToolWindow)
658 {
659 Log(("Destroying tool window ...\n"));
660
661 /* Destroy the tool window. */
662 DestroyWindow(g_hwndToolWindow);
663 g_hwndToolWindow = NULL;
664
665 UnregisterClass("VBoxTrayToolWndClass", g_hInstance);
666 }
667}
668
669static int vboxTrayCreateToolWindow(void)
670{
671 DWORD dwErr = ERROR_SUCCESS;
672
673 /* Create a custom window class. */
674 WNDCLASSEX wc = { 0 };
675 wc.cbSize = sizeof(WNDCLASSEX);
676 wc.style = CS_NOCLOSE;
677 wc.lpfnWndProc = (WNDPROC)vboxToolWndProc;
678 wc.hInstance = g_hInstance;
679 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
680 wc.lpszClassName = "VBoxTrayToolWndClass";
681
682 if (!RegisterClassEx(&wc))
683 {
684 dwErr = GetLastError();
685 Log(("Registering invisible tool window failed, error = %08X\n", dwErr));
686 }
687 else
688 {
689 /*
690 * Create our (invisible) tool window.
691 * Note: The window name ("VBoxTrayToolWnd") and class ("VBoxTrayToolWndClass") is
692 * needed for posting globally registered messages to VBoxTray and must not be
693 * changed! Otherwise things get broken!
694 *
695 */
696 g_hwndToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
697 "VBoxTrayToolWndClass", "VBoxTrayToolWnd",
698 WS_POPUPWINDOW,
699 -200, -200, 100, 100, NULL, NULL, g_hInstance, NULL);
700 if (!g_hwndToolWindow)
701 {
702 dwErr = GetLastError();
703 Log(("Creating invisible tool window failed, error = %08X\n", dwErr));
704 }
705 else
706 {
707 /* Reload the cursor(s). */
708 hlpReloadCursor();
709
710 Log(("Invisible tool window handle = %p\n", g_hwndToolWindow));
711 }
712 }
713
714 if (dwErr != ERROR_SUCCESS)
715 vboxTrayDestroyToolWindow();
716 return RTErrConvertFromWin32(dwErr);
717}
718
719static int vboxTraySetupSeamless(void)
720{
721 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore. */
722 SECURITY_ATTRIBUTES SecAttr;
723 DWORD dwErr = ERROR_SUCCESS;
724 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
725 BOOL fRC;
726
727 SecAttr.nLength = sizeof(SecAttr);
728 SecAttr.bInheritHandle = FALSE;
729 SecAttr.lpSecurityDescriptor = &secDesc;
730 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
731 fRC = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
732 if (!fRC)
733 {
734 dwErr = GetLastError();
735 Log(("SetSecurityDescriptorDacl failed with last error = %08X\n", dwErr));
736 }
737 else
738 {
739 /* For Vista and up we need to change the integrity of the security descriptor, too. */
740 uint64_t const uNtVersion = RTSystemGetNtVersion();
741 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
742 {
743 BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize);
744 *(void **)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA =
745 RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorA");
746 Log(("pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %p\n",
747 RT_CB_LOG_CAST(pfnConvertStringSecurityDescriptorToSecurityDescriptorA)));
748 if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA)
749 {
750 PSECURITY_DESCRIPTOR pSD;
751 PACL pSacl = NULL;
752 BOOL fSaclPresent = FALSE;
753 BOOL fSaclDefaulted = FALSE;
754
755 fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */
756 SDDL_REVISION_1, &pSD, NULL);
757 if (!fRC)
758 {
759 dwErr = GetLastError();
760 Log(("ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr));
761 }
762 else
763 {
764 fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted);
765 if (!fRC)
766 {
767 dwErr = GetLastError();
768 Log(("GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
769 }
770 else
771 {
772 fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE);
773 if (!fRC)
774 {
775 dwErr = GetLastError();
776 Log(("SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
777 }
778 }
779 }
780 }
781 }
782
783 if ( dwErr == ERROR_SUCCESS
784 && uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Only for W2K and up ... */
785 {
786 g_hSeamlessWtNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_WT_EVENT_NAME);
787 if (g_hSeamlessWtNotifyEvent == NULL)
788 {
789 dwErr = GetLastError();
790 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
791 }
792
793 g_hSeamlessKmNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
794 if (g_hSeamlessKmNotifyEvent == NULL)
795 {
796 dwErr = GetLastError();
797 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
798 }
799 }
800 }
801 return RTErrConvertFromWin32(dwErr);
802}
803
804static void vboxTrayShutdownSeamless(void)
805{
806 if (g_hSeamlessWtNotifyEvent)
807 {
808 CloseHandle(g_hSeamlessWtNotifyEvent);
809 g_hSeamlessWtNotifyEvent = NULL;
810 }
811
812 if (g_hSeamlessKmNotifyEvent)
813 {
814 CloseHandle(g_hSeamlessKmNotifyEvent);
815 g_hSeamlessKmNotifyEvent = NULL;
816 }
817}
818
819static void VBoxTrayCheckDt()
820{
821 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
822 if (vboxDtHandleEvent())
823 {
824 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
825 VBoxConsoleEnable(!fOldAllowedState);
826 }
827}
828
829static int vboxTrayServiceMain(void)
830{
831 int rc = VINF_SUCCESS;
832 LogFunc(("Entering vboxTrayServiceMain\n"));
833
834 g_hStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
835 if (g_hStopSem == NULL)
836 {
837 rc = RTErrConvertFromWin32(GetLastError());
838 LogFunc(("CreateEvent for stopping VBoxTray failed, rc=%Rrc\n", rc));
839 }
840 else
841 {
842 /*
843 * Start services listed in the vboxServiceTable.
844 */
845 VBOXSERVICEENV svcEnv;
846 svcEnv.hInstance = g_hInstance;
847
848 /* Initializes disp-if to default (XPDM) mode. */
849 VBoxDispIfInit(&svcEnv.dispIf); /* Cannot fail atm. */
850 #ifdef VBOX_WITH_WDDM
851 /*
852 * For now the display mode will be adjusted to WDDM mode if needed
853 * on display service initialization when it detects the display driver type.
854 */
855 #endif
856
857 /* Finally start all the built-in services! */
858 rc = vboxTrayServicesStart(&svcEnv);
859 if (RT_FAILURE(rc))
860 {
861 /* Terminate service if something went wrong. */
862 vboxTrayServicesStop(&svcEnv);
863 }
864 else
865 {
866 uint64_t const uNtVersion = RTSystemGetNtVersion();
867 rc = vboxTrayCreateTrayIcon();
868 if ( RT_SUCCESS(rc)
869 && uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Only for W2K and up ... */
870 {
871 /* We're ready to create the tooltip balloon.
872 Check in 10 seconds (@todo make seconds configurable) ... */
873 SetTimer(g_hwndToolWindow,
874 TIMERID_VBOXTRAY_CHECK_HOSTVERSION,
875 10 * 1000, /* 10 seconds */
876 NULL /* No timerproc */);
877 }
878
879 if (RT_SUCCESS(rc))
880 {
881 /* Report the host that we're up and running! */
882 hlpReportStatus(VBoxGuestFacilityStatus_Active);
883 }
884
885 if (RT_SUCCESS(rc))
886 {
887 /* Boost thread priority to make sure we wake up early for seamless window notifications
888 * (not sure if it actually makes any difference though). */
889 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
890
891 /*
892 * Main execution loop
893 * Wait for the stop semaphore to be posted or a window event to arrive
894 */
895
896 HANDLE hWaitEvent[4] = {0};
897 DWORD dwEventCount = 0;
898
899 hWaitEvent[dwEventCount++] = g_hStopSem;
900
901 /* Check if seamless mode is not active and add seamless event to the list */
902 if (0 != g_hSeamlessWtNotifyEvent)
903 {
904 hWaitEvent[dwEventCount++] = g_hSeamlessWtNotifyEvent;
905 }
906
907 if (0 != g_hSeamlessKmNotifyEvent)
908 {
909 hWaitEvent[dwEventCount++] = g_hSeamlessKmNotifyEvent;
910 }
911
912 if (0 != vboxDtGetNotifyEvent())
913 {
914 hWaitEvent[dwEventCount++] = vboxDtGetNotifyEvent();
915 }
916
917 LogFlowFunc(("Number of events to wait in main loop: %ld\n", dwEventCount));
918 while (true)
919 {
920 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
921 waitResult = waitResult - WAIT_OBJECT_0;
922
923 /* Only enable for message debugging, lots of traffic! */
924 //Log(("Wait result = %ld\n", waitResult));
925
926 if (waitResult == 0)
927 {
928 LogFunc(("Event 'Exit' triggered\n"));
929 /* exit */
930 break;
931 }
932 else
933 {
934 BOOL fHandled = FALSE;
935 if (waitResult < RT_ELEMENTS(hWaitEvent))
936 {
937 if (hWaitEvent[waitResult])
938 {
939 if (hWaitEvent[waitResult] == g_hSeamlessWtNotifyEvent)
940 {
941 LogFunc(("Event 'Seamless' triggered\n"));
942
943 /* seamless window notification */
944 VBoxSeamlessCheckWindows(false);
945 fHandled = TRUE;
946 }
947 else if (hWaitEvent[waitResult] == g_hSeamlessKmNotifyEvent)
948 {
949 LogFunc(("Event 'Km Seamless' triggered\n"));
950
951 /* seamless window notification */
952 VBoxSeamlessCheckWindows(true);
953 fHandled = TRUE;
954 }
955 else if (hWaitEvent[waitResult] == vboxDtGetNotifyEvent())
956 {
957 LogFunc(("Event 'Dt' triggered\n"));
958 VBoxTrayCheckDt();
959 fHandled = TRUE;
960 }
961 }
962 }
963
964 if (!fHandled)
965 {
966 /* timeout or a window message, handle it */
967 MSG msg;
968 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
969 {
970#ifdef DEBUG_andy
971 LogFlowFunc(("PeekMessage %u\n", msg.message));
972#endif
973 if (msg.message == WM_QUIT)
974 {
975 LogFunc(("Terminating ...\n"));
976 SetEvent(g_hStopSem);
977 }
978 TranslateMessage(&msg);
979 DispatchMessage(&msg);
980 }
981 }
982 }
983 }
984 LogFunc(("Returned from main loop, exiting ...\n"));
985 }
986 LogFunc(("Waiting for services to stop ...\n"));
987 vboxTrayServicesStop(&svcEnv);
988 } /* Services started */
989 CloseHandle(g_hStopSem);
990 } /* Stop event created */
991
992 vboxTrayRemoveTrayIcon();
993
994 LogFunc(("Leaving with rc=%Rrc\n", rc));
995 return rc;
996}
997
998/**
999 * Main function
1000 */
1001#ifdef IPRT_NO_CRT
1002int main(int cArgs, char **papszArgs)
1003#else
1004int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
1005#endif
1006{
1007 char szLogFile[RTPATH_MAX] = {0};
1008
1009#ifdef IPRT_NO_CRT
1010 int rc = RTR3InitExe(cArgs, &papszArgs, RTR3INIT_FLAGS_STANDALONE_APP);
1011 if (RT_FAILURE(rc))
1012 return RTMsgInitFailure(rc);
1013#else
1014 RT_NOREF(hPrevInstance, lpCmdLine, nCmdShow);
1015 /** @todo r=bird: WTF do you use __argc & __argv here only to parse the
1016 * command line furthe down?!? Makes no effing sense, espcially given
1017 * that RTR3InitExe will return valid UTF-8, doing exactly the
1018 * same stuff as you do here.
1019 * aaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAARG! */
1020 int rc = RTR3InitExe(__argc, &__argv, RTR3INIT_FLAGS_STANDALONE_APP);
1021 if (RT_FAILURE(rc))
1022 return RTMsgInitFailure(rc);
1023
1024 LPWSTR pwszCmdLine = GetCommandLineW();
1025 if (!pwszCmdLine)
1026 return RTMsgErrorExit(RTEXITCODE_FAILURE, "GetCommandLineW failed");
1027
1028 char *pszCmdLine;
1029 rc = RTUtf16ToUtf8(pwszCmdLine, &pszCmdLine); /* leaked */
1030 if (RT_FAILURE(rc))
1031 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to convert the command line: %Rrc", rc);
1032
1033 int cArgs;
1034 char **papszArgs;
1035 rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszCmdLine, RTGETOPTARGV_CNV_QUOTE_MS_CRT, NULL);
1036 if (RT_SUCCESS(rc))
1037#endif
1038 {
1039 /*
1040 * Parse the top level arguments until we find a command.
1041 */
1042 static const RTGETOPTDEF s_aOptions[] =
1043 {
1044 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1045 { "-help", 'h', RTGETOPT_REQ_NOTHING },
1046 { "/help", 'h', RTGETOPT_REQ_NOTHING },
1047 { "/?", 'h', RTGETOPT_REQ_NOTHING },
1048 { "--logfile", 'l', RTGETOPT_REQ_STRING },
1049 { "-l", 'l', RTGETOPT_REQ_STRING },
1050 { "/l", 'l', RTGETOPT_REQ_STRING },
1051 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1052 { "-v", 'v', RTGETOPT_REQ_NOTHING },
1053 { "/v", 'v', RTGETOPT_REQ_NOTHING },
1054 { "--version", 'V', RTGETOPT_REQ_NOTHING },
1055 { "-version", 'V', RTGETOPT_REQ_NOTHING },
1056 { "/version", 'V', RTGETOPT_REQ_NOTHING },
1057 { "/V", 'V', RTGETOPT_REQ_NOTHING }
1058 };
1059
1060 RTGETOPTSTATE GetState;
1061 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
1062 if (RT_FAILURE(rc))
1063 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
1064
1065 int ch;
1066 RTGETOPTUNION ValueUnion;
1067 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
1068 {
1069 switch (ch)
1070 {
1071 case 'h':
1072 hlpShowMessageBox(VBOX_VBOXTRAY_TITLE, MB_ICONINFORMATION, "-- %s v%u.%u.%ur%u --\n"
1073 "\n"
1074 "Command Line Parameters:\n\n"
1075 "-l, --logfile <file>\n"
1076 " Enables logging to a file\n"
1077 "-v, --verbose\n"
1078 " Increases verbosity\n"
1079 "-V, --version\n"
1080 " Displays version number and exit\n"
1081 "-?, -h, --help\n"
1082 " Displays this help text and exit\n"
1083 "\n"
1084 "Examples:\n"
1085 " %s -vvv\n",
1086 VBOX_VBOXTRAY_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV,
1087 papszArgs[0], papszArgs[0]);
1088 return RTEXITCODE_SUCCESS;
1089
1090 case 'l':
1091 if (*ValueUnion.psz == '\0')
1092 szLogFile[0] = '\0';
1093 else
1094 {
1095 rc = RTPathAbs(ValueUnion.psz, szLogFile, sizeof(szLogFile));
1096 if (RT_FAILURE(rc))
1097 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on log file path: %Rrc (%s)",
1098 rc, ValueUnion.psz);
1099 }
1100 break;
1101
1102 case 'v':
1103 g_cVerbosity++;
1104 break;
1105
1106 case 'V':
1107 hlpShowMessageBox(VBOX_VBOXTRAY_TITLE, MB_ICONINFORMATION,
1108 "Version: %u.%u.%ur%u",
1109 VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
1110 return RTEXITCODE_SUCCESS;
1111
1112 default:
1113 rc = RTGetOptPrintError(ch, &ValueUnion);
1114 break;
1115 }
1116 }
1117
1118 RTGetOptArgvFree(papszArgs);
1119 }
1120#ifndef IPRT_NO_CRT
1121 else
1122 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptArgvFromString failed: %Rrc", rc);
1123#endif
1124
1125 /* Note: Do not use a global namespace ("Global\\") for mutex name here,
1126 * will blow up NT4 compatibility! */
1127 HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, VBOX_VBOXTRAY_TITLE);
1128 if ( hMutexAppRunning != NULL
1129 && GetLastError() == ERROR_ALREADY_EXISTS)
1130 {
1131 /* VBoxTray already running? Bail out. */
1132 CloseHandle (hMutexAppRunning);
1133 hMutexAppRunning = NULL;
1134 return RTEXITCODE_SUCCESS;
1135 }
1136
1137 rc = vboxTrayLogCreate(szLogFile[0] ? szLogFile : NULL);
1138 if (RT_SUCCESS(rc))
1139 {
1140 LogRel(("Verbosity level: %d\n", g_cVerbosity));
1141
1142 rc = VbglR3Init();
1143 if (RT_SUCCESS(rc))
1144 {
1145 /* Log the major windows NT version: */
1146 uint64_t const uNtVersion = RTSystemGetNtVersion();
1147 LogRel(("Windows version %u.%u build %u (uNtVersion=%#RX64)\n", RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVersion),
1148 RTSYSTEM_NT_VERSION_GET_MINOR(uNtVersion), RTSYSTEM_NT_VERSION_GET_BUILD(uNtVersion), uNtVersion ));
1149
1150 /* Set the instance handle. */
1151#ifdef IPRT_NO_CRT
1152 g_hInstance = GetModuleHandleW(NULL);
1153#else
1154 g_hInstance = hInstance;
1155#endif
1156
1157 hlpReportStatus(VBoxGuestFacilityStatus_Init);
1158 rc = vboxTrayCreateToolWindow();
1159 if (RT_SUCCESS(rc))
1160 {
1161 VBoxCapsInit();
1162
1163 rc = vboxStInit(g_hwndToolWindow);
1164 if (!RT_SUCCESS(rc))
1165 {
1166 LogFlowFunc(("vboxStInit failed, rc=%Rrc\n", rc));
1167 /* ignore the St Init failure. this can happen for < XP win that do not support WTS API
1168 * in that case the session is treated as active connected to the physical console
1169 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1170 Assert(vboxStIsActiveConsole());
1171 }
1172
1173 rc = vboxDtInit();
1174 if (!RT_SUCCESS(rc))
1175 {
1176 LogFlowFunc(("vboxDtInit failed, rc=%Rrc\n", rc));
1177 /* ignore the Dt Init failure. this can happen for < XP win that do not support WTS API
1178 * in that case the session is treated as active connected to the physical console
1179 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1180 Assert(vboxDtIsInputDesktop());
1181 }
1182
1183 rc = VBoxAcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, true);
1184 if (!RT_SUCCESS(rc))
1185 LogFlowFunc(("VBoxAcquireGuestCaps failed with rc=%Rrc, ignoring ...\n", rc));
1186
1187 rc = vboxTraySetupSeamless(); /** @todo r=andy Do we really want to be this critical for the whole application? */
1188 if (RT_SUCCESS(rc))
1189 {
1190 rc = vboxTrayServiceMain();
1191 if (RT_SUCCESS(rc))
1192 hlpReportStatus(VBoxGuestFacilityStatus_Terminating);
1193 vboxTrayShutdownSeamless();
1194 }
1195
1196 /* it should be safe to call vboxDtTerm even if vboxStInit above failed */
1197 vboxDtTerm();
1198
1199 /* it should be safe to call vboxStTerm even if vboxStInit above failed */
1200 vboxStTerm();
1201
1202 VBoxCapsTerm();
1203
1204 vboxTrayDestroyToolWindow();
1205 }
1206 if (RT_SUCCESS(rc))
1207 hlpReportStatus(VBoxGuestFacilityStatus_Terminated);
1208 else
1209 {
1210 LogRel(("Error while starting, rc=%Rrc\n", rc));
1211 hlpReportStatus(VBoxGuestFacilityStatus_Failed);
1212 }
1213
1214 LogRel(("Ended\n"));
1215 VbglR3Term();
1216 }
1217 else
1218 LogRel(("VbglR3Init failed: %Rrc\n", rc));
1219 }
1220
1221 /* Release instance mutex. */
1222 if (hMutexAppRunning != NULL)
1223 {
1224 CloseHandle(hMutexAppRunning);
1225 hMutexAppRunning = NULL;
1226 }
1227
1228 vboxTrayLogDestroy();
1229
1230 return RT_SUCCESS(rc) ? 0 : 1;
1231}
1232
1233/**
1234 * Window procedure for our main tool window.
1235 */
1236static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1237{
1238 LogFlowFunc(("hWnd=%p, uMsg=%u\n", hWnd, uMsg));
1239
1240 switch (uMsg)
1241 {
1242 case WM_CREATE:
1243 {
1244 LogFunc(("Tool window created\n"));
1245
1246 int rc = vboxTrayRegisterGlobalMessages(&g_vboxGlobalMessageTable[0]);
1247 if (RT_FAILURE(rc))
1248 LogFunc(("Error registering global window messages, rc=%Rrc\n", rc));
1249 return 0;
1250 }
1251
1252 case WM_CLOSE:
1253 return 0;
1254
1255 case WM_DESTROY:
1256 {
1257 LogFunc(("Tool window destroyed\n"));
1258 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1259 return 0;
1260 }
1261
1262 case WM_TIMER:
1263 {
1264 if (VBoxCapsCheckTimer(wParam))
1265 return 0;
1266 if (vboxDtCheckTimer(wParam))
1267 return 0;
1268 if (vboxStCheckTimer(wParam))
1269 return 0;
1270
1271 switch (wParam)
1272 {
1273 case TIMERID_VBOXTRAY_CHECK_HOSTVERSION:
1274 if (RT_SUCCESS(VBoxCheckHostVersion()))
1275 {
1276 /* After successful run we don't need to check again. */
1277 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1278 }
1279 return 0;
1280
1281 default:
1282 break;
1283 }
1284
1285 break; /* Make sure other timers get processed the usual way! */
1286 }
1287
1288 case WM_VBOXTRAY_TRAY_ICON:
1289 {
1290 switch (LOWORD(lParam))
1291 {
1292 case WM_LBUTTONDBLCLK:
1293 break;
1294 case WM_RBUTTONDOWN:
1295 {
1296 if (!g_cVerbosity) /* Don't show menu when running in non-verbose mode. */
1297 break;
1298
1299 POINT lpCursor;
1300 if (GetCursorPos(&lpCursor))
1301 {
1302 HMENU hContextMenu = CreatePopupMenu();
1303 if (hContextMenu)
1304 {
1305 UINT_PTR uMenuItem = 9999;
1306 UINT fMenuItem = MF_BYPOSITION | MF_STRING;
1307 if (InsertMenuW(hContextMenu, UINT_MAX, fMenuItem, uMenuItem, L"Exit"))
1308 {
1309 SetForegroundWindow(hWnd);
1310
1311 const bool fBlockWhileTracking = true;
1312
1313 UINT fTrack = TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN;
1314
1315 if (fBlockWhileTracking)
1316 fTrack |= TPM_RETURNCMD | TPM_NONOTIFY;
1317
1318 uMsg = TrackPopupMenu(hContextMenu, fTrack, lpCursor.x, lpCursor.y, 0, hWnd, NULL);
1319 if ( uMsg
1320 && fBlockWhileTracking)
1321 {
1322 if (uMsg == uMenuItem)
1323 PostMessage(g_hwndToolWindow, WM_QUIT, 0, 0);
1324 }
1325 else if (!uMsg)
1326 LogFlowFunc(("Tracking popup menu failed with %ld\n", GetLastError()));
1327 }
1328
1329 DestroyMenu(hContextMenu);
1330 }
1331 }
1332 break;
1333 }
1334 }
1335 return 0;
1336 }
1337
1338 case WM_VBOX_SEAMLESS_ENABLE:
1339 {
1340 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1341 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1342 VBoxSeamlessCheckWindows(true);
1343 return 0;
1344 }
1345
1346 case WM_VBOX_SEAMLESS_DISABLE:
1347 {
1348 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1349 return 0;
1350 }
1351
1352 case WM_DISPLAYCHANGE:
1353 ASMAtomicUoWriteU32(&g_fGuestDisplaysChanged, 1);
1354 // No break or return is intentional here.
1355 case WM_VBOX_SEAMLESS_UPDATE:
1356 {
1357 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1358 VBoxSeamlessCheckWindows(true);
1359 return 0;
1360 }
1361
1362 case WM_VBOX_GRAPHICS_SUPPORTED:
1363 {
1364 VBoxGrapicsSetSupported(TRUE);
1365 return 0;
1366 }
1367
1368 case WM_VBOX_GRAPHICS_UNSUPPORTED:
1369 {
1370 VBoxGrapicsSetSupported(FALSE);
1371 return 0;
1372 }
1373
1374 case WM_WTSSESSION_CHANGE:
1375 {
1376 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
1377 if (vboxStHandleEvent(wParam))
1378 {
1379 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
1380 VBoxConsoleEnable(!fOldAllowedState);
1381 }
1382 return 0;
1383 }
1384
1385 default:
1386 {
1387 /* Handle all globally registered window messages. */
1388 if (vboxTrayHandleGlobalMessages(&g_vboxGlobalMessageTable[0], uMsg,
1389 wParam, lParam))
1390 {
1391 return 0; /* We handled the message. @todo Add return value!*/
1392 }
1393 break; /* We did not handle the message, dispatch to DefWndProc. */
1394 }
1395 }
1396
1397 /* Only if message was *not* handled by our switch above, dispatch to DefWindowProc. */
1398 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1399}
1400
1401/* St (session [state] tracking) functionality API impl */
1402
1403typedef struct VBOXST
1404{
1405 HWND hWTSAPIWnd;
1406 RTLDRMOD hLdrModWTSAPI32;
1407 BOOL fIsConsole;
1408 WTS_CONNECTSTATE_CLASS enmConnectState;
1409 UINT_PTR idDelayedInitTimer;
1410 BOOL (WINAPI * pfnWTSRegisterSessionNotification)(HWND hWnd, DWORD dwFlags);
1411 BOOL (WINAPI * pfnWTSUnRegisterSessionNotification)(HWND hWnd);
1412 BOOL (WINAPI * pfnWTSQuerySessionInformationA)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR *ppBuffer, DWORD *pBytesReturned);
1413} VBOXST;
1414
1415static VBOXST gVBoxSt;
1416
1417static int vboxStCheckState()
1418{
1419 int rc = VINF_SUCCESS;
1420 WTS_CONNECTSTATE_CLASS *penmConnectState = NULL;
1421 USHORT *pProtocolType = NULL;
1422 DWORD cbBuf = 0;
1423 if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState,
1424 (LPTSTR *)&penmConnectState, &cbBuf))
1425 {
1426 if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientProtocolType,
1427 (LPTSTR *)&pProtocolType, &cbBuf))
1428 {
1429 gVBoxSt.fIsConsole = (*pProtocolType == 0);
1430 gVBoxSt.enmConnectState = *penmConnectState;
1431 return VINF_SUCCESS;
1432 }
1433
1434 DWORD dwErr = GetLastError();
1435 LogFlowFunc(("WTSQuerySessionInformationA WTSClientProtocolType failed, error = %08X\n", dwErr));
1436 rc = RTErrConvertFromWin32(dwErr);
1437 }
1438 else
1439 {
1440 DWORD dwErr = GetLastError();
1441 LogFlowFunc(("WTSQuerySessionInformationA WTSConnectState failed, error = %08X\n", dwErr));
1442 rc = RTErrConvertFromWin32(dwErr);
1443 }
1444
1445 /* failure branch, set to "console-active" state */
1446 gVBoxSt.fIsConsole = TRUE;
1447 gVBoxSt.enmConnectState = WTSActive;
1448
1449 return rc;
1450}
1451
1452static int vboxStInit(HWND hWnd)
1453{
1454 RT_ZERO(gVBoxSt);
1455 int rc = RTLdrLoadSystem("WTSAPI32.DLL", false /*fNoUnload*/, &gVBoxSt.hLdrModWTSAPI32);
1456 if (RT_SUCCESS(rc))
1457 {
1458 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSRegisterSessionNotification",
1459 (void **)&gVBoxSt.pfnWTSRegisterSessionNotification);
1460 if (RT_SUCCESS(rc))
1461 {
1462 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSUnRegisterSessionNotification",
1463 (void **)&gVBoxSt.pfnWTSUnRegisterSessionNotification);
1464 if (RT_SUCCESS(rc))
1465 {
1466 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSQuerySessionInformationA",
1467 (void **)&gVBoxSt.pfnWTSQuerySessionInformationA);
1468 if (RT_FAILURE(rc))
1469 LogFlowFunc(("WTSQuerySessionInformationA not found\n"));
1470 }
1471 else
1472 LogFlowFunc(("WTSUnRegisterSessionNotification not found\n"));
1473 }
1474 else
1475 LogFlowFunc(("WTSRegisterSessionNotification not found\n"));
1476 if (RT_SUCCESS(rc))
1477 {
1478 gVBoxSt.hWTSAPIWnd = hWnd;
1479 if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION))
1480 vboxStCheckState();
1481 else
1482 {
1483 DWORD dwErr = GetLastError();
1484 LogFlowFunc(("WTSRegisterSessionNotification failed, error = %08X\n", dwErr));
1485 if (dwErr == RPC_S_INVALID_BINDING)
1486 {
1487 gVBoxSt.idDelayedInitTimer = SetTimer(gVBoxSt.hWTSAPIWnd, TIMERID_VBOXTRAY_ST_DELAYED_INIT_TIMER,
1488 2000, (TIMERPROC)NULL);
1489 gVBoxSt.fIsConsole = TRUE;
1490 gVBoxSt.enmConnectState = WTSActive;
1491 rc = VINF_SUCCESS;
1492 }
1493 else
1494 rc = RTErrConvertFromWin32(dwErr);
1495 }
1496
1497 if (RT_SUCCESS(rc))
1498 return VINF_SUCCESS;
1499 }
1500
1501 RTLdrClose(gVBoxSt.hLdrModWTSAPI32);
1502 }
1503 else
1504 LogFlowFunc(("WTSAPI32 load failed, rc = %Rrc\n", rc));
1505
1506 RT_ZERO(gVBoxSt);
1507 gVBoxSt.fIsConsole = TRUE;
1508 gVBoxSt.enmConnectState = WTSActive;
1509 return rc;
1510}
1511
1512static void vboxStTerm(void)
1513{
1514 if (!gVBoxSt.hWTSAPIWnd)
1515 {
1516 LogFlowFunc(("vboxStTerm called for non-initialized St\n"));
1517 return;
1518 }
1519
1520 if (gVBoxSt.idDelayedInitTimer)
1521 {
1522 /* notification is not registered, just kill timer */
1523 KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer);
1524 gVBoxSt.idDelayedInitTimer = 0;
1525 }
1526 else
1527 {
1528 if (!gVBoxSt.pfnWTSUnRegisterSessionNotification(gVBoxSt.hWTSAPIWnd))
1529 {
1530 LogFlowFunc(("WTSAPI32 load failed, error = %08X\n", GetLastError()));
1531 }
1532 }
1533
1534 RTLdrClose(gVBoxSt.hLdrModWTSAPI32);
1535 RT_ZERO(gVBoxSt);
1536}
1537
1538#define VBOXST_DBG_MAKECASE(_val) case _val: return #_val;
1539
1540static const char* vboxStDbgGetString(DWORD val)
1541{
1542 switch (val)
1543 {
1544 VBOXST_DBG_MAKECASE(WTS_CONSOLE_CONNECT);
1545 VBOXST_DBG_MAKECASE(WTS_CONSOLE_DISCONNECT);
1546 VBOXST_DBG_MAKECASE(WTS_REMOTE_CONNECT);
1547 VBOXST_DBG_MAKECASE(WTS_REMOTE_DISCONNECT);
1548 VBOXST_DBG_MAKECASE(WTS_SESSION_LOGON);
1549 VBOXST_DBG_MAKECASE(WTS_SESSION_LOGOFF);
1550 VBOXST_DBG_MAKECASE(WTS_SESSION_LOCK);
1551 VBOXST_DBG_MAKECASE(WTS_SESSION_UNLOCK);
1552 VBOXST_DBG_MAKECASE(WTS_SESSION_REMOTE_CONTROL);
1553 default:
1554 LogFlowFunc(("invalid WTS state %d\n", val));
1555 return "Unknown";
1556 }
1557}
1558
1559static BOOL vboxStCheckTimer(WPARAM wEvent)
1560{
1561 if (wEvent != gVBoxSt.idDelayedInitTimer)
1562 return FALSE;
1563
1564 if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION))
1565 {
1566 KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer);
1567 gVBoxSt.idDelayedInitTimer = 0;
1568 vboxStCheckState();
1569 }
1570 else
1571 {
1572 LogFlowFunc(("timer WTSRegisterSessionNotification failed, error = %08X\n", GetLastError()));
1573 Assert(gVBoxSt.fIsConsole == TRUE);
1574 Assert(gVBoxSt.enmConnectState == WTSActive);
1575 }
1576
1577 return TRUE;
1578}
1579
1580
1581static BOOL vboxStHandleEvent(WPARAM wEvent)
1582{
1583 RT_NOREF(wEvent);
1584 LogFlowFunc(("WTS Event: %s\n", vboxStDbgGetString(wEvent)));
1585 BOOL fOldIsActiveConsole = vboxStIsActiveConsole();
1586
1587 vboxStCheckState();
1588
1589 return !vboxStIsActiveConsole() != !fOldIsActiveConsole;
1590}
1591
1592static BOOL vboxStIsActiveConsole(void)
1593{
1594 return (gVBoxSt.enmConnectState == WTSActive && gVBoxSt.fIsConsole);
1595}
1596
1597/*
1598 * Dt (desktop [state] tracking) functionality API impl
1599 *
1600 * !!!NOTE: this API is NOT thread-safe!!!
1601 * */
1602
1603typedef struct VBOXDT
1604{
1605 HANDLE hNotifyEvent;
1606 BOOL fIsInputDesktop;
1607 UINT_PTR idTimer;
1608 RTLDRMOD hLdrModHook;
1609 BOOL (* pfnVBoxHookInstallActiveDesktopTracker)(HMODULE hDll);
1610 BOOL (* pfnVBoxHookRemoveActiveDesktopTracker)();
1611 HDESK (WINAPI * pfnGetThreadDesktop)(DWORD dwThreadId);
1612 HDESK (WINAPI * pfnOpenInputDesktop)(DWORD dwFlags, BOOL fInherit, ACCESS_MASK dwDesiredAccess);
1613 BOOL (WINAPI * pfnCloseDesktop)(HDESK hDesktop);
1614} VBOXDT;
1615
1616static VBOXDT gVBoxDt;
1617
1618static BOOL vboxDtCalculateIsInputDesktop()
1619{
1620 BOOL fIsInputDt = FALSE;
1621 HDESK hInput = gVBoxDt.pfnOpenInputDesktop(0, FALSE, DESKTOP_CREATEWINDOW);
1622 if (hInput)
1623 {
1624// DWORD dwThreadId = GetCurrentThreadId();
1625// HDESK hThreadDt = gVBoxDt.pfnGetThreadDesktop(dwThreadId);
1626// if (hThreadDt)
1627// {
1628 fIsInputDt = TRUE;
1629// }
1630// else
1631// {
1632// DWORD dwErr = GetLastError();
1633// LogFlowFunc(("pfnGetThreadDesktop for Seamless failed, last error = %08X\n", dwErr));
1634// }
1635
1636 gVBoxDt.pfnCloseDesktop(hInput);
1637 }
1638 else
1639 {
1640// DWORD dwErr = GetLastError();
1641// LogFlowFunc(("pfnOpenInputDesktop for Seamless failed, last error = %08X\n", dwErr));
1642 }
1643 return fIsInputDt;
1644}
1645
1646static BOOL vboxDtCheckTimer(WPARAM wParam)
1647{
1648 if (wParam != gVBoxDt.idTimer)
1649 return FALSE;
1650
1651 VBoxTrayCheckDt();
1652
1653 return TRUE;
1654}
1655
1656static int vboxDtInit()
1657{
1658 RT_ZERO(gVBoxDt);
1659
1660 int rc;
1661 gVBoxDt.hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, VBOXHOOK_GLOBAL_DT_EVENT_NAME);
1662 if (gVBoxDt.hNotifyEvent != NULL)
1663 {
1664 /* Load the hook dll and resolve the necessary entry points. */
1665 rc = RTLdrLoadAppPriv(VBOXHOOK_DLL_NAME, &gVBoxDt.hLdrModHook);
1666 if (RT_SUCCESS(rc))
1667 {
1668 rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookInstallActiveDesktopTracker",
1669 (void **)&gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker);
1670 if (RT_SUCCESS(rc))
1671 {
1672 rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookRemoveActiveDesktopTracker",
1673 (void **)&gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker);
1674 if (RT_FAILURE(rc))
1675 LogFlowFunc(("VBoxHookRemoveActiveDesktopTracker not found\n"));
1676 }
1677 else
1678 LogFlowFunc(("VBoxHookInstallActiveDesktopTracker not found\n"));
1679 if (RT_SUCCESS(rc))
1680 {
1681 /* Try get the system APIs we need. */
1682 *(void **)&gVBoxDt.pfnGetThreadDesktop = RTLdrGetSystemSymbol("user32.dll", "GetThreadDesktop");
1683 if (!gVBoxDt.pfnGetThreadDesktop)
1684 {
1685 LogFlowFunc(("GetThreadDesktop not found\n"));
1686 rc = VERR_NOT_SUPPORTED;
1687 }
1688
1689 *(void **)&gVBoxDt.pfnOpenInputDesktop = RTLdrGetSystemSymbol("user32.dll", "OpenInputDesktop");
1690 if (!gVBoxDt.pfnOpenInputDesktop)
1691 {
1692 LogFlowFunc(("OpenInputDesktop not found\n"));
1693 rc = VERR_NOT_SUPPORTED;
1694 }
1695
1696 *(void **)&gVBoxDt.pfnCloseDesktop = RTLdrGetSystemSymbol("user32.dll", "CloseDesktop");
1697 if (!gVBoxDt.pfnCloseDesktop)
1698 {
1699 LogFlowFunc(("CloseDesktop not found\n"));
1700 rc = VERR_NOT_SUPPORTED;
1701 }
1702
1703 if (RT_SUCCESS(rc))
1704 {
1705 BOOL fRc = FALSE;
1706 /* For Vista and up we need to change the integrity of the security descriptor, too. */
1707 uint64_t const uNtVersion = RTSystemGetNtVersion();
1708 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
1709 {
1710 HMODULE hModHook = (HMODULE)RTLdrGetNativeHandle(gVBoxDt.hLdrModHook);
1711 Assert((uintptr_t)hModHook != ~(uintptr_t)0);
1712 fRc = gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker(hModHook);
1713 if (!fRc)
1714 LogFlowFunc(("pfnVBoxHookInstallActiveDesktopTracker failed, last error = %08X\n", GetLastError()));
1715 }
1716
1717 if (!fRc)
1718 {
1719 gVBoxDt.idTimer = SetTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_DT_TIMER, 500, (TIMERPROC)NULL);
1720 if (!gVBoxDt.idTimer)
1721 {
1722 DWORD dwErr = GetLastError();
1723 LogFlowFunc(("SetTimer error %08X\n", dwErr));
1724 rc = RTErrConvertFromWin32(dwErr);
1725 }
1726 }
1727
1728 if (RT_SUCCESS(rc))
1729 {
1730 gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop();
1731 return VINF_SUCCESS;
1732 }
1733 }
1734 }
1735
1736 RTLdrClose(gVBoxDt.hLdrModHook);
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 CloseHandle(gVBoxDt.hNotifyEvent);
1746 }
1747 else
1748 {
1749 DWORD dwErr = GetLastError();
1750 LogFlowFunc(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
1751 rc = RTErrConvertFromWin32(dwErr);
1752 }
1753
1754
1755 RT_ZERO(gVBoxDt);
1756 gVBoxDt.fIsInputDesktop = TRUE;
1757
1758 return rc;
1759}
1760
1761static void vboxDtTerm()
1762{
1763 if (!gVBoxDt.hLdrModHook)
1764 return;
1765
1766 gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker();
1767
1768 RTLdrClose(gVBoxDt.hLdrModHook);
1769 CloseHandle(gVBoxDt.hNotifyEvent);
1770
1771 RT_ZERO(gVBoxDt);
1772}
1773/* @returns true on "IsInputDesktop" state change */
1774static BOOL vboxDtHandleEvent()
1775{
1776 BOOL fIsInputDesktop = gVBoxDt.fIsInputDesktop;
1777 gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop();
1778 return !fIsInputDesktop != !gVBoxDt.fIsInputDesktop;
1779}
1780
1781static HANDLE vboxDtGetNotifyEvent()
1782{
1783 return gVBoxDt.hNotifyEvent;
1784}
1785
1786/* @returns true iff the application (VBoxTray) desktop is input */
1787static BOOL vboxDtIsInputDesktop()
1788{
1789 return gVBoxDt.fIsInputDesktop;
1790}
1791
1792/* we need to perform Acquire/Release using the file handled we use for rewuesting events from VBoxGuest
1793 * otherwise Acquisition mechanism will treat us as different client and will not propagate necessary requests
1794 * */
1795static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg)
1796{
1797 Log(("VBoxAcquireGuestCaps or(0x%x), not(0x%x), cfx(%d)\n", fOr, fNot, fCfg));
1798 int rc = VbglR3AcquireGuestCaps(fOr, fNot, fCfg);
1799 if (RT_FAILURE(rc))
1800 LogFlowFunc(("VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed: %Rrc\n", rc));
1801 return rc;
1802}
1803
1804typedef enum VBOXCAPS_ENTRY_ACSTATE
1805{
1806 /* the given cap is released */
1807 VBOXCAPS_ENTRY_ACSTATE_RELEASED = 0,
1808 /* the given cap acquisition is in progress */
1809 VBOXCAPS_ENTRY_ACSTATE_ACQUIRING,
1810 /* the given cap is acquired */
1811 VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
1812} VBOXCAPS_ENTRY_ACSTATE;
1813
1814
1815struct VBOXCAPS_ENTRY;
1816struct VBOXCAPS;
1817
1818typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE,(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled));
1819
1820typedef struct VBOXCAPS_ENTRY
1821{
1822 uint32_t fCap;
1823 uint32_t iCap;
1824 VBOXCAPS_ENTRY_FUNCSTATE enmFuncState;
1825 VBOXCAPS_ENTRY_ACSTATE enmAcState;
1826 PFNVBOXCAPS_ENTRY_ON_ENABLE pfnOnEnable;
1827} VBOXCAPS_ENTRY;
1828
1829
1830typedef struct VBOXCAPS
1831{
1832 UINT_PTR idTimer;
1833 VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT];
1834} VBOXCAPS;
1835
1836static VBOXCAPS gVBoxCaps;
1837
1838static DECLCALLBACK(void) vboxCapsOnEnableSeamless(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled)
1839{
1840 RT_NOREF(pConsole, pCap);
1841 if (fEnabled)
1842 {
1843 Log(("vboxCapsOnEnableSeamless: ENABLED\n"));
1844 Assert(pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
1845 Assert(pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1846 VBoxSeamlessEnable();
1847 }
1848 else
1849 {
1850 Log(("vboxCapsOnEnableSeamless: DISABLED\n"));
1851 Assert(pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRED || pCap->enmFuncState != VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1852 VBoxSeamlessDisable();
1853 }
1854}
1855
1856static void vboxCapsEntryAcStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_ACSTATE enmAcState)
1857{
1858 VBOXCAPS *pConsole = &gVBoxCaps;
1859
1860 Log(("vboxCapsEntryAcStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
1861 enmAcState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
1862
1863 if (pCap->enmAcState == enmAcState)
1864 return;
1865
1866 VBOXCAPS_ENTRY_ACSTATE enmOldAcState = pCap->enmAcState;
1867 pCap->enmAcState = enmAcState;
1868
1869 if (enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
1870 {
1871 if (pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1872 {
1873 if (pCap->pfnOnEnable)
1874 pCap->pfnOnEnable(pConsole, pCap, TRUE);
1875 }
1876 }
1877 else if (enmOldAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1878 {
1879 if (pCap->pfnOnEnable)
1880 pCap->pfnOnEnable(pConsole, pCap, FALSE);
1881 }
1882}
1883
1884static void vboxCapsEntryFuncStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
1885{
1886 VBOXCAPS *pConsole = &gVBoxCaps;
1887
1888 Log(("vboxCapsEntryFuncStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
1889 enmFuncState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
1890
1891 if (pCap->enmFuncState == enmFuncState)
1892 return;
1893
1894 VBOXCAPS_ENTRY_FUNCSTATE enmOldFuncState = pCap->enmFuncState;
1895
1896 pCap->enmFuncState = enmFuncState;
1897
1898 if (enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1899 {
1900 Assert(enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1901 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
1902 {
1903 if (pCap->pfnOnEnable)
1904 pCap->pfnOnEnable(pConsole, pCap, TRUE);
1905 }
1906 }
1907 else if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1908 {
1909 if (pCap->pfnOnEnable)
1910 pCap->pfnOnEnable(pConsole, pCap, FALSE);
1911 }
1912}
1913
1914static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
1915{
1916 VBOXCAPS *pConsole = &gVBoxCaps;
1917 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCup];
1918 vboxCapsEntryFuncStateSet(pCap, enmFuncState);
1919}
1920
1921static int VBoxCapsInit()
1922{
1923 VBOXCAPS *pConsole = &gVBoxCaps;
1924 memset(pConsole, 0, sizeof (*pConsole));
1925 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].fCap = VMMDEV_GUEST_SUPPORTS_SEAMLESS;
1926 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].iCap = VBOXCAPS_ENTRY_IDX_SEAMLESS;
1927 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].pfnOnEnable = vboxCapsOnEnableSeamless;
1928 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].fCap = VMMDEV_GUEST_SUPPORTS_GRAPHICS;
1929 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].iCap = VBOXCAPS_ENTRY_IDX_GRAPHICS;
1930 return VINF_SUCCESS;
1931}
1932
1933static int VBoxCapsReleaseAll()
1934{
1935 VBOXCAPS *pConsole = &gVBoxCaps;
1936 Log(("VBoxCapsReleaseAll\n"));
1937 int rc = VBoxAcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, false);
1938 if (!RT_SUCCESS(rc))
1939 {
1940 LogFlowFunc(("vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc));
1941 return rc;
1942 }
1943
1944 if (pConsole->idTimer)
1945 {
1946 Log(("killing console timer\n"));
1947 KillTimer(g_hwndToolWindow, pConsole->idTimer);
1948 pConsole->idTimer = 0;
1949 }
1950
1951 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
1952 {
1953 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_RELEASED);
1954 }
1955
1956 return rc;
1957}
1958
1959static void VBoxCapsTerm()
1960{
1961 VBOXCAPS *pConsole = &gVBoxCaps;
1962 VBoxCapsReleaseAll();
1963 memset(pConsole, 0, sizeof (*pConsole));
1964}
1965
1966static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap)
1967{
1968 VBOXCAPS *pConsole = &gVBoxCaps;
1969 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED;
1970}
1971
1972static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap)
1973{
1974 VBOXCAPS *pConsole = &gVBoxCaps;
1975 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
1976 && pConsole->aCaps[iCap].enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED;
1977}
1978
1979static BOOL VBoxCapsCheckTimer(WPARAM wParam)
1980{
1981 VBOXCAPS *pConsole = &gVBoxCaps;
1982 if (wParam != pConsole->idTimer)
1983 return FALSE;
1984
1985 uint32_t u32AcquiredCaps = 0;
1986 BOOL fNeedNewTimer = FALSE;
1987
1988 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
1989 {
1990 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[i];
1991 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRING)
1992 continue;
1993
1994 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
1995 if (RT_SUCCESS(rc))
1996 {
1997 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
1998 u32AcquiredCaps |= pCap->fCap;
1999 }
2000 else
2001 {
2002 Assert(rc == VERR_RESOURCE_BUSY);
2003 fNeedNewTimer = TRUE;
2004 }
2005 }
2006
2007 if (!fNeedNewTimer)
2008 {
2009 KillTimer(g_hwndToolWindow, pConsole->idTimer);
2010 /* cleanup timer data */
2011 pConsole->idTimer = 0;
2012 }
2013
2014 return TRUE;
2015}
2016
2017static int VBoxCapsEntryRelease(uint32_t iCap)
2018{
2019 VBOXCAPS *pConsole = &gVBoxCaps;
2020 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
2021 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_RELEASED)
2022 {
2023 LogFlowFunc(("invalid cap[%d] state[%d] on release\n", iCap, pCap->enmAcState));
2024 return VERR_INVALID_STATE;
2025 }
2026
2027 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
2028 {
2029 int rc = VBoxAcquireGuestCaps(0, pCap->fCap, false);
2030 AssertRC(rc);
2031 }
2032
2033 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_RELEASED);
2034
2035 return VINF_SUCCESS;
2036}
2037
2038static int VBoxCapsEntryAcquire(uint32_t iCap)
2039{
2040 VBOXCAPS *pConsole = &gVBoxCaps;
2041 Assert(VBoxConsoleIsAllowed());
2042 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
2043 Log(("VBoxCapsEntryAcquire %d\n", iCap));
2044 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_RELEASED)
2045 {
2046 LogFlowFunc(("invalid cap[%d] state[%d] on acquire\n", iCap, pCap->enmAcState));
2047 return VERR_INVALID_STATE;
2048 }
2049
2050 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRING);
2051 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
2052 if (RT_SUCCESS(rc))
2053 {
2054 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
2055 return VINF_SUCCESS;
2056 }
2057
2058 if (rc != VERR_RESOURCE_BUSY)
2059 {
2060 LogFlowFunc(("vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc));
2061 return rc;
2062 }
2063
2064 LogFlowFunc(("iCap %d is busy!\n", iCap));
2065
2066 /* the cap was busy, most likely it is still used by other VBoxTray instance running in another session,
2067 * queue the retry timer */
2068 if (!pConsole->idTimer)
2069 {
2070 pConsole->idTimer = SetTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CAPS_TIMER, 100, (TIMERPROC)NULL);
2071 if (!pConsole->idTimer)
2072 {
2073 DWORD dwErr = GetLastError();
2074 LogFlowFunc(("SetTimer error %08X\n", dwErr));
2075 return RTErrConvertFromWin32(dwErr);
2076 }
2077 }
2078
2079 return rc;
2080}
2081
2082static int VBoxCapsAcquireAllSupported()
2083{
2084 VBOXCAPS *pConsole = &gVBoxCaps;
2085 Log(("VBoxCapsAcquireAllSupported\n"));
2086 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
2087 {
2088 if (pConsole->aCaps[i].enmFuncState >= VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED)
2089 {
2090 Log(("VBoxCapsAcquireAllSupported acquiring cap %d, state %d\n", i, pConsole->aCaps[i].enmFuncState));
2091 VBoxCapsEntryAcquire(i);
2092 }
2093 else
2094 {
2095 LogFlowFunc(("VBoxCapsAcquireAllSupported: WARN: cap %d not supported, state %d\n", i, pConsole->aCaps[i].enmFuncState));
2096 }
2097 }
2098 return VINF_SUCCESS;
2099}
2100
2101static BOOL VBoxConsoleIsAllowed()
2102{
2103 return vboxDtIsInputDesktop() && vboxStIsActiveConsole();
2104}
2105
2106static void VBoxConsoleEnable(BOOL fEnable)
2107{
2108 if (fEnable)
2109 VBoxCapsAcquireAllSupported();
2110 else
2111 VBoxCapsReleaseAll();
2112}
2113
2114static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported)
2115{
2116 if (fSupported)
2117 {
2118 VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
2119
2120 if (VBoxConsoleIsAllowed())
2121 VBoxCapsEntryAcquire(iCap);
2122 }
2123 else
2124 {
2125 VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED);
2126
2127 VBoxCapsEntryRelease(iCap);
2128 }
2129}
2130
2131void VBoxSeamlessSetSupported(BOOL fSupported)
2132{
2133 VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_SEAMLESS, fSupported);
2134}
2135
2136static void VBoxGrapicsSetSupported(BOOL fSupported)
2137{
2138 VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_GRAPHICS, fSupported);
2139}
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