VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceDisplayConfig.cpp

Last change on this file was 106304, checked in by vboxsync, 7 weeks ago

VBoxService/VBoxServiceDisplayConfig: Don't debug assert in vgsvcDisplayConfigInit() when not finding some of the optional external symbols, as this will result in an asserting debug binary on older guests (e.g. Win7).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.2 KB
Line 
1/** @file
2 * VBoxService - Guest displays handling.
3 */
4
5/*
6 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.virtualbox.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * SPDX-License-Identifier: GPL-3.0-only
25 */
26
27#include <iprt/win/windows.h>
28#include <wtsapi32.h>
29
30#include <iprt/errcore.h>
31#include <iprt/system.h>
32
33#include <VBox/VBoxGuestLib.h>
34#include "VBoxServiceInternal.h"
35#include "VBoxServiceUtils.h"
36
37#undef NTDDI_VERSION
38#define NTDDI_VERSION NTDDI_LONGHORN
39#include <iprt/win/d3dkmthk.h>
40
41#define VBOX_WITH_WDDM
42#include <VBoxDisplay.h>
43
44
45/*********************************************************************************************************************************
46* Global Variables *
47*********************************************************************************************************************************/
48
49static PFND3DKMT_ENUMADAPTERS g_pfnD3DKMTEnumAdapters;
50static PFND3DKMT_OPENADAPTERFROMLUID g_pfnD3DKMTOpenAdapterFromLuid;
51static PFND3DKMT_CLOSEADAPTER g_pfnD3DKMTCloseAdapter;
52static PFND3DKMT_ESCAPE g_pfnD3DKMTEscape;
53
54static decltype(WTSFreeMemory) *g_pfnWTSFreeMemory = NULL;
55static decltype(WTSQuerySessionInformationA) *g_pfnWTSQuerySessionInformationA = NULL;
56static decltype(WTSEnumerateSessionsA) *g_pfnWTSEnumerateSessionsA = NULL;
57
58/**
59 * @interface_method_impl{VBOXSERVICE,pfnInit}
60 */
61static DECLCALLBACK(int) vgsvcDisplayConfigInit(void)
62{
63 if (RTSystemGetNtVersion() < RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
64 {
65 VGSvcVerbose(1, "displayconfig requires Windows Vista or later\n");
66 return VERR_SERVICE_DISABLED;
67 }
68
69 RTLDRMOD hLdrMod;
70 int rc = RTLdrLoadSystem("gdi32.dll", true /*fNoUnload*/, &hLdrMod);
71 if (RT_SUCCESS(rc))
72 {
73 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTEnumAdapters", (void **)&g_pfnD3DKMTEnumAdapters);
74 if (RT_SUCCESS(rc))
75 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTOpenAdapterFromLuid", (void **)&g_pfnD3DKMTOpenAdapterFromLuid);
76 if (RT_SUCCESS(rc))
77 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTCloseAdapter", (void **)&g_pfnD3DKMTCloseAdapter);
78 if (RT_SUCCESS(rc))
79 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTEscape", (void **)&g_pfnD3DKMTEscape);
80 RTLdrClose(hLdrMod);
81 }
82
83 if (RT_FAILURE(rc))
84 {
85 VGSvcVerbose(1, "d3dkmthk API is not available (%Rrc)\n", rc);
86 g_pfnD3DKMTEnumAdapters = NULL;
87 g_pfnD3DKMTOpenAdapterFromLuid = NULL;
88 g_pfnD3DKMTCloseAdapter = NULL;
89 g_pfnD3DKMTEscape = NULL;
90 return VERR_SERVICE_DISABLED;
91 }
92
93 rc = RTLdrLoadSystem("wtsapi32.dll", true /*fNoUnload*/, &hLdrMod);
94 if (RT_SUCCESS(rc))
95 {
96 rc = RTLdrGetSymbol(hLdrMod, "WTSFreeMemory", (void **)&g_pfnWTSFreeMemory);
97 if (RT_SUCCESS(rc))
98 rc = RTLdrGetSymbol(hLdrMod, "WTSQuerySessionInformationA", (void **)&g_pfnWTSQuerySessionInformationA);
99 if (RT_SUCCESS(rc))
100 rc = RTLdrGetSymbol(hLdrMod, "WTSEnumerateSessionsA", (void **)&g_pfnWTSEnumerateSessionsA);
101 RTLdrClose(hLdrMod);
102 }
103
104 if (RT_FAILURE(rc))
105 {
106 VGSvcVerbose(1, "WtsApi32.dll API is not available (%Rrc)\n", rc);
107 g_pfnWTSFreeMemory = NULL;
108 g_pfnWTSQuerySessionInformationA = NULL;
109 g_pfnWTSEnumerateSessionsA = NULL;
110 return VERR_SERVICE_DISABLED;
111 }
112
113 return VINF_SUCCESS;
114}
115
116void ReconnectDisplays(uint32_t cDisplays, VMMDevDisplayDef *paDisplays)
117{
118 D3DKMT_HANDLE hAdapter;
119 D3DKMT_ENUMADAPTERS EnumAdapters = {0};
120 NTSTATUS rcNt;
121 uint32_t u32ConnectMask = 0, u32DisconnectMask = 0;
122
123 for(uint32_t i = 0; i < cDisplays; i++)
124 {
125 uint32_t u32Mask = RT_BIT(paDisplays[i].idDisplay);
126
127 if (paDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
128 u32DisconnectMask |= u32Mask;
129 else
130 u32ConnectMask |= u32Mask;
131 }
132
133 VGSvcVerbose(3, "ReconnectDisplays u32ConnectMask 0x%RX32, u32DisconnectMask 0x%RX32\n", u32ConnectMask, u32DisconnectMask);
134
135 EnumAdapters.NumAdapters = RT_ELEMENTS(EnumAdapters.Adapters);
136 rcNt = g_pfnD3DKMTEnumAdapters(&EnumAdapters);
137
138 VGSvcVerbose(3, "D3DKMTEnumAdapters rcNt=%#x NumAdapters=%d\n", rcNt, EnumAdapters.NumAdapters);
139
140 for(ULONG id = 0; id < EnumAdapters.NumAdapters; id++)
141 {
142 D3DKMT_ADAPTERINFO *pAdapterInfo = &EnumAdapters.Adapters[id];
143 VGSvcVerbose(3, "#%d: NumOfSources=%d hAdapter=0x%p Luid(%u, %u)\n", id,
144 pAdapterInfo->NumOfSources, pAdapterInfo->hAdapter, pAdapterInfo->AdapterLuid.HighPart, pAdapterInfo->AdapterLuid.LowPart);
145 }
146
147 D3DKMT_OPENADAPTERFROMLUID OpenAdapterData;
148 RT_ZERO(OpenAdapterData);
149 OpenAdapterData.AdapterLuid = EnumAdapters.Adapters[0].AdapterLuid;
150 rcNt = g_pfnD3DKMTOpenAdapterFromLuid(&OpenAdapterData);
151 VGSvcVerbose(3, "D3DKMTOpenAdapterFromLuid rcNt=%#x hAdapter=0x%p\n", rcNt, OpenAdapterData.hAdapter);
152
153 hAdapter = OpenAdapterData.hAdapter;
154
155 if (hAdapter)
156 {
157 VBOXDISPIFESCAPE_RECONNECT_TARGETS VBoxEscapeReconnectTargets = {{0}};
158
159 VBoxEscapeReconnectTargets.EscapeHdr.escapeCode = VBOXESC_RECONNECT_TARGETS;
160 VBoxEscapeReconnectTargets.EscapeHdr.u32CmdSpecific = 0;
161 VBoxEscapeReconnectTargets.u32ConnectMask = u32ConnectMask;
162 VBoxEscapeReconnectTargets.u32DisconnectMask = u32DisconnectMask;
163
164 D3DKMT_ESCAPE EscapeData = {0};
165 EscapeData.hAdapter = hAdapter;
166 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
167 EscapeData.Flags.HardwareAccess = 1;
168 EscapeData.pPrivateDriverData = &VBoxEscapeReconnectTargets;
169 EscapeData.PrivateDriverDataSize = sizeof (VBoxEscapeReconnectTargets);
170
171 rcNt = g_pfnD3DKMTEscape(&EscapeData);
172 VGSvcVerbose(3, "D3DKMTEscape rcNt=%#x\n", rcNt);
173
174 D3DKMT_CLOSEADAPTER CloseAdapter;
175 CloseAdapter.hAdapter = hAdapter;
176
177 rcNt = g_pfnD3DKMTCloseAdapter(&CloseAdapter);
178 VGSvcVerbose(3, "D3DKMTCloseAdapter rcNt=%#x\n", rcNt);
179 }
180}
181
182static char* WTSSessionState2Str(WTS_CONNECTSTATE_CLASS State)
183{
184 switch(State)
185 {
186 RT_CASE_RET_STR(WTSActive);
187 RT_CASE_RET_STR(WTSConnected);
188 RT_CASE_RET_STR(WTSConnectQuery);
189 RT_CASE_RET_STR(WTSShadow);
190 RT_CASE_RET_STR(WTSDisconnected);
191 RT_CASE_RET_STR(WTSIdle);
192 RT_CASE_RET_STR(WTSListen);
193 RT_CASE_RET_STR(WTSReset);
194 RT_CASE_RET_STR(WTSDown);
195 RT_CASE_RET_STR(WTSInit);
196 default:
197 return "Unknown";
198 }
199}
200
201bool HasActiveLocalUser(void)
202{
203 WTS_SESSION_INFO *paSessionInfos = NULL;
204 DWORD cSessionInfos = 0;
205 bool fRet = false;
206
207 if (g_pfnWTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &paSessionInfos, &cSessionInfos))
208 {
209 VGSvcVerbose(3, "WTSEnumerateSessionsA got %u sessions\n", cSessionInfos);
210
211 for(DWORD i = 0; i < cSessionInfos; i++)
212 {
213 VGSvcVerbose(3, "WTS session[%u] SessionId (%u) pWinStationName (%s) State (%s %u)\n", i,
214 paSessionInfos[i].SessionId, paSessionInfos[i].pWinStationName,
215 WTSSessionState2Str(paSessionInfos[i].State), paSessionInfos[i].State);
216
217 if (paSessionInfos[i].State == WTSActive && RTStrNICmpAscii(paSessionInfos[i].pWinStationName, RT_STR_TUPLE("Console")) == 0)
218 {
219 VGSvcVerbose(2, "Found active WTS session %u connected to Console\n", paSessionInfos[i].SessionId);
220 fRet = true;
221 }
222 }
223 }
224 else
225 {
226 VGSvcError("WTSEnumerateSessionsA failed %#x\n", GetLastError());
227 }
228
229 if (paSessionInfos)
230 g_pfnWTSFreeMemory(paSessionInfos);
231
232 return fRet;
233}
234
235/**
236 * @interface_method_impl{VBOXSERVICE,pfnWorker}
237 */
238DECLCALLBACK(int) vgsvcDisplayConfigWorker(bool volatile *pfShutdown)
239{
240 int rc = VINF_SUCCESS;
241
242 /*
243 * Tell the control thread that it can continue spawning services.
244 */
245 RTThreadUserSignal(RTThreadSelf());
246 /*
247 * The Work Loop.
248 */
249
250 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
251 VGSvcVerbose(3, "VbglR3CtlFilterMask set rc=%Rrc\n", rc);
252
253 for (;;)
254 {
255 uint32_t fEvents = 0;
256
257 if (HasActiveLocalUser())
258 {
259 RTThreadSleep(1000);
260 }
261 else
262 {
263 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
264 VGSvcVerbose(3, "VbglR3AcquireGuestCaps acquire VMMDEV_GUEST_SUPPORTS_GRAPHICS rc=%Rrc\n", rc);
265
266 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 2000 /*ms*/, &fEvents);
267 VGSvcVerbose(3, "VbglR3WaitEvent rc=%Rrc\n", rc);
268
269 if (RT_SUCCESS(rc))
270 {
271 VMMDevDisplayDef aDisplays[64];
272 uint32_t cDisplays = RT_ELEMENTS(aDisplays);
273
274 rc = VbglR3GetDisplayChangeRequestMulti(cDisplays, &cDisplays, &aDisplays[0], true);
275 VGSvcVerbose(3, "VbglR3GetDisplayChangeRequestMulti rc=%Rrc cDisplays=%d\n", rc, cDisplays);
276 if (cDisplays > 0)
277 {
278 for(uint32_t i = 0; i < cDisplays; i++)
279 {
280 VGSvcVerbose(2, "%u) Display[%u] flags=%#x (%dx%d)\n", i, aDisplays[i].idDisplay,
281 aDisplays[i].fDisplayFlags,
282 aDisplays[i].cx, aDisplays[i].cy);
283 }
284
285 ReconnectDisplays(cDisplays, &aDisplays[0]);
286 }
287 }
288 else
289 {
290 /* To prevent CPU throttle in case of multiple failures */
291 RTThreadSleep(200);
292 }
293
294 rc = VbglR3AcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS, false);
295 VGSvcVerbose(3, "VbglR3AcquireGuestCaps release VMMDEV_GUEST_SUPPORTS_GRAPHICS rc=%Rrc\n", rc);
296 }
297
298 if (*pfShutdown)
299 break;
300 }
301
302 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST);
303 VGSvcVerbose(3, "VbglR3CtlFilterMask cleared rc=%Rrc\n", rc);
304
305 return rc;
306}
307
308/**
309 * @interface_method_impl{VBOXSERVICE,pfnStop}
310 */
311static DECLCALLBACK(void) vgsvcDisplayConfigStop(void)
312{
313}
314
315/**
316 * @interface_method_impl{VBOXSERVICE,pfnTerm}
317 */
318static DECLCALLBACK(void) vgsvcDisplayConfigTerm(void)
319{
320}
321
322/**
323 * The 'displayconfig' service description.
324 */
325VBOXSERVICE g_DisplayConfig =
326{
327 /* pszName. */
328 "displayconfig",
329 /* pszDescription. */
330 "Display configuration",
331 /* pszUsage. */
332 NULL,
333 /* pszOptions. */
334 NULL,
335 /* methods */
336 VGSvcDefaultPreInit,
337 VGSvcDefaultOption,
338 vgsvcDisplayConfigInit,
339 vgsvcDisplayConfigWorker,
340 vgsvcDisplayConfigStop,
341 vgsvcDisplayConfigTerm
342};
Note: See TracBrowser for help on using the repository browser.

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