VirtualBox

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

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

Additions: bugref:8622: Use crc of user name in name of ipc pipe

  • 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 64285 2016-10-16 20:08:03Z 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);
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 g_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 RT_NOREF(wParam, lParam);
198 return vboxTrayCreateTrayIcon();
199}
200
201static int vboxTrayCreateTrayIcon(void)
202{
203 HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_VIRTUALBOX));
204 if (hIcon == NULL)
205 {
206 DWORD dwErr = GetLastError();
207 LogFunc(("Could not load tray icon, error %08X\n", dwErr));
208 return RTErrConvertFromWin32(dwErr);
209 }
210
211 /* Prepare the system tray icon. */
212 RT_ZERO(g_NotifyIconData);
213 g_NotifyIconData.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA);
214 g_NotifyIconData.hWnd = g_hwndToolWindow;
215 g_NotifyIconData.uID = ID_TRAYICON;
216 g_NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
217 g_NotifyIconData.uCallbackMessage = WM_VBOXTRAY_TRAY_ICON;
218 g_NotifyIconData.hIcon = hIcon;
219
220 sprintf(g_NotifyIconData.szTip, "%s Guest Additions %d.%d.%dr%d",
221 VBOX_PRODUCT, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
222
223 int rc = VINF_SUCCESS;
224 if (!Shell_NotifyIcon(NIM_ADD, &g_NotifyIconData))
225 {
226 DWORD dwErr = GetLastError();
227 LogFunc(("Could not create tray icon, error=%ld\n", dwErr));
228 rc = RTErrConvertFromWin32(dwErr);
229 RT_ZERO(g_NotifyIconData);
230 }
231
232 if (hIcon)
233 DestroyIcon(hIcon);
234 return rc;
235}
236
237static void vboxTrayRemoveTrayIcon(void)
238{
239 if (g_NotifyIconData.cbSize > 0)
240 {
241 /* Remove the system tray icon and refresh system tray. */
242 Shell_NotifyIcon(NIM_DELETE, &g_NotifyIconData);
243 HWND hTrayWnd = FindWindow("Shell_TrayWnd", NULL); /* We assume we only have one tray atm. */
244 if (hTrayWnd)
245 {
246 HWND hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", NULL);
247 if (hTrayNotifyWnd)
248 SendMessage(hTrayNotifyWnd, WM_PAINT, 0, NULL);
249 }
250 RT_ZERO(g_NotifyIconData);
251 }
252}
253
254/**
255 * The service thread.
256 *
257 * @returns Whatever the worker function returns.
258 * @param ThreadSelf My thread handle.
259 * @param pvUser The service index.
260 */
261static DECLCALLBACK(int) vboxTrayServiceThread(RTTHREAD ThreadSelf, void *pvUser)
262{
263 PVBOXSERVICEINFO pSvc = (PVBOXSERVICEINFO)pvUser;
264 AssertPtr(pSvc);
265
266#ifndef RT_OS_WINDOWS
267 /*
268 * Block all signals for this thread. Only the main thread will handle signals.
269 */
270 sigset_t signalMask;
271 sigfillset(&signalMask);
272 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
273#endif
274
275 int rc = pSvc->pDesc->pfnWorker(pSvc->pInstance, &pSvc->fShutdown);
276 ASMAtomicXchgBool(&pSvc->fShutdown, true);
277 RTThreadUserSignal(ThreadSelf);
278
279 LogFunc(("Worker for '%s' ended with %Rrc\n", pSvc->pDesc->pszName, rc));
280 return rc;
281}
282
283static int vboxTrayServicesStart(PVBOXSERVICEENV pEnv)
284{
285 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
286
287 LogRel(("Starting services ...\n"));
288
289 int rc = VINF_SUCCESS;
290
291 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
292 {
293 PVBOXSERVICEINFO pSvc = &g_aServices[i];
294 LogRel(("Starting service '%s' ...\n", pSvc->pDesc->pszName));
295
296 pSvc->hThread = NIL_RTTHREAD;
297 pSvc->pInstance = NULL;
298 pSvc->fStarted = false;
299 pSvc->fShutdown = false;
300
301 int rc2 = VINF_SUCCESS;
302
303 if (pSvc->pDesc->pfnInit)
304 rc2 = pSvc->pDesc->pfnInit(pEnv, &pSvc->pInstance);
305
306 if (RT_FAILURE(rc2))
307 {
308 LogRel(("Failed to initialize service '%s', rc=%Rrc\n", pSvc->pDesc->pszName, rc2));
309 if (rc2 == VERR_NOT_SUPPORTED)
310 LogRel(("Service '%s' is not supported on this system\n", pSvc->pDesc->pszName));
311 rc2 = VINF_SUCCESS;
312 /* Keep going. */
313 }
314 else
315 {
316 if (pSvc->pDesc->pfnWorker)
317 {
318 rc2 = RTThreadCreate(&pSvc->hThread, vboxTrayServiceThread, pSvc /* pvUser */,
319 0 /* Default stack size */, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, pSvc->pDesc->pszName);
320 if (RT_SUCCESS(rc2))
321 {
322 pSvc->fStarted = true;
323
324 RTThreadUserWait(pSvc->hThread, 30 * 1000 /* Timeout in ms */);
325 if (pSvc->fShutdown)
326 {
327 LogRel(("Service '%s' failed to start!\n", pSvc->pDesc->pszName));
328 rc = VERR_GENERAL_FAILURE;
329 }
330 else
331 LogRel(("Service '%s' started\n", pSvc->pDesc->pszName));
332 }
333 else
334 {
335 LogRel(("Failed to start thread for service '%s': %Rrc\n", rc2));
336 if (pSvc->pDesc->pfnDestroy)
337 pSvc->pDesc->pfnDestroy(pSvc->pInstance);
338 }
339 }
340 }
341
342 if (RT_SUCCESS(rc))
343 rc = rc2;
344 }
345
346 if (RT_SUCCESS(rc))
347 LogRel(("All services started\n"));
348 else
349 LogRel(("Services started, but some with errors\n"));
350
351 LogFlowFuncLeaveRC(rc);
352 return rc;
353}
354
355static int vboxTrayServicesStop(VBOXSERVICEENV *pEnv)
356{
357 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
358
359 LogRel2(("Stopping all services ...\n"));
360
361 /*
362 * Signal all the services.
363 */
364 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
365 ASMAtomicWriteBool(&g_aServices[i].fShutdown, true);
366
367 /*
368 * Do the pfnStop callback on all running services.
369 */
370 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
371 {
372 PVBOXSERVICEINFO pSvc = &g_aServices[i];
373 if ( pSvc->fStarted
374 && pSvc->pDesc->pfnStop)
375 {
376 LogRel2(("Calling stop function for service '%s' ...\n", pSvc->pDesc->pszName));
377 int rc2 = pSvc->pDesc->pfnStop(pSvc->pInstance);
378 if (RT_FAILURE(rc2))
379 LogRel(("Failed to stop service '%s': %Rrc\n", pSvc->pDesc->pszName, rc2));
380 }
381 }
382
383 LogRel2(("All stop functions for services called\n"));
384
385 int rc = VINF_SUCCESS;
386
387 /*
388 * Wait for all the service threads to complete.
389 */
390 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
391 {
392 PVBOXSERVICEINFO pSvc = &g_aServices[i];
393 if (!pSvc->fEnabled) /* Only stop services which were started before. */
394 continue;
395
396 if (pSvc->hThread != NIL_RTTHREAD)
397 {
398 LogRel2(("Waiting for service '%s' to stop ...\n", pSvc->pDesc->pszName));
399 int rc2 = VINF_SUCCESS;
400 for (int j = 0; j < 30; j++) /* Wait 30 seconds in total */
401 {
402 rc2 = RTThreadWait(pSvc->hThread, 1000 /* Wait 1 second */, NULL);
403 if (RT_SUCCESS(rc2))
404 break;
405 }
406 if (RT_FAILURE(rc2))
407 {
408 LogRel(("Service '%s' failed to stop (%Rrc)\n", pSvc->pDesc->pszName, rc2));
409 if (RT_SUCCESS(rc))
410 rc = rc2;
411 }
412 }
413
414 if (pSvc->pDesc->pfnDestroy)
415 {
416 LogRel2(("Terminating service '%s' ...\n", pSvc->pDesc->pszName));
417 pSvc->pDesc->pfnDestroy(pSvc->pInstance);
418 }
419 }
420
421 if (RT_SUCCESS(rc))
422 LogRel(("All services stopped\n"));
423
424 LogFlowFuncLeaveRC(rc);
425 return rc;
426}
427
428static int vboxTrayRegisterGlobalMessages(PVBOXGLOBALMESSAGE pTable)
429{
430 int rc = VINF_SUCCESS;
431 if (pTable == NULL) /* No table to register? Skip. */
432 return rc;
433 while ( pTable->pszName
434 && RT_SUCCESS(rc))
435 {
436 /* Register global accessible window messages. */
437 pTable->uMsgID = RegisterWindowMessage(TEXT(pTable->pszName));
438 if (!pTable->uMsgID)
439 {
440 DWORD dwErr = GetLastError();
441 Log(("Registering global message \"%s\" failed, error = %08X\n", dwErr));
442 rc = RTErrConvertFromWin32(dwErr);
443 }
444
445 /* Advance to next table element. */
446 pTable++;
447 }
448 return rc;
449}
450
451static bool vboxTrayHandleGlobalMessages(PVBOXGLOBALMESSAGE pTable, UINT uMsg,
452 WPARAM wParam, LPARAM lParam)
453{
454 if (pTable == NULL)
455 return false;
456 while (pTable && pTable->pszName)
457 {
458 if (pTable->uMsgID == uMsg)
459 {
460 if (pTable->pfnHandler)
461 pTable->pfnHandler(wParam, lParam);
462 return true;
463 }
464
465 /* Advance to next table element. */
466 pTable++;
467 }
468 return false;
469}
470
471static int vboxTrayOpenBaseDriver(void)
472{
473 /* Open VBox guest driver. */
474 DWORD dwErr = ERROR_SUCCESS;
475 g_hVBoxDriver = CreateFile(VBOXGUEST_DEVICE_NAME,
476 GENERIC_READ | GENERIC_WRITE,
477 FILE_SHARE_READ | FILE_SHARE_WRITE,
478 NULL,
479 OPEN_EXISTING,
480 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
481 NULL);
482 if (g_hVBoxDriver == INVALID_HANDLE_VALUE)
483 {
484 dwErr = GetLastError();
485 LogRel(("Could not open VirtualBox Guest Additions driver! Please install / start it first! Error = %08X\n", dwErr));
486 }
487 return RTErrConvertFromWin32(dwErr);
488}
489
490static void vboxTrayCloseBaseDriver(void)
491{
492 if (g_hVBoxDriver)
493 {
494 CloseHandle(g_hVBoxDriver);
495 g_hVBoxDriver = NULL;
496 }
497}
498
499/**
500 * Release logger callback.
501 *
502 * @return IPRT status code.
503 * @param pLoggerRelease
504 * @param enmPhase
505 * @param pfnLog
506 */
507static void vboxTrayLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
508{
509 /* Some introductory information. */
510 static RTTIMESPEC s_TimeSpec;
511 char szTmp[256];
512 if (enmPhase == RTLOGPHASE_BEGIN)
513 RTTimeNow(&s_TimeSpec);
514 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
515
516 switch (enmPhase)
517 {
518 case RTLOGPHASE_BEGIN:
519 {
520 pfnLog(pLoggerRelease,
521 "VBoxTray %s r%s %s (%s %s) release log\n"
522 "Log opened %s\n",
523 RTBldCfgVersion(), RTBldCfgRevisionStr(), VBOX_BUILD_TARGET,
524 __DATE__, __TIME__, szTmp);
525
526 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
527 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
528 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
529 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
530 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
531 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
532 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
533 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
534 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
535 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
536 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
537
538 /* the package type is interesting for Linux distributions */
539 char szExecName[RTPATH_MAX];
540 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
541 pfnLog(pLoggerRelease,
542 "Executable: %s\n"
543 "Process ID: %u\n"
544 "Package type: %s"
545#ifdef VBOX_OSE
546 " (OSE)"
547#endif
548 "\n",
549 pszExecName ? pszExecName : "unknown",
550 RTProcSelf(),
551 VBOX_PACKAGE_STRING);
552 break;
553 }
554
555 case RTLOGPHASE_PREROTATE:
556 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
557 break;
558
559 case RTLOGPHASE_POSTROTATE:
560 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
561 break;
562
563 case RTLOGPHASE_END:
564 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
565 break;
566
567 default:
568 /* nothing */;
569 }
570}
571
572/**
573 * Creates the default release logger outputting to the specified file.
574 * Pass NULL for disabled logging.
575 *
576 * @return IPRT status code.
577 * @param pszLogFile Filename for log output. Optional.
578 */
579static int vboxTrayLogCreate(const char *pszLogFile)
580{
581 /* Create release logger (stdout + file). */
582 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
583 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
584#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
585 fFlags |= RTLOGFLAGS_USECRLF;
586#endif
587 char szError[RTPATH_MAX + 128] = "";
588 int rc = RTLogCreateEx(&g_pLoggerRelease, fFlags,
589#ifdef DEBUG
590 "all.e.l.f",
591 "VBOXTRAY_LOG",
592#else
593 "all",
594 "VBOXTRAY_RELEASE_LOG",
595#endif
596 RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_STDOUT,
597 vboxTrayLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
598 szError, sizeof(szError), pszLogFile);
599 if (RT_SUCCESS(rc))
600 {
601#ifdef DEBUG
602 RTLogSetDefaultInstance(g_pLoggerRelease);
603#else
604 /* Register this logger as the release logger. */
605 RTLogRelSetDefaultInstance(g_pLoggerRelease);
606#endif
607 /* Explicitly flush the log in case of VBOXTRAY_RELEASE_LOG=buffered. */
608 RTLogFlush(g_pLoggerRelease);
609 }
610 else
611 MessageBox(GetDesktopWindow(),
612 szError, "VBoxTray - Logging Error", MB_OK | MB_ICONERROR);
613
614 return rc;
615}
616
617static void vboxTrayLogDestroy(void)
618{
619 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
620}
621
622static void vboxTrayDestroyToolWindow(void)
623{
624 if (g_hwndToolWindow)
625 {
626 Log(("Destroying tool window ...\n"));
627
628 /* Destroy the tool window. */
629 DestroyWindow(g_hwndToolWindow);
630 g_hwndToolWindow = NULL;
631
632 UnregisterClass("VBoxTrayToolWndClass", g_hInstance);
633 }
634}
635
636static int vboxTrayCreateToolWindow(void)
637{
638 DWORD dwErr = ERROR_SUCCESS;
639
640 /* Create a custom window class. */
641 WNDCLASSEX wc = { 0 };
642 wc.cbSize = sizeof(WNDCLASSEX);
643 wc.style = CS_NOCLOSE;
644 wc.lpfnWndProc = (WNDPROC)vboxToolWndProc;
645 wc.hInstance = g_hInstance;
646 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
647 wc.lpszClassName = "VBoxTrayToolWndClass";
648
649 if (!RegisterClassEx(&wc))
650 {
651 dwErr = GetLastError();
652 Log(("Registering invisible tool window failed, error = %08X\n", dwErr));
653 }
654 else
655 {
656 /*
657 * Create our (invisible) tool window.
658 * Note: The window name ("VBoxTrayToolWnd") and class ("VBoxTrayToolWndClass") is
659 * needed for posting globally registered messages to VBoxTray and must not be
660 * changed! Otherwise things get broken!
661 *
662 */
663 g_hwndToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
664 "VBoxTrayToolWndClass", "VBoxTrayToolWnd",
665 WS_POPUPWINDOW,
666 -200, -200, 100, 100, NULL, NULL, g_hInstance, NULL);
667 if (!g_hwndToolWindow)
668 {
669 dwErr = GetLastError();
670 Log(("Creating invisible tool window failed, error = %08X\n", dwErr));
671 }
672 else
673 {
674 /* Reload the cursor(s). */
675 hlpReloadCursor();
676
677 Log(("Invisible tool window handle = %p\n", g_hwndToolWindow));
678 }
679 }
680
681 if (dwErr != ERROR_SUCCESS)
682 vboxTrayDestroyToolWindow();
683 return RTErrConvertFromWin32(dwErr);
684}
685
686static int vboxTraySetupSeamless(void)
687{
688 OSVERSIONINFO info;
689 g_dwMajorVersion = 5; /* Default to Windows XP. */
690 info.dwOSVersionInfoSize = sizeof(info);
691 if (GetVersionEx(&info))
692 {
693 Log(("Windows version %ld.%ld\n", info.dwMajorVersion, info.dwMinorVersion));
694 g_dwMajorVersion = info.dwMajorVersion;
695 }
696
697 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore. */
698 SECURITY_ATTRIBUTES SecAttr;
699 DWORD dwErr = ERROR_SUCCESS;
700 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
701 BOOL fRC;
702
703 SecAttr.nLength = sizeof(SecAttr);
704 SecAttr.bInheritHandle = FALSE;
705 SecAttr.lpSecurityDescriptor = &secDesc;
706 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
707 fRC = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
708 if (!fRC)
709 {
710 dwErr = GetLastError();
711 Log(("SetSecurityDescriptorDacl failed with last error = %08X\n", dwErr));
712 }
713 else
714 {
715 /* For Vista and up we need to change the integrity of the security descriptor, too. */
716 if (g_dwMajorVersion >= 6)
717 {
718 BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize);
719 *(void **)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA =
720 RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorA");
721 Log(("pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %x\n", pfnConvertStringSecurityDescriptorToSecurityDescriptorA));
722 if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA)
723 {
724 PSECURITY_DESCRIPTOR pSD;
725 PACL pSacl = NULL;
726 BOOL fSaclPresent = FALSE;
727 BOOL fSaclDefaulted = FALSE;
728
729 fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */
730 SDDL_REVISION_1, &pSD, NULL);
731 if (!fRC)
732 {
733 dwErr = GetLastError();
734 Log(("ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr));
735 }
736 else
737 {
738 fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted);
739 if (!fRC)
740 {
741 dwErr = GetLastError();
742 Log(("GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
743 }
744 else
745 {
746 fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE);
747 if (!fRC)
748 {
749 dwErr = GetLastError();
750 Log(("SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
751 }
752 }
753 }
754 }
755 }
756
757 if ( dwErr == ERROR_SUCCESS
758 && g_dwMajorVersion >= 5) /* Only for W2K and up ... */
759 {
760 g_hSeamlessWtNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_WT_EVENT_NAME);
761 if (g_hSeamlessWtNotifyEvent == NULL)
762 {
763 dwErr = GetLastError();
764 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
765 }
766
767 g_hSeamlessKmNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
768 if (g_hSeamlessKmNotifyEvent == NULL)
769 {
770 dwErr = GetLastError();
771 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
772 }
773 }
774 }
775 return RTErrConvertFromWin32(dwErr);
776}
777
778static void vboxTrayShutdownSeamless(void)
779{
780 if (g_hSeamlessWtNotifyEvent)
781 {
782 CloseHandle(g_hSeamlessWtNotifyEvent);
783 g_hSeamlessWtNotifyEvent = NULL;
784 }
785
786 if (g_hSeamlessKmNotifyEvent)
787 {
788 CloseHandle(g_hSeamlessKmNotifyEvent);
789 g_hSeamlessKmNotifyEvent = NULL;
790 }
791}
792
793static void VBoxTrayCheckDt()
794{
795 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
796 if (vboxDtHandleEvent())
797 {
798 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
799 VBoxConsoleEnable(!fOldAllowedState);
800 }
801}
802
803static int vboxTrayServiceMain(void)
804{
805 int rc = VINF_SUCCESS;
806 LogFunc(("Entering vboxTrayServiceMain\n"));
807
808 g_hStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
809 if (g_hStopSem == NULL)
810 {
811 rc = RTErrConvertFromWin32(GetLastError());
812 LogFunc(("CreateEvent for stopping VBoxTray failed, rc=%Rrc\n", rc));
813 }
814 else
815 {
816 /*
817 * Start services listed in the vboxServiceTable.
818 */
819 VBOXSERVICEENV svcEnv;
820 svcEnv.hInstance = g_hInstance;
821 svcEnv.hDriver = g_hVBoxDriver;
822
823 /* Initializes disp-if to default (XPDM) mode. */
824 VBoxDispIfInit(&svcEnv.dispIf); /* Cannot fail atm. */
825 #ifdef VBOX_WITH_WDDM
826 /*
827 * For now the display mode will be adjusted to WDDM mode if needed
828 * on display service initialization when it detects the display driver type.
829 */
830 #endif
831
832 /* Finally start all the built-in services! */
833 rc = vboxTrayServicesStart(&svcEnv);
834 if (RT_FAILURE(rc))
835 {
836 /* Terminate service if something went wrong. */
837 vboxTrayServicesStop(&svcEnv);
838 }
839 else
840 {
841 rc = vboxTrayCreateTrayIcon();
842 if ( RT_SUCCESS(rc)
843 && g_dwMajorVersion >= 5) /* Only for W2K and up ... */
844 {
845 /* We're ready to create the tooltip balloon.
846 Check in 10 seconds (@todo make seconds configurable) ... */
847 SetTimer(g_hwndToolWindow,
848 TIMERID_VBOXTRAY_CHECK_HOSTVERSION,
849 10 * 1000, /* 10 seconds */
850 NULL /* No timerproc */);
851 }
852
853 if (RT_SUCCESS(rc))
854 {
855 /* Do the Shared Folders auto-mounting stuff. */
856 rc = VBoxSharedFoldersAutoMount();
857 if (RT_SUCCESS(rc))
858 {
859 /* Report the host that we're up and running! */
860 hlpReportStatus(VBoxGuestFacilityStatus_Active);
861 }
862 }
863
864 if (RT_SUCCESS(rc))
865 {
866 /* Boost thread priority to make sure we wake up early for seamless window notifications
867 * (not sure if it actually makes any difference though). */
868 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
869
870 /*
871 * Main execution loop
872 * Wait for the stop semaphore to be posted or a window event to arrive
873 */
874
875 HANDLE hWaitEvent[4] = {0};
876 DWORD dwEventCount = 0;
877
878 hWaitEvent[dwEventCount++] = g_hStopSem;
879
880 /* Check if seamless mode is not active and add seamless event to the list */
881 if (0 != g_hSeamlessWtNotifyEvent)
882 {
883 hWaitEvent[dwEventCount++] = g_hSeamlessWtNotifyEvent;
884 }
885
886 if (0 != g_hSeamlessKmNotifyEvent)
887 {
888 hWaitEvent[dwEventCount++] = g_hSeamlessKmNotifyEvent;
889 }
890
891 if (0 != vboxDtGetNotifyEvent())
892 {
893 hWaitEvent[dwEventCount++] = vboxDtGetNotifyEvent();
894 }
895
896 LogFlowFunc(("Number of events to wait in main loop: %ld\n", dwEventCount));
897 while (true)
898 {
899 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
900 waitResult = waitResult - WAIT_OBJECT_0;
901
902 /* Only enable for message debugging, lots of traffic! */
903 //Log(("Wait result = %ld\n", waitResult));
904
905 if (waitResult == 0)
906 {
907 LogFunc(("Event 'Exit' triggered\n"));
908 /* exit */
909 break;
910 }
911 else
912 {
913 BOOL fHandled = FALSE;
914 if (waitResult < RT_ELEMENTS(hWaitEvent))
915 {
916 if (hWaitEvent[waitResult])
917 {
918 if (hWaitEvent[waitResult] == g_hSeamlessWtNotifyEvent)
919 {
920 LogFunc(("Event 'Seamless' triggered\n"));
921
922 /* seamless window notification */
923 VBoxSeamlessCheckWindows(false);
924 fHandled = TRUE;
925 }
926 else if (hWaitEvent[waitResult] == g_hSeamlessKmNotifyEvent)
927 {
928 LogFunc(("Event 'Km Seamless' triggered\n"));
929
930 /* seamless window notification */
931 VBoxSeamlessCheckWindows(true);
932 fHandled = TRUE;
933 }
934 else if (hWaitEvent[waitResult] == vboxDtGetNotifyEvent())
935 {
936 LogFunc(("Event 'Dt' triggered\n"));
937 VBoxTrayCheckDt();
938 fHandled = TRUE;
939 }
940 }
941 }
942
943 if (!fHandled)
944 {
945 /* timeout or a window message, handle it */
946 MSG msg;
947 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
948 {
949#ifdef DEBUG_andy
950 LogFlowFunc(("PeekMessage %u\n", msg.message));
951#endif
952 if (msg.message == WM_QUIT)
953 {
954 LogFunc(("Terminating ...\n"));
955 SetEvent(g_hStopSem);
956 }
957 TranslateMessage(&msg);
958 DispatchMessage(&msg);
959 }
960 }
961 }
962 }
963 LogFunc(("Returned from main loop, exiting ...\n"));
964 }
965 LogFunc(("Waiting for services to stop ...\n"));
966 vboxTrayServicesStop(&svcEnv);
967 } /* Services started */
968 CloseHandle(g_hStopSem);
969 } /* Stop event created */
970
971 vboxTrayRemoveTrayIcon();
972
973 LogFunc(("Leaving with rc=%Rrc\n", rc));
974 return rc;
975}
976
977/**
978 * Main function
979 */
980int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
981{
982 RT_NOREF(hPrevInstance, lpCmdLine, nCmdShow);
983
984 /* Note: Do not use a global namespace ("Global\\") for mutex name here,
985 * will blow up NT4 compatibility! */
986 HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, "VBoxTray");
987 if ( hMutexAppRunning != NULL
988 && GetLastError() == ERROR_ALREADY_EXISTS)
989 {
990 /* VBoxTray already running? Bail out. */
991 CloseHandle (hMutexAppRunning);
992 hMutexAppRunning = NULL;
993 return 0;
994 }
995
996 LogRel(("%s r%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()));
997
998 int rc = RTR3InitExeNoArguments(0);
999 if (RT_SUCCESS(rc))
1000 rc = vboxTrayLogCreate(NULL /* pszLogFile */);
1001
1002 if (RT_SUCCESS(rc))
1003 {
1004 rc = VbglR3Init();
1005 if (RT_SUCCESS(rc))
1006 rc = vboxTrayOpenBaseDriver();
1007 }
1008
1009 if (RT_SUCCESS(rc))
1010 {
1011 /* Save instance handle. */
1012 g_hInstance = hInstance;
1013
1014 hlpReportStatus(VBoxGuestFacilityStatus_Init);
1015 rc = vboxTrayCreateToolWindow();
1016 if (RT_SUCCESS(rc))
1017 {
1018 VBoxCapsInit();
1019
1020 rc = vboxStInit(g_hwndToolWindow);
1021 if (!RT_SUCCESS(rc))
1022 {
1023 LogFlowFunc(("vboxStInit failed, rc=%Rrc\n", rc));
1024 /* ignore the St Init failure. this can happen for < XP win that do not support WTS API
1025 * in that case the session is treated as active connected to the physical console
1026 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1027 Assert(vboxStIsActiveConsole());
1028 }
1029
1030 rc = vboxDtInit();
1031 if (!RT_SUCCESS(rc))
1032 {
1033 LogFlowFunc(("vboxDtInit failed, rc=%Rrc\n", rc));
1034 /* ignore the Dt Init failure. this can happen for < XP win that do not support WTS API
1035 * in that case the session is treated as active connected to the physical console
1036 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1037 Assert(vboxDtIsInputDesktop());
1038 }
1039
1040 rc = VBoxAcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, true);
1041 if (!RT_SUCCESS(rc))
1042 LogFlowFunc(("VBoxAcquireGuestCaps failed with rc=%Rrc, ignoring ...\n", rc));
1043
1044 rc = vboxTraySetupSeamless(); /** @todo r=andy Do we really want to be this critical for the whole application? */
1045 if (RT_SUCCESS(rc))
1046 {
1047 rc = vboxTrayServiceMain();
1048 if (RT_SUCCESS(rc))
1049 hlpReportStatus(VBoxGuestFacilityStatus_Terminating);
1050 vboxTrayShutdownSeamless();
1051 }
1052
1053 /* it should be safe to call vboxDtTerm even if vboxStInit above failed */
1054 vboxDtTerm();
1055
1056 /* it should be safe to call vboxStTerm even if vboxStInit above failed */
1057 vboxStTerm();
1058
1059 VBoxCapsTerm();
1060
1061 vboxTrayDestroyToolWindow();
1062 }
1063 if (RT_SUCCESS(rc))
1064 hlpReportStatus(VBoxGuestFacilityStatus_Terminated);
1065 }
1066
1067 if (RT_FAILURE(rc))
1068 {
1069 LogRel(("Error while starting, rc=%Rrc\n", rc));
1070 hlpReportStatus(VBoxGuestFacilityStatus_Failed);
1071 }
1072 LogRel(("Ended\n"));
1073 vboxTrayCloseBaseDriver();
1074
1075 /* Release instance mutex. */
1076 if (hMutexAppRunning != NULL)
1077 {
1078 CloseHandle(hMutexAppRunning);
1079 hMutexAppRunning = NULL;
1080 }
1081
1082 VbglR3Term();
1083
1084 vboxTrayLogDestroy();
1085
1086 return RT_SUCCESS(rc) ? 0 : 1;
1087}
1088
1089/**
1090 * Window procedure for our main tool window.
1091 */
1092static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1093{
1094 LogFlowFunc(("hWnd=%p, uMsg=%u\n", hWnd, uMsg));
1095
1096 switch (uMsg)
1097 {
1098 case WM_CREATE:
1099 {
1100 LogFunc(("Tool window created\n"));
1101
1102 int rc = vboxTrayRegisterGlobalMessages(&g_vboxGlobalMessageTable[0]);
1103 if (RT_FAILURE(rc))
1104 LogFunc(("Error registering global window messages, rc=%Rrc\n", rc));
1105 return 0;
1106 }
1107
1108 case WM_CLOSE:
1109 return 0;
1110
1111 case WM_DESTROY:
1112 {
1113 LogFunc(("Tool window destroyed\n"));
1114 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1115 return 0;
1116 }
1117
1118 case WM_TIMER:
1119 {
1120 if (VBoxCapsCheckTimer(wParam))
1121 return 0;
1122 if (vboxDtCheckTimer(wParam))
1123 return 0;
1124 if (vboxStCheckTimer(wParam))
1125 return 0;
1126
1127 switch (wParam)
1128 {
1129 case TIMERID_VBOXTRAY_CHECK_HOSTVERSION:
1130 if (RT_SUCCESS(VBoxCheckHostVersion()))
1131 {
1132 /* After successful run we don't need to check again. */
1133 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1134 }
1135 return 0;
1136
1137 default:
1138 break;
1139 }
1140
1141 break; /* Make sure other timers get processed the usual way! */
1142 }
1143
1144 case WM_VBOXTRAY_TRAY_ICON:
1145 {
1146 switch (lParam)
1147 {
1148 case WM_LBUTTONDBLCLK:
1149 break;
1150
1151 case WM_RBUTTONDOWN:
1152 break;
1153 }
1154 return 0;
1155 }
1156
1157 case WM_VBOX_SEAMLESS_ENABLE:
1158 {
1159 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1160 return 0;
1161 }
1162
1163 case WM_VBOX_SEAMLESS_DISABLE:
1164 {
1165 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1166 return 0;
1167 }
1168
1169 case WM_DISPLAYCHANGE:
1170 ASMAtomicUoWriteU32(&g_fGuestDisplaysChanged, 1);
1171 // No break or return is intentional here.
1172 case WM_VBOX_SEAMLESS_UPDATE:
1173 {
1174 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1175 VBoxSeamlessCheckWindows(true);
1176 return 0;
1177 }
1178
1179 case WM_VBOX_GRAPHICS_SUPPORTED:
1180 {
1181 VBoxGrapicsSetSupported(TRUE);
1182 return 0;
1183 }
1184
1185 case WM_VBOX_GRAPHICS_UNSUPPORTED:
1186 {
1187 VBoxGrapicsSetSupported(FALSE);
1188 return 0;
1189 }
1190
1191 case WM_WTSSESSION_CHANGE:
1192 {
1193 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
1194 if (vboxStHandleEvent(wParam))
1195 {
1196 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
1197 VBoxConsoleEnable(!fOldAllowedState);
1198 }
1199 return 0;
1200 }
1201
1202 default:
1203 {
1204 /* Handle all globally registered window messages. */
1205 if (vboxTrayHandleGlobalMessages(&g_vboxGlobalMessageTable[0], uMsg,
1206 wParam, lParam))
1207 {
1208 return 0; /* We handled the message. @todo Add return value!*/
1209 }
1210 break; /* We did not handle the message, dispatch to DefWndProc. */
1211 }
1212 }
1213
1214 /* Only if message was *not* handled by our switch above, dispatch to DefWindowProc. */
1215 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1216}
1217
1218/* St (session [state] tracking) functionality API impl */
1219
1220typedef struct VBOXST
1221{
1222 HWND hWTSAPIWnd;
1223 RTLDRMOD hLdrModWTSAPI32;
1224 BOOL fIsConsole;
1225 WTS_CONNECTSTATE_CLASS enmConnectState;
1226 UINT_PTR idDelayedInitTimer;
1227 BOOL (WINAPI * pfnWTSRegisterSessionNotification)(HWND hWnd, DWORD dwFlags);
1228 BOOL (WINAPI * pfnWTSUnRegisterSessionNotification)(HWND hWnd);
1229 BOOL (WINAPI * pfnWTSQuerySessionInformationA)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR *ppBuffer, DWORD *pBytesReturned);
1230} VBOXST;
1231
1232static VBOXST gVBoxSt;
1233
1234static int vboxStCheckState()
1235{
1236 int rc = VINF_SUCCESS;
1237 WTS_CONNECTSTATE_CLASS *penmConnectState = NULL;
1238 USHORT *pProtocolType = NULL;
1239 DWORD cbBuf = 0;
1240 if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState,
1241 (LPTSTR *)&penmConnectState, &cbBuf))
1242 {
1243 if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientProtocolType,
1244 (LPTSTR *)&pProtocolType, &cbBuf))
1245 {
1246 gVBoxSt.fIsConsole = (*pProtocolType == 0);
1247 gVBoxSt.enmConnectState = *penmConnectState;
1248 return VINF_SUCCESS;
1249 }
1250
1251 DWORD dwErr = GetLastError();
1252 LogFlowFunc(("WTSQuerySessionInformationA WTSClientProtocolType failed, error = %08X\n", dwErr));
1253 rc = RTErrConvertFromWin32(dwErr);
1254 }
1255 else
1256 {
1257 DWORD dwErr = GetLastError();
1258 LogFlowFunc(("WTSQuerySessionInformationA WTSConnectState failed, error = %08X\n", dwErr));
1259 rc = RTErrConvertFromWin32(dwErr);
1260 }
1261
1262 /* failure branch, set to "console-active" state */
1263 gVBoxSt.fIsConsole = TRUE;
1264 gVBoxSt.enmConnectState = WTSActive;
1265
1266 return rc;
1267}
1268
1269static int vboxStInit(HWND hWnd)
1270{
1271 RT_ZERO(gVBoxSt);
1272 int rc = RTLdrLoadSystem("WTSAPI32.DLL", false /*fNoUnload*/, &gVBoxSt.hLdrModWTSAPI32);
1273 if (RT_SUCCESS(rc))
1274 {
1275 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSRegisterSessionNotification",
1276 (void **)&gVBoxSt.pfnWTSRegisterSessionNotification);
1277 if (RT_SUCCESS(rc))
1278 {
1279 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSUnRegisterSessionNotification",
1280 (void **)&gVBoxSt.pfnWTSUnRegisterSessionNotification);
1281 if (RT_SUCCESS(rc))
1282 {
1283 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSQuerySessionInformationA",
1284 (void **)&gVBoxSt.pfnWTSQuerySessionInformationA);
1285 if (RT_FAILURE(rc))
1286 LogFlowFunc(("WTSQuerySessionInformationA not found\n"));
1287 }
1288 else
1289 LogFlowFunc(("WTSUnRegisterSessionNotification not found\n"));
1290 }
1291 else
1292 LogFlowFunc(("WTSRegisterSessionNotification not found\n"));
1293 if (RT_SUCCESS(rc))
1294 {
1295 gVBoxSt.hWTSAPIWnd = hWnd;
1296 if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION))
1297 vboxStCheckState();
1298 else
1299 {
1300 DWORD dwErr = GetLastError();
1301 LogFlowFunc(("WTSRegisterSessionNotification failed, error = %08X\n", dwErr));
1302 if (dwErr == RPC_S_INVALID_BINDING)
1303 {
1304 gVBoxSt.idDelayedInitTimer = SetTimer(gVBoxSt.hWTSAPIWnd, TIMERID_VBOXTRAY_ST_DELAYED_INIT_TIMER,
1305 2000, (TIMERPROC)NULL);
1306 gVBoxSt.fIsConsole = TRUE;
1307 gVBoxSt.enmConnectState = WTSActive;
1308 rc = VINF_SUCCESS;
1309 }
1310 else
1311 rc = RTErrConvertFromWin32(dwErr);
1312 }
1313
1314 if (RT_SUCCESS(rc))
1315 return VINF_SUCCESS;
1316 }
1317
1318 RTLdrClose(gVBoxSt.hLdrModWTSAPI32);
1319 }
1320 else
1321 LogFlowFunc(("WTSAPI32 load failed, rc = %Rrc\n", rc));
1322
1323 RT_ZERO(gVBoxSt);
1324 gVBoxSt.fIsConsole = TRUE;
1325 gVBoxSt.enmConnectState = WTSActive;
1326 return rc;
1327}
1328
1329static void vboxStTerm(void)
1330{
1331 if (!gVBoxSt.hWTSAPIWnd)
1332 {
1333 LogFlowFunc(("vboxStTerm called for non-initialized St\n"));
1334 return;
1335 }
1336
1337 if (gVBoxSt.idDelayedInitTimer)
1338 {
1339 /* notification is not registered, just kill timer */
1340 KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer);
1341 gVBoxSt.idDelayedInitTimer = 0;
1342 }
1343 else
1344 {
1345 if (!gVBoxSt.pfnWTSUnRegisterSessionNotification(gVBoxSt.hWTSAPIWnd))
1346 {
1347 LogFlowFunc(("WTSAPI32 load failed, error = %08X\n", GetLastError()));
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 LogFlowFunc(("timer WTSRegisterSessionNotification failed, error = %08X\n", GetLastError()));
1390 Assert(gVBoxSt.fIsConsole == TRUE);
1391 Assert(gVBoxSt.enmConnectState == WTSActive);
1392 }
1393
1394 return TRUE;
1395}
1396
1397
1398static BOOL vboxStHandleEvent(WPARAM wEvent)
1399{
1400 RT_NOREF(wEvent);
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(void)
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 LogFlowFunc(("pfnVBoxHookInstallActiveDesktopTracker failed, last error = %08X\n", GetLastError()));
1540 }
1541
1542 if (!fRc)
1543 {
1544 gVBoxDt.idTimer = SetTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_DT_TIMER, 500, (TIMERPROC)NULL);
1545 if (!gVBoxDt.idTimer)
1546 {
1547 DWORD dwErr = GetLastError();
1548 LogFlowFunc(("SetTimer error %08X\n", dwErr));
1549 rc = RTErrConvertFromWin32(dwErr);
1550 }
1551 }
1552
1553 if (RT_SUCCESS(rc))
1554 {
1555 gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop();
1556 return VINF_SUCCESS;
1557 }
1558 }
1559 }
1560
1561 RTLdrClose(gVBoxDt.hLdrModHook);
1562 }
1563 else
1564 {
1565 DWORD dwErr = GetLastError();
1566 LogFlowFunc(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
1567 rc = RTErrConvertFromWin32(dwErr);
1568 }
1569
1570 CloseHandle(gVBoxDt.hNotifyEvent);
1571 }
1572 else
1573 {
1574 DWORD dwErr = GetLastError();
1575 LogFlowFunc(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
1576 rc = RTErrConvertFromWin32(dwErr);
1577 }
1578
1579
1580 RT_ZERO(gVBoxDt);
1581 gVBoxDt.fIsInputDesktop = TRUE;
1582
1583 return rc;
1584}
1585
1586static void vboxDtTerm()
1587{
1588 if (!gVBoxDt.hLdrModHook)
1589 return;
1590
1591 gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker();
1592
1593 RTLdrClose(gVBoxDt.hLdrModHook);
1594 CloseHandle(gVBoxDt.hNotifyEvent);
1595
1596 RT_ZERO(gVBoxDt);
1597}
1598/* @returns true on "IsInputDesktop" state change */
1599static BOOL vboxDtHandleEvent()
1600{
1601 BOOL fIsInputDesktop = gVBoxDt.fIsInputDesktop;
1602 gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop();
1603 return !fIsInputDesktop != !gVBoxDt.fIsInputDesktop;
1604}
1605
1606static HANDLE vboxDtGetNotifyEvent()
1607{
1608 return gVBoxDt.hNotifyEvent;
1609}
1610
1611/* @returns true iff the application (VBoxTray) desktop is input */
1612static BOOL vboxDtIsInputDesktop()
1613{
1614 return gVBoxDt.fIsInputDesktop;
1615}
1616
1617/* we need to perform Acquire/Release using the file handled we use for rewuesting events from VBoxGuest
1618 * otherwise Acquisition mechanism will treat us as different client and will not propagate necessary requests
1619 * */
1620static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg)
1621{
1622 DWORD cbReturned = 0;
1623 VBoxGuestCapsAquire Info;
1624 Log(("VBoxAcquireGuestCaps or(0x%x), not(0x%x), cfx(%d)\n", fOr, fNot, fCfg));
1625 Info.enmFlags = fCfg ? VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE : VBOXGUESTCAPSACQUIRE_FLAGS_NONE;
1626 Info.rc = VERR_NOT_IMPLEMENTED;
1627 Info.u32OrMask = fOr;
1628 Info.u32NotMask = fNot;
1629 if (!DeviceIoControl(g_hVBoxDriver, VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE, &Info, sizeof(Info), &Info, sizeof(Info), &cbReturned, NULL))
1630 {
1631 DWORD LastErr = GetLastError();
1632 LogFlowFunc(("DeviceIoControl VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed LastErr %d\n", LastErr));
1633 return RTErrConvertFromWin32(LastErr);
1634 }
1635
1636 int rc = Info.rc;
1637 if (!RT_SUCCESS(rc))
1638 {
1639 LogFlowFunc(("VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed rc %d\n", rc));
1640 return rc;
1641 }
1642
1643 return rc;
1644}
1645
1646typedef enum VBOXCAPS_ENTRY_ACSTATE
1647{
1648 /* the given cap is released */
1649 VBOXCAPS_ENTRY_ACSTATE_RELEASED = 0,
1650 /* the given cap acquisition is in progress */
1651 VBOXCAPS_ENTRY_ACSTATE_ACQUIRING,
1652 /* the given cap is acquired */
1653 VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
1654} VBOXCAPS_ENTRY_ACSTATE;
1655
1656
1657struct VBOXCAPS_ENTRY;
1658struct VBOXCAPS;
1659
1660typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE)(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled);
1661
1662typedef struct VBOXCAPS_ENTRY
1663{
1664 uint32_t fCap;
1665 uint32_t iCap;
1666 VBOXCAPS_ENTRY_FUNCSTATE enmFuncState;
1667 VBOXCAPS_ENTRY_ACSTATE enmAcState;
1668 PFNVBOXCAPS_ENTRY_ON_ENABLE pfnOnEnable;
1669} VBOXCAPS_ENTRY;
1670
1671
1672typedef struct VBOXCAPS
1673{
1674 UINT_PTR idTimer;
1675 VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT];
1676} VBOXCAPS;
1677
1678static VBOXCAPS gVBoxCaps;
1679
1680static DECLCALLBACK(void) vboxCapsOnEnableSeamless(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled)
1681{
1682 RT_NOREF(pConsole, pCap);
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.

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