VirtualBox

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

Last change on this file since 46196 was 46196, checked in by vboxsync, 12 years ago

VBoxGuest/VBoxTray: allow enabling caps acquisition mode w/o acquiring caps

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

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