VirtualBox

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

Last change on this file since 84720 was 84720, checked in by vboxsync, 5 years ago

Guest Additions/VBoxTray: Be more verbose when a certain service does not start; added more documentation about how to treat service starting failures.

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