VirtualBox

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

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

VBoxTray: Follow-up to r93636: Remove VRDP session handling.

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