VirtualBox

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

Last change on this file since 30928 was 30928, checked in by vboxsync, 14 years ago

VBoxTray: Modularized tray icon handling, fixed icon leak.

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