VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use