VirtualBox

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

Last change on this file was 99828, checked in by vboxsync, 12 months ago

*: A bunch of adjustments that allows using /permissive- with Visual C++ (qt 6.x necessity).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.9 KB
RevLine 
[21219]1/* $Id: VBoxVRDP.cpp 99828 2023-05-17 13:48:57Z vboxsync $ */
[4795]2/** @file
3 * VBoxVRDP - VBox VRDP connection notification
4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[4795]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
[4795]26 */
27
[95960]28#include <iprt/assert.h>
29#include <iprt/ldr.h>
[95961]30#include <VBox/log.h>
[95960]31
[4795]32/* 0x0501 for SPI_SETDROPSHADOW */
33#define _WIN32_WINNT 0x0501
[62679]34#include <iprt/win/windows.h>
[95960]35
36#include <VBox/VMMDev.h> /* for VMMDEV_EVENT_VRDP and VRDP_EXPERIENCE_LEVEL_XXX */
37
[8850]38#include "VBoxTray.h"
[33966]39#include "VBoxHelpers.h"
[4795]40#include "VBoxVRDP.h"
[51469]41
42
[4795]43
44/* The guest receives VRDP_ACTIVE/VRDP_INACTIVE notifications.
45 *
46 * When VRDP_ACTIVE is received, the guest asks host about the experience level.
47 * The experience level is an integer value, different values disable some GUI effects.
48 *
49 * On VRDP_INACTIVE the original values are restored.
50 *
51 * Note: that this is not controlled from the client, that is a per VM settings.
52 *
53 * Note: theming is disabled separately by EnableTheming.
54 */
55
56#define VBOX_SPI_STRING 0
57#define VBOX_SPI_BOOL_PTR 1
58#define VBOX_SPI_BOOL 2
59#define VBOX_SPI_PTR 3
60
61static ANIMATIONINFO animationInfoDisable =
62{
63 sizeof (ANIMATIONINFO),
64 FALSE
65};
66
[99828]67typedef struct VBOXVRDPEXPPARAM
[4795]68{
69 const char *name;
70 UINT uActionSet;
71 UINT uActionGet;
72 uint32_t level; /* The parameter remain enabled at this or higher level. */
73 int type;
74 void *pvDisable;
75 UINT cbSavedValue;
76 char achSavedValue[2 * MAX_PATH]; /* Large enough to save the bitmap path. */
[57741]77} VBOXVRDPEXPPARAM, *PVBOXVRDPEXPPARAM;
[4795]78
[99828]79typedef struct VBOXVRDPCONTEXT
[57741]80{
81 const VBOXSERVICEENV *pEnv;
82
83 uint32_t level;
84 BOOL fSavedThemeEnabled;
85
86 RTLDRMOD hModUxTheme;
87
88 HRESULT (* pfnEnableTheming)(BOOL fEnable);
89 BOOL (* pfnIsThemeActive)(VOID);
90} VBOXVRDPCONTEXT, *PVBOXVRDPCONTEXT;
91
92static VBOXVRDPCONTEXT g_Ctx = { 0 };
93
[4795]94#define SPI_(l, a) #a, SPI_SET##a, SPI_GET##a, VRDP_EXPERIENCE_LEVEL_##l
95
[57741]96static VBOXVRDPEXPPARAM s_aSPIParams[] =
[4795]97{
[99828]98 { SPI_(MEDIUM, DESKWALLPAPER), VBOX_SPI_STRING, (void *)"" },
[4795]99 { SPI_(FULL, DROPSHADOW), VBOX_SPI_BOOL_PTR, },
100 { SPI_(HIGH, FONTSMOOTHING), VBOX_SPI_BOOL, },
101 { SPI_(FULL, MENUFADE), VBOX_SPI_BOOL_PTR, },
102 { SPI_(FULL, COMBOBOXANIMATION), VBOX_SPI_BOOL_PTR, },
103 { SPI_(FULL, CURSORSHADOW), VBOX_SPI_BOOL_PTR, },
104 { SPI_(HIGH, GRADIENTCAPTIONS), VBOX_SPI_BOOL_PTR, },
105 { SPI_(FULL, LISTBOXSMOOTHSCROLLING), VBOX_SPI_BOOL_PTR, },
106 { SPI_(FULL, MENUANIMATION), VBOX_SPI_BOOL_PTR, },
107 { SPI_(FULL, SELECTIONFADE), VBOX_SPI_BOOL_PTR, },
108 { SPI_(FULL, TOOLTIPANIMATION), VBOX_SPI_BOOL_PTR, },
109 { SPI_(FULL, ANIMATION), VBOX_SPI_PTR, &animationInfoDisable, sizeof (ANIMATIONINFO) },
110 { SPI_(MEDIUM, DRAGFULLWINDOWS), VBOX_SPI_BOOL, }
111};
112
113#undef SPI_
114
[57741]115static void vboxExperienceSet(uint32_t uLevel)
[4795]116{
[57741]117 for (size_t i = 0; i < RT_ELEMENTS(s_aSPIParams); i++)
[4795]118 {
[57741]119 PVBOXVRDPEXPPARAM pParam = &s_aSPIParams[i];
120 if (pParam->level > uLevel)
[4795]121 {
122 /*
123 * The parameter has to be disabled.
124 */
[57741]125 LogFlowFunc(("Saving %s\n", pParam->name));
[13836]126
[4795]127 /* Save the current value. */
[57741]128 switch (pParam->type)
[4795]129 {
130 case VBOX_SPI_STRING:
131 {
132 /* The 2nd parameter is size in characters of the buffer.
[13836]133 * The 3rd parameter points to the buffer.
[4795]134 */
[57741]135 SystemParametersInfo (pParam->uActionGet,
[4795]136 MAX_PATH,
[57741]137 pParam->achSavedValue,
[4795]138 0);
139 } break;
140
141 case VBOX_SPI_BOOL:
142 case VBOX_SPI_BOOL_PTR:
143 {
144 /* The 3rd parameter points to BOOL. */
[57741]145 SystemParametersInfo (pParam->uActionGet,
[4795]146 0,
[57741]147 pParam->achSavedValue,
[4795]148 0);
149 } break;
150
151 case VBOX_SPI_PTR:
152 {
[13836]153 /* The 3rd parameter points to the structure.
[4795]154 * The cbSize member of this structure must be set.
155 * The uiParam parameter must alos be set.
156 */
[57741]157 if (pParam->cbSavedValue > sizeof (pParam->achSavedValue))
[4795]158 {
[57741]159 LogFlowFunc(("Not enough space %d > %d\n", pParam->cbSavedValue, sizeof (pParam->achSavedValue)));
[4795]160 break;
161 }
[13836]162
[57741]163 *(UINT *)&pParam->achSavedValue[0] = pParam->cbSavedValue;
[13836]164
[57741]165 SystemParametersInfo (s_aSPIParams[i].uActionGet,
166 s_aSPIParams[i].cbSavedValue,
167 s_aSPIParams[i].achSavedValue,
[4795]168 0);
169 } break;
170
171 default:
172 break;
173 }
[13836]174
[57741]175 LogFlowFunc(("Disabling %s\n", pParam->name));
[13836]176
[4795]177 /* Disable the feature. */
[57741]178 switch (pParam->type)
[4795]179 {
180 case VBOX_SPI_STRING:
181 {
182 /* The 3rd parameter points to the string. */
[57741]183 SystemParametersInfo (pParam->uActionSet,
[4795]184 0,
[57741]185 pParam->pvDisable,
[4795]186 SPIF_SENDCHANGE);
187 } break;
188
189 case VBOX_SPI_BOOL:
190 {
191 /* The 2nd parameter is BOOL. */
[57741]192 SystemParametersInfo (pParam->uActionSet,
[4795]193 FALSE,
194 NULL,
195 SPIF_SENDCHANGE);
196 } break;
197
198 case VBOX_SPI_BOOL_PTR:
199 {
200 /* The 3rd parameter is NULL to disable. */
[57741]201 SystemParametersInfo (pParam->uActionSet,
[4795]202 0,
203 NULL,
204 SPIF_SENDCHANGE);
205 } break;
206
207 case VBOX_SPI_PTR:
208 {
209 /* The 3rd parameter points to the structure. */
[57741]210 SystemParametersInfo (pParam->uActionSet,
[4795]211 0,
[57741]212 pParam->pvDisable,
[4795]213 SPIF_SENDCHANGE);
214 } break;
215
216 default:
217 break;
218 }
219 }
220 }
221}
222
[57741]223static void vboxExperienceRestore(uint32_t uLevel)
[4795]224{
225 int i;
[57741]226 for (i = 0; i < RT_ELEMENTS(s_aSPIParams); i++)
[4795]227 {
[57741]228 PVBOXVRDPEXPPARAM pParam = &s_aSPIParams[i];
229 if (pParam->level > uLevel)
[4795]230 {
[57741]231 LogFlowFunc(("Restoring %s\n", pParam->name));
[13836]232
[4795]233 /* Restore the feature. */
[57741]234 switch (pParam->type)
[4795]235 {
236 case VBOX_SPI_STRING:
237 {
238 /* The 3rd parameter points to the string. */
[57741]239 SystemParametersInfo (pParam->uActionSet,
[4795]240 0,
[57741]241 pParam->achSavedValue,
[4795]242 SPIF_SENDCHANGE);
243 } break;
244
245 case VBOX_SPI_BOOL:
246 {
247 /* The 2nd parameter is BOOL. */
[57741]248 SystemParametersInfo (pParam->uActionSet,
249 *(BOOL *)&pParam->achSavedValue[0],
[4795]250 NULL,
251 SPIF_SENDCHANGE);
252 } break;
253
254 case VBOX_SPI_BOOL_PTR:
255 {
256 /* The 3rd parameter is NULL to disable. */
[57741]257 BOOL fSaved = *(BOOL *)&pParam->achSavedValue[0];
[4795]258
[57741]259 SystemParametersInfo (pParam->uActionSet,
[4795]260 0,
261 fSaved? &fSaved: NULL,
262 SPIF_SENDCHANGE);
263 } break;
264
265 case VBOX_SPI_PTR:
266 {
267 /* The 3rd parameter points to the structure. */
[57741]268 SystemParametersInfo (pParam->uActionSet,
[4795]269 0,
[57741]270 pParam->achSavedValue,
[4795]271 SPIF_SENDCHANGE);
272 } break;
273
274 default:
275 break;
276 }
277 }
278 }
279}
280
[57741]281static DECLCALLBACK(int) VBoxVRDPInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
[4795]282{
[57741]283 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
284 AssertPtrReturn(ppInstance, VERR_INVALID_POINTER);
[4795]285
[57741]286 LogFlowFuncEnter();
[13836]287
[57741]288 PVBOXVRDPCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
289 AssertPtr(pCtx);
[4795]290
[57741]291 pCtx->pEnv = pEnv;
292 pCtx->level = VRDP_EXPERIENCE_LEVEL_FULL;
293 pCtx->fSavedThemeEnabled = FALSE;
[4795]294
[57741]295 int rc = RTLdrLoadSystem("UxTheme.dll", false /*fNoUnload*/, &g_Ctx.hModUxTheme);
[46593]296 if (RT_SUCCESS(rc))
[4795]297 {
[57741]298 *(PFNRT *)&pCtx->pfnEnableTheming = RTLdrGetFunction(g_Ctx.hModUxTheme, "EnableTheming");
299 *(PFNRT *)&pCtx->pfnIsThemeActive = RTLdrGetFunction(g_Ctx.hModUxTheme, "IsThemeActive");
300
301 *ppInstance = &g_Ctx;
[4795]302 }
303 else
304 {
[57741]305 g_Ctx.hModUxTheme = NIL_RTLDRMOD;
306 g_Ctx.pfnEnableTheming = NULL;
307 g_Ctx.pfnIsThemeActive = NULL;
[4795]308 }
[13836]309
[62135]310 /* Tell the caller that the service does not work but it is OK to continue. */
311 if (RT_FAILURE(rc))
312 rc = VERR_NOT_SUPPORTED;
313
[57741]314 LogFlowFuncLeaveRC(rc);
315 return rc;
[4795]316}
317
[57741]318static DECLCALLBACK(void) VBoxVRDPDestroy(void *pInstance)
319{
320 AssertPtrReturnVoid(pInstance);
[4795]321
[57741]322 LogFlowFuncEnter();
323
324 PVBOXVRDPCONTEXT pCtx = (PVBOXVRDPCONTEXT)pInstance;
325
[4795]326 vboxExperienceRestore (pCtx->level);
[57741]327 if (pCtx->hModUxTheme != NIL_RTLDRMOD)
[46593]328 {
[57741]329 RTLdrClose(g_Ctx.hModUxTheme);
330 pCtx->hModUxTheme = NIL_RTLDRMOD;
[46593]331 }
[57741]332
[4795]333 return;
334}
335
336/**
337 * Thread function to wait for and process mode change requests
338 */
[68437]339static DECLCALLBACK(int) VBoxVRDPWorker(void *pvInstance, bool volatile *pfShutdown)
[4795]340{
[68437]341 AssertPtrReturn(pvInstance, VERR_INVALID_POINTER);
342 PVBOXVRDPCONTEXT pCtx = (PVBOXVRDPCONTEXT)pvInstance;
[57741]343
344 LogFlowFuncEnter();
345
346 /*
[68437]347 * Tell the control thread that it can continue spawning services.
[57741]348 */
349 RTThreadUserSignal(RTThreadSelf());
350
[68437]351 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_VRDP, 0 /*fNot*/);
352 if (RT_FAILURE(rc))
[4795]353 {
[68437]354 LogRel(("VbglR3CtlFilterMask(VMMDEV_EVENT_VRDP, 0) failed with %Rrc, exiting...\n"));
355 return rc;
[4795]356 }
357
[57741]358 for (;;)
[4795]359 {
[68437]360 /*
361 * Wait for the event, checking the shutdown flag both before and after the call.
362 */
363 if (*pfShutdown)
[4795]364 {
[68437]365 rc = VINF_SUCCESS;
366 break;
367 }
[4795]368
[68437]369 uint32_t fEvent = 0;
370 rc = VbglR3WaitEvent(VMMDEV_EVENT_VRDP, 5000 /*ms*/, &fEvent);
371
372 if (*pfShutdown)
373 {
374 rc = VINF_SUCCESS;
375 break;
376 }
377
378 if (RT_SUCCESS(rc))
379 {
[4795]380 /* did we get the right event? */
[68437]381 if (fEvent & VMMDEV_EVENT_VRDP)
[4795]382 {
[68437]383 bool fActive = false;
384 uint32_t uExperienceLevel = 0;
385 rc = VbglR3VrdpGetChangeRequest(&fActive, &uExperienceLevel);
386 if (RT_SUCCESS(rc))
[4795]387 {
[68437]388 LogFlowFunc(("u8VRDPActive = %d, level %d\n", fActive, uExperienceLevel));
[13836]389
[68437]390 if (fActive)
[4795]391 {
[68437]392 pCtx->level = uExperienceLevel;
[4795]393 vboxExperienceSet (pCtx->level);
[13836]394
[4795]395 if (pCtx->level == VRDP_EXPERIENCE_LEVEL_ZERO
396 && pCtx->pfnEnableTheming
397 && pCtx->pfnIsThemeActive)
398 {
399 pCtx->fSavedThemeEnabled = pCtx->pfnIsThemeActive ();
[13836]400
[51469]401 LogFlowFunc(("pCtx->fSavedThemeEnabled = %d\n", pCtx->fSavedThemeEnabled));
[13836]402
[4795]403 if (pCtx->fSavedThemeEnabled)
404 {
405 pCtx->pfnEnableTheming (FALSE);
406 }
407 }
408 }
409 else
410 {
411 if (pCtx->level == VRDP_EXPERIENCE_LEVEL_ZERO
412 && pCtx->pfnEnableTheming
413 && pCtx->pfnIsThemeActive)
414 {
415 if (pCtx->fSavedThemeEnabled)
416 {
[63100]417 /** @todo the call returns S_OK but theming remains disabled. */
[4795]418 HRESULT hrc = pCtx->pfnEnableTheming (TRUE);
[63100]419 LogFlowFunc(("enabling theme rc = 0x%08X\n", hrc)); NOREF(hrc);
[4795]420 pCtx->fSavedThemeEnabled = FALSE;
421 }
422 }
[13836]423
[4795]424 vboxExperienceRestore (pCtx->level);
[13836]425
[4795]426 pCtx->level = VRDP_EXPERIENCE_LEVEL_FULL;
427 }
428 }
429 else
430 {
431 /* sleep a bit to not eat too much CPU in case the above call always fails */
[57741]432 RTThreadSleep(10);
[4795]433 }
434 }
[13836]435 }
[68437]436 /* sleep a bit to not eat too much CPU in case the above call always fails */
[4795]437 else
[57741]438 RTThreadSleep(50);
439 }
[4795]440
[68437]441 int rc2 = VbglR3CtlFilterMask(0 /*fOr*/, VMMDEV_EVENT_VRDP);
442 if (RT_FAILURE(rc2))
443 LogRel(("VbglR3CtlFilterMask(0 /*fOr*/, VMMDEV_EVENT_VRDP) failed with %Rrc\n", rc));
[4795]444
[57741]445 LogFlowFuncLeaveRC(rc);
446 return rc;
[4795]447}
448
[57741]449/**
450 * The service description.
451 */
452VBOXSERVICEDESC g_SvcDescVRDP =
453{
454 /* pszName. */
455 "VRDP",
456 /* pszDescription. */
457 "VRDP Connection Notification",
458 /* methods */
459 VBoxVRDPInit,
460 VBoxVRDPWorker,
461 NULL /* pfnStop */,
462 VBoxVRDPDestroy
463};
464
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use