VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxService/VBoxService.cpp@ 3803

Last change on this file since 3803 was 3789, checked in by vboxsync, 18 years ago

Changes for seamless mode switching

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.4 KB
Line 
1/** @file
2 * VBoxService - Guest Additions Service
3 */
4
5/*
6 * Copyright (C) 2006-2007 innotek GmbH
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License as published by the Free Software Foundation,
12 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
13 * distribution. VirtualBox OSE is distributed in the hope that it will
14 * be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * If you received this file as part of a commercial VirtualBox
17 * distribution, then only the terms of your commercial VirtualBox
18 * license agreement apply instead of the previous paragraph.
19 *
20 */
21
22#include "VBoxService.h"
23#include "resource.h"
24#include <malloc.h>
25
26#include "helpers.h"
27
28/* global variables */
29HANDLE gVBoxDriver;
30HANDLE gStopSem;
31SERVICE_STATUS gVBoxServiceStatus;
32SERVICE_STATUS_HANDLE gVBoxServiceStatusHandle;
33HINSTANCE gInstance;
34HWND gToolWindow;
35
36
37/* prototypes */
38VOID DisplayChangeThread(void *dummy);
39LRESULT CALLBACK VBoxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
40
41
42#ifdef DEBUG
43/**
44 * Helper function to send a message to WinDbg
45 *
46 * @param String message string
47 */
48void WriteLog(char *String, ...)
49{
50 DWORD cbReturned;
51 CHAR Buffer[1024];
52 VMMDevReqLogString *pReq = (VMMDevReqLogString *)Buffer;
53
54 va_list va;
55
56 va_start(va, String);
57
58 vmmdevInitRequest(&pReq->header, VMMDevReq_LogString);
59 vsprintf(pReq->szString, String, va);
60 OutputDebugStringA(pReq->szString);
61 pReq->header.size += strlen(pReq->szString);
62
63 DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, pReq, pReq->header.size,
64 pReq, pReq->header.size, &cbReturned, NULL);
65
66 va_end (va);
67 return;
68}
69#endif
70
71
72
73/* The shared clipboard service prototypes. */
74int VBoxClipboardInit (const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread);
75unsigned __stdcall VBoxClipboardThread (void *pInstance);
76void VBoxClipboardDestroy (const VBOXSERVICEENV *pEnv, void *pInstance);
77/* The seamless windows service prototypes */
78int VBoxSeamlessInit (const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread);
79unsigned __stdcall VBoxSeamlessThread (void *pInstance);
80void VBoxSeamlessDestroy (const VBOXSERVICEENV *pEnv, void *pInstance);
81
82/* The service table. */
83static VBOXSERVICEINFO vboxServiceTable[] =
84{
85 {
86 "Shared Clipboard",
87 VBoxClipboardInit,
88 VBoxClipboardThread,
89 VBoxClipboardDestroy
90 },
91 {
92 "Seamless Windows",
93 VBoxSeamlessInit,
94 VBoxSeamlessThread,
95 VBoxSeamlessDestroy
96 },
97 {
98 NULL
99 }
100};
101
102static int vboxStartServices (VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
103{
104 pEnv->hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
105
106 if (!pEnv->hStopEvent)
107 {
108 /* Could not create event. */
109 return VERR_NOT_SUPPORTED;
110 }
111
112 while (pTable->pszName)
113 {
114 dprintf(("Starting %s...\n", pTable->pszName));
115
116 int rc = VINF_SUCCESS;
117
118 bool fStartThread = false;
119
120 pTable->hThread = (HANDLE)0;
121 pTable->pInstance = NULL;
122 pTable->fStarted = false;
123
124 if (pTable->pfnInit)
125 {
126 rc = pTable->pfnInit (pEnv, &pTable->pInstance, &fStartThread);
127 }
128
129 if (VBOX_FAILURE (rc))
130 {
131 dprintf(("Failed to initialize rc = %Vrc.\n", rc));
132 }
133 else
134 {
135 if (pTable->pfnThread && fStartThread)
136 {
137 unsigned threadid;
138
139 pTable->hThread = (HANDLE)_beginthreadex (NULL, /* security */
140 0, /* stacksize */
141 pTable->pfnThread,
142 pTable->pInstance,
143 0, /* initflag */
144 &threadid);
145
146 if (pTable->hThread == (HANDLE)(0))
147 {
148 rc = VERR_NOT_SUPPORTED;
149 }
150 }
151
152 if (VBOX_FAILURE (rc))
153 {
154 dprintf(("Failed to start the thread.\n"));
155
156 if (pTable->pfnDestroy)
157 {
158 pTable->pfnDestroy (pEnv, pTable->pInstance);
159 }
160 }
161 else
162 {
163 pTable->fStarted = true;
164 }
165 }
166
167 /* Advance to next table element. */
168 pTable++;
169 }
170
171 return VINF_SUCCESS;
172}
173
174static void vboxStopServices (VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
175{
176 if (!pEnv->hStopEvent)
177 {
178 return;
179 }
180
181 /* Signal to all threads. */
182 SetEvent(pEnv->hStopEvent);
183
184 while (pTable->pszName)
185 {
186 if (pTable->fStarted)
187 {
188 if (pTable->pfnThread)
189 {
190 /* There is a thread, wait for termination. */
191 WaitForSingleObject(pTable->hThread, INFINITE);
192
193 CloseHandle (pTable->hThread);
194 pTable->hThread = 0;
195 }
196
197 if (pTable->pfnDestroy)
198 {
199 pTable->pfnDestroy (pEnv, pTable->pInstance);
200 }
201
202 pTable->fStarted = false;
203 }
204
205 /* Advance to next table element. */
206 pTable++;
207 }
208
209 CloseHandle (pEnv->hStopEvent);
210}
211
212
213void WINAPI VBoxServiceStart(void)
214{
215 dprintf(("VBoxService: Start\n"));
216
217 VBOXSERVICEENV svcEnv;
218
219 DWORD status = NO_ERROR;
220
221 /* open VBox guest driver */
222 gVBoxDriver = CreateFile(VBOXGUEST_DEVICE_NAME,
223 GENERIC_READ | GENERIC_WRITE,
224 FILE_SHARE_READ | FILE_SHARE_WRITE,
225 NULL,
226 OPEN_EXISTING,
227 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
228 NULL);
229 if (gVBoxDriver == INVALID_HANDLE_VALUE)
230 {
231 dprintf(("VBoxService: could not open VBox Guest Additions driver! rc = %d\n", GetLastError()));
232 status = ERROR_GEN_FAILURE;
233 }
234
235 dprintf(("VBoxService: Driver h %p, st %p\n", gVBoxDriver, status));
236
237 if (status == NO_ERROR)
238 {
239 /* create a custom window class */
240 WNDCLASS windowClass = {0};
241 windowClass.style = CS_NOCLOSE;
242 windowClass.lpfnWndProc = (WNDPROC)VBoxToolWndProc;
243 windowClass.hInstance = gInstance;
244 windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
245 windowClass.lpszClassName = "VirtualBoxTool";
246 if (!RegisterClass(&windowClass))
247 status = GetLastError();
248 }
249
250 dprintf(("VBoxService: Class st %p\n", status));
251
252 if (status == NO_ERROR)
253 {
254 /* create our window */
255 gToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
256 "VirtualBoxTool", "VirtualBoxTool",
257 WS_POPUPWINDOW,
258 -200, -200, 100, 100, NULL, NULL, gInstance, NULL);
259 if (!gToolWindow)
260 status = GetLastError();
261 else
262 {
263 /* move the window beneach the mouse pointer so that we get access to it */
264 POINT mousePos;
265 GetCursorPos(&mousePos);
266 SetWindowPos(gToolWindow, HWND_TOPMOST, mousePos.x - 10, mousePos.y - 10, 0, 0,
267 SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
268 /* change the mouse pointer so that we can go for a hardware shape */
269 SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
270 SetCursor(LoadCursor(NULL, IDC_ARROW));
271 /* move back our tool window */
272 SetWindowPos(gToolWindow, HWND_TOPMOST, -200, -200, 0, 0,
273 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
274 }
275 }
276
277 dprintf(("VBoxService: Window h %p, st %p\n", gToolWindow, status));
278
279 if (status == NO_ERROR)
280 {
281 gStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
282 if (gStopSem == NULL)
283 {
284 dprintf(("VBoxService: CreateEvent failed: rc = %d\n", GetLastError()));
285 return;
286 }
287 }
288
289 /*
290 * Start services listed in the vboxServiceTable.
291 */
292 svcEnv.hInstance = gInstance;
293 svcEnv.hDriver = gVBoxDriver;
294
295 if (status == NO_ERROR)
296 {
297 int rc = vboxStartServices (&svcEnv, vboxServiceTable);
298
299 if (VBOX_FAILURE (rc))
300 {
301 status = ERROR_GEN_FAILURE;
302 }
303 }
304
305 /* create display change thread */
306 HANDLE hDisplayChangeThread;
307 if (status == NO_ERROR)
308 {
309 hDisplayChangeThread = (HANDLE)_beginthread(DisplayChangeThread, 0, NULL);
310 if ((int)hDisplayChangeThread == -1L)
311 status = ERROR_GEN_FAILURE;
312 }
313
314 dprintf(("VBoxService: hDisplayChangeThread h %p, st %p\n", hDisplayChangeThread, status));
315
316 /* terminate service if something went wrong */
317 if (status != NO_ERROR)
318 {
319 vboxStopServices (&svcEnv, vboxServiceTable);
320 return;
321 }
322
323 BOOL fTrayIconCreated = false;
324
325 /* prepare the system tray icon */
326 NOTIFYICONDATA ndata;
327 memset (&ndata, 0, sizeof (ndata));
328 ndata.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA);
329 ndata.hWnd = gToolWindow;
330 ndata.uID = 2000;
331 ndata.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
332 ndata.uCallbackMessage = WM_USER;
333 ndata.hIcon = LoadIcon(gInstance, MAKEINTRESOURCE(IDI_VIRTUALBOX));
334 sprintf(ndata.szTip, "innotek VirtualBox Guest Additions %d.%d.%d", VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD);
335
336 dprintf(("VBoxService: ndata.hWnd %08X, ndata.hIcon = %p\n", ndata.hWnd, ndata.hIcon));
337
338 /*
339 * Main execution loop
340 * Wait for the stop semaphore to be posted or a window event to arrive
341 */
342 while(true)
343 {
344 DWORD waitResult = MsgWaitForMultipleObjectsEx(1, &gStopSem, 500, QS_ALLINPUT, 0);
345 if (waitResult == WAIT_OBJECT_0)
346 {
347 dprintf(("VBoxService: exit\n"));
348 /* exit */
349 break;
350 }
351 else if (waitResult == WAIT_OBJECT_0 + 1)
352 {
353 /* a window message, handle it */
354 MSG msg;
355 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
356 {
357 dprintf(("VBoxService: msg %p\n", msg.message));
358 if (msg.message == WM_QUIT)
359 {
360 dprintf(("VBoxService: WM_QUIT!\n"));
361 SetEvent(gStopSem);
362 continue;
363 }
364 TranslateMessage(&msg);
365 DispatchMessage(&msg);
366 }
367 }
368 else /* timeout */
369 {
370#ifndef DEBUG_sandervl
371 dprintf(("VBoxService: timed out\n"));
372#endif
373 /* we might have to repeat this operation because the shell might not be loaded yet */
374 if (!fTrayIconCreated)
375 {
376 fTrayIconCreated = Shell_NotifyIcon(NIM_ADD, &ndata);
377 dprintf(("VBoxService: fTrayIconCreated = %d, err %08X\n", fTrayIconCreated, GetLastError ()));
378 }
379 }
380 }
381
382 dprintf(("VBoxService: returned from main loop, exiting...\n"));
383
384 /* remove the system tray icon */
385 Shell_NotifyIcon(NIM_DELETE, &ndata);
386
387 dprintf(("VBoxService: waiting for display change thread...\n"));
388
389 /* wait for the display change thread to terminate */
390 WaitForSingleObject(hDisplayChangeThread, INFINITE);
391
392 vboxStopServices (&svcEnv, vboxServiceTable);
393
394 dprintf(("VBoxService: destroying tool window...\n"));
395
396 /* destroy the tool window */
397 DestroyWindow(gToolWindow);
398
399 UnregisterClass("VirtualBoxTool", gInstance);
400
401 CloseHandle(gVBoxDriver);
402 CloseHandle(gStopSem);
403
404 dprintf(("VBoxService: leaving service main function\n"));
405
406 return;
407}
408
409
410/**
411 * Main function
412 */
413int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
414{
415 dprintf(("VBoxService: WinMain\n"));
416 gInstance = hInstance;
417 VBoxServiceStart ();
418 return 0;
419}
420
421static bool isVBoxDisplayDriverActive (void)
422{
423 bool result = false;
424
425 DISPLAY_DEVICE dispDevice;
426
427 FillMemory(&dispDevice, sizeof(DISPLAY_DEVICE), 0);
428
429 dispDevice.cb = sizeof(DISPLAY_DEVICE);
430
431 INT devNum = 0;
432
433 while (EnumDisplayDevices(NULL,
434 devNum,
435 &dispDevice,
436 0))
437 {
438 DDCLOG(("DevNum:%d\nName:%s\nString:%s\nID:%s\nKey:%s\nFlags=%08X\n\n",
439 devNum,
440 &dispDevice.DeviceName[0],
441 &dispDevice.DeviceString[0],
442 &dispDevice.DeviceID[0],
443 &dispDevice.DeviceKey[0],
444 dispDevice.StateFlags));
445
446 if (dispDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
447 {
448 DDCLOG(("Primary device.\n"));
449
450 if (strcmp(&dispDevice.DeviceString[0], "VirtualBox Graphics Adapter") == 0)
451 {
452 DDCLOG(("VBox display driver is active.\n"));
453 result = true;
454 }
455
456 break;
457 }
458
459 FillMemory(&dispDevice, sizeof(DISPLAY_DEVICE), 0);
460
461 dispDevice.cb = sizeof(DISPLAY_DEVICE);
462
463 devNum++;
464 }
465
466 return result;
467}
468
469/* ChangeDisplaySettingsEx does not exist in NT. ResizeDisplayDevice uses the function. */
470typedef LONG WINAPI defChangeDisplaySettingsEx(LPCTSTR lpszDeviceName, LPDEVMODE lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam);
471static defChangeDisplaySettingsEx *pChangeDisplaySettingsEx = NULL;
472
473/* Returns TRUE to try again. */
474static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
475{
476 BOOL fModeReset = (Width == 0 && Height == 0 && BitsPerPixel == 0);
477
478 DISPLAY_DEVICE DisplayDevice;
479
480 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
481 DisplayDevice.cb = sizeof(DisplayDevice);
482
483 /* Find out how many display devices the system has */
484 DWORD NumDevices = 0;
485 DWORD i = 0;
486 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
487 {
488 DDCLOG(("[%d] %s\n", i, DisplayDevice.DeviceName));
489
490 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
491 {
492 DDCLOG(("Found primary device. err %d\n", GetLastError ()));
493 NumDevices++;
494 }
495 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
496 {
497
498 DDCLOG(("Found secondary device. err %d\n", GetLastError ()));
499 NumDevices++;
500 }
501
502 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
503 DisplayDevice.cb = sizeof(DisplayDevice);
504 i++;
505 }
506
507 DDCLOG(("Found total %d devices. err %d\n", NumDevices, GetLastError ()));
508
509 if (NumDevices == 0 || Id >= NumDevices)
510 {
511 DDCLOG(("Requested identifier %d is invalid. err %d\n", Id, GetLastError ()));
512 return FALSE;
513 }
514
515 DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices);
516 DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices);
517 RECTL *paRects = (RECTL *)alloca (sizeof (RECTL) * NumDevices);
518
519 /* Fetch information about current devices and modes. */
520 DWORD DevNum = 0;
521 DWORD DevPrimaryNum = 0;
522
523 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
524 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
525
526 i = 0;
527 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
528 {
529 DDCLOG(("[%d(%d)] %s\n", i, DevNum, DisplayDevice.DeviceName));
530
531 BOOL bFetchDevice = FALSE;
532
533 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
534 {
535 DDCLOG(("Found primary device. err %d\n", GetLastError ()));
536 DevPrimaryNum = DevNum;
537 bFetchDevice = TRUE;
538 }
539 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
540 {
541
542 DDCLOG(("Found secondary device. err %d\n", GetLastError ()));
543 bFetchDevice = TRUE;
544 }
545
546 if (bFetchDevice)
547 {
548 if (DevNum >= NumDevices)
549 {
550 DDCLOG(("%d >= %d\n", NumDevices, DevNum));
551 return FALSE;
552 }
553
554 paDisplayDevices[DevNum] = DisplayDevice;
555
556 ZeroMemory(&paDeviceModes[DevNum], sizeof(DEVMODE));
557 paDeviceModes[DevNum].dmSize = sizeof(DEVMODE);
558 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
559 ENUM_REGISTRY_SETTINGS, &paDeviceModes[DevNum]))
560 {
561 DDCLOG(("EnumDisplaySettings err %d\n", GetLastError ()));
562 return FALSE;
563 }
564
565 DDCLOG(("%dx%d at %d,%d\n",
566 paDeviceModes[DevNum].dmPelsWidth,
567 paDeviceModes[DevNum].dmPelsHeight,
568 paDeviceModes[DevNum].dmPosition.x,
569 paDeviceModes[DevNum].dmPosition.y));
570
571 paRects[DevNum].left = paDeviceModes[DevNum].dmPosition.x;
572 paRects[DevNum].top = paDeviceModes[DevNum].dmPosition.y;
573 paRects[DevNum].right = paDeviceModes[DevNum].dmPosition.x + paDeviceModes[DevNum].dmPelsWidth;
574 paRects[DevNum].bottom = paDeviceModes[DevNum].dmPosition.y + paDeviceModes[DevNum].dmPelsHeight;
575 DevNum++;
576 }
577
578 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
579 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
580 i++;
581 }
582
583 if (Width == 0)
584 {
585 Width = paRects[Id].right - paRects[Id].left;
586 }
587
588 if (Height == 0)
589 {
590 Height = paRects[Id].bottom - paRects[Id].top;
591 }
592
593 /* Check whether a mode reset or a change is requested. */
594 if ( !fModeReset
595 && paRects[Id].right - paRects[Id].left == Width
596 && paRects[Id].bottom - paRects[Id].top == Height
597 && paDeviceModes[Id].dmBitsPerPel == BitsPerPixel)
598 {
599 DDCLOG(("VBoxService: already at desired resolution.\n"));
600 return FALSE;
601 }
602
603 resizeRect(paRects, NumDevices, DevPrimaryNum, Id, Width, Height);
604#ifdef DDCLOG
605 for (i = 0; i < NumDevices; i++)
606 {
607 DDCLOG(("[%d]: %d,%d %dx%d\n",
608 i, paRects[i].left, paRects[i].top,
609 paRects[i].right - paRects[i].left,
610 paRects[i].bottom - paRects[i].top));
611 }
612#endif /* DDCLOG */
613
614 /* Without this, Windows will not ask the miniport for its
615 * mode table but uses an internal cache instead.
616 */
617 DEVMODE tempDevMode;
618 ZeroMemory (&tempDevMode, sizeof (tempDevMode));
619 tempDevMode.dmSize = sizeof(DEVMODE);
620 EnumDisplaySettings(NULL, 0xffffff, &tempDevMode);
621
622 /* Assign the new rectangles to displays. */
623 for (i = 0; i < NumDevices; i++)
624 {
625 paDeviceModes[i].dmPosition.x = paRects[i].left;
626 paDeviceModes[i].dmPosition.y = paRects[i].top;
627 paDeviceModes[i].dmPelsWidth = paRects[i].right - paRects[i].left;
628 paDeviceModes[i].dmPelsHeight = paRects[i].bottom - paRects[i].top;
629
630 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH;
631
632 if ( i == Id
633 && BitsPerPixel != 0)
634 {
635 paDeviceModes[i].dmFields |= DM_BITSPERPEL;
636 paDeviceModes[i].dmBitsPerPel = BitsPerPixel;
637 }
638
639 pChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName,
640 &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
641 DDCLOG(("ChangeDisplaySettings position err %d\n", GetLastError ()));
642 }
643
644 /* A second call to ChangeDisplaySettings updates the monitor. */
645 LONG status = ChangeDisplaySettings(NULL, 0);
646 DDCLOG(("ChangeDisplaySettings update status %d\n", status));
647 if (status == DISP_CHANGE_SUCCESSFUL || status == DISP_CHANGE_BADMODE)
648 {
649 /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
650 return FALSE;
651 }
652
653 /* Retry the request. */
654 return TRUE;
655}
656
657/**
658 * Thread function to wait for and process display change
659 * requests
660 */
661VOID DisplayChangeThread(void *dummy)
662{
663 bool fTerminate = false;
664 VBoxGuestFilterMaskInfo maskInfo;
665 DWORD cbReturned;
666
667 HMODULE hUser = GetModuleHandle("USER32");
668
669 if (hUser)
670 {
671 pChangeDisplaySettingsEx = (defChangeDisplaySettingsEx *)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
672 DDCLOG(("VBoxService: pChangeDisplaySettingsEx = %p\n", pChangeDisplaySettingsEx));
673 }
674
675 maskInfo.u32OrMask = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
676 maskInfo.u32NotMask = 0;
677 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
678 {
679 DDCLOG(("VBoxService: DeviceIOControl(CtlMask - or) succeeded\n"));
680 }
681 else
682 {
683 dprintf(("VBoxService: DeviceIOControl(CtlMask) failed, DisplayChangeThread exited\n"));
684 return;
685 }
686
687 do
688 {
689 /* wait for a display change event */
690 VBoxGuestWaitEventInfo waitEvent;
691 waitEvent.u32TimeoutIn = 100;
692 waitEvent.u32EventMaskIn = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
693 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
694 {
695 DDCLOG(("VBoxService: DeviceIOControl succeded\n"));
696
697 /* are we supposed to stop? */
698 if (WaitForSingleObject(gStopSem, 0) == WAIT_OBJECT_0)
699 break;
700
701 DDCLOG(("VBoxService: checking event\n"));
702
703 /* did we get the right event? */
704 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
705 {
706 DDCLOG(("VBoxService: going to get display change information.\n"));
707
708 /* We got at least one event. Read the requested resolution
709 * and try to set it until success. New events will not be seen
710 * but a new resolution will be read in this poll loop.
711 */
712 for (;;)
713 {
714 /* get the display change request */
715 VMMDevDisplayChangeRequest2 displayChangeRequest = {0};
716 displayChangeRequest.header.size = sizeof(VMMDevDisplayChangeRequest2);
717 displayChangeRequest.header.version = VMMDEV_REQUEST_HEADER_VERSION;
718 displayChangeRequest.header.requestType = VMMDevReq_GetDisplayChangeRequest2;
719 displayChangeRequest.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
720 BOOL fDisplayChangeQueried = DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest2),
721 &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest2), &cbReturned, NULL);
722 if (!fDisplayChangeQueried)
723 {
724 /* Try the old version of the request for old VBox hosts. */
725 displayChangeRequest.header.size = sizeof(VMMDevDisplayChangeRequest);
726 displayChangeRequest.header.version = VMMDEV_REQUEST_HEADER_VERSION;
727 displayChangeRequest.header.requestType = VMMDevReq_GetDisplayChangeRequest;
728 displayChangeRequest.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
729 fDisplayChangeQueried = DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest),
730 &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest), &cbReturned, NULL);
731 displayChangeRequest.display = 0;
732 }
733
734 if (fDisplayChangeQueried)
735 {
736 DDCLOG(("VBoxService: VMMDevReq_GetDisplayChangeRequest2: %dx%dx%d at %d\n", displayChangeRequest.xres, displayChangeRequest.yres, displayChangeRequest.bpp, displayChangeRequest.display));
737
738 /* Horizontal resolution must be a multiple of 8, round down. */
739 displayChangeRequest.xres &= 0xfff8;
740
741 /*
742 * Only try to change video mode if the active display driver is VBox additions.
743 */
744 if (isVBoxDisplayDriverActive ())
745 {
746 if (pChangeDisplaySettingsEx != 0)
747 {
748 /* W2K or later. */
749 if (!ResizeDisplayDevice(displayChangeRequest.display,
750 displayChangeRequest.xres,
751 displayChangeRequest.yres,
752 displayChangeRequest.bpp))
753 {
754 break;
755 }
756 }
757 else
758 {
759 /* Single monitor NT. */
760 DEVMODE devMode;
761 memset (&devMode, 0, sizeof (devMode));
762 devMode.dmSize = sizeof(DEVMODE);
763
764 /* get the current screen setup */
765 if (EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &devMode))
766 {
767 dprintf(("VBoxService: Current mode: %dx%dx%d at %d,%d\n", devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel, devMode.dmPosition.x, devMode.dmPosition.y));
768
769 /* Check whether a mode reset or a change is requested. */
770 if (displayChangeRequest.xres || displayChangeRequest.yres || displayChangeRequest.bpp)
771 {
772 /* A change is requested.
773 * Set values which are not to be changed to the current values.
774 */
775 if (!displayChangeRequest.xres)
776 displayChangeRequest.xres = devMode.dmPelsWidth;
777 if (!displayChangeRequest.yres)
778 displayChangeRequest.yres = devMode.dmPelsHeight;
779 if (!displayChangeRequest.bpp)
780 displayChangeRequest.bpp = devMode.dmBitsPerPel;
781 }
782 else
783 {
784 /* All zero values means a forced mode reset. Do nothing. */
785 }
786
787 /* Verify that the mode is indeed changed. */
788 if ( devMode.dmPelsWidth == displayChangeRequest.xres
789 && devMode.dmPelsHeight == displayChangeRequest.yres
790 && devMode.dmBitsPerPel == displayChangeRequest.bpp)
791 {
792 dprintf(("VBoxService: already at desired resolution.\n"));
793 break;
794 }
795
796 // without this, Windows will not ask the miniport for its
797 // mode table but uses an internal cache instead
798 DEVMODE tempDevMode = {0};
799 tempDevMode.dmSize = sizeof(DEVMODE);
800 EnumDisplaySettings(NULL, 0xffffff, &tempDevMode);
801
802 /* adjust the values that are supposed to change */
803 if (displayChangeRequest.xres)
804 devMode.dmPelsWidth = displayChangeRequest.xres;
805 if (displayChangeRequest.yres)
806 devMode.dmPelsHeight = displayChangeRequest.yres;
807 if (displayChangeRequest.bpp)
808 devMode.dmBitsPerPel = displayChangeRequest.bpp;
809
810 DDCLOG(("VBoxService: setting the new mode %dx%dx%d\n", devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel));
811
812 /* set the new mode */
813 LONG status = ChangeDisplaySettings(&devMode, CDS_UPDATEREGISTRY);
814 if (status != DISP_CHANGE_SUCCESSFUL)
815 {
816 dprintf(("VBoxService: error from ChangeDisplaySettings: %d\n", status));
817
818 if (status == DISP_CHANGE_BADMODE)
819 {
820 /* Our driver can not set the requested mode. Stop trying. */
821 break;
822 }
823 }
824 else
825 {
826 /* Successfully set new video mode. */
827 break;
828 }
829 }
830 else
831 {
832 dprintf(("VBoxService: error from EnumDisplaySettings: %d\n", GetLastError ()));
833 break;
834 }
835 }
836 }
837 else
838 {
839 dprintf(("VBoxService: vboxDisplayDriver is not active.\n"));
840 }
841
842 /* Retry the change a bit later. */
843 /* are we supposed to stop? */
844 if (WaitForSingleObject(gStopSem, 1000) == WAIT_OBJECT_0)
845 {
846 fTerminate = true;
847 break;
848 }
849 }
850 else
851 {
852 dprintf(("VBoxService: error from DeviceIoControl IOCTL_VBOXGUEST_VMMREQUEST\n"));
853 /* sleep a bit to not eat too much CPU while retrying */
854 /* are we supposed to stop? */
855 if (WaitForSingleObject(gStopSem, 50) == WAIT_OBJECT_0)
856 {
857 fTerminate = true;
858 break;
859 }
860 }
861 }
862 }
863 } else
864 {
865 dprintf(("VBoxService: error 0 from DeviceIoControl IOCTL_VBOXGUEST_WAITEVENT\n"));
866 /* sleep a bit to not eat too much CPU in case the above call always fails */
867 if (WaitForSingleObject(gStopSem, 10) == WAIT_OBJECT_0)
868 {
869 fTerminate = true;
870 break;
871 }
872 }
873 } while (!fTerminate);
874
875 maskInfo.u32OrMask = 0;
876 maskInfo.u32NotMask = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
877 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
878 {
879 DDCLOG(("VBoxService: DeviceIOControl(CtlMask - not) succeeded\n"));
880 }
881 else
882 {
883 dprintf(("VBoxService: DeviceIOControl(CtlMask) failed\n"));
884 }
885
886 dprintf(("VBoxService: finished display change request thread\n"));
887}
888
889/**
890 * Window procedure for our tool window
891 */
892LRESULT CALLBACK VBoxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
893{
894 switch (msg)
895 {
896 case WM_CLOSE:
897 break;
898
899 case WM_DESTROY:
900 break;
901
902 default:
903 return DefWindowProc(hwnd, msg, wParam, lParam);
904 }
905 return 0;
906}
907
Note: See TracBrowser for help on using the repository browser.

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