VirtualBox

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

Last change on this file since 86996 was 85371, checked in by vboxsync, 4 years ago

DnD: Revamped code to simplify / untangle of internal data handling:

  • C-ifying and renaming classes DnDURIList / DnDURIObject -> DnDTransferList / DnDTransferObject
  • Added testcases for DnDTransferList / DnDTransferObject + DnDPath API
  • Reduced memory footprint
  • Greatly simplified / stripped down internal data flow of Main side
  • More (optional) release logging for further diagnosis

Work in progress.

  • 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 85371 2020-07-17 10:02:58Z 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_SvcDescDnD, 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 DECLCALLBACK(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 = %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