VirtualBox

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

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

VBoxTray: Corrected some logging prefixes.

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