VirtualBox

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

Last change on this file since 27991 was 27991, checked in by vboxsync, 15 years ago

yet another burn fix (sorry)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.2 KB
Line 
1/** @file
2 * VBoxTray - Guest Additions Tray Application
3 */
4
5/*
6 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
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 (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21#include "VBoxTray.h"
22#include "VBoxSeamless.h"
23#include "VBoxClipboard.h"
24#include "VBoxDisplay.h"
25#include "VBoxRestore.h"
26#include "VBoxVRDP.h"
27#include "VBoxHostVersion.h"
28#include <VBoxHook.h>
29#include "resource.h"
30#include <malloc.h>
31#include <VBoxGuestInternal.h>
32
33#include "helpers.h"
34#include <sddl.h>
35
36#include <iprt/buildconfig.h>
37
38/* global variables */
39HANDLE gVBoxDriver;
40HANDLE gStopSem;
41HANDLE ghSeamlessNotifyEvent = 0;
42SERVICE_STATUS gVBoxServiceStatus;
43SERVICE_STATUS_HANDLE gVBoxServiceStatusHandle;
44HINSTANCE gInstance;
45HWND gToolWindow;
46
47/* Prototypes */
48VOID DisplayChangeThread(void *dummy);
49LRESULT CALLBACK VBoxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
50
51/* The service table. */
52static VBOXSERVICEINFO vboxServiceTable[] =
53{
54 {
55 "Display",
56 VBoxDisplayInit,
57 VBoxDisplayThread,
58 VBoxDisplayDestroy,
59 },
60 {
61 "Shared Clipboard",
62 VBoxClipboardInit,
63 VBoxClipboardThread,
64 VBoxClipboardDestroy
65 },
66 {
67 "Seamless Windows",
68 VBoxSeamlessInit,
69 VBoxSeamlessThread,
70 VBoxSeamlessDestroy
71 },
72#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
73 {
74 "Restore",
75 VBoxRestoreInit,
76 VBoxRestoreThread,
77 VBoxRestoreDestroy,
78 },
79#endif
80 {
81 "VRDP",
82 VBoxVRDPInit,
83 VBoxVRDPThread,
84 VBoxVRDPDestroy,
85 },
86 {
87 NULL
88 }
89};
90
91static int vboxStartServices (VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
92{
93 Log(("VBoxTray: Starting services...\n"));
94
95 pEnv->hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
96
97 if (!pEnv->hStopEvent)
98 {
99 /* Could not create event. */
100 return VERR_NOT_SUPPORTED;
101 }
102
103 while (pTable->pszName)
104 {
105 Log(("Starting %s...\n", pTable->pszName));
106
107 int rc = VINF_SUCCESS;
108
109 bool fStartThread = false;
110
111 pTable->hThread = (HANDLE)0;
112 pTable->pInstance = NULL;
113 pTable->fStarted = false;
114
115 if (pTable->pfnInit)
116 {
117 rc = pTable->pfnInit (pEnv, &pTable->pInstance, &fStartThread);
118 }
119
120 if (RT_FAILURE (rc))
121 {
122 Log(("Failed to initialize rc = %Rrc.\n", rc));
123 }
124 else
125 {
126 if (pTable->pfnThread && fStartThread)
127 {
128 unsigned threadid;
129
130 pTable->hThread = (HANDLE)_beginthreadex (NULL, /* security */
131 0, /* stacksize */
132 pTable->pfnThread,
133 pTable->pInstance,
134 0, /* initflag */
135 &threadid);
136
137 if (pTable->hThread == (HANDLE)(0))
138 {
139 rc = VERR_NOT_SUPPORTED;
140 }
141 }
142
143 if (RT_FAILURE (rc))
144 {
145 Log(("Failed to start the thread.\n"));
146
147 if (pTable->pfnDestroy)
148 {
149 pTable->pfnDestroy (pEnv, pTable->pInstance);
150 }
151 }
152 else
153 {
154 pTable->fStarted = true;
155 }
156 }
157
158 /* Advance to next table element. */
159 pTable++;
160 }
161
162 return VINF_SUCCESS;
163}
164
165static void vboxStopServices (VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
166{
167 if (!pEnv->hStopEvent)
168 {
169 return;
170 }
171
172 /* Signal to all threads. */
173 SetEvent(pEnv->hStopEvent);
174
175 while (pTable->pszName)
176 {
177 if (pTable->fStarted)
178 {
179 if (pTable->pfnThread)
180 {
181 /* There is a thread, wait for termination. */
182 WaitForSingleObject(pTable->hThread, INFINITE);
183
184 CloseHandle (pTable->hThread);
185 pTable->hThread = 0;
186 }
187
188 if (pTable->pfnDestroy)
189 {
190 pTable->pfnDestroy (pEnv, pTable->pInstance);
191 }
192
193 pTable->fStarted = false;
194 }
195
196 /* Advance to next table element. */
197 pTable++;
198 }
199
200 CloseHandle (pEnv->hStopEvent);
201}
202
203
204/** Attempt to force Windows to reload the cursor image by attaching to the
205 * thread of the window currently under the mouse, hiding the cursor and
206 * showing it again. This could fail to work in any number of ways (no
207 * window under the cursor, the cursor has moved to a different window while
208 * we are processing), but we just accept this, as the cursor will be reloaded
209 * at some point anyway. */
210void VBoxServiceReloadCursor(void)
211{
212 LogFlowFunc(("\n"));
213 POINT mousePos;
214 HWND hWin;
215 DWORD hThread, hCurrentThread;
216
217 GetCursorPos(&mousePos);
218 hWin = WindowFromPoint(mousePos);
219 if (hWin)
220 {
221 hThread = GetWindowThreadProcessId(hWin, NULL);
222 hCurrentThread = GetCurrentThreadId();
223 if (hCurrentThread != hThread)
224 AttachThreadInput(hCurrentThread, hThread, TRUE);
225 }
226 ShowCursor(false);
227 ShowCursor(true);
228 if (hWin && (hCurrentThread != hThread))
229 AttachThreadInput(hCurrentThread, hThread, FALSE);
230 LogFlowFunc(("exiting\n"));
231}
232
233
234void WINAPI VBoxServiceStart(void)
235{
236 Log(("VBoxTray: Leaving service main function"));
237
238 VBOXSERVICEENV svcEnv;
239
240 DWORD status = NO_ERROR;
241
242 /* open VBox guest driver */
243 gVBoxDriver = CreateFile(VBOXGUEST_DEVICE_NAME,
244 GENERIC_READ | GENERIC_WRITE,
245 FILE_SHARE_READ | FILE_SHARE_WRITE,
246 NULL,
247 OPEN_EXISTING,
248 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
249 NULL);
250 if (gVBoxDriver == INVALID_HANDLE_VALUE)
251 {
252 LogRel(("VBoxTray: Could not open VirtualBox Guest Additions driver! Please install / start it first! rc = %d\n", GetLastError()));
253 status = ERROR_GEN_FAILURE;
254 }
255
256 Log(("VBoxTray: Driver Handle = %p, Status = %p\n", gVBoxDriver, status));
257
258 if (status == NO_ERROR)
259 {
260 /* create a custom window class */
261 WNDCLASS windowClass = {0};
262 windowClass.style = CS_NOCLOSE;
263 windowClass.lpfnWndProc = (WNDPROC)VBoxToolWndProc;
264 windowClass.hInstance = gInstance;
265 windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
266 windowClass.lpszClassName = "VirtualBoxTool";
267 if (!RegisterClass(&windowClass))
268 status = GetLastError();
269 }
270
271 Log(("VBoxTray: Class st %p\n", status));
272
273 if (status == NO_ERROR)
274 {
275 /* create our window */
276 gToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
277 "VirtualBoxTool", "VirtualBoxTool",
278 WS_POPUPWINDOW,
279 -200, -200, 100, 100, NULL, NULL, gInstance, NULL);
280 if (!gToolWindow)
281 status = GetLastError();
282 else
283 VBoxServiceReloadCursor();
284 }
285
286 Log(("VBoxTray: Window Handle = %p, Status = %p\n", gToolWindow, status));
287
288 OSVERSIONINFO info;
289 DWORD dwMajorVersion = 5; /* default XP */
290 info.dwOSVersionInfoSize = sizeof(info);
291 if (GetVersionEx(&info))
292 {
293 Log(("VBoxTray: Windows version major %d minor %d\n", info.dwMajorVersion, info.dwMinorVersion));
294 dwMajorVersion = info.dwMajorVersion;
295 }
296
297 if (status == NO_ERROR)
298 {
299 gStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
300 if (gStopSem == NULL)
301 {
302 Log(("VBoxTray: CreateEvent for Stopping failed: rc = %d\n", GetLastError()));
303 return;
304 }
305
306 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore */
307 SECURITY_ATTRIBUTES SecAttr;
308 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
309 BOOL ret;
310
311 SecAttr.nLength = sizeof(SecAttr);
312 SecAttr.bInheritHandle = FALSE;
313 SecAttr.lpSecurityDescriptor = &secDesc;
314 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
315 ret = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
316 if (!ret)
317 Log(("VBoxTray: SetSecurityDescriptorDacl failed with %d\n", GetLastError()));
318
319 /* For Vista and up we need to change the integrity of the security descriptor too */
320 if (dwMajorVersion >= 6)
321 {
322 HMODULE hModule;
323
324 BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize);
325
326 hModule = LoadLibrary("ADVAPI32.DLL");
327 if (hModule)
328 {
329 PSECURITY_DESCRIPTOR pSD;
330 PACL pSacl = NULL;
331 BOOL fSaclPresent = FALSE;
332 BOOL fSaclDefaulted = FALSE;
333
334 *(uintptr_t *)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA = (uintptr_t)GetProcAddress(hModule, "ConvertStringSecurityDescriptorToSecurityDescriptorA");
335
336 Log(("VBoxTray: pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %x\n", pfnConvertStringSecurityDescriptorToSecurityDescriptorA));
337 if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA)
338 {
339 ret = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */
340 SDDL_REVISION_1, &pSD, NULL);
341 if (!ret)
342 Log(("VBoxTray: ConvertStringSecurityDescriptorToSecurityDescriptorA failed with %d\n", GetLastError()));
343
344 ret = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted);
345 if (!ret)
346 Log(("VBoxTray: GetSecurityDescriptorSacl failed with %d\n", GetLastError()));
347
348 ret = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE);
349 if (!ret)
350 Log(("VBoxTray: SetSecurityDescriptorSacl failed with %d\n", GetLastError()));
351 }
352 }
353 }
354
355 if (dwMajorVersion >= 5) /* Only for W2K and up ... */
356 {
357 ghSeamlessNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_EVENT_NAME);
358 if (ghSeamlessNotifyEvent == NULL)
359 {
360 Log(("VBoxTray: CreateEvent for Seamless failed: rc = %d\n", GetLastError()));
361 return;
362 }
363 }
364 }
365
366 /*
367 * Start services listed in the vboxServiceTable.
368 */
369 svcEnv.hInstance = gInstance;
370 svcEnv.hDriver = gVBoxDriver;
371
372 /* initializes disp-if to default (XPDM) mode */
373 status = VBoxDispIfInit(&svcEnv.dispIf);
374#ifdef VBOXWDDM
375 /* for now the display mode will be adjusted to WDDM mode if needed
376 * on display service initialization when it detects the display driver type */
377#endif
378
379 if (status == NO_ERROR)
380 {
381 int rc = vboxStartServices (&svcEnv, vboxServiceTable);
382
383 if (RT_FAILURE (rc))
384 {
385 status = ERROR_GEN_FAILURE;
386 }
387 }
388
389 /* terminate service if something went wrong */
390 if (status != NO_ERROR)
391 {
392 vboxStopServices (&svcEnv, vboxServiceTable);
393 return;
394 }
395
396 BOOL fTrayIconCreated = false;
397
398 /* prepare the system tray icon */
399 NOTIFYICONDATA ndata;
400 RT_ZERO (ndata);
401 ndata.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA);
402 ndata.hWnd = gToolWindow;
403 ndata.uID = ID_TRAYICON;
404 ndata.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
405 ndata.uCallbackMessage = WM_VBOX_TRAY;
406 ndata.hIcon = LoadIcon(gInstance, MAKEINTRESOURCE(IDI_VIRTUALBOX));
407 sprintf(ndata.szTip, "Sun VirtualBox Guest Additions %d.%d.%dr%d", VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
408 Log(("VBoxTray: ndata.hWnd %08X, ndata.hIcon = %p\n", ndata.hWnd, ndata.hIcon));
409
410 /* Boost thread priority to make sure we wake up early for seamless window notifications (not sure if it actually makes any difference though) */
411 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
412
413 /*
414 * Main execution loop
415 * Wait for the stop semaphore to be posted or a window event to arrive
416 */
417
418 DWORD dwEventCount = 2;
419 HANDLE hWaitEvent[2] = {gStopSem, ghSeamlessNotifyEvent};
420
421 if (0 == ghSeamlessNotifyEvent) /* If seamless mode is not active / supported, reduce event array count */
422 dwEventCount = 1;
423
424 Log(("VBoxTray: Number of events to wait in main loop: %ld\n", dwEventCount));
425
426 while(true)
427 {
428 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
429 waitResult = waitResult - WAIT_OBJECT_0;
430
431 Log(("VBoxTray: Wait result = %ld.\n", waitResult));
432
433 if (waitResult == 0)
434 {
435 Log(("VBoxTray: Event 'Exit' triggered.\n"));
436 /* exit */
437 break;
438 }
439 else if ((waitResult == 1) && (ghSeamlessNotifyEvent!=0)) /* Only jump in, if seamless is active! */
440 {
441 Log(("VBoxTray: Event 'Seamless' triggered.\n"));
442
443 /* seamless window notification */
444 VBoxSeamlessCheckWindows();
445 }
446 else
447 {
448 /* timeout or a window message, handle it */
449 MSG msg;
450 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
451 {
452 Log(("VBoxTray: msg %p\n", msg.message));
453 if (msg.message == WM_QUIT)
454 {
455 Log(("VBoxTray: WM_QUIT!\n"));
456 SetEvent(gStopSem);
457 continue;
458 }
459 TranslateMessage(&msg);
460 DispatchMessage(&msg);
461 }
462 /* we might have to repeat this operation because the shell might not be loaded yet */
463 if (!fTrayIconCreated)
464 {
465 fTrayIconCreated = Shell_NotifyIcon(NIM_ADD, &ndata);
466 Log(("VBoxTray: fTrayIconCreated = %d, err %08X\n", fTrayIconCreated, GetLastError ()));
467
468 /* We're ready to create the tooltip balloon. */
469 if (fTrayIconCreated && dwMajorVersion >= 5)
470 {
471 /* Check in 10 seconds (@todo make seconds configurable) ... */
472 SetTimer(gToolWindow,
473 WM_VBOX_CHECK_HOSTVERSION,
474 10000, /* 10 seconds */
475 NULL /* no timerproc */);
476 }
477 }
478 }
479 }
480
481 Log(("VBoxTray: Returned from main loop, exiting ...\n"));
482
483 /* remove the system tray icon and refresh system tray */
484 Shell_NotifyIcon(NIM_DELETE, &ndata);
485 HWND hTrayWnd = FindWindow("Shell_TrayWnd", NULL); /* We assume we only have one tray atm */
486 HWND hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", NULL);
487 if (hTrayNotifyWnd)
488 SendMessage(hTrayNotifyWnd, WM_PAINT, 0, NULL);
489
490 Log(("VBoxTray: waiting for display change thread ...\n"));
491
492 vboxStopServices (&svcEnv, vboxServiceTable);
493
494 Log(("VBoxTray: Destroying tool window ...\n"));
495
496 /* destroy the tool window */
497 DestroyWindow(gToolWindow);
498
499 UnregisterClass("VirtualBoxTool", gInstance);
500
501 CloseHandle(gVBoxDriver);
502 CloseHandle(gStopSem);
503 CloseHandle(ghSeamlessNotifyEvent);
504
505 Log(("VBoxTray: Leaving service main function\n"));
506
507 return;
508}
509
510
511/**
512 * Main function
513 */
514int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
515{
516 /* Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
517 HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, "VBoxTray");
518 if ( (hMutexAppRunning != NULL)
519 && (GetLastError() == ERROR_ALREADY_EXISTS))
520 {
521 /* Close the mutex for this application instance. */
522 CloseHandle (hMutexAppRunning);
523 hMutexAppRunning = NULL;
524 return 0;
525 }
526
527 int rc = RTR3Init();
528 if (RT_FAILURE(rc))
529 return rc;
530
531 rc = VbglR3Init();
532 if (RT_FAILURE(rc))
533 return rc;
534
535 LogRel(("VBoxTray: %s r%s started.\n", RTBldCfgVersion(), RTBldCfgRevisionStr()));
536
537 gInstance = hInstance;
538 VBoxServiceStart();
539
540 LogRel(("VBoxTray: Ended.\n"));
541
542 /* Release instance mutex. */
543 if (hMutexAppRunning != NULL) {
544 CloseHandle(hMutexAppRunning);
545 hMutexAppRunning = NULL;
546 }
547
548 VbglR3Term();
549 return 0;
550}
551
552/**
553 * Window procedure for our tool window
554 */
555LRESULT CALLBACK VBoxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
556{
557 switch (msg)
558 {
559 case WM_CLOSE:
560 break;
561
562 case WM_DESTROY:
563 KillTimer(gToolWindow, WM_VBOX_CHECK_HOSTVERSION);
564 break;
565
566 case WM_TIMER:
567 switch (wParam)
568 {
569 case WM_VBOX_CHECK_HOSTVERSION:
570 if (RT_SUCCESS(VBoxCheckHostVersion()))
571 {
572 /* After successful run we don't need to check again. */
573 KillTimer(gToolWindow, WM_VBOX_CHECK_HOSTVERSION);
574 }
575 return 0;
576
577 default:
578 break;
579 }
580
581 break;
582
583 case WM_VBOX_TRAY:
584 switch (lParam)
585 {
586 case WM_LBUTTONDBLCLK:
587 break;
588
589 case WM_RBUTTONDOWN:
590 break;
591 }
592 break;
593
594 case WM_VBOX_INSTALL_SEAMLESS_HOOK:
595 VBoxSeamlessInstallHook();
596 break;
597
598 case WM_VBOX_REMOVE_SEAMLESS_HOOK:
599 VBoxSeamlessRemoveHook();
600 break;
601
602 case WM_VBOX_SEAMLESS_UPDATE:
603 VBoxSeamlessCheckWindows();
604 break;
605
606 case WM_VBOX_RESTORED:
607 VBoxRestoreSession();
608 break;
609
610 case WM_VBOX_CHECK_VRDP:
611 VBoxRestoreCheckVRDP();
612 break;
613
614 default:
615 return DefWindowProc(hwnd, msg, wParam, lParam);
616 }
617 return 0;
618}
619
620/* display driver interface abstraction for XPDM & WDDM
621 * with WDDM we can not use ExtEscape to communicate with our driver
622 * because we do not have XPDM display driver any more, i.e. escape requests are handled by cdd
623 * that knows nothing about us */
624DWORD VBoxDispIfInit(PVBOXDISPIF pIf)
625{
626 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
627 return NO_ERROR;
628}
629
630DWORD VBoxDispIfTerm(PVBOXDISPIF pIf)
631{
632 pIf->enmMode = VBOXDISPIF_MODE_UNKNOWN;
633 return NO_ERROR;
634}
635
636static DWORD vboxDispIfEscapeXPDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
637{
638 HDC hdc = GetDC(HWND_DESKTOP);
639 VOID *pvData = cbData ? VBOXDISPIFESCAPE_DATA(pEscape, VOID) : NULL;
640 int iRet = ExtEscape(hdc, pEscape->escapeCode, cbData, (LPCSTR)pvData, 0, NULL);
641 ReleaseDC(HWND_DESKTOP, hdc);
642 if (iRet > 0)
643 return VINF_SUCCESS;
644 else if (iRet == 0)
645 return ERROR_NOT_SUPPORTED;
646 /* else */
647 return ERROR_GEN_FAILURE;
648}
649
650#ifdef VBOXWDDM
651static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF pIf)
652{
653 DWORD err = NO_ERROR;
654 OSVERSIONINFO OSinfo;
655 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
656 GetVersionEx (&OSinfo);
657 if (OSinfo.dwMajorVersion >= 6)
658 {
659 /* this is vista and up */
660 Log((__FUNCTION__": this is vista and up\n"));
661 HMODULE hGdi32 = GetModuleHandle("gdi32");
662 if (hGdi32 != NULL)
663 {
664 bool bSupported = true;
665 pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc = (PFND3DKMT_OPENADAPTERFROMHDC)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromHdc");
666 Log((__FUNCTION__"pfnD3DKMTOpenAdapterFromHdc = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc));
667 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc);
668
669 pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName = (PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromGdiDisplayName");
670 Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName));
671 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName);
672
673 pIf->modeData.wddm.pfnD3DKMTCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(hGdi32, "D3DKMTCloseAdapter");
674 Log((__FUNCTION__": pfnD3DKMTCloseAdapter = %p\n", pIf->modeData.wddm.pfnD3DKMTCloseAdapter));
675 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter);
676
677 pIf->modeData.wddm.pfnD3DKMTEscape = (PFND3DKMT_ESCAPE)GetProcAddress(hGdi32, "D3DKMTEscape");
678 Log((__FUNCTION__": pfnD3DKMTEscape = %p\n", pIf->modeData.wddm.pfnD3DKMTEscape));
679 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter);
680
681 pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn = (PFND3DKMT_INVALIDATEACTIVEVIDPN)GetProcAddress(hGdi32, "D3DKMTInvalidateActiveVidPn");
682 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn = %p\n", pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn));
683 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn);
684
685 if (!bSupported)
686 {
687 Log((__FUNCTION__": one of pfnD3DKMT function pointers failed to initialize\n"));
688 err = ERROR_NOT_SUPPORTED;
689 }
690 }
691 else
692 {
693 Log((__FUNCTION__": GetModuleHandle(gdi32) failed, err(%d)\n", GetLastError()));
694 err = ERROR_NOT_SUPPORTED;
695 }
696 }
697 else
698 {
699 Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_WDDM, because os is not Vista or upper\n"));
700 err = ERROR_NOT_SUPPORTED;
701 }
702
703 return err;
704}
705
706typedef DECLCALLBACK(BOOLEAN) FNVBOXDISPIFWDDM_ADAPTEROP(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, LPCWSTR pDevName, PVOID pContext);
707typedef FNVBOXDISPIFWDDM_ADAPTEROP *PFNVBOXDISPIFWDDM_ADAPTEROP;
708static DWORD vboxDispIfWDDMAdapterOp(PCVBOXDISPIF pIf, LPCWSTR pDevName, PFNVBOXDISPIFWDDM_ADAPTEROP pfnOp, PVOID pContext)
709{
710 D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME OpenAdapterData = {0};
711 wcsncpy(OpenAdapterData.DeviceName, pDevName, RT_ELEMENTS(OpenAdapterData.DeviceName) - 1 /* the last one is always \0 */);
712 DWORD err = ERROR_GEN_FAILURE;
713 NTSTATUS Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName(&OpenAdapterData);
714 if (!Status)
715 {
716 BOOLEAN bCloseAdapter = pfnOp(pIf, OpenAdapterData.hAdapter, OpenAdapterData.DeviceName, pContext);
717
718 if (bCloseAdapter)
719 {
720 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
721 ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter;
722 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
723 if (Status)
724 {
725 Log((__FUNCTION__": pfnD3DKMTCloseAdapter failed, Status (0x%x)\n", Status));
726 /* ignore */
727 Status = 0;
728 }
729 }
730 }
731 else
732 Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName failed, Status (0x%x)\n", Status));
733
734 return err;
735}
736
737typedef struct
738{
739 NTSTATUS Status;
740 PVBOXDISPIFESCAPE pEscape;
741 int cbData;
742} VBOXDISPIFWDDM_ESCAPEOP_CONTEXT, *PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT;
743
744DECLCALLBACK(BOOLEAN) vboxDispIfEscapeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, LPCWSTR pDevName, PVOID pContext)
745{
746 PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT)pContext;
747
748 D3DKMT_ESCAPE EscapeData = {0};
749 EscapeData.hAdapter = hAdapter;
750 //EscapeData.hDevice = NULL;
751 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
752 EscapeData.Flags.HardwareAccess = 1;
753 EscapeData.pPrivateDriverData = pCtx->pEscape;
754 EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(pCtx->cbData);
755 //EscapeData.hContext = NULL;
756
757 pCtx->Status = pIf->modeData.wddm.pfnD3DKMTEscape(&EscapeData);
758
759 return TRUE;
760}
761
762static DWORD vboxDispIfEscapeWDDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
763{
764 VBOXDISPIFWDDM_ESCAPEOP_CONTEXT Ctx = {0};
765 Ctx.pEscape = pEscape;
766 Ctx.cbData = cbData;
767 DWORD err = vboxDispIfWDDMAdapterOp(pIf, L"\\\\.\\DISPLAY1", vboxDispIfEscapeWDDMOp, &Ctx);
768 if (err == NO_ERROR)
769 {
770 if (!Ctx.Status)
771 err = NO_ERROR;
772 else
773 {
774 if (Ctx.Status == 0xC00000BBL) /* not supported */
775 err = ERROR_NOT_SUPPORTED;
776 else
777 err = ERROR_GEN_FAILURE;
778 Log((__FUNCTION__": pfnD3DKMTEscape failed, Status (0x%x)\n", Ctx.Status));
779 }
780 }
781 else
782 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
783
784 return err;
785}
786
787typedef struct
788{
789 NTSTATUS Status;
790 VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO Info;
791} VBOXDISPIFWDDM_RESIZEOP_CONTEXT, *PVBOXDISPIFWDDM_RESIZEOP_CONTEXT;
792
793DECLCALLBACK(BOOLEAN) vboxDispIfResizeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, LPCWSTR pDevName, PVOID pContext)
794{
795 PVBOXDISPIFWDDM_RESIZEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_RESIZEOP_CONTEXT)pContext;
796
797 D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0};
798 uint32_t cbData = VBOXWDDM_RECOMMENDVIDPN_SIZE(1);
799 PVBOXWDDM_RECOMMENDVIDPN pData = (PVBOXWDDM_RECOMMENDVIDPN)malloc(cbData);
800 if (pData)
801 {
802 memset(pData, 0, cbData);
803 pData->cScreenInfos = 1;
804 memcpy(&pData->aScreenInfos[0], &pCtx->Info, sizeof (VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO));
805
806 IAVidPnData.hAdapter = hAdapter;
807 IAVidPnData.pPrivateDriverData = pData;
808 IAVidPnData.PrivateDriverDataSize = cbData;
809
810 pCtx->Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData);
811 if (pCtx->Status)
812 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", pCtx->Status));
813
814 free(pData);
815 }
816 else
817 {
818 Log((__FUNCTION__": malloc failed\n"));
819 pCtx->Status = -1;
820 }
821
822 return TRUE;
823}
824
825static DWORD vboxDispIfResizeWDDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
826{
827 VBOXDISPIFWDDM_RESIZEOP_CONTEXT Ctx = {0};
828 Ctx.Info.Id = Id;
829 Ctx.Info.Width = Width;
830 Ctx.Info.Height = Height;
831 Ctx.Info.BitsPerPixel = BitsPerPixel;
832 DWORD err = vboxDispIfWDDMAdapterOp(pIf, L"\\\\.\\DISPLAY1", vboxDispIfResizeWDDMOp, &Ctx);
833 if (err == NO_ERROR)
834 {
835 if (!Ctx.Status)
836 err = NO_ERROR;
837 else
838 {
839 if (Ctx.Status == 0xC00000BBL) /* not supported */
840 err = ERROR_NOT_SUPPORTED;
841 else
842 err = ERROR_GEN_FAILURE;
843 Log((__FUNCTION__": vboxDispIfResizeWDDMOp failed, Status (0x%x)\n", Ctx.Status));
844 }
845 }
846 else
847 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
848
849 return err;
850}
851#endif
852
853DWORD VBoxDispIfEscape(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
854{
855 switch (pIf->enmMode)
856 {
857 case VBOXDISPIF_MODE_XPDM_NT4:
858 case VBOXDISPIF_MODE_XPDM:
859 return vboxDispIfEscapeXPDM(pIf, pEscape, cbData);
860#ifdef VBOXWDDM
861 case VBOXDISPIF_MODE_WDDM:
862 return vboxDispIfEscapeWDDM(pIf, pEscape, cbData);
863#endif
864 default:
865 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
866 return ERROR_INVALID_PARAMETER;
867 }
868}
869
870static DWORD vboxDispIfResizeXPDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
871{
872 return ERROR_NOT_SUPPORTED;
873}
874
875DWORD VBoxDispIfResize(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
876{
877 switch (pIf->enmMode)
878 {
879 case VBOXDISPIF_MODE_XPDM_NT4:
880 return ERROR_NOT_SUPPORTED;
881 case VBOXDISPIF_MODE_XPDM:
882 return vboxDispIfResizeXPDM(pIf, Id, Width, Height, BitsPerPixel);
883#ifdef VBOXWDDM
884 case VBOXDISPIF_MODE_WDDM:
885 return vboxDispIfResizeWDDM(pIf, Id, Width, Height, BitsPerPixel);
886#endif
887 default:
888 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
889 return ERROR_INVALID_PARAMETER;
890 }
891}
892
893static DWORD vboxDispIfSwitchToXPDM_NT4(PVBOXDISPIF pIf)
894{
895 return NO_ERROR;
896}
897
898static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf)
899{
900 DWORD err = NO_ERROR;
901 AssertBreakpoint();
902 OSVERSIONINFO OSinfo;
903 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
904 GetVersionEx (&OSinfo);
905 if (OSinfo.dwMajorVersion >= 5)
906 {
907 HMODULE hUser = GetModuleHandle("USER32");
908 if (NULL != hUser)
909 {
910 bool bSupported = true;
911 *(uintptr_t *)&pIf->modeData.xpdm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
912 Log((__FUNCTION__": pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.xpdm.pfnChangeDisplaySettingsEx));
913 bSupported &= !!(pIf->modeData.xpdm.pfnChangeDisplaySettingsEx);
914
915 if (!bSupported)
916 {
917 Log((__FUNCTION__": pfnChangeDisplaySettingsEx function pointer failed to initialize\n"));
918 err = ERROR_NOT_SUPPORTED;
919 }
920 }
921 else
922 {
923 Log((__FUNCTION__": failed to get USER32 handle, err (%d)\n", GetLastError()));
924 err = ERROR_NOT_SUPPORTED;
925 }
926 }
927 else
928 {
929 Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n"));
930 err = ERROR_NOT_SUPPORTED;
931 }
932
933 return err;
934}
935
936DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_MODE *penmOldMode)
937{
938 /* @todo: may need to addd synchronization in case we want to change modes dynamically
939 * i.e. currently the mode is supposed to be initialized once on service initialization */
940 if (penmOldMode)
941 *penmOldMode = pIf->enmMode;
942
943 if (enmMode == pIf->enmMode)
944 return VINF_ALREADY_INITIALIZED;
945
946 DWORD err = NO_ERROR;
947 switch (enmMode)
948 {
949 case VBOXDISPIF_MODE_XPDM_NT4:
950 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM_NT4\n"));
951 err = vboxDispIfSwitchToXPDM_NT4(pIf);
952 if (err == NO_ERROR)
953 {
954 Log((__FUNCTION__": successfully switched to XPDM_NT4 mode\n"));
955 pIf->enmMode = VBOXDISPIF_MODE_XPDM_NT4;
956 }
957 else
958 Log((__FUNCTION__": failed to switch to XPDM_NT4 mode, err (%d)\n", err));
959 break;
960 case VBOXDISPIF_MODE_XPDM:
961 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM\n"));
962 err = vboxDispIfSwitchToXPDM(pIf);
963 if (err == NO_ERROR)
964 {
965 Log((__FUNCTION__": successfully switched to XPDM mode\n"));
966 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
967 }
968 else
969 Log((__FUNCTION__": failed to switch to XPDM mode, err (%d)\n", err));
970 break;
971#ifdef VBOXWDDM
972 case VBOXDISPIF_MODE_WDDM:
973 {
974 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_WDDM\n"));
975 err = vboxDispIfSwitchToWDDM(pIf);
976 if (err == NO_ERROR)
977 {
978 Log((__FUNCTION__": successfully switched to WDDM mode\n"));
979 pIf->enmMode = VBOXDISPIF_MODE_WDDM;
980 }
981 else
982 Log((__FUNCTION__": failed to switch to WDDM mode, err (%d)\n", err));
983 break;
984 }
985#endif
986 default:
987 err = ERROR_INVALID_PARAMETER;
988 break;
989 }
990 return err;
991}
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