VirtualBox

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

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

VBoxTray: Do a thread yield when reloading the cursor. Otherwise (re)displaying fails sometimes.

  • 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 SwitchToThread();
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 if (status == NO_ERROR)
303 {
304 gStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
305 if (gStopSem == NULL)
306 {
307 Log(("VBoxTray: CreateEvent for Stopping failed: rc = %d\n", GetLastError()));
308 return;
309 }
310
311 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore */
312 SECURITY_ATTRIBUTES SecAttr;
313 OSVERSIONINFO info;
314 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
315 DWORD dwMajorVersion = 5; /* default XP */
316 BOOL ret;
317
318 SecAttr.nLength = sizeof(SecAttr);
319 SecAttr.bInheritHandle = FALSE;
320 SecAttr.lpSecurityDescriptor = &secDesc;
321 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
322 ret = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
323 if (!ret)
324 Log(("VBoxTray: SetSecurityDescriptorDacl failed with %d\n", GetLastError()));
325
326 info.dwOSVersionInfoSize = sizeof(info);
327 if (GetVersionEx(&info))
328 {
329 Log(("VBoxTray: Windows version major %d minor %d\n", info.dwMajorVersion, info.dwMinorVersion));
330 dwMajorVersion = info.dwMajorVersion;
331 }
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 = 2000;
411 ndata.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
412 ndata.uCallbackMessage = WM_USER;
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
416 Log(("VBoxTray: ndata.hWnd %08X, ndata.hIcon = %p\n", ndata.hWnd, ndata.hIcon));
417
418 /* Boost thread priority to make sure we wake up early for seamless window notifications (not sure if it actually makes any difference though) */
419 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
420
421 /*
422 * Main execution loop
423 * Wait for the stop semaphore to be posted or a window event to arrive
424 */
425
426 DWORD dwEventCount = 2;
427 HANDLE hWaitEvent[2] = {gStopSem, ghSeamlessNotifyEvent};
428
429 if (0 == ghSeamlessNotifyEvent) /* If seamless mode is not active / supported, reduce event array count */
430 dwEventCount = 1;
431
432 Log(("VBoxTray: Number of events to wait in main loop: %ld\n", dwEventCount));
433
434 while(true)
435 {
436 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
437 waitResult = waitResult - WAIT_OBJECT_0;
438
439 Log(("VBoxTray: Wait result = %ld.\n", waitResult));
440
441 if (waitResult == 0)
442 {
443 Log(("VBoxTray: Event 'Exit' triggered.\n"));
444 /* exit */
445 break;
446 }
447 else if ((waitResult == 1) && (ghSeamlessNotifyEvent!=0)) /* Only jump in, if seamless is active! */
448 {
449 Log(("VBoxTray: Event 'Seamless' triggered.\n"));
450
451 /* seamless window notification */
452 VBoxSeamlessCheckWindows();
453 }
454 else
455 {
456 /* timeout or a window message, handle it */
457 MSG msg;
458 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
459 {
460 Log(("VBoxTray: msg %p\n", msg.message));
461 if (msg.message == WM_QUIT)
462 {
463 Log(("VBoxTray: WM_QUIT!\n"));
464 SetEvent(gStopSem);
465 continue;
466 }
467 TranslateMessage(&msg);
468 DispatchMessage(&msg);
469 }
470 /* we might have to repeat this operation because the shell might not be loaded yet */
471 if (!fTrayIconCreated)
472 {
473 fTrayIconCreated = Shell_NotifyIcon(NIM_ADD, &ndata);
474 Log(("VBoxTray: fTrayIconCreated = %d, err %08X\n", fTrayIconCreated, GetLastError ()));
475 }
476 }
477 }
478
479 Log(("VBoxTray: Returned from main loop, exiting ...\n"));
480
481 /* remove the system tray icon */
482 Shell_NotifyIcon(NIM_DELETE, &ndata);
483
484 Log(("VBoxTray: waiting for display change thread ...\n"));
485
486 vboxStopServices (&svcEnv, vboxServiceTable);
487
488 Log(("VBoxTray: Destroying tool window ...\n"));
489
490 /* destroy the tool window */
491 DestroyWindow(gToolWindow);
492
493 UnregisterClass("VirtualBoxTool", gInstance);
494
495 CloseHandle(gVBoxDriver);
496 CloseHandle(gStopSem);
497 CloseHandle(ghSeamlessNotifyEvent);
498
499 Log(("VBoxTray: Leaving service main function\n"));
500
501 return;
502}
503
504
505/**
506 * Main function
507 */
508int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
509{
510 /* Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
511 HANDLE hMutexAppRunning = CreateMutex (NULL, FALSE, "VBoxTray");
512 if ( (hMutexAppRunning != NULL)
513 && (GetLastError() == ERROR_ALREADY_EXISTS))
514 {
515 /* Close the mutex for this application instance. */
516 CloseHandle (hMutexAppRunning);
517 hMutexAppRunning = NULL;
518 return 0;
519 }
520
521 int rc = RTR3Init();
522 if (RT_FAILURE(rc))
523 return rc;
524
525 rc = VbglR3Init();
526 if (RT_FAILURE(rc))
527 return rc;
528
529 LogRel(("VBoxTray: Started.\n"));
530
531 gInstance = hInstance;
532 VBoxServiceStart();
533
534 LogRel(("VBoxTray: Ended.\n"));
535
536 /* Release instance mutex. */
537 if (hMutexAppRunning != NULL) {
538 CloseHandle (hMutexAppRunning);
539 hMutexAppRunning = NULL;
540 }
541
542 VbglR3Term();
543 return 0;
544}
545
546/**
547 * Window procedure for our tool window
548 */
549LRESULT CALLBACK VBoxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
550{
551 switch (msg)
552 {
553 case WM_CLOSE:
554 break;
555
556 case WM_DESTROY:
557 break;
558
559 case WM_VBOX_INSTALL_SEAMLESS_HOOK:
560 VBoxSeamlessInstallHook();
561 break;
562
563 case WM_VBOX_REMOVE_SEAMLESS_HOOK:
564 VBoxSeamlessRemoveHook();
565 break;
566
567 case WM_VBOX_SEAMLESS_UPDATE:
568 VBoxSeamlessCheckWindows();
569 break;
570
571 case WM_VBOX_RESTORED:
572 VBoxRestoreSession();
573 break;
574
575 case WM_VBOX_CHECK_VRDP:
576 VBoxRestoreCheckVRDP();
577 break;
578
579 default:
580 return DefWindowProc(hwnd, msg, wParam, lParam);
581 }
582 return 0;
583}
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