/* $Id: VBoxTray.cpp 46625 2013-06-18 13:28:52Z vboxsync $ */ /** @file * VBoxTray - Guest Additions Tray Application */ /* * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /******************************************************************************* * Header Files * *******************************************************************************/ #include "VBoxTray.h" #include "VBoxTrayMsg.h" #include "VBoxHelpers.h" #include "VBoxSeamless.h" #include "VBoxClipboard.h" #include "VBoxDisplay.h" #include "VBoxRestore.h" #include "VBoxVRDP.h" #include "VBoxHostVersion.h" #include "VBoxSharedFolders.h" #include "VBoxIPC.h" #include "VBoxLA.h" #include "VBoxMMR.h" #include #include "resource.h" #include #include #include #include #include /* Default desktop state tracking */ #include #ifdef DEBUG_misha #define WARN(_m) do { \ Assert(0); \ Log(_m); \ } while (0) #else #define WARN(_m) do { \ Log(_m); \ } while (0) #endif /* * St (session [state] tracking) functionality API * * !!!NOTE: this API is NOT thread-safe!!! * it is supposed to be called & used from within the window message handler thread * of the window passed to vboxStInit */ static int vboxStInit(HWND hWnd); static void vboxStTerm(void); /* @returns true on "IsActiveConsole" state change */ static BOOL vboxStHandleEvent(WPARAM EventID, LPARAM SessionID); static BOOL vboxStIsActiveConsole(); static BOOL vboxStCheckTimer(WPARAM wEvent); /* * Dt (desktop [state] tracking) functionality API * * !!!NOTE: this API is NOT thread-safe!!! * */ static int vboxDtInit(); static void vboxDtTerm(); /* @returns true on "IsInputDesktop" state change */ static BOOL vboxDtHandleEvent(); /* @returns true iff the application (VBoxTray) desktop is input */ static BOOL vboxDtIsInputDesktop(); static HANDLE vboxDtGetNotifyEvent(); static BOOL vboxDtCheckTimer(WPARAM wParam); /* caps API */ #define VBOXCAPS_ENTRY_IDX_SEAMLESS 0 #define VBOXCAPS_ENTRY_IDX_GRAPHICS 1 #define VBOXCAPS_ENTRY_IDX_COUNT 2 typedef enum VBOXCAPS_ENTRY_FUNCSTATE { /* the cap is unsupported */ VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED = 0, /* the cap is supported */ VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED, /* the cap functionality is started, it can be disabled however if its AcState is not ACQUIRED */ VBOXCAPS_ENTRY_FUNCSTATE_STARTED, } VBOXCAPS_ENTRY_FUNCSTATE; static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState); static int VBoxCapsInit(); static int VBoxCapsReleaseAll(); static void VBoxCapsTerm(); static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap); static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap); static BOOL VBoxCapsCheckTimer(WPARAM wParam); static int VBoxCapsEntryRelease(uint32_t iCap); static int VBoxCapsEntryAcquire(uint32_t iCap); static int VBoxCapsAcquireAllSupported(); /* console-related caps API */ static BOOL VBoxConsoleIsAllowed(); static void VBoxConsoleEnable(BOOL fEnable); static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported); static void VBoxGrapicsSetSupported(BOOL fSupported); /******************************************************************************* * Internal Functions * *******************************************************************************/ static int vboxTrayCreateTrayIcon(void); static LRESULT CALLBACK vboxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); /* Global message handler prototypes. */ static int vboxTrayGlMsgTaskbarCreated(WPARAM lParam, LPARAM wParam); /*static int vboxTrayGlMsgShowBalloonMsg(WPARAM lParam, LPARAM wParam);*/ static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg); /******************************************************************************* * Global Variables * *******************************************************************************/ HANDLE ghVBoxDriver; HANDLE ghStopSem; HANDLE ghSeamlessWtNotifyEvent = 0; SERVICE_STATUS gVBoxServiceStatus; SERVICE_STATUS_HANDLE gVBoxServiceStatusHandle; HINSTANCE ghInstance; HWND ghwndToolWindow; NOTIFYICONDATA gNotifyIconData; DWORD gMajorVersion; /* The service table. */ static VBOXSERVICEINFO vboxServiceTable[] = { { "Display", VBoxDisplayInit, VBoxDisplayThread, VBoxDisplayDestroy }, { "Shared Clipboard", VBoxClipboardInit, VBoxClipboardThread, VBoxClipboardDestroy }, { "Seamless Windows", VBoxSeamlessInit, VBoxSeamlessThread, VBoxSeamlessDestroy }, #ifdef VBOX_WITH_VRDP_SESSION_HANDLING { "Restore", VBoxRestoreInit, VBoxRestoreThread, VBoxRestoreDestroy }, #endif { "VRDP", VBoxVRDPInit, VBoxVRDPThread, VBoxVRDPDestroy }, { "IPC", VBoxIPCInit, VBoxIPCThread, VBoxIPCDestroy }, { "Location Awareness", VBoxLAInit, VBoxLAThread, VBoxLADestroy }, #ifdef VBOX_WITH_MMR { "Multimedia Redirection", VBoxMMRInit, VBoxMMRThread, VBoxMMRDestroy }, #endif { NULL } }; /* The global message table. */ static VBOXGLOBALMESSAGE s_vboxGlobalMessageTable[] = { /* Windows specific stuff. */ { "TaskbarCreated", vboxTrayGlMsgTaskbarCreated }, /* VBoxTray specific stuff. */ /** @todo Add new messages here! */ { NULL } }; /** * Gets called whenever the Windows main taskbar * get (re-)created. Nice to install our tray icon. * * @return IPRT status code. * @param wParam * @param lParam */ static int vboxTrayGlMsgTaskbarCreated(WPARAM wParam, LPARAM lParam) { return vboxTrayCreateTrayIcon(); } static int vboxTrayCreateTrayIcon(void) { HICON hIcon = LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_VIRTUALBOX)); if (hIcon == NULL) { DWORD dwErr = GetLastError(); Log(("VBoxTray: Could not load tray icon, error %08X\n", dwErr)); return RTErrConvertFromWin32(dwErr); } /* Prepare the system tray icon. */ RT_ZERO(gNotifyIconData); gNotifyIconData.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA); gNotifyIconData.hWnd = ghwndToolWindow; gNotifyIconData.uID = ID_TRAYICON; gNotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; gNotifyIconData.uCallbackMessage = WM_VBOXTRAY_TRAY_ICON; gNotifyIconData.hIcon = hIcon; sprintf(gNotifyIconData.szTip, "%s Guest Additions %d.%d.%dr%d", VBOX_PRODUCT, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV); int rc = VINF_SUCCESS; if (!Shell_NotifyIcon(NIM_ADD, &gNotifyIconData)) { DWORD dwErr = GetLastError(); Log(("VBoxTray: Could not create tray icon, error = %08X\n", dwErr)); rc = RTErrConvertFromWin32(dwErr); RT_ZERO(gNotifyIconData); } if (hIcon) DestroyIcon(hIcon); return rc; } static void vboxTrayRemoveTrayIcon() { if (gNotifyIconData.cbSize > 0) { /* Remove the system tray icon and refresh system tray. */ Shell_NotifyIcon(NIM_DELETE, &gNotifyIconData); HWND hTrayWnd = FindWindow("Shell_TrayWnd", NULL); /* We assume we only have one tray atm. */ if (hTrayWnd) { HWND hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", NULL); if (hTrayNotifyWnd) SendMessage(hTrayNotifyWnd, WM_PAINT, 0, NULL); } RT_ZERO(gNotifyIconData); } } static int vboxTrayStartServices(VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable) { Log(("VBoxTray: Starting services ...\n")); pEnv->hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!pEnv->hStopEvent) { /* Could not create event. */ return VERR_NOT_SUPPORTED; } while (pTable->pszName) { Log(("VBoxTray: Starting %s ...\n", pTable->pszName)); int rc = VINF_SUCCESS; bool fStartThread = false; pTable->hThread = (HANDLE)0; pTable->pInstance = NULL; pTable->fStarted = false; if (pTable->pfnInit) rc = pTable->pfnInit (pEnv, &pTable->pInstance, &fStartThread); if (RT_FAILURE(rc)) { Log(("VBoxTray: Failed to initialize rc = %Rrc\n", rc)); } else { if (pTable->pfnThread && fStartThread) { unsigned threadid; pTable->hThread = (HANDLE)_beginthreadex(NULL, /* security */ 0, /* stacksize */ pTable->pfnThread, pTable->pInstance, 0, /* initflag */ &threadid); if (pTable->hThread == (HANDLE)(0)) rc = VERR_NOT_SUPPORTED; } if (RT_SUCCESS(rc)) pTable->fStarted = true; else { Log(("VBoxTray: Failed to start the thread\n")); if (pTable->pfnDestroy) pTable->pfnDestroy(pEnv, pTable->pInstance); } } /* Advance to next table element. */ pTable++; } return VINF_SUCCESS; } static void vboxTrayStopServices(VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable) { if (!pEnv->hStopEvent) return; /* Signal to all threads. */ SetEvent(pEnv->hStopEvent); while (pTable->pszName) { if (pTable->fStarted) { if (pTable->pfnThread) { /* There is a thread, wait for termination. */ WaitForSingleObject(pTable->hThread, INFINITE); CloseHandle(pTable->hThread); pTable->hThread = 0; } if (pTable->pfnDestroy) pTable->pfnDestroy (pEnv, pTable->pInstance); pTable->fStarted = false; } /* Advance to next table element. */ pTable++; } CloseHandle(pEnv->hStopEvent); } static int vboxTrayRegisterGlobalMessages(PVBOXGLOBALMESSAGE pTable) { int rc = VINF_SUCCESS; if (pTable == NULL) /* No table to register? Skip. */ return rc; while ( pTable->pszName && RT_SUCCESS(rc)) { /* Register global accessible window messages. */ pTable->uMsgID = RegisterWindowMessage(TEXT(pTable->pszName)); if (!pTable->uMsgID) { DWORD dwErr = GetLastError(); Log(("VBoxTray: Registering global message \"%s\" failed, error = %08X\n", dwErr)); rc = RTErrConvertFromWin32(dwErr); } /* Advance to next table element. */ pTable++; } return rc; } static bool vboxTrayHandleGlobalMessages(PVBOXGLOBALMESSAGE pTable, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (pTable == NULL) return false; while (pTable && pTable->pszName) { if (pTable->uMsgID == uMsg) { if (pTable->pfnHandler) pTable->pfnHandler(wParam, lParam); return true; } /* Advance to next table element. */ pTable++; } return false; } static int vboxTrayOpenBaseDriver(void) { /* Open VBox guest driver. */ DWORD dwErr = ERROR_SUCCESS; ghVBoxDriver = CreateFile(VBOXGUEST_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (ghVBoxDriver == INVALID_HANDLE_VALUE) { dwErr = GetLastError(); LogRel(("VBoxTray: Could not open VirtualBox Guest Additions driver! Please install / start it first! Error = %08X\n", dwErr)); } return RTErrConvertFromWin32(dwErr); } static void vboxTrayCloseBaseDriver(void) { if (ghVBoxDriver) { CloseHandle(ghVBoxDriver); ghVBoxDriver = NULL; } } static void vboxTrayDestroyToolWindow(void) { if (ghwndToolWindow) { Log(("VBoxTray: Destroying tool window ...\n")); /* Destroy the tool window. */ DestroyWindow(ghwndToolWindow); ghwndToolWindow = NULL; UnregisterClass("VBoxTrayToolWndClass", ghInstance); } } static int vboxTrayCreateToolWindow(void) { DWORD dwErr = ERROR_SUCCESS; /* Create a custom window class. */ WNDCLASS windowClass = {0}; windowClass.style = CS_NOCLOSE; windowClass.lpfnWndProc = (WNDPROC)vboxToolWndProc; windowClass.hInstance = ghInstance; windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); windowClass.lpszClassName = "VBoxTrayToolWndClass"; if (!RegisterClass(&windowClass)) { dwErr = GetLastError(); Log(("VBoxTray: Registering invisible tool window failed, error = %08X\n", dwErr)); } else { /* * Create our (invisible) tool window. * Note: The window name ("VBoxTrayToolWnd") and class ("VBoxTrayToolWndClass") is * needed for posting globally registered messages to VBoxTray and must not be * changed! Otherwise things get broken! * */ ghwndToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST, "VBoxTrayToolWndClass", "VBoxTrayToolWnd", WS_POPUPWINDOW, -200, -200, 100, 100, NULL, NULL, ghInstance, NULL); if (!ghwndToolWindow) { dwErr = GetLastError(); Log(("VBoxTray: Creating invisible tool window failed, error = %08X\n", dwErr)); } else { /* Reload the cursor(s). */ hlpReloadCursor(); Log(("VBoxTray: Invisible tool window handle = %p\n", ghwndToolWindow)); } } if (dwErr != ERROR_SUCCESS) vboxTrayDestroyToolWindow(); return RTErrConvertFromWin32(dwErr); } static int vboxTraySetupSeamless(void) { OSVERSIONINFO info; gMajorVersion = 5; /* Default to Windows XP. */ info.dwOSVersionInfoSize = sizeof(info); if (GetVersionEx(&info)) { Log(("VBoxTray: Windows version %ld.%ld\n", info.dwMajorVersion, info.dwMinorVersion)); gMajorVersion = info.dwMajorVersion; } /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore. */ SECURITY_ATTRIBUTES SecAttr; DWORD dwErr = ERROR_SUCCESS; char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH]; BOOL fRC; SecAttr.nLength = sizeof(SecAttr); SecAttr.bInheritHandle = FALSE; SecAttr.lpSecurityDescriptor = &secDesc; InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); fRC = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE); if (!fRC) { dwErr = GetLastError(); Log(("VBoxTray: SetSecurityDescriptorDacl failed with last error = %08X\n", dwErr)); } else { /* For Vista and up we need to change the integrity of the security descriptor, too. */ if (gMajorVersion >= 6) { BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize); *(void **)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA = RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorA"); Log(("VBoxTray: pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %x\n", pfnConvertStringSecurityDescriptorToSecurityDescriptorA)); if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA) { PSECURITY_DESCRIPTOR pSD; PACL pSacl = NULL; BOOL fSaclPresent = FALSE; BOOL fSaclDefaulted = FALSE; fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */ SDDL_REVISION_1, &pSD, NULL); if (!fRC) { dwErr = GetLastError(); Log(("VBoxTray: ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr)); } else { fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted); if (!fRC) { dwErr = GetLastError(); Log(("VBoxTray: GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr)); } else { fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE); if (!fRC) { dwErr = GetLastError(); Log(("VBoxTray: SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr)); } } } } } if ( dwErr == ERROR_SUCCESS && gMajorVersion >= 5) /* Only for W2K and up ... */ { ghSeamlessWtNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_WT_EVENT_NAME); if (ghSeamlessWtNotifyEvent == NULL) { dwErr = GetLastError(); Log(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr)); } } } return RTErrConvertFromWin32(dwErr); } static void vboxTrayShutdownSeamless(void) { if (ghSeamlessWtNotifyEvent) { CloseHandle(ghSeamlessWtNotifyEvent); ghSeamlessWtNotifyEvent = NULL; } } static void VBoxTrayCheckDt() { BOOL fOldAllowedState = VBoxConsoleIsAllowed(); if (vboxDtHandleEvent()) { if (!VBoxConsoleIsAllowed() != !fOldAllowedState) VBoxConsoleEnable(!fOldAllowedState); } } static int vboxTrayServiceMain(void) { int rc = VINF_SUCCESS; Log(("VBoxTray: Entering vboxTrayServiceMain\n")); ghStopSem = CreateEvent(NULL, TRUE, FALSE, NULL); if (ghStopSem == NULL) { rc = RTErrConvertFromWin32(GetLastError()); Log(("VBoxTray: CreateEvent for stopping VBoxTray failed, rc=%Rrc\n", rc)); } else { /* * Start services listed in the vboxServiceTable. */ VBOXSERVICEENV svcEnv; svcEnv.hInstance = ghInstance; svcEnv.hDriver = ghVBoxDriver; /* Initializes disp-if to default (XPDM) mode. */ VBoxDispIfInit(&svcEnv.dispIf); /* Cannot fail atm. */ #ifdef VBOX_WITH_WDDM /* * For now the display mode will be adjusted to WDDM mode if needed * on display service initialization when it detects the display driver type. */ #endif /* Finally start all the built-in services! */ rc = vboxTrayStartServices(&svcEnv, vboxServiceTable); if (RT_FAILURE(rc)) { /* Terminate service if something went wrong. */ vboxTrayStopServices(&svcEnv, vboxServiceTable); } else { rc = vboxTrayCreateTrayIcon(); if ( RT_SUCCESS(rc) && gMajorVersion >= 5) /* Only for W2K and up ... */ { /* We're ready to create the tooltip balloon. Check in 10 seconds (@todo make seconds configurable) ... */ SetTimer(ghwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION, 10 * 1000, /* 10 seconds */ NULL /* No timerproc */); } if (RT_SUCCESS(rc)) { /* Do the Shared Folders auto-mounting stuff. */ rc = VBoxSharedFoldersAutoMount(); if (RT_SUCCESS(rc)) { /* Report the host that we're up and running! */ hlpReportStatus(VBoxGuestFacilityStatus_Active); } } if (RT_SUCCESS(rc)) { /* Boost thread priority to make sure we wake up early for seamless window notifications * (not sure if it actually makes any difference though). */ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); /* * Main execution loop * Wait for the stop semaphore to be posted or a window event to arrive */ HANDLE hWaitEvent[3] = {0}; DWORD dwEventCount = 0; hWaitEvent[dwEventCount++] = ghStopSem; /* Check if seamless mode is not active and add seamless event to the list */ if (0 != ghSeamlessWtNotifyEvent) { hWaitEvent[dwEventCount++] = ghSeamlessWtNotifyEvent; } if (0 != vboxDtGetNotifyEvent()) { hWaitEvent[dwEventCount++] = vboxDtGetNotifyEvent(); } Log(("VBoxTray: Number of events to wait in main loop: %ld\n", dwEventCount)); while (true) { DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0); waitResult = waitResult - WAIT_OBJECT_0; /* Only enable for message debugging, lots of traffic! */ //Log(("VBoxTray: Wait result = %ld\n", waitResult)); if (waitResult == 0) { Log(("VBoxTray: Event 'Exit' triggered\n")); /* exit */ break; } else { BOOL fHandled = FALSE; if (waitResult < RT_ELEMENTS(hWaitEvent)) { if (hWaitEvent[waitResult]) { if (hWaitEvent[waitResult] == ghSeamlessWtNotifyEvent) { Log(("VBoxTray: Event 'Seamless' triggered\n")); /* seamless window notification */ VBoxSeamlessCheckWindows(); fHandled = TRUE; } else if (hWaitEvent[waitResult] == vboxDtGetNotifyEvent()) { Log(("VBoxTray: Event 'Dt' triggered\n")); VBoxTrayCheckDt(); fHandled = TRUE; } } } if (!fHandled) { /* timeout or a window message, handle it */ MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { Log(("VBoxTray: msg %p\n", msg.message)); if (msg.message == WM_QUIT) { Log(("VBoxTray: WM_QUIT!\n")); SetEvent(ghStopSem); } TranslateMessage(&msg); DispatchMessage(&msg); } } } } Log(("VBoxTray: Returned from main loop, exiting ...\n")); } Log(("VBoxTray: Waiting for services to stop ...\n")); vboxTrayStopServices(&svcEnv, vboxServiceTable); } /* Services started */ CloseHandle(ghStopSem); } /* Stop event created */ vboxTrayRemoveTrayIcon(); Log(("VBoxTray: Leaving vboxTrayServiceMain with rc=%Rrc\n", rc)); return rc; } /** * Main function */ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { /* Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */ HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, "VBoxTray"); if ( hMutexAppRunning != NULL && GetLastError() == ERROR_ALREADY_EXISTS) { /* Close the mutex for this application instance. */ CloseHandle (hMutexAppRunning); hMutexAppRunning = NULL; return 0; } LogRel(("VBoxTray: %s r%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr())); int rc = RTR3InitExeNoArguments(0); if (RT_SUCCESS(rc)) { rc = VbglR3Init(); if (RT_SUCCESS(rc)) rc = vboxTrayOpenBaseDriver(); } if (RT_SUCCESS(rc)) { /* Save instance handle. */ ghInstance = hInstance; hlpReportStatus(VBoxGuestFacilityStatus_Init); rc = vboxTrayCreateToolWindow(); if (RT_SUCCESS(rc)) { VBoxCapsInit(); rc = vboxStInit(ghwndToolWindow); if (!RT_SUCCESS(rc)) { WARN(("VBoxTray: vboxStInit failed, rc %d\n")); /* ignore the St Init failure. this can happen for < XP win that do not support WTS API * in that case the session is treated as active connected to the physical console * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */ Assert(vboxStIsActiveConsole()); } rc = vboxDtInit(); if (!RT_SUCCESS(rc)) { WARN(("VBoxTray: vboxDtInit failed, rc %d\n")); /* ignore the Dt Init failure. this can happen for < XP win that do not support WTS API * in that case the session is treated as active connected to the physical console * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */ Assert(vboxDtIsInputDesktop()); } rc = VBoxAcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, true); if (!RT_SUCCESS(rc)) { WARN(("VBoxAcquireGuestCaps cfg failed rc %d, ignoring..\n", rc)); } rc = vboxTraySetupSeamless(); if (RT_SUCCESS(rc)) { Log(("VBoxTray: Init successful\n")); rc = vboxTrayServiceMain(); if (RT_SUCCESS(rc)) hlpReportStatus(VBoxGuestFacilityStatus_Terminating); vboxTrayShutdownSeamless(); } /* it should be safe to call vboxDtTerm even if vboxStInit above failed */ vboxDtTerm(); /* it should be safe to call vboxStTerm even if vboxStInit above failed */ vboxStTerm(); VBoxCapsTerm(); vboxTrayDestroyToolWindow(); } if (RT_SUCCESS(rc)) hlpReportStatus(VBoxGuestFacilityStatus_Terminated); } if (RT_FAILURE(rc)) { LogRel(("VBoxTray: Error while starting, rc=%Rrc\n", rc)); hlpReportStatus(VBoxGuestFacilityStatus_Failed); } LogRel(("VBoxTray: Ended\n")); vboxTrayCloseBaseDriver(); /* Release instance mutex. */ if (hMutexAppRunning != NULL) { CloseHandle(hMutexAppRunning); hMutexAppRunning = NULL; } VbglR3Term(); return RT_SUCCESS(rc) ? 0 : 1; } /** * Window procedure for our tool window */ static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: { Log(("VBoxTray: Tool window created\n")); int rc = vboxTrayRegisterGlobalMessages(&s_vboxGlobalMessageTable[0]); if (RT_FAILURE(rc)) Log(("VBoxTray: Error registering global window messages, rc=%Rrc\n", rc)); return 0; } case WM_CLOSE: return 0; case WM_DESTROY: Log(("VBoxTray: Tool window destroyed\n")); KillTimer(ghwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION); return 0; case WM_TIMER: if (VBoxCapsCheckTimer(wParam)) return 0; if (vboxDtCheckTimer(wParam)) return 0; if (vboxStCheckTimer(wParam)) return 0; switch (wParam) { case TIMERID_VBOXTRAY_CHECK_HOSTVERSION: if (RT_SUCCESS(VBoxCheckHostVersion())) { /* After successful run we don't need to check again. */ KillTimer(ghwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION); } return 0; default: break; } break; /* Make sure other timers get processed the usual way! */ case WM_VBOXTRAY_TRAY_ICON: switch (lParam) { case WM_LBUTTONDBLCLK: break; case WM_RBUTTONDOWN: break; } return 0; case WM_VBOX_SEAMLESS_ENABLE: VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_STARTED); return 0; case WM_VBOX_SEAMLESS_DISABLE: VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED); return 0; case WM_DISPLAYCHANGE: case WM_VBOX_SEAMLESS_UPDATE: if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS)) VBoxSeamlessCheckWindows(); return 0; case WM_VBOX_GRAPHICS_SUPPORTED: VBoxGrapicsSetSupported(TRUE); return 0; case WM_VBOX_GRAPHICS_UNSUPPORTED: VBoxGrapicsSetSupported(FALSE); return 0; case WM_VBOXTRAY_VM_RESTORED: VBoxRestoreSession(); return 0; case WM_VBOXTRAY_VRDP_CHECK: VBoxRestoreCheckVRDP(); return 0; case WM_WTSSESSION_CHANGE: { BOOL fOldAllowedState = VBoxConsoleIsAllowed(); if (vboxStHandleEvent(wParam, lParam)) { if (!VBoxConsoleIsAllowed() != !fOldAllowedState) VBoxConsoleEnable(!fOldAllowedState); } return 0; } default: /* Handle all globally registered window messages. */ if (vboxTrayHandleGlobalMessages(&s_vboxGlobalMessageTable[0], uMsg, wParam, lParam)) { return 0; /* We handled the message. @todo Add return value!*/ } break; /* We did not handle the message, dispatch to DefWndProc. */ } /* Only if message was *not* handled by our switch above, dispatch * to DefWindowProc. */ return DefWindowProc(hWnd, uMsg, wParam, lParam); } /* St (session [state] tracking) functionality API impl */ typedef struct VBOXST { HWND hWTSAPIWnd; RTLDRMOD hLdrModWTSAPI32; BOOL fIsConsole; WTS_CONNECTSTATE_CLASS enmConnectState; UINT_PTR idDelayedInitTimer; BOOL (WINAPI * pfnWTSRegisterSessionNotification)(HWND hWnd, DWORD dwFlags); BOOL (WINAPI * pfnWTSUnRegisterSessionNotification)(HWND hWnd); BOOL (WINAPI * pfnWTSQuerySessionInformationA)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR *ppBuffer, DWORD *pBytesReturned); } VBOXST; static VBOXST gVBoxSt; static int vboxStCheckState() { int rc = VINF_SUCCESS; WTS_CONNECTSTATE_CLASS *penmConnectState = NULL; USHORT *pProtocolType = NULL; DWORD cbBuf = 0; if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *)&penmConnectState, &cbBuf)) { if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientProtocolType, (LPTSTR *)&pProtocolType, &cbBuf)) { gVBoxSt.fIsConsole = (*pProtocolType == 0); gVBoxSt.enmConnectState = *penmConnectState; return VINF_SUCCESS; } DWORD dwErr = GetLastError(); WARN(("VBoxTray: WTSQuerySessionInformationA WTSClientProtocolType failed, error = %08X\n", dwErr)); rc = RTErrConvertFromWin32(dwErr); } else { DWORD dwErr = GetLastError(); WARN(("VBoxTray: WTSQuerySessionInformationA WTSConnectState failed, error = %08X\n", dwErr)); rc = RTErrConvertFromWin32(dwErr); } /* failure branch, set to "console-active" state */ gVBoxSt.fIsConsole = TRUE; gVBoxSt.enmConnectState = WTSActive; return rc; } static int vboxStInit(HWND hWnd) { RT_ZERO(gVBoxSt); int rc = RTLdrLoadSystem("WTSAPI32.DLL", false /*fNoUnload*/, &gVBoxSt.hLdrModWTSAPI32); if (RT_SUCCESS(rc)) { rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSRegisterSessionNotification", (void **)&gVBoxSt.pfnWTSRegisterSessionNotification); if (RT_SUCCESS(rc)) { rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSUnRegisterSessionNotification", (void **)&gVBoxSt.pfnWTSUnRegisterSessionNotification); if (RT_SUCCESS(rc)) { rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSQuerySessionInformationA", (void **)&gVBoxSt.pfnWTSQuerySessionInformationA); if (RT_FAILURE(rc)) WARN(("VBoxTray: WTSQuerySessionInformationA not found\n")); } else WARN(("VBoxTray: WTSUnRegisterSessionNotification not found\n")); } else WARN(("VBoxTray: WTSRegisterSessionNotification not found\n")); if (RT_SUCCESS(rc)) { gVBoxSt.hWTSAPIWnd = hWnd; if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION)) vboxStCheckState(); else { DWORD dwErr = GetLastError(); WARN(("VBoxTray: WTSRegisterSessionNotification failed, error = %08X\n", dwErr)); if (dwErr == RPC_S_INVALID_BINDING) { gVBoxSt.idDelayedInitTimer = SetTimer(gVBoxSt.hWTSAPIWnd, TIMERID_VBOXTRAY_ST_DELAYED_INIT_TIMER, 2000, (TIMERPROC)NULL); gVBoxSt.fIsConsole = TRUE; gVBoxSt.enmConnectState = WTSActive; rc = VINF_SUCCESS; } else rc = RTErrConvertFromWin32(dwErr); } if (RT_SUCCESS(rc)) return VINF_SUCCESS; } RTLdrClose(gVBoxSt.hLdrModWTSAPI32); } else WARN(("VBoxTray: WTSAPI32 load failed, rc = %Rrc\n", rc)); RT_ZERO(gVBoxSt); gVBoxSt.fIsConsole = TRUE; gVBoxSt.enmConnectState = WTSActive; return rc; } static void vboxStTerm(void) { if (!gVBoxSt.hWTSAPIWnd) { WARN(("VBoxTray: vboxStTerm called for non-initialized St\n")); return; } if (gVBoxSt.idDelayedInitTimer) { /* notification is not registered, just kill timer */ KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer); gVBoxSt.idDelayedInitTimer = 0; } else { if (!gVBoxSt.pfnWTSUnRegisterSessionNotification(gVBoxSt.hWTSAPIWnd)) { DWORD dwErr = GetLastError(); WARN(("VBoxTray: WTSAPI32 load failed, error = %08X\n", dwErr)); } } RTLdrClose(gVBoxSt.hLdrModWTSAPI32); RT_ZERO(gVBoxSt); } #define VBOXST_DBG_MAKECASE(_val) case _val: return #_val; static const char* vboxStDbgGetString(DWORD val) { switch (val) { VBOXST_DBG_MAKECASE(WTS_CONSOLE_CONNECT); VBOXST_DBG_MAKECASE(WTS_CONSOLE_DISCONNECT); VBOXST_DBG_MAKECASE(WTS_REMOTE_CONNECT); VBOXST_DBG_MAKECASE(WTS_REMOTE_DISCONNECT); VBOXST_DBG_MAKECASE(WTS_SESSION_LOGON); VBOXST_DBG_MAKECASE(WTS_SESSION_LOGOFF); VBOXST_DBG_MAKECASE(WTS_SESSION_LOCK); VBOXST_DBG_MAKECASE(WTS_SESSION_UNLOCK); VBOXST_DBG_MAKECASE(WTS_SESSION_REMOTE_CONTROL); default: WARN(("VBoxTray: invalid WTS state %d\n", val)); return "Unknown"; } } static BOOL vboxStCheckTimer(WPARAM wEvent) { if (wEvent != gVBoxSt.idDelayedInitTimer) return FALSE; if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION)) { KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer); gVBoxSt.idDelayedInitTimer = 0; vboxStCheckState(); } else { DWORD dwErr = GetLastError(); WARN(("VBoxTray: timer WTSRegisterSessionNotification failed, error = %08X\n", dwErr)); Assert(gVBoxSt.fIsConsole == TRUE); Assert(gVBoxSt.enmConnectState == WTSActive); } return TRUE; } static BOOL vboxStHandleEvent(WPARAM wEvent, LPARAM SessionID) { WARN(("VBoxTray: WTS Event: %s\n", vboxStDbgGetString(wEvent))); BOOL fOldIsActiveConsole = vboxStIsActiveConsole(); vboxStCheckState(); return !vboxStIsActiveConsole() != !fOldIsActiveConsole; } static BOOL vboxStIsActiveConsole() { return (gVBoxSt.enmConnectState == WTSActive && gVBoxSt.fIsConsole); } /* * Dt (desktop [state] tracking) functionality API impl * * !!!NOTE: this API is NOT thread-safe!!! * */ typedef struct VBOXDT { HANDLE hNotifyEvent; BOOL fIsInputDesktop; UINT_PTR idTimer; RTLDRMOD hLdrModHook; BOOL (* pfnVBoxHookInstallActiveDesktopTracker)(HMODULE hDll); BOOL (* pfnVBoxHookRemoveActiveDesktopTracker)(); HDESK (WINAPI * pfnGetThreadDesktop)(DWORD dwThreadId); HDESK (WINAPI * pfnOpenInputDesktop)(DWORD dwFlags, BOOL fInherit, ACCESS_MASK dwDesiredAccess); BOOL (WINAPI * pfnCloseDesktop)(HDESK hDesktop); } VBOXDT; static VBOXDT gVBoxDt; static BOOL vboxDtCalculateIsInputDesktop() { BOOL fIsInputDt = FALSE; HDESK hInput = gVBoxDt.pfnOpenInputDesktop(0, FALSE, DESKTOP_CREATEWINDOW); if (hInput) { // DWORD dwThreadId = GetCurrentThreadId(); // HDESK hThreadDt = gVBoxDt.pfnGetThreadDesktop(dwThreadId); // if (hThreadDt) // { fIsInputDt = TRUE; // } // else // { // DWORD dwErr = GetLastError(); // WARN(("VBoxTray: pfnGetThreadDesktop for Seamless failed, last error = %08X\n", dwErr)); // } gVBoxDt.pfnCloseDesktop(hInput); } else { DWORD dwErr = GetLastError(); // WARN(("VBoxTray: pfnOpenInputDesktop for Seamless failed, last error = %08X\n", dwErr)); } return fIsInputDt; } static BOOL vboxDtCheckTimer(WPARAM wParam) { if (wParam != gVBoxDt.idTimer) return FALSE; VBoxTrayCheckDt(); return TRUE; } static int vboxDtInit() { int rc = VINF_SUCCESS; OSVERSIONINFO info; gMajorVersion = 5; /* Default to Windows XP. */ info.dwOSVersionInfoSize = sizeof(info); if (GetVersionEx(&info)) { WARN(("VBoxTray: Windows version %ld.%ld\n", info.dwMajorVersion, info.dwMinorVersion)); gMajorVersion = info.dwMajorVersion; } RT_ZERO(gVBoxDt); gVBoxDt.hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, VBOXHOOK_GLOBAL_DT_EVENT_NAME); if (gVBoxDt.hNotifyEvent != NULL) { /* Load the hook dll and resolve the necessary entry points. */ rc = RTLdrLoadAppPriv(VBOXHOOK_DLL_NAME, &gVBoxDt.hLdrModHook); if (RT_SUCCESS(rc)) { rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookInstallActiveDesktopTracker", (void **)&gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker); if (RT_SUCCESS(rc)) { rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookRemoveActiveDesktopTracker", (void **)&gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker); if (RT_FAILURE(rc)) WARN(("VBoxTray: VBoxHookRemoveActiveDesktopTracker not found\n")); } else WARN(("VBoxTray: VBoxHookInstallActiveDesktopTracker not found\n")); if (RT_SUCCESS(rc)) { /* Try get the system APIs we need. */ *(void **)&gVBoxDt.pfnGetThreadDesktop = RTLdrGetSystemSymbol("user32.dll", "GetThreadDesktop"); if (!gVBoxDt.pfnGetThreadDesktop) { WARN(("VBoxTray: GetThreadDesktop not found\n")); rc = VERR_NOT_SUPPORTED; } *(void **)&gVBoxDt.pfnOpenInputDesktop = RTLdrGetSystemSymbol("user32.dll", "OpenInputDesktop"); if (!gVBoxDt.pfnOpenInputDesktop) { WARN(("VBoxTray: OpenInputDesktop not found\n")); rc = VERR_NOT_SUPPORTED; } *(void **)&gVBoxDt.pfnCloseDesktop = RTLdrGetSystemSymbol("user32.dll", "CloseDesktop"); if (!gVBoxDt.pfnCloseDesktop) { WARN(("VBoxTray: CloseDesktop not found\n")); rc = VERR_NOT_SUPPORTED; } if (RT_SUCCESS(rc)) { BOOL fRc = FALSE; /* For Vista and up we need to change the integrity of the security descriptor, too. */ if (gMajorVersion >= 6) { HMODULE hModHook = (HMODULE)RTLdrGetNativeHandle(gVBoxDt.hLdrModHook); Assert((uintptr_t)hModHook != ~(uintptr_t)0); fRc = gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker(hModHook); if (!fRc) { DWORD dwErr = GetLastError(); WARN(("VBoxTray: pfnVBoxHookInstallActiveDesktopTracker failed, last error = %08X\n", dwErr)); } } if (!fRc) { gVBoxDt.idTimer = SetTimer(ghwndToolWindow, TIMERID_VBOXTRAY_DT_TIMER, 500, (TIMERPROC)NULL); if (!gVBoxDt.idTimer) { DWORD dwErr = GetLastError(); WARN(("VBoxTray: SetTimer error %08X\n", dwErr)); rc = RTErrConvertFromWin32(dwErr); } } if (RT_SUCCESS(rc)) { gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop(); return VINF_SUCCESS; } } } RTLdrClose(gVBoxDt.hLdrModHook); } else { DWORD dwErr = GetLastError(); WARN(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr)); rc = RTErrConvertFromWin32(dwErr); } CloseHandle(gVBoxDt.hNotifyEvent); } else { DWORD dwErr = GetLastError(); WARN(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr)); rc = RTErrConvertFromWin32(dwErr); } RT_ZERO(gVBoxDt); gVBoxDt.fIsInputDesktop = TRUE; return rc; } static void vboxDtTerm() { if (!gVBoxDt.hLdrModHook) return; gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker(); RTLdrClose(gVBoxDt.hLdrModHook); CloseHandle(gVBoxDt.hNotifyEvent); RT_ZERO(gVBoxDt); } /* @returns true on "IsInputDesktop" state change */ static BOOL vboxDtHandleEvent() { BOOL fIsInputDesktop = gVBoxDt.fIsInputDesktop; gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop(); return !fIsInputDesktop != !gVBoxDt.fIsInputDesktop; } static HANDLE vboxDtGetNotifyEvent() { return gVBoxDt.hNotifyEvent; } /* @returns true iff the application (VBoxTray) desktop is input */ static BOOL vboxDtIsInputDesktop() { return gVBoxDt.fIsInputDesktop; } /* we need to perform Acquire/Release using the file handled we use for rewuesting events from VBoxGuest * otherwise Acquisition mechanism will treat us as different client and will not propagate necessary requests * */ static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg) { DWORD cbReturned = 0; VBoxGuestCapsAquire Info; Log(("VBoxTray: VBoxAcquireGuestCaps or(0x%x), not(0x%x), cfx(%d)\n", fOr, fNot, fCfg)); Info.enmFlags = fCfg ? VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE : VBOXGUESTCAPSACQUIRE_FLAGS_NONE; Info.rc = VERR_NOT_IMPLEMENTED; Info.u32OrMask = fOr; Info.u32NotMask = fNot; if (!DeviceIoControl(ghVBoxDriver, VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE, &Info, sizeof(Info), &Info, sizeof(Info), &cbReturned, NULL)) { DWORD LastErr = GetLastError(); WARN(("VBoxTray: DeviceIoControl VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed LastErr %d\n", LastErr)); return RTErrConvertFromWin32(LastErr); } int rc = Info.rc; if (!RT_SUCCESS(rc)) { WARN(("VBoxTray: VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed rc %d\n", rc)); return rc; } return rc; } typedef enum VBOXCAPS_ENTRY_ACSTATE { /* the given cap is released */ VBOXCAPS_ENTRY_ACSTATE_RELEASED = 0, /* the given cap acquisition is in progress */ VBOXCAPS_ENTRY_ACSTATE_ACQUIRING, /* the given cap is acquired */ VBOXCAPS_ENTRY_ACSTATE_ACQUIRED } VBOXCAPS_ENTRY_ACSTATE; struct VBOXCAPS_ENTRY; struct VBOXCAPS; typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE)(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled); typedef struct VBOXCAPS_ENTRY { uint32_t fCap; uint32_t iCap; VBOXCAPS_ENTRY_FUNCSTATE enmFuncState; VBOXCAPS_ENTRY_ACSTATE enmAcState; PFNVBOXCAPS_ENTRY_ON_ENABLE pfnOnEnable; } VBOXCAPS_ENTRY; typedef struct VBOXCAPS { UINT_PTR idTimer; VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT]; } VBOXCAPS; static VBOXCAPS gVBoxCaps; static DECLCALLBACK(void) vboxCapsOnEnableSeamles(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled) { if (fEnabled) { Log(("VBoxTray: vboxCapsOnEnableSeamles: ENABLED\n")); Assert(pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED); Assert(pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED); VBoxSeamlessInstallHook(); } else { Log(("VBoxTray: vboxCapsOnEnableSeamles: DISABLED\n")); Assert(pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRED || pCap->enmFuncState != VBOXCAPS_ENTRY_FUNCSTATE_STARTED); VBoxSeamlessRemoveHook(); } } static void vboxCapsEntryAcStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_ACSTATE enmAcState) { VBOXCAPS *pConsole = &gVBoxCaps; Log(("VBoxTray: vboxCapsEntryAcStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n", enmAcState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState)); if (pCap->enmAcState == enmAcState) return; VBOXCAPS_ENTRY_ACSTATE enmOldAcState = pCap->enmAcState; pCap->enmAcState = enmAcState; if (enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED) { if (pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED) { if (pCap->pfnOnEnable) pCap->pfnOnEnable(pConsole, pCap, TRUE); } } else if (enmOldAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED) { if (pCap->pfnOnEnable) pCap->pfnOnEnable(pConsole, pCap, FALSE); } } static void vboxCapsEntryFuncStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState) { VBOXCAPS *pConsole = &gVBoxCaps; Log(("VBoxTray: vboxCapsEntryFuncStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n", enmFuncState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState)); if (pCap->enmFuncState == enmFuncState) return; VBOXCAPS_ENTRY_FUNCSTATE enmOldFuncState = pCap->enmFuncState; pCap->enmFuncState = enmFuncState; if (enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED) { Assert(enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED); if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED) { if (pCap->pfnOnEnable) pCap->pfnOnEnable(pConsole, pCap, TRUE); } } else if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED) { if (pCap->pfnOnEnable) pCap->pfnOnEnable(pConsole, pCap, FALSE); } } static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState) { VBOXCAPS *pConsole = &gVBoxCaps; VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCup]; vboxCapsEntryFuncStateSet(pCap, enmFuncState); } static int VBoxCapsInit() { VBOXCAPS *pConsole = &gVBoxCaps; memset(pConsole, 0, sizeof (*pConsole)); pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].fCap = VMMDEV_GUEST_SUPPORTS_SEAMLESS; pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].iCap = VBOXCAPS_ENTRY_IDX_SEAMLESS; pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].pfnOnEnable = vboxCapsOnEnableSeamles; pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].fCap = VMMDEV_GUEST_SUPPORTS_GRAPHICS; pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].iCap = VBOXCAPS_ENTRY_IDX_GRAPHICS; return VINF_SUCCESS; } static int VBoxCapsReleaseAll() { VBOXCAPS *pConsole = &gVBoxCaps; Log(("VBoxTray: VBoxCapsReleaseAll\n")); int rc = VBoxAcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, false); if (!RT_SUCCESS(rc)) { WARN(("VBoxTray: vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc)); return rc; } if (pConsole->idTimer) { Log(("VBoxTray: killing console timer\n")); KillTimer(ghwndToolWindow, pConsole->idTimer); pConsole->idTimer = 0; } for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i) { vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_RELEASED); } return rc; } static void VBoxCapsTerm() { VBOXCAPS *pConsole = &gVBoxCaps; VBoxCapsReleaseAll(); memset(pConsole, 0, sizeof (*pConsole)); } static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap) { VBOXCAPS *pConsole = &gVBoxCaps; return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED; } static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap) { VBOXCAPS *pConsole = &gVBoxCaps; return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && pConsole->aCaps[iCap].enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED; } static BOOL VBoxCapsCheckTimer(WPARAM wParam) { VBOXCAPS *pConsole = &gVBoxCaps; if (wParam != pConsole->idTimer) return FALSE; uint32_t u32AcquiredCaps = 0; BOOL fNeedNewTimer = FALSE; for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i) { VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[i]; if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRING) continue; int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false); if (RT_SUCCESS(rc)) { vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_ACQUIRED); u32AcquiredCaps |= pCap->fCap; } else { Assert(rc == VERR_RESOURCE_BUSY); fNeedNewTimer = TRUE; } } if (!fNeedNewTimer) { KillTimer(ghwndToolWindow, pConsole->idTimer); /* cleanup timer data */ pConsole->idTimer = 0; } return TRUE; } static int VBoxCapsEntryRelease(uint32_t iCap) { VBOXCAPS *pConsole = &gVBoxCaps; VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap]; if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_RELEASED) { WARN(("VBoxTray: invalid cap[%d] state[%d] on release\n", iCap, pCap->enmAcState)); return VERR_INVALID_STATE; } if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED) { int rc = VBoxAcquireGuestCaps(0, pCap->fCap, false); AssertRC(rc); } vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_RELEASED); return VINF_SUCCESS; } static int VBoxCapsEntryAcquire(uint32_t iCap) { VBOXCAPS *pConsole = &gVBoxCaps; Assert(VBoxConsoleIsAllowed()); VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap]; Log(("VBoxTray: VBoxCapsEntryAcquire %d\n", iCap)); if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_RELEASED) { WARN(("VBoxTray: invalid cap[%d] state[%d] on acquire\n", iCap, pCap->enmAcState)); return VERR_INVALID_STATE; } vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRING); int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false); if (RT_SUCCESS(rc)) { vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRED); return VINF_SUCCESS; } if (rc != VERR_RESOURCE_BUSY) { WARN(("VBoxTray: vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc)); return rc; } WARN(("VBoxTray: iCap %d is busy!\n", iCap)); /* the cap was busy, most likely it is still used by other VBoxTray instance running in another session, * queue the retry timer */ if (!pConsole->idTimer) { pConsole->idTimer = SetTimer(ghwndToolWindow, TIMERID_VBOXTRAY_CAPS_TIMER, 100, (TIMERPROC)NULL); if (!pConsole->idTimer) { DWORD dwErr = GetLastError(); WARN(("VBoxTray: SetTimer error %08X\n", dwErr)); return RTErrConvertFromWin32(dwErr); } } return rc; } static int VBoxCapsAcquireAllSupported() { VBOXCAPS *pConsole = &gVBoxCaps; Log(("VBoxTray: VBoxCapsAcquireAllSupported\n")); for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i) { if (pConsole->aCaps[i].enmFuncState >= VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED) { Log(("VBoxTray: VBoxCapsAcquireAllSupported acquiring cap %d, state %d\n", i, pConsole->aCaps[i].enmFuncState)); VBoxCapsEntryAcquire(i); } else { WARN(("VBoxTray: VBoxCapsAcquireAllSupported: WARN: cap %d not supported, state %d\n", i, pConsole->aCaps[i].enmFuncState)); } } return VINF_SUCCESS; } static BOOL VBoxConsoleIsAllowed() { return vboxDtIsInputDesktop() && vboxStIsActiveConsole(); } static void VBoxConsoleEnable(BOOL fEnable) { if (fEnable) VBoxCapsAcquireAllSupported(); else VBoxCapsReleaseAll(); } static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported) { if (fSupported) { VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED); if (VBoxConsoleIsAllowed()) VBoxCapsEntryAcquire(iCap); } else { VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED); VBoxCapsEntryRelease(iCap); } } void VBoxSeamlessSetSupported(BOOL fSupported) { VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_SEAMLESS, fSupported); } static void VBoxGrapicsSetSupported(BOOL fSupported) { VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_GRAPHICS, fSupported); }