VirtualBox

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

最後變更 在這個檔案從95890是 95868,由 vboxsync 提交於 3 年 前

VBoxTray/VBoxDisIf.cpp+VBoxSeamless.cpp: Replaced malloc, realloc and free with the IPRT equivalents (RTMemAlloc, RTMemRealloc, RTMemFree) - someone clearly didn't get the memo about using IPRT instead of CRT. bugref:10261

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 18.6 KB
 
1/* $Id: VBoxSeamless.cpp 95868 2022-07-27 01:28:13Z vboxsync $ */
2/** @file
3 * VBoxSeamless - Seamless windows
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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#define LOG_GROUP LOG_GROUP_DEFAULT
23#define _WIN32_WINNT 0x0500
24#include <iprt/win/windows.h>
25
26#include <VBox/log.h>
27
28#include <iprt/assert.h>
29#include <iprt/ldr.h>
30#include <iprt/mem.h>
31#include <iprt/system.h>
32
33#include <VBoxDisplay.h> /** @todo r=bird: Presumably the ../include/VBoxDisplay.h file rather than ./VBoxDisplay.h. WTF??? */
34#include <VBoxHook.h> /* from ../include/ */
35
36#include "VBoxTray.h"
37#include "VBoxHelpers.h"
38#include "VBoxSeamless.h"
39
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45typedef struct _VBOXSEAMLESSCONTEXT
46{
47 const VBOXSERVICEENV *pEnv;
48
49 RTLDRMOD hModHook;
50
51 BOOL (* pfnVBoxHookInstallWindowTracker)(HMODULE hDll);
52 BOOL (* pfnVBoxHookRemoveWindowTracker)();
53
54 PVBOXDISPIFESCAPE lpEscapeData;
55} VBOXSEAMLESSCONTEXT, *PVBOXSEAMLESSCONTEXT;
56
57typedef struct
58{
59 HDC hdc;
60 HRGN hrgn;
61} VBOX_ENUM_PARAM, *PVBOX_ENUM_PARAM;
62
63
64/*********************************************************************************************************************************
65* Global Variables *
66*********************************************************************************************************************************/
67static VBOXSEAMLESSCONTEXT g_Ctx = { 0 };
68
69
70/*********************************************************************************************************************************
71* Internal Functions *
72*********************************************************************************************************************************/
73void VBoxLogString(HANDLE hDriver, char *pszStr);
74
75
76
77static DECLCALLBACK(int) VBoxSeamlessInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
78{
79 LogFlowFuncEnter();
80
81 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
82 AssertPtr(pCtx);
83
84 pCtx->pEnv = pEnv;
85 pCtx->hModHook = NIL_RTLDRMOD;
86
87 int rc;
88
89 /* We have to jump out here when using NT4, otherwise it complains about
90 a missing API function "UnhookWinEvent" used by the dynamically loaded VBoxHook.dll below */
91 uint64_t const uNtVersion = RTSystemGetNtVersion();
92 if (uNtVersion < RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Windows NT 4.0 or older */
93 {
94 LogRel(("Seamless: Windows NT 4.0 or older not supported!\n"));
95 rc = VERR_NOT_SUPPORTED;
96 }
97 else
98 {
99 /* Will fail if SetWinEventHook is not present (version < NT4 SP6 apparently) */
100 rc = RTLdrLoadAppPriv(VBOXHOOK_DLL_NAME, &pCtx->hModHook);
101 if (RT_SUCCESS(rc))
102 {
103 *(PFNRT *)&pCtx->pfnVBoxHookInstallWindowTracker = RTLdrGetFunction(pCtx->hModHook, "VBoxHookInstallWindowTracker");
104 *(PFNRT *)&pCtx->pfnVBoxHookRemoveWindowTracker = RTLdrGetFunction(pCtx->hModHook, "VBoxHookRemoveWindowTracker");
105
106 if ( pCtx->pfnVBoxHookInstallWindowTracker
107 && pCtx->pfnVBoxHookRemoveWindowTracker)
108 {
109 VBoxSeamlessSetSupported(TRUE);
110
111 *ppInstance = pCtx;
112 }
113 else
114 {
115 LogRel(("Seamless: Not supported, skipping\n"));
116 rc = VERR_NOT_SUPPORTED;
117 }
118 }
119 else
120 {
121 LogRel(("Seamless: Could not load %s (%Rrc), skipping\n", VBOXHOOK_DLL_NAME, rc));
122 rc = VERR_NOT_SUPPORTED;
123 }
124 }
125
126 LogFlowFuncLeaveRC(rc);
127 return rc;
128}
129
130static DECLCALLBACK(void) VBoxSeamlessDestroy(void *pInstance)
131{
132 LogFlowFuncEnter();
133
134 if (!pInstance)
135 return;
136
137 PVBOXSEAMLESSCONTEXT pCtx = (PVBOXSEAMLESSCONTEXT)pInstance;
138 AssertPtr(pCtx);
139
140 VBoxSeamlessSetSupported(FALSE);
141
142 /* Inform the host that we no longer support the seamless window mode. */
143 if (pCtx->pfnVBoxHookRemoveWindowTracker)
144 pCtx->pfnVBoxHookRemoveWindowTracker();
145 if (pCtx->hModHook != NIL_RTLDRMOD)
146 {
147 RTLdrClose(pCtx->hModHook);
148 pCtx->hModHook = NIL_RTLDRMOD;
149 }
150 return;
151}
152
153static void VBoxSeamlessInstallHook(void)
154{
155 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
156 AssertPtr(pCtx);
157
158 if (pCtx->pfnVBoxHookInstallWindowTracker)
159 {
160 /* Check current visible region state */
161 VBoxSeamlessCheckWindows(true);
162
163 HMODULE hMod = (HMODULE)RTLdrGetNativeHandle(pCtx->hModHook);
164 Assert(hMod != (HMODULE)~(uintptr_t)0);
165 pCtx->pfnVBoxHookInstallWindowTracker(hMod);
166 }
167}
168
169static void VBoxSeamlessRemoveHook(void)
170{
171 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
172 AssertPtr(pCtx);
173
174 if (pCtx->pfnVBoxHookRemoveWindowTracker)
175 pCtx->pfnVBoxHookRemoveWindowTracker();
176
177 if (pCtx->lpEscapeData)
178 {
179 RTMemFree(pCtx->lpEscapeData);
180 pCtx->lpEscapeData = NULL;
181 }
182}
183
184extern HANDLE g_hSeamlessKmNotifyEvent;
185
186static VBOXDISPIF_SEAMLESS gVBoxDispIfSeamless; /** @todo r=andy Move this into VBOXSEAMLESSCONTEXT? */
187
188
189void VBoxSeamlessEnable(void)
190{
191 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
192 AssertPtr(pCtx);
193
194 Assert(g_hSeamlessKmNotifyEvent);
195
196 VBoxDispIfSeamlessCreate(&pCtx->pEnv->dispIf, &gVBoxDispIfSeamless, g_hSeamlessKmNotifyEvent);
197
198 VBoxSeamlessInstallHook();
199}
200
201void VBoxSeamlessDisable(void)
202{
203 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
204 AssertPtr(pCtx);
205 NOREF(pCtx);
206
207 VBoxSeamlessRemoveHook();
208
209 VBoxDispIfSeamlessTerm(&gVBoxDispIfSeamless);
210}
211
212BOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam) RT_NOTHROW_DEF
213{
214 PVBOX_ENUM_PARAM lpParam = (PVBOX_ENUM_PARAM)lParam;
215 DWORD dwStyle, dwExStyle;
216 RECT rectWindow, rectVisible;
217
218 dwStyle = GetWindowLong(hwnd, GWL_STYLE);
219 dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
220
221 if ( !(dwStyle & WS_VISIBLE) || (dwStyle & WS_CHILD))
222 return TRUE;
223
224 LogFlow(("VBoxTray: VBoxEnumFunc %x\n", hwnd));
225 /* Only visible windows that are present on the desktop are interesting here */
226 if (!GetWindowRect(hwnd, &rectWindow))
227 {
228 return TRUE;
229 }
230
231 char szWindowText[256];
232 char szWindowClass[256];
233 HWND hStart = NULL;
234
235 szWindowText[0] = 0;
236 szWindowClass[0] = 0;
237
238 GetWindowText(hwnd, szWindowText, sizeof(szWindowText));
239 GetClassName(hwnd, szWindowClass, sizeof(szWindowClass));
240
241 uint64_t const uNtVersion = RTSystemGetNtVersion();
242 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
243 {
244 hStart = ::FindWindowEx(GetDesktopWindow(), NULL, "Button", "Start");
245
246 if ( hwnd == hStart && !strcmp(szWindowText, "Start") )
247 {
248 /* for vista and above. To solve the issue of small bar above
249 * the Start button when mouse is hovered over the start button in seamless mode.
250 * Difference of 7 is observed in Win 7 platform between the dimensions of rectangle with Start title and its shadow.
251 */
252 rectWindow.top += 7;
253 rectWindow.bottom -=7;
254 }
255 }
256
257 rectVisible = rectWindow;
258
259 /* Filter out Windows XP shadow windows */
260 /** @todo still shows inside the guest */
261 if ( szWindowText[0] == 0 &&
262 (dwStyle == (WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS)
263 && dwExStyle == (WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST))
264 || (dwStyle == (WS_POPUP | WS_VISIBLE | WS_DISABLED | WS_CLIPSIBLINGS | WS_CLIPCHILDREN)
265 && dwExStyle == (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE))
266 || (dwStyle == (WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN)
267 && dwExStyle == (WS_EX_TOOLWINDOW)) )
268 {
269 Log(("VBoxTray: Filter out shadow window style=%x exstyle=%x\n", dwStyle, dwExStyle));
270 Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d) (filtered)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
271 Log(("VBoxTray: title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
272 return TRUE;
273 }
274
275 /** Such a windows covers the whole screen making desktop background*/
276 if (strcmp(szWindowText, "Program Manager") && strcmp(szWindowClass, "ApplicationFrameWindow"))
277 {
278 Log(("VBoxTray: Enum hwnd=%x rect (%d,%d)-(%d,%d) [%d x %d](applying)\n", hwnd,
279 rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom,
280 rectWindow.left - rectWindow.right, rectWindow.bottom - rectWindow.top));
281 Log(("VBoxTray: title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
282
283 HRGN hrgn = CreateRectRgn(0, 0, 0, 0);
284
285 int ret = GetWindowRgn(hwnd, hrgn);
286
287 if (ret == ERROR)
288 {
289 Log(("VBoxTray: GetWindowRgn failed with rc=%d, adding antire rect\n", GetLastError()));
290 SetRectRgn(hrgn, rectVisible.left, rectVisible.top, rectVisible.right, rectVisible.bottom);
291 }
292 else
293 {
294 /* this region is relative to the window origin instead of the desktop origin */
295 OffsetRgn(hrgn, rectWindow.left, rectWindow.top);
296 }
297
298 if (lpParam->hrgn)
299 {
300 /* create a union of the current visible region and the visible rectangle of this window. */
301 CombineRgn(lpParam->hrgn, lpParam->hrgn, hrgn, RGN_OR);
302 DeleteObject(hrgn);
303 }
304 else
305 lpParam->hrgn = hrgn;
306 }
307 else
308 {
309 Log(("VBoxTray: Enum hwnd=%x rect (%d,%d)-(%d,%d) [%d x %d](ignored)\n", hwnd,
310 rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom,
311 rectWindow.left - rectWindow.right, rectWindow.bottom - rectWindow.top));
312 Log(("VBoxTray: title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
313 }
314
315 return TRUE; /* continue enumeration */
316}
317
318void VBoxSeamlessCheckWindows(bool fForce)
319{
320 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
321 AssertPtr(pCtx);
322
323 if (!VBoxDispIfSeamlesIsValid(&gVBoxDispIfSeamless))
324 return;
325
326 VBOX_ENUM_PARAM param;
327
328 param.hdc = GetDC(HWND_DESKTOP);
329 param.hrgn = 0;
330
331 EnumWindows(VBoxEnumFunc, (LPARAM)&param);
332
333 if (param.hrgn)
334 {
335 DWORD cbSize = GetRegionData(param.hrgn, 0, NULL);
336 if (cbSize)
337 {
338 PVBOXDISPIFESCAPE lpEscapeData = (PVBOXDISPIFESCAPE)RTMemAllocZ(VBOXDISPIFESCAPE_SIZE(cbSize));
339 if (lpEscapeData)
340 {
341 lpEscapeData->escapeCode = VBOXESC_SETVISIBLEREGION;
342 LPRGNDATA lpRgnData = VBOXDISPIFESCAPE_DATA(lpEscapeData, RGNDATA);
343
344 cbSize = GetRegionData(param.hrgn, cbSize, lpRgnData);
345 if (cbSize)
346 {
347#ifdef LOG_ENABLED
348 RECT *paRects = (RECT *)&lpRgnData->Buffer[0];
349 Log(("VBoxTray: New visible region: \n"));
350 for (DWORD i = 0; i < lpRgnData->rdh.nCount; i++)
351 Log(("VBoxTray: visible rect (%d,%d)(%d,%d)\n",
352 paRects[i].left, paRects[i].top, paRects[i].right, paRects[i].bottom));
353#endif
354
355 LPRGNDATA lpCtxRgnData = VBOXDISPIFESCAPE_DATA(pCtx->lpEscapeData, RGNDATA);
356
357 if ( fForce
358 || !pCtx->lpEscapeData
359 || (lpCtxRgnData->rdh.dwSize + lpCtxRgnData->rdh.nRgnSize != cbSize)
360 || memcmp(lpCtxRgnData, lpRgnData, cbSize))
361 {
362 /* send to display driver */
363 VBoxDispIfSeamlessSubmit(&gVBoxDispIfSeamless, lpEscapeData, cbSize);
364
365 if (pCtx->lpEscapeData)
366 RTMemFree(pCtx->lpEscapeData);
367 pCtx->lpEscapeData = lpEscapeData;
368 }
369 else
370 Log(("VBoxTray: Visible rectangles haven't changed; ignore\n"));
371 }
372 if (lpEscapeData != pCtx->lpEscapeData)
373 RTMemFree(lpEscapeData);
374 }
375 }
376
377 DeleteObject(param.hrgn);
378 }
379
380 ReleaseDC(HWND_DESKTOP, param.hdc);
381}
382
383/**
384 * Thread function to wait for and process seamless mode change
385 * requests
386 */
387static DECLCALLBACK(int) VBoxSeamlessWorker(void *pvInstance, bool volatile *pfShutdown)
388{
389 AssertPtrReturn(pvInstance, VERR_INVALID_POINTER);
390 LogFlowFunc(("pvInstance=%p\n", pvInstance));
391
392 /*
393 * Tell the control thread that it can continue spawning services.
394 */
395 RTThreadUserSignal(RTThreadSelf());
396
397 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 0 /*fNot*/);
398 if (RT_FAILURE(rc))
399 {
400 LogRel(("Seamless: VbglR3CtlFilterMask(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST,0) failed with %Rrc, exiting ...\n", rc));
401 return rc;
402 }
403
404 BOOL fWasScreenSaverActive = FALSE;
405 for (;;)
406 {
407 /*
408 * Wait for a seamless change event, check for shutdown both before and after.
409 */
410 if (*pfShutdown)
411 {
412 rc = VINF_SUCCESS;
413 break;
414 }
415
416 /** @todo r=andy We do duplicate code here (see VbglR3SeamlessWaitEvent()). */
417 uint32_t fEvent = 0;
418 rc = VbglR3WaitEvent(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 5000 /*ms*/, &fEvent);
419
420 if (*pfShutdown)
421 {
422 rc = VINF_SUCCESS;
423 break;
424 }
425
426 if (RT_SUCCESS(rc))
427 {
428 /* did we get the right event? */
429 if (fEvent & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST)
430 {
431 /*
432 * We got at least one event. Read the requested resolution
433 * and try to set it until success. New events will not be seen
434 * but a new resolution will be read in this poll loop.
435 */
436 for (;;)
437 {
438 /* get the seamless change request */
439 VMMDevSeamlessMode enmMode = (VMMDevSeamlessMode)-1;
440 rc = VbglR3SeamlessGetLastEvent(&enmMode);
441 if (RT_SUCCESS(rc))
442 {
443 LogFlowFunc(("Mode changed to %d\n", enmMode));
444
445 BOOL fRet;
446 switch (enmMode)
447 {
448 case VMMDev_Seamless_Disabled:
449 if (fWasScreenSaverActive)
450 {
451 LogRel(("Seamless: Re-enabling the screensaver\n"));
452 fRet = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0);
453 if (!fRet)
454 LogRel(("Seamless: SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %ld\n", GetLastError()));
455 }
456 PostMessage(g_hwndToolWindow, WM_VBOX_SEAMLESS_DISABLE, 0, 0);
457 break;
458
459 case VMMDev_Seamless_Visible_Region:
460 fRet = SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fWasScreenSaverActive, 0);
461 if (!fRet)
462 LogRel(("Seamless: SystemParametersInfo SPI_GETSCREENSAVEACTIVE failed with %ld\n", GetLastError()));
463
464 if (fWasScreenSaverActive)
465 LogRel(("Seamless: Disabling the screensaver\n"));
466
467 fRet = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
468 if (!fRet)
469 LogRel(("Seamless: SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %ld\n", GetLastError()));
470 PostMessage(g_hwndToolWindow, WM_VBOX_SEAMLESS_ENABLE, 0, 0);
471 break;
472
473 case VMMDev_Seamless_Host_Window:
474 break;
475
476 default:
477 AssertFailed();
478 break;
479 }
480 break;
481 }
482
483 LogRel(("Seamless: VbglR3SeamlessGetLastEvent() failed with %Rrc\n", rc));
484
485 if (*pfShutdown)
486 break;
487
488 /* sleep a bit to not eat too much CPU while retrying */
489 RTThreadSleep(10);
490 }
491 }
492 }
493 /* sleep a bit to not eat too much CPU in case the above call always fails */
494 else if (rc != VERR_TIMEOUT)
495 RTThreadSleep(10);
496 }
497
498 int rc2 = VbglR3CtlFilterMask(0 /*fOk*/, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST);
499 if (RT_FAILURE(rc2))
500 LogRel(("Seamless: VbglR3CtlFilterMask(0, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST) failed with %Rrc\n", rc));
501
502 LogFlowFuncLeaveRC(rc);
503 return rc;
504}
505
506/**
507 * The service description.
508 */
509VBOXSERVICEDESC g_SvcDescSeamless =
510{
511 /* pszName. */
512 "seamless",
513 /* pszDescription. */
514 "Seamless Windows",
515 /* methods */
516 VBoxSeamlessInit,
517 VBoxSeamlessWorker,
518 NULL /* pfnStop */,
519 VBoxSeamlessDestroy
520};
521
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette