VirtualBox

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

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

Committed a bit too much (r125394).

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