VirtualBox

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

Last change on this file since 60526 was 60526, checked in by vboxsync, 9 years ago

bugref:8250: fix

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

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