VirtualBox

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

Last change on this file since 51287 was 51224, checked in by vboxsync, 11 years ago

Additions/VBoxGuest: remove VRDP session handling (never really used), cleanup

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