VBoxSeamless.cpp revision 9fb5c93ff4449e19077128c07c080acbed35ceea
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync/* $Id: $ */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync/** @file
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * VBoxSeamless - Seamless windows
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync/*
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync *
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * available from http://www.virtualbox.org. This file is free software;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * you can redistribute it and/or modify it under the terms of the GNU
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * General Public License (GPL) as published by the Free Software
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync *
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * additional information or have any questions.
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#define _WIN32_WINNT 0x0500
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#include <windows.h>
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#include "VBoxTray.h"
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#include "VBoxSeamless.h"
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#include <VBoxHook.h>
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#include <VBoxDisplay.h>
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#include <VBox/VMMDev.h>
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#include <iprt/assert.h>
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#include "helpers.h"
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsynctypedef struct _VBOXSEAMLESSCONTEXT
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync const VBOXSERVICEENV *pEnv;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync HMODULE hModule;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync BOOL (* pfnVBoxInstallHook)(HMODULE hDll);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync BOOL (* pfnVBoxRemoveHook)();
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync LPRGNDATA lpRgnData;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync} VBOXSEAMLESSCONTEXT;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsynctypedef struct
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync HDC hdc;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync HRGN hrgn;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync RECT rect;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync} VBOX_ENUM_PARAM, *PVBOX_ENUM_PARAM;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncstatic VBOXSEAMLESSCONTEXT gCtx = {0};
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncvoid VBoxLogString(HANDLE hDriver, char *pszStr);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncint VBoxSeamlessInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessInit\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync *pfStartThread = false;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync gCtx.pEnv = pEnv;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync OSVERSIONINFO OSinfo;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync GetVersionEx (&OSinfo);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* We have to jump out here when using NT4, otherwise it complains about
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync a missing API function "UnhookWinEvent" used by the dynamically loaded VBoxHook.dll below */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (OSinfo.dwMajorVersion <= 4) /* Windows NT 4.0 or older */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessInit: Windows NT 4.0 or older not supported!\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return VERR_NOT_SUPPORTED;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* Will fail if SetWinEventHook is not present (version < NT4 SP6 apparently) */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync gCtx.hModule = LoadLibrary(VBOXHOOK_DLL_NAME);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (gCtx.hModule)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync *(uintptr_t *)&gCtx.pfnVBoxInstallHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxInstallHook");
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync *(uintptr_t *)&gCtx.pfnVBoxRemoveHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxRemoveHook");
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* inform the host that we support the seamless window mode */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync VMMDevReqGuestCapabilities vmmreqGuestCaps = {0};
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync vmmdevInitRequest((VMMDevRequestHeader*)&vmmreqGuestCaps, VMMDevReq_ReportGuestCapabilities);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync vmmreqGuestCaps.caps = VMMDEV_GUEST_SUPPORTS_SEAMLESS;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync DWORD cbReturned;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (!DeviceIoControl(pEnv->hDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(vmmreqGuestCaps)), &vmmreqGuestCaps, sizeof(vmmreqGuestCaps),
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync &vmmreqGuestCaps, sizeof(vmmreqGuestCaps), &cbReturned, NULL))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessInit: VMMDevReq_ReportGuestCapabilities: error doing IOCTL, last error: %d\n", GetLastError()));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return VERR_INVALID_PARAMETER;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync *pfStartThread = true;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync *ppInstance = &gCtx;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return VINF_SUCCESS;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessInit: LoadLibrary failed with %d\n", GetLastError()));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return VERR_INVALID_PARAMETER;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return VINF_SUCCESS;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync}
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncvoid VBoxSeamlessDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessDestroy\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* inform the host that we no longer support the seamless window mode */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync VMMDevReqGuestCapabilities vmmreqGuestCaps = {0};
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync vmmdevInitRequest((VMMDevRequestHeader*)&vmmreqGuestCaps, VMMDevReq_ReportGuestCapabilities);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync vmmreqGuestCaps.caps = 0;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync DWORD cbReturned;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (!DeviceIoControl(pEnv->hDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(vmmreqGuestCaps)), &vmmreqGuestCaps, sizeof(vmmreqGuestCaps),
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync &vmmreqGuestCaps, sizeof(vmmreqGuestCaps), &cbReturned, NULL))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VMMDevReq_ReportGuestCapabilities: error doing IOCTL, last error: %d\n", GetLastError()));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (gCtx.pfnVBoxRemoveHook)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync gCtx.pfnVBoxRemoveHook();
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (gCtx.hModule)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync FreeLibrary(gCtx.hModule);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync gCtx.hModule = 0;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync}
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncvoid VBoxSeamlessInstallHook()
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (gCtx.pfnVBoxInstallHook)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* Check current visible region state */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync VBoxSeamlessCheckWindows();
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync gCtx.pfnVBoxInstallHook(gCtx.hModule);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync}
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncvoid VBoxSeamlessRemoveHook()
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (gCtx.pfnVBoxRemoveHook)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync gCtx.pfnVBoxRemoveHook();
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (gCtx.lpRgnData)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync free(gCtx.lpRgnData);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync gCtx.lpRgnData = NULL;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync}
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncBOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync PVBOX_ENUM_PARAM lpParam = (PVBOX_ENUM_PARAM)lParam;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync DWORD dwStyle, dwExStyle;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync RECT rectWindow, rectVisible;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync dwStyle = GetWindowLong(hwnd, GWL_STYLE);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if ( !(dwStyle & WS_VISIBLE)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync || (dwStyle & WS_CHILD))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return TRUE;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxEnumFunc %x\n", hwnd));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* Only visible windows that are present on the desktop are interesting here */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if ( GetWindowRect(hwnd, &rectWindow)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync && IntersectRect(&rectVisible, &lpParam->rect, &rectWindow))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync char szWindowText[256];
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync szWindowText[0] = 0;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync GetWindowText(hwnd, szWindowText, sizeof(szWindowText));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* Filter out Windows XP shadow windows */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /** @todo still shows inside the guest */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if ( szWindowText[0] == 0
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync && dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync && dwExStyle == (WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_TRANSPARENT|WS_EX_TOPMOST))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("Filter out shadow window style=%x exstyle=%x\n", dwStyle, dwExStyle));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return TRUE;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /** @todo will this suffice? The Program Manager window covers the whole screen */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (strcmp(szWindowText, "Program Manager"))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("Enum hwnd=%x rect (%d,%d) (%d,%d)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync HRGN hrgn = CreateRectRgn(0,0,0,0);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync int ret = GetWindowRgn(hwnd, hrgn);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (ret == ERROR)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("GetWindowRgn failed with rc=%d\n", GetLastError()));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync SetRectRgn(hrgn, rectVisible.left, rectVisible.top, rectVisible.right, rectVisible.bottom);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* this region is relative to the window origin instead of the desktop origin */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync OffsetRgn(hrgn, rectWindow.left, rectWindow.top);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (lpParam->hrgn)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* create a union of the current visible region and the visible rectangle of this window. */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync CombineRgn(lpParam->hrgn, lpParam->hrgn, hrgn, RGN_OR);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync DeleteObject(hrgn);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync lpParam->hrgn = hrgn;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("Enum hwnd=%x rect (%d,%d) (%d,%d) (ignored)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("title=%s style=%x\n", szWindowText, dwStyle));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return TRUE; /* continue enumeration */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync}
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncvoid VBoxSeamlessCheckWindows()
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync VBOX_ENUM_PARAM param;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync param.hdc = GetDC(HWND_DESKTOP);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync param.hrgn = 0;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync GetWindowRect(GetDesktopWindow(), &param.rect);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxRecheckVisibleWindows desktop=%x rect (%d,%d) (%d,%d)\n", GetDesktopWindow(), param.rect.left, param.rect.top, param.rect.right, param.rect.bottom));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync EnumWindows(VBoxEnumFunc, (LPARAM)&param);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (param.hrgn)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync DWORD cbSize;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync cbSize = GetRegionData(param.hrgn, 0, NULL);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (cbSize)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync LPRGNDATA lpRgnData = (LPRGNDATA)malloc(cbSize);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync memset(lpRgnData, 0, cbSize);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (lpRgnData)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync cbSize = GetRegionData(param.hrgn, cbSize, lpRgnData);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (cbSize)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#ifdef DEBUG
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync RECT *lpRect = (RECT *)&lpRgnData->Buffer[0];
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("New visible region: \n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync for (DWORD i=0;i<lpRgnData->rdh.nCount;i++)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("visible rect (%d,%d)(%d,%d)\n", lpRect[i].left, lpRect[i].top, lpRect[i].right, lpRect[i].bottom));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync#endif
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if ( !gCtx.lpRgnData
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync || (gCtx.lpRgnData->rdh.dwSize + gCtx.lpRgnData->rdh.nRgnSize != cbSize)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync || memcmp(gCtx.lpRgnData, lpRgnData, cbSize))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* send to display driver */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync ExtEscape(param.hdc, VBOXESC_SETVISIBLEREGION, cbSize, (LPCSTR)lpRgnData, 0, NULL);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (gCtx.lpRgnData)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync free(gCtx.lpRgnData);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync gCtx.lpRgnData = lpRgnData;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("Visible rectangles haven't changed; ignore\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (lpRgnData != gCtx.lpRgnData)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync free(lpRgnData);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync DeleteObject(param.hrgn);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync ReleaseDC(HWND_DESKTOP, param.hdc);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync}
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync/**
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * Thread function to wait for and process seamless mode change
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * requests
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsyncunsigned __stdcall VBoxSeamlessThread(void *pInstance)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync{
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync VBOXSEAMLESSCONTEXT *pCtx = (VBOXSEAMLESSCONTEXT *)pInstance;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync bool fTerminate = false;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync VBoxGuestFilterMaskInfo maskInfo;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync DWORD cbReturned;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync BOOL fWasScreenSaverActive = FALSE, ret;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync maskInfo.u32OrMask = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync maskInfo.u32NotMask = 0;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (DeviceIoControl (gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: DeviceIOControl(CtlMask - or) succeeded\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: DeviceIOControl(CtlMask) failed, SeamlessChangeThread exited\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return 0;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync do
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* wait for a seamless change event */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync VBoxGuestWaitEventInfo waitEvent;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync waitEvent.u32TimeoutIn = 5000;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync waitEvent.u32EventMaskIn = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: DeviceIOControl succeded\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* are we supposed to stop? */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync break;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: checking event\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* did we get the right event? */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxTray: going to get seamless change information.\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* We got at least one event. Read the requested resolution
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * and try to set it until success. New events will not be seen
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync * but a new resolution will be read in this poll loop.
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync for (;;)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* get the seamless change request */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync VMMDevSeamlessChangeRequest seamlessChangeRequest = {0};
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync vmmdevInitRequest((VMMDevRequestHeader*)&seamlessChangeRequest, VMMDevReq_GetSeamlessChangeRequest);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync BOOL fSeamlessChangeQueried = DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(seamlessChangeRequest)), &seamlessChangeRequest, sizeof(seamlessChangeRequest),
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync &seamlessChangeRequest, sizeof(seamlessChangeRequest), &cbReturned, NULL);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (fSeamlessChangeQueried)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: mode change to %d\n", seamlessChangeRequest.mode));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync switch(seamlessChangeRequest.mode)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync case VMMDev_Seamless_Disabled:
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (fWasScreenSaverActive)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("Re-enabling the screensaver\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (!ret)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync PostMessage(gToolWindow, WM_VBOX_REMOVE_SEAMLESS_HOOK, 0, 0);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync break;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync case VMMDev_Seamless_Visible_Region:
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync ret = SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fWasScreenSaverActive, 0);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (!ret)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("SystemParametersInfo SPI_GETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (fWasScreenSaverActive)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("Disabling the screensaver\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (!ret)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync PostMessage(gToolWindow, WM_VBOX_INSTALL_SEAMLESS_HOOK, 0, 0);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync break;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync case VMMDev_Seamless_Host_Window:
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync break;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync default:
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync AssertFailed();
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync break;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync break;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: error from DeviceIoControl VBOXGUEST_IOCTL_VMMREQUEST\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* sleep a bit to not eat too much CPU while retrying */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* are we supposed to stop? */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 50) == WAIT_OBJECT_0)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync fTerminate = true;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync break;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxTray: error 0 from DeviceIoControl VBOXGUEST_IOCTL_WAITEVENT\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync /* sleep a bit to not eat too much CPU in case the above call always fails */
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 10) == WAIT_OBJECT_0)
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync fTerminate = true;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync break;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync while (!fTerminate);
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync maskInfo.u32OrMask = 0;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync maskInfo.u32NotMask = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync if (DeviceIoControl (gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: DeviceIOControl(CtlMask - not) succeeded\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync else
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync {
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: DeviceIOControl(CtlMask) failed\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync }
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync Log(("VBoxSeamlessThread: finished seamless change request thread\n"));
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync return 0;
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync}
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync
040abec2534dadc53ebc8fa378ef03f4feecb7dbvboxsync