VirtualBox

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

Last change on this file since 62865 was 62865, checked in by vboxsync, 8 years ago

3D: report guest screen position to the host when the user rearranges screens in the guest. bugref:8450

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