VirtualBox

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

Last change on this file since 21888 was 21888, checked in by vboxsync, 16 years ago

Additions/WINNT: fix problems of the mouse cursor not being shown when mouse integration is enabled or disabled

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 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 "VBoxStatistics.h"
28#include "VBoxMemBalloon.h"
29#include <VBoxHook.h>
30#include "resource.h"
31#include <malloc.h>
32#include <VBoxGuestInternal.h>
33
34#include "helpers.h"
35#include <sddl.h>
36
37/* global variables */
38HANDLE gVBoxDriver;
39HANDLE gStopSem;
40HANDLE ghSeamlessNotifyEvent = 0;
41SERVICE_STATUS gVBoxServiceStatus;
42SERVICE_STATUS_HANDLE gVBoxServiceStatusHandle;
43HINSTANCE gInstance;
44HWND gToolWindow;
45
46/* Prototypes */
47VOID DisplayChangeThread(void *dummy);
48LRESULT CALLBACK VBoxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
49
50/* The service table. */
51static VBOXSERVICEINFO vboxServiceTable[] =
52{
53 {
54 "Display",
55 VBoxDisplayInit,
56 VBoxDisplayThread,
57 VBoxDisplayDestroy,
58 },
59 {
60 "Shared Clipboard",
61 VBoxClipboardInit,
62 VBoxClipboardThread,
63 VBoxClipboardDestroy
64 },
65 {
66 "Seamless Windows",
67 VBoxSeamlessInit,
68 VBoxSeamlessThread,
69 VBoxSeamlessDestroy
70 },
71#ifdef VBOX_WITH_MANAGEMENT
72 {
73 "Memory Balloon",
74 VBoxMemBalloonInit,
75 VBoxMemBalloonThread,
76 VBoxMemBalloonDestroy,
77 },
78 {
79 "Guest Statistics",
80 VBoxStatsInit,
81 VBoxStatsThread,
82 VBoxStatsDestroy,
83 },
84#endif
85#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
86 {
87 "Restore",
88 VBoxRestoreInit,
89 VBoxRestoreThread,
90 VBoxRestoreDestroy,
91 },
92#endif
93 {
94 "VRDP",
95 VBoxVRDPInit,
96 VBoxVRDPThread,
97 VBoxVRDPDestroy,
98 },
99 {
100 NULL
101 }
102};
103
104static int vboxStartServices (VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
105{
106 Log(("VBoxTray: Starting services...\n"));
107
108 pEnv->hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
109
110 if (!pEnv->hStopEvent)
111 {
112 /* Could not create event. */
113 return VERR_NOT_SUPPORTED;
114 }
115
116 while (pTable->pszName)
117 {
118 Log(("Starting %s...\n", pTable->pszName));
119
120 int rc = VINF_SUCCESS;
121
122 bool fStartThread = false;
123
124 pTable->hThread = (HANDLE)0;
125 pTable->pInstance = NULL;
126 pTable->fStarted = false;
127
128 if (pTable->pfnInit)
129 {
130 rc = pTable->pfnInit (pEnv, &pTable->pInstance, &fStartThread);
131 }
132
133 if (RT_FAILURE (rc))
134 {
135 Log(("Failed to initialize rc = %Rrc.\n", rc));
136 }
137 else
138 {
139 if (pTable->pfnThread && fStartThread)
140 {
141 unsigned threadid;
142
143 pTable->hThread = (HANDLE)_beginthreadex (NULL, /* security */
144 0, /* stacksize */
145 pTable->pfnThread,
146 pTable->pInstance,
147 0, /* initflag */
148 &threadid);
149
150 if (pTable->hThread == (HANDLE)(0))
151 {
152 rc = VERR_NOT_SUPPORTED;
153 }
154 }
155
156 if (RT_FAILURE (rc))
157 {
158 Log(("Failed to start the thread.\n"));
159
160 if (pTable->pfnDestroy)
161 {
162 pTable->pfnDestroy (pEnv, pTable->pInstance);
163 }
164 }
165 else
166 {
167 pTable->fStarted = true;
168 }
169 }
170
171 /* Advance to next table element. */
172 pTable++;
173 }
174
175 return VINF_SUCCESS;
176}
177
178static void vboxStopServices (VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
179{
180 if (!pEnv->hStopEvent)
181 {
182 return;
183 }
184
185 /* Signal to all threads. */
186 SetEvent(pEnv->hStopEvent);
187
188 while (pTable->pszName)
189 {
190 if (pTable->fStarted)
191 {
192 if (pTable->pfnThread)
193 {
194 /* There is a thread, wait for termination. */
195 WaitForSingleObject(pTable->hThread, INFINITE);
196
197 CloseHandle (pTable->hThread);
198 pTable->hThread = 0;
199 }
200
201 if (pTable->pfnDestroy)
202 {
203 pTable->pfnDestroy (pEnv, pTable->pInstance);
204 }
205
206 pTable->fStarted = false;
207 }
208
209 /* Advance to next table element. */
210 pTable++;
211 }
212
213 CloseHandle (pEnv->hStopEvent);
214}
215
216
217/** Attempt to force Windows to reload the cursor image by attaching to the
218 * thread of the window currently under the mouse, hiding the cursor and
219 * showing it again. This could fail to work in any number of ways (no
220 * window under the cursor, the cursor has moved to a different window while
221 * we are processing), but we just accept this, as the cursor will be reloaded
222 * at some point anyway. */
223void VBoxServiceReloadCursor(void)
224{
225 LogFlowFunc(("\n"));
226 POINT mousePos;
227 HWND hWin;
228 DWORD hThread, hCurrentThread;
229
230 GetCursorPos(&mousePos);
231 hWin = WindowFromPoint(mousePos);
232 if (hWin)
233 {
234 hThread = GetWindowThreadProcessId(hWin, NULL);
235 hCurrentThread = GetCurrentThreadId();
236 if (hCurrentThread != hThread)
237 AttachThreadInput(hCurrentThread, hThread, TRUE);
238 }
239 ShowCursor(false);
240 ShowCursor(true);
241 if (hWin && (hCurrentThread != hThread))
242 AttachThreadInput(hCurrentThread, hThread, FALSE);
243 LogFlowFunc(("exiting\n"));
244}
245
246
247void WINAPI VBoxServiceStart(void)
248{
249 Log(("VBoxTray: Leaving service main function"));
250
251 VBOXSERVICEENV svcEnv;
252
253 DWORD status = NO_ERROR;
254
255 /* open VBox guest driver */
256 gVBoxDriver = CreateFile(VBOXGUEST_DEVICE_NAME,
257 GENERIC_READ | GENERIC_WRITE,
258 FILE_SHARE_READ | FILE_SHARE_WRITE,
259 NULL,
260 OPEN_EXISTING,
261 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
262 NULL);
263 if (gVBoxDriver == INVALID_HANDLE_VALUE)
264 {
265 LogRel(("VBoxTray: Could not open VirtualBox Guest Additions driver! Please install / start it first! rc = %d\n", GetLastError()));
266 status = ERROR_GEN_FAILURE;
267 }
268
269 Log(("VBoxTray: Driver Handle = %p, Status = %p\n", gVBoxDriver, status));
270
271 if (status == NO_ERROR)
272 {
273 /* create a custom window class */
274 WNDCLASS windowClass = {0};
275 windowClass.style = CS_NOCLOSE;
276 windowClass.lpfnWndProc = (WNDPROC)VBoxToolWndProc;
277 windowClass.hInstance = gInstance;
278 windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
279 windowClass.lpszClassName = "VirtualBoxTool";
280 if (!RegisterClass(&windowClass))
281 status = GetLastError();
282 }
283
284 Log(("VBoxTray: Class st %p\n", status));
285
286 if (status == NO_ERROR)
287 {
288 /* create our window */
289 gToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
290 "VirtualBoxTool", "VirtualBoxTool",
291 WS_POPUPWINDOW,
292 -200, -200, 100, 100, NULL, NULL, gInstance, NULL);
293 if (!gToolWindow)
294 status = GetLastError();
295 else
296 VBoxServiceReloadCursor();
297 }
298
299 Log(("VBoxTray: Window Handle = %p, Status = %p\n", gToolWindow, status));
300
301 if (status == NO_ERROR)
302 {
303 gStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
304 if (gStopSem == NULL)
305 {
306 Log(("VBoxTray: CreateEvent for Stopping failed: rc = %d\n", GetLastError()));
307 return;
308 }
309
310 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore */
311 SECURITY_ATTRIBUTES SecAttr;
312 OSVERSIONINFO info;
313 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
314 DWORD dwMajorVersion = 5; /* default XP */
315 BOOL ret;
316
317 SecAttr.nLength = sizeof(SecAttr);
318 SecAttr.bInheritHandle = FALSE;
319 SecAttr.lpSecurityDescriptor = &secDesc;
320 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
321 ret = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
322 if (!ret)
323 Log(("VBoxTray: SetSecurityDescriptorDacl failed with %d\n", GetLastError()));
324
325 info.dwOSVersionInfoSize = sizeof(info);
326 if (GetVersionEx(&info))
327 {
328 Log(("VBoxTray: Windows version major %d minor %d\n", info.dwMajorVersion, info.dwMinorVersion));
329 dwMajorVersion = info.dwMajorVersion;
330 }
331
332 /* For Vista and up we need to change the integrity of the security descriptor too */
333 if (dwMajorVersion >= 6)
334 {
335 HMODULE hModule;
336
337 BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize);
338
339 hModule = LoadLibrary("ADVAPI32.DLL");
340 if (hModule)
341 {
342 PSECURITY_DESCRIPTOR pSD;
343 PACL pSacl = NULL;
344 BOOL fSaclPresent = FALSE;
345 BOOL fSaclDefaulted = FALSE;
346
347 *(uintptr_t *)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA = (uintptr_t)GetProcAddress(hModule, "ConvertStringSecurityDescriptorToSecurityDescriptorA");
348
349 Log(("VBoxTray: pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %x\n", pfnConvertStringSecurityDescriptorToSecurityDescriptorA));
350 if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA)
351 {
352 ret = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */
353 SDDL_REVISION_1, &pSD, NULL);
354 if (!ret)
355 Log(("VBoxTray: ConvertStringSecurityDescriptorToSecurityDescriptorA failed with %d\n", GetLastError()));
356
357 ret = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted);
358 if (!ret)
359 Log(("VBoxTray: GetSecurityDescriptorSacl failed with %d\n", GetLastError()));
360
361 ret = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE);
362 if (!ret)
363 Log(("VBoxTray: SetSecurityDescriptorSacl failed with %d\n", GetLastError()));
364 }
365 }
366 }
367
368 if (dwMajorVersion >= 5) /* Only for W2K and up ... */
369 {
370 ghSeamlessNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_EVENT_NAME);
371 if (ghSeamlessNotifyEvent == NULL)
372 {
373 Log(("VBoxTray: CreateEvent for Seamless failed: rc = %d\n", GetLastError()));
374 return;
375 }
376 }
377 }
378
379 /*
380 * Start services listed in the vboxServiceTable.
381 */
382 svcEnv.hInstance = gInstance;
383 svcEnv.hDriver = gVBoxDriver;
384
385 if (status == NO_ERROR)
386 {
387 int rc = vboxStartServices (&svcEnv, vboxServiceTable);
388
389 if (RT_FAILURE (rc))
390 {
391 status = ERROR_GEN_FAILURE;
392 }
393 }
394
395 /* terminate service if something went wrong */
396 if (status != NO_ERROR)
397 {
398 vboxStopServices (&svcEnv, vboxServiceTable);
399 return;
400 }
401
402 BOOL fTrayIconCreated = false;
403
404 /* prepare the system tray icon */
405 NOTIFYICONDATA ndata;
406 memset (&ndata, 0, sizeof (ndata));
407 ndata.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA);
408 ndata.hWnd = gToolWindow;
409 ndata.uID = 2000;
410 ndata.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
411 ndata.uCallbackMessage = WM_USER;
412 ndata.hIcon = LoadIcon(gInstance, MAKEINTRESOURCE(IDI_VIRTUALBOX));
413 sprintf(ndata.szTip, "Sun VirtualBox Guest Additions %d.%d.%dr%d", VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
414
415 Log(("VBoxTray: ndata.hWnd %08X, ndata.hIcon = %p\n", ndata.hWnd, ndata.hIcon));
416
417 /* Boost thread priority to make sure we wake up early for seamless window notifications (not sure if it actually makes any difference though) */
418 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
419
420 /*
421 * Main execution loop
422 * Wait for the stop semaphore to be posted or a window event to arrive
423 */
424
425 DWORD dwEventCount = 2;
426 HANDLE hWaitEvent[2] = {gStopSem, ghSeamlessNotifyEvent};
427
428 if (0 == ghSeamlessNotifyEvent) /* If seamless mode is not active / supported, reduce event array count */
429 dwEventCount = 1;
430
431 Log(("VBoxTray: Number of events to wait in main loop: %ld\n", dwEventCount));
432
433 while(true)
434 {
435 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
436 waitResult = waitResult - WAIT_OBJECT_0;
437
438 Log(("VBoxTray: Wait result = %ld.\n", waitResult));
439
440 if (waitResult == 0)
441 {
442 Log(("VBoxTray: Event 'Exit' triggered.\n"));
443 /* exit */
444 break;
445 }
446 else if ((waitResult == 1) && (ghSeamlessNotifyEvent!=0)) /* Only jump in, if seamless is active! */
447 {
448 Log(("VBoxTray: Event 'Seamless' triggered.\n"));
449
450 /* seamless window notification */
451 VBoxSeamlessCheckWindows();
452 }
453 else
454 {
455 /* timeout or a window message, handle it */
456 MSG msg;
457 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
458 {
459 Log(("VBoxTray: msg %p\n", msg.message));
460 if (msg.message == WM_QUIT)
461 {
462 Log(("VBoxTray: WM_QUIT!\n"));
463 SetEvent(gStopSem);
464 continue;
465 }
466 TranslateMessage(&msg);
467 DispatchMessage(&msg);
468 }
469 /* we might have to repeat this operation because the shell might not be loaded yet */
470 if (!fTrayIconCreated)
471 {
472 fTrayIconCreated = Shell_NotifyIcon(NIM_ADD, &ndata);
473 Log(("VBoxTray: fTrayIconCreated = %d, err %08X\n", fTrayIconCreated, GetLastError ()));
474 }
475 }
476 }
477
478 Log(("VBoxTray: Returned from main loop, exiting ...\n"));
479
480 /* remove the system tray icon */
481 Shell_NotifyIcon(NIM_DELETE, &ndata);
482
483 Log(("VBoxTray: waiting for display change thread ...\n"));
484
485 vboxStopServices (&svcEnv, vboxServiceTable);
486
487 Log(("VBoxTray: Destroying tool window ...\n"));
488
489 /* destroy the tool window */
490 DestroyWindow(gToolWindow);
491
492 UnregisterClass("VirtualBoxTool", gInstance);
493
494 CloseHandle(gVBoxDriver);
495 CloseHandle(gStopSem);
496 CloseHandle(ghSeamlessNotifyEvent);
497
498 Log(("VBoxTray: Leaving service main function\n"));
499
500 return;
501}
502
503
504/**
505 * Main function
506 */
507int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
508{
509 /* Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
510 HANDLE hMutexAppRunning = CreateMutex (NULL, FALSE, "VBoxTray");
511 if ( (hMutexAppRunning != NULL)
512 && (GetLastError() == ERROR_ALREADY_EXISTS))
513 {
514 /* Close the mutex for this application instance. */
515 CloseHandle (hMutexAppRunning);
516 hMutexAppRunning = NULL;
517 return 0;
518 }
519
520 int rc = RTR3Init();
521 if (RT_FAILURE(rc))
522 return rc;
523
524 rc = VbglR3Init();
525 if (RT_FAILURE(rc))
526 return rc;
527
528 LogRel(("VBoxTray: Started.\n"));
529
530 gInstance = hInstance;
531 VBoxServiceStart();
532
533 LogRel(("VBoxTray: Ended.\n"));
534
535 /* Release instance mutex. */
536 if (hMutexAppRunning != NULL) {
537 CloseHandle (hMutexAppRunning);
538 hMutexAppRunning = NULL;
539 }
540
541 VbglR3Term();
542 return 0;
543}
544
545/**
546 * Window procedure for our tool window
547 */
548LRESULT CALLBACK VBoxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
549{
550 switch (msg)
551 {
552 case WM_CLOSE:
553 break;
554
555 case WM_DESTROY:
556 break;
557
558 case WM_VBOX_INSTALL_SEAMLESS_HOOK:
559 VBoxSeamlessInstallHook();
560 break;
561
562 case WM_VBOX_REMOVE_SEAMLESS_HOOK:
563 VBoxSeamlessRemoveHook();
564 break;
565
566 case WM_VBOX_SEAMLESS_UPDATE:
567 VBoxSeamlessCheckWindows();
568 break;
569
570 case WM_VBOX_RESTORED:
571 VBoxRestoreSession();
572 break;
573
574 case WM_VBOX_CHECK_VRDP:
575 VBoxRestoreCheckVRDP();
576 break;
577
578 default:
579 return DefWindowProc(hwnd, msg, wParam, lParam);
580 }
581 return 0;
582}
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