VirtualBox

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

Last change on this file since 75407 was 75407, checked in by vboxsync, 6 years ago

VBoxSharedFolders,VBoxService,VBoxTray: New go at auto mounting shared folders, now also at runtime. bugref:3544

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