VirtualBox

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

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

VBoxTray: Fix for icon regression from r135890 (Artwork/win/TemplateExe.rc).

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