VirtualBox

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

Last change on this file since 78425 was 77557, checked in by vboxsync, 6 years ago

IPRT: Moved the RTLogSetGroupLimit() functionality into the RTLogCreateEx() call, adding an env var for overriding it (suffix '_MAX_PER_GROUP', e.g. 'export VBOX_RELEASE_LOG_MAX_PER_GROUP=0' for no limit).

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