VirtualBox

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

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

VBoxTray/seamless: more fixes

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