VirtualBox

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

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

VBoxTray: write version and revision to release log.

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