VirtualBox

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

Last change on this file since 50752 was 49891, checked in by vboxsync, 11 years ago

Merged private draganddrop branch into trunk.

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