VirtualBox

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

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

VBoxTray: Refresh system tray after deleting icon.

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