VirtualBox

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

Last change on this file since 84721 was 84721, checked in by vboxsync, 5 years ago

Guest Additions/VBoxTray: Be more verbose when a certain service does not start; added more documentation about how to treat service starting failures [build fix].

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