VirtualBox

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

Last change on this file since 90829 was 90829, checked in by vboxsync, 3 years ago

IPRT,VMM,SUPDrv,++: Reworked the IPRT logger structure and how the VMM ring-0 uses it. bugref:10086

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