VirtualBox

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

Last change on this file since 78476 was 78466, checked in by vboxsync, 6 years ago

VBoxTray: Shared folder automounting is done by VBoxService since 6.0, removing the VBoxTray code.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette