VirtualBox

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

Last change on this file since 106411 was 106411, checked in by vboxsync, 3 months ago

Additions/VBoxTray: Implemented ability for easier user-controllable logging (also via verbose levels), support for running in foreground mode (with a console window attached to) and selective starting of sub services to easier pinpoint errors in release builds. Cleaned up initialization / termination code a little. See command line help for new options. bugref:10763

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.2 KB
Line 
1/* $Id: VBoxTray.cpp 106411 2024-10-17 07:44:43Z vboxsync $ */
2/** @file
3 * VBoxTray - Guest Additions Tray Application
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <package-generated.h>
33#include "product-generated.h"
34
35#include "VBoxTray.h"
36#include "VBoxTrayInternal.h"
37#include "VBoxTrayMsg.h"
38#include "VBoxHelpers.h"
39#include "VBoxSeamless.h"
40#include "VBoxClipboard.h"
41#include "VBoxDisplay.h"
42#include "VBoxVRDP.h"
43#include "VBoxHostVersion.h"
44#ifdef VBOX_WITH_DRAG_AND_DROP
45# include "VBoxDnD.h"
46#endif
47#include "VBoxIPC.h"
48#include "VBoxLA.h"
49#include <VBoxHook.h>
50
51#include <sddl.h>
52
53#include <iprt/asm.h>
54#include <iprt/buildconfig.h>
55#include <iprt/file.h>
56#include <iprt/getopt.h>
57#include <iprt/ldr.h>
58#include <iprt/message.h>
59#include <iprt/path.h>
60#include <iprt/process.h>
61#include <iprt/stream.h>
62#include <iprt/system.h>
63#include <iprt/time.h>
64#include <iprt/utf16.h>
65
66#include <VBox/log.h>
67#include <VBox/err.h>
68
69
70/*********************************************************************************************************************************
71* Internal Functions *
72*********************************************************************************************************************************/
73static void VBoxGrapicsSetSupported(BOOL fSupported);
74static int vboxTrayCreateTrayIcon(void);
75static LRESULT CALLBACK vboxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
76
77/* Global message handler prototypes. */
78static int vboxTrayGlMsgTaskbarCreated(WPARAM lParam, LPARAM wParam);
79
80
81/*********************************************************************************************************************************
82* Global Variables *
83*********************************************************************************************************************************/
84/** Mutex for checking if VBoxTray already is running. */
85HANDLE g_hMutexAppRunning = NULL;
86/** Whether VBoxTray is connected to a (parent) console. */
87bool g_fHasConsole = false;
88/** The current verbosity level. */
89unsigned g_cVerbosity = 0;
90HANDLE g_hStopSem;
91HANDLE g_hSeamlessWtNotifyEvent = 0;
92HANDLE g_hSeamlessKmNotifyEvent = 0;
93HINSTANCE g_hInstance = NULL;
94HWND g_hwndToolWindow;
95NOTIFYICONDATA g_NotifyIconData;
96
97uint32_t g_fGuestDisplaysChanged = 0;
98
99
100/**
101 * The details of the services that has been compiled in.
102 */
103static VBOXTRAYSVCINFO g_aServices[] =
104{
105 { &g_SvcDescDisplay, NIL_RTTHREAD, NULL, false, false, false, false, true },
106#ifdef VBOX_WITH_SHARED_CLIPBOARD
107 { &g_SvcDescClipboard, NIL_RTTHREAD, NULL, false, false, false, false, true },
108#endif
109 { &g_SvcDescSeamless, NIL_RTTHREAD, NULL, false, false, false, false, true },
110 { &g_SvcDescVRDP, NIL_RTTHREAD, NULL, false, false, false, false, true },
111 { &g_SvcDescIPC, NIL_RTTHREAD, NULL, false, false, false, false, true },
112 { &g_SvcDescLA, NIL_RTTHREAD, NULL, false, false, false, false, true },
113#ifdef VBOX_WITH_DRAG_AND_DROP
114 { &g_SvcDescDnD, NIL_RTTHREAD, NULL, false, false, false, false, true }
115#endif
116};
117
118/**
119 * The global message table.
120 */
121static VBOXTRAYGLOBALMSG g_vboxGlobalMessageTable[] =
122{
123 /* Windows specific stuff. */
124 {
125 "TaskbarCreated",
126 vboxTrayGlMsgTaskbarCreated
127 },
128
129 /* VBoxTray specific stuff. */
130 /** @todo Add new messages here! */
131 {
132 NULL
133 }
134};
135
136/**
137 * Gets called whenever the Windows main taskbar
138 * get (re-)created. Nice to install our tray icon.
139 *
140 * @return VBox status code.
141 * @param wParam
142 * @param lParam
143 */
144static int vboxTrayGlMsgTaskbarCreated(WPARAM wParam, LPARAM lParam)
145{
146 RT_NOREF(wParam, lParam);
147 return vboxTrayCreateTrayIcon();
148}
149
150/**
151 * Creates VBoxTray's tray icon.
152 *
153 * @returns VBox status code.
154 */
155static int vboxTrayCreateTrayIcon(void)
156{
157 HICON hIcon = LoadIcon(g_hInstance, "IDI_ICON1"); /* see Artwork/win/TemplateR3.rc */
158 if (hIcon == NULL)
159 {
160 DWORD const dwErr = GetLastError();
161 VBoxTrayError("Could not load tray icon (%#x)\n", dwErr);
162 return RTErrConvertFromWin32(dwErr);
163 }
164
165 /* Prepare the system tray icon. */
166 RT_ZERO(g_NotifyIconData);
167 g_NotifyIconData.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA);
168 g_NotifyIconData.hWnd = g_hwndToolWindow;
169 g_NotifyIconData.uID = ID_TRAYICON;
170 g_NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
171 g_NotifyIconData.uCallbackMessage = WM_VBOXTRAY_TRAY_ICON;
172 g_NotifyIconData.hIcon = hIcon;
173
174 RTStrPrintf(g_NotifyIconData.szTip, sizeof(g_NotifyIconData.szTip), "%s Guest Additions %d.%d.%dr%d",
175 VBOX_PRODUCT, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
176
177 int rc = VINF_SUCCESS;
178 if (!Shell_NotifyIcon(NIM_ADD, &g_NotifyIconData))
179 {
180 DWORD const dwErr = GetLastError();
181 VBoxTrayError("Could not create tray icon (%#x)\n", dwErr);
182 rc = RTErrConvertFromWin32(dwErr);
183 RT_ZERO(g_NotifyIconData);
184 }
185
186 if (hIcon)
187 DestroyIcon(hIcon);
188 return rc;
189}
190
191/**
192 * Removes VBoxTray's tray icon.
193 *
194 * @returns VBox status code.
195 */
196static void vboxTrayRemoveTrayIcon(void)
197{
198 if (g_NotifyIconData.cbSize > 0)
199 {
200 /* Remove the system tray icon and refresh system tray. */
201 Shell_NotifyIcon(NIM_DELETE, &g_NotifyIconData);
202 HWND hTrayWnd = FindWindow("Shell_TrayWnd", NULL); /* We assume we only have one tray atm. */
203 if (hTrayWnd)
204 {
205 HWND hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", NULL);
206 if (hTrayNotifyWnd)
207 SendMessage(hTrayNotifyWnd, WM_PAINT, 0, NULL);
208 }
209 RT_ZERO(g_NotifyIconData);
210 }
211}
212
213/**
214 * The service thread.
215 *
216 * @returns Whatever the worker function returns.
217 * @param ThreadSelf My thread handle.
218 * @param pvUser The service index.
219 */
220static DECLCALLBACK(int) vboxTrayServiceThread(RTTHREAD ThreadSelf, void *pvUser)
221{
222 PVBOXTRAYSVCINFO pSvc = (PVBOXTRAYSVCINFO)pvUser;
223 AssertPtr(pSvc);
224
225#ifndef RT_OS_WINDOWS
226 /*
227 * Block all signals for this thread. Only the main thread will handle signals.
228 */
229 sigset_t signalMask;
230 sigfillset(&signalMask);
231 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
232#endif
233
234 int rc = pSvc->pDesc->pfnWorker(pSvc->pInstance, &pSvc->fShutdown);
235 ASMAtomicXchgBool(&pSvc->fShutdown, true);
236 RTThreadUserSignal(ThreadSelf);
237
238 VBoxTrayVerbose(1, "Thread for '%s' ended with %Rrc\n", pSvc->pDesc->pszName, rc);
239 return rc;
240}
241
242/**
243 * Lazily calls the pfnPreInit method on each service.
244 *
245 * @returns VBox status code, error message displayed.
246 */
247static int vboxTrayServicesLazyPreInit(void)
248{
249 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
250 if (!g_aServices[j].fPreInited)
251 {
252 int rc = g_aServices[j].pDesc->pfnPreInit();
253 if (RT_FAILURE(rc))
254 return VBoxTrayError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
255 g_aServices[j].fPreInited = true;
256 }
257 return VINF_SUCCESS;
258}
259
260/**
261 * Starts all services.
262 *
263 * @returns VBox status code.
264 * @param pEnv Service environment to use.
265 */
266static int vboxTrayServicesStart(PVBOXTRAYSVCENV pEnv)
267{
268 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
269
270 VBoxTrayInfo("Starting services ...\n");
271
272 int rc = VINF_SUCCESS;
273
274 size_t cServicesStarted = 0;
275
276 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
277 {
278 PVBOXTRAYSVCINFO pSvc = &g_aServices[i];
279
280 if (!pSvc->fEnabled)
281 {
282 VBoxTrayInfo("Skipping starting service '%s' (disabled)\n", pSvc->pDesc->pszName);
283 continue;
284 }
285
286 VBoxTrayInfo("Starting service '%s' ...\n", pSvc->pDesc->pszName);
287
288 pSvc->hThread = NIL_RTTHREAD;
289 pSvc->pInstance = NULL;
290 pSvc->fStarted = false;
291 pSvc->fShutdown = false;
292
293 int rc2 = VINF_SUCCESS;
294
295 if (pSvc->pDesc->pfnInit)
296 rc2 = pSvc->pDesc->pfnInit(pEnv, &pSvc->pInstance);
297
298 if (RT_FAILURE(rc2))
299 {
300 switch (rc2)
301 {
302 case VERR_NOT_SUPPORTED:
303 VBoxTrayInfo("Service '%s' is not supported on this system\n", pSvc->pDesc->pszName);
304 rc2 = VINF_SUCCESS; /* Keep going. */
305 break;
306
307 case VERR_HGCM_SERVICE_NOT_FOUND:
308 VBoxTrayInfo("Service '%s' is not available on the host\n", pSvc->pDesc->pszName);
309 rc2 = VINF_SUCCESS; /* Keep going. */
310 break;
311
312 default:
313 VBoxTrayError("Failed to initialize service '%s', rc=%Rrc\n", pSvc->pDesc->pszName, rc2);
314 break;
315 }
316 }
317 else
318 {
319 if (pSvc->pDesc->pfnWorker)
320 {
321 rc2 = RTThreadCreate(&pSvc->hThread, vboxTrayServiceThread, pSvc /* pvUser */,
322 0 /* Default stack size */, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, pSvc->pDesc->pszName);
323 if (RT_SUCCESS(rc2))
324 {
325 pSvc->fStarted = true;
326
327 RTThreadUserWait(pSvc->hThread, 30 * 1000 /* Timeout in ms */);
328 if (pSvc->fShutdown)
329 {
330 VBoxTrayError("Service '%s' failed to start!\n", pSvc->pDesc->pszName);
331 rc = VERR_GENERAL_FAILURE;
332 }
333 else
334 {
335 cServicesStarted++;
336 VBoxTrayInfo("Service '%s' started\n", pSvc->pDesc->pszName);
337 }
338 }
339 else
340 {
341 VBoxTrayInfo("Failed to start thread for service '%s': %Rrc\n", rc2);
342 if (pSvc->pDesc->pfnDestroy)
343 pSvc->pDesc->pfnDestroy(pSvc->pInstance);
344 }
345 }
346 }
347
348 if (RT_SUCCESS(rc))
349 rc = rc2;
350 }
351
352 VBoxTrayInfo("%zu/%zu service(s) started\n", cServicesStarted, RT_ELEMENTS(g_aServices));
353 if (RT_FAILURE(rc))
354 VBoxTrayInfo("Some service(s) reported errors when starting -- see log above\n");
355
356 LogFlowFuncLeaveRC(rc);
357 return rc;
358}
359
360/**
361 * Stops all services.
362 *
363 * @returns VBox status code.
364 * @param pEnv Service environment to use.
365 */
366static int vboxTrayServicesStop(VBOXTRAYSVCENV *pEnv)
367{
368 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
369
370 VBoxTrayVerbose(1, "Stopping all services ...\n");
371
372 /*
373 * Signal all the services.
374 */
375 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
376 ASMAtomicWriteBool(&g_aServices[i].fShutdown, true);
377
378 /*
379 * Do the pfnStop callback on all running services.
380 */
381 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
382 {
383 PVBOXTRAYSVCINFO pSvc = &g_aServices[i];
384 if ( pSvc->fStarted
385 && pSvc->pDesc->pfnStop)
386 {
387 VBoxTrayVerbose(1, "Calling stop function for service '%s' ...\n", pSvc->pDesc->pszName);
388 int rc2 = pSvc->pDesc->pfnStop(pSvc->pInstance);
389 if (RT_FAILURE(rc2))
390 VBoxTrayError("Failed to stop service '%s': %Rrc\n", pSvc->pDesc->pszName, rc2);
391 }
392 }
393
394 VBoxTrayVerbose(2, "All stop functions for services called\n");
395
396 int rc = VINF_SUCCESS;
397
398 /*
399 * Wait for all the service threads to complete.
400 */
401 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
402 {
403 PVBOXTRAYSVCINFO pSvc = &g_aServices[i];
404 if (!pSvc->fEnabled) /* Only stop services which were started before. */
405 continue;
406
407 if (pSvc->hThread != NIL_RTTHREAD)
408 {
409 VBoxTrayVerbose(1, "Waiting for service '%s' to stop ...\n", pSvc->pDesc->pszName);
410 int rc2 = VINF_SUCCESS;
411 for (int j = 0; j < 30; j++) /* Wait 30 seconds in total */
412 {
413 rc2 = RTThreadWait(pSvc->hThread, 1000 /* Wait 1 second */, NULL);
414 if (RT_SUCCESS(rc2))
415 break;
416 }
417 if (RT_FAILURE(rc2))
418 {
419 VBoxTrayError("Service '%s' failed to stop (%Rrc)\n", pSvc->pDesc->pszName, rc2);
420 if (RT_SUCCESS(rc))
421 rc = rc2;
422 }
423 }
424
425 if ( pSvc->pDesc->pfnDestroy
426 && pSvc->pInstance) /* pInstance might be NULL if initialization of a service failed. */
427 {
428 VBoxTrayVerbose(1, "Terminating service '%s' ...\n", pSvc->pDesc->pszName);
429 pSvc->pDesc->pfnDestroy(pSvc->pInstance);
430 }
431 }
432
433 if (RT_SUCCESS(rc))
434 VBoxTrayVerbose(1, "All services stopped\n");
435
436 LogFlowFuncLeaveRC(rc);
437 return rc;
438}
439
440/**
441 * Registers all global window messages of a specific table.
442 *
443 * @returns VBox status code.
444 * @param pTable Table to register messages for.
445 */
446static int vboxTrayRegisterGlobalMessages(PVBOXTRAYGLOBALMSG pTable)
447{
448 int rc = VINF_SUCCESS;
449 if (pTable == NULL) /* No table to register? Skip. */
450 return rc;
451 while ( pTable->pszName
452 && RT_SUCCESS(rc))
453 {
454 /* Register global accessible window messages. */
455 pTable->uMsgID = RegisterWindowMessage(TEXT(pTable->pszName));
456 if (!pTable->uMsgID)
457 {
458 DWORD dwErr = GetLastError();
459 VBoxTrayError("Registering global message \"%s\" failed, error = %08X\n", pTable->pszName, dwErr);
460 rc = RTErrConvertFromWin32(dwErr);
461 }
462
463 /* Advance to next table element. */
464 pTable++;
465 }
466 return rc;
467}
468
469/**
470 * Handler for global (registered) window messages.
471 *
472 * @returns \c true if message got handeled, \c false if not.
473 * @param pTable Message table to look message up in.
474 * @param uMsg Message ID to handle.
475 * @param wParam WPARAM of the message.
476 * @param lParam LPARAM of the message.
477 */
478static bool vboxTrayHandleGlobalMessages(PVBOXTRAYGLOBALMSG pTable, UINT uMsg,
479 WPARAM wParam, LPARAM lParam)
480{
481 if (pTable == NULL)
482 return false;
483 while (pTable && pTable->pszName)
484 {
485 if (pTable->uMsgID == uMsg)
486 {
487 if (pTable->pfnHandler)
488 pTable->pfnHandler(wParam, lParam);
489 return true;
490 }
491
492 /* Advance to next table element. */
493 pTable++;
494 }
495 return false;
496}
497
498/**
499 * Destroys the invisible tool window of VBoxTray.
500 */
501static void vboxTrayDestroyToolWindow(void)
502{
503 if (g_hwndToolWindow)
504 {
505 /* Destroy the tool window. */
506 DestroyWindow(g_hwndToolWindow);
507 g_hwndToolWindow = NULL;
508
509 UnregisterClass("VBoxTrayToolWndClass", g_hInstance);
510 }
511}
512
513/**
514 * Creates the invisible tool window of VBoxTray.
515 *
516 * @returns VBox status code.
517 */
518static int vboxTrayCreateToolWindow(void)
519{
520 DWORD dwErr = ERROR_SUCCESS;
521
522 /* Create a custom window class. */
523 WNDCLASSEX wc = { 0 };
524 wc.cbSize = sizeof(WNDCLASSEX);
525 wc.style = CS_NOCLOSE;
526 wc.lpfnWndProc = (WNDPROC)vboxToolWndProc;
527 wc.hInstance = g_hInstance;
528 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
529 wc.lpszClassName = "VBoxTrayToolWndClass";
530
531 if (!RegisterClassEx(&wc))
532 {
533 dwErr = GetLastError();
534 VBoxTrayError("Registering invisible tool window failed, error = %08X\n", dwErr);
535 }
536 else
537 {
538 /*
539 * Create our (invisible) tool window.
540 * Note: The window name ("VBoxTrayToolWnd") and class ("VBoxTrayToolWndClass") is
541 * needed for posting globally registered messages to VBoxTray and must not be
542 * changed! Otherwise things get broken!
543 *
544 */
545 g_hwndToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
546 "VBoxTrayToolWndClass", "VBoxTrayToolWnd",
547 WS_POPUPWINDOW,
548 -200, -200, 100, 100, NULL, NULL, g_hInstance, NULL);
549 if (!g_hwndToolWindow)
550 {
551 dwErr = GetLastError();
552 Log(("Creating invisible tool window failed, error = %08X\n", dwErr));
553 }
554 else
555 {
556 /* Reload the cursor(s). */
557 hlpReloadCursor();
558
559 Log(("Invisible tool window handle = %p\n", g_hwndToolWindow));
560 }
561 }
562
563 if (dwErr != ERROR_SUCCESS)
564 vboxTrayDestroyToolWindow();
565
566 int const rc = RTErrConvertFromWin32(dwErr);
567
568 if (RT_FAILURE(rc))
569 VBoxTrayError("Could not create tool window, rc=%Rrc\n", rc);
570
571 return rc;
572}
573
574static int vboxTraySetupSeamless(void)
575{
576 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore. */
577 SECURITY_ATTRIBUTES SecAttr;
578 DWORD dwErr = ERROR_SUCCESS;
579 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
580 BOOL fRC;
581
582 SecAttr.nLength = sizeof(SecAttr);
583 SecAttr.bInheritHandle = FALSE;
584 SecAttr.lpSecurityDescriptor = &secDesc;
585 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
586 fRC = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
587 if (!fRC)
588 {
589 dwErr = GetLastError();
590 Log(("SetSecurityDescriptorDacl failed with last error = %08X\n", dwErr));
591 }
592 else
593 {
594 /* For Vista and up we need to change the integrity of the security descriptor, too. */
595 uint64_t const uNtVersion = RTSystemGetNtVersion();
596 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
597 {
598 BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize);
599 *(void **)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA =
600 RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorA");
601 Log(("pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %p\n",
602 RT_CB_LOG_CAST(pfnConvertStringSecurityDescriptorToSecurityDescriptorA)));
603 if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA)
604 {
605 PSECURITY_DESCRIPTOR pSD;
606 PACL pSacl = NULL;
607 BOOL fSaclPresent = FALSE;
608 BOOL fSaclDefaulted = FALSE;
609
610 fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */
611 SDDL_REVISION_1, &pSD, NULL);
612 if (!fRC)
613 {
614 dwErr = GetLastError();
615 Log(("ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr));
616 }
617 else
618 {
619 fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted);
620 if (!fRC)
621 {
622 dwErr = GetLastError();
623 Log(("GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
624 }
625 else
626 {
627 fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE);
628 if (!fRC)
629 {
630 dwErr = GetLastError();
631 Log(("SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
632 }
633 }
634 }
635 }
636 }
637
638 if ( dwErr == ERROR_SUCCESS
639 && uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Only for W2K and up ... */
640 {
641 g_hSeamlessWtNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_WT_EVENT_NAME);
642 if (g_hSeamlessWtNotifyEvent == NULL)
643 {
644 dwErr = GetLastError();
645 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
646 }
647
648 g_hSeamlessKmNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
649 if (g_hSeamlessKmNotifyEvent == NULL)
650 {
651 dwErr = GetLastError();
652 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
653 }
654 }
655 }
656
657 int const rc = RTErrConvertFromWin32(dwErr);
658
659 if (RT_FAILURE(rc))
660 VBoxTrayError("Could not setup seamless, rc=%Rrc\n", rc);
661
662 return rc;
663}
664
665static void vboxTrayShutdownSeamless(void)
666{
667 if (g_hSeamlessWtNotifyEvent)
668 {
669 CloseHandle(g_hSeamlessWtNotifyEvent);
670 g_hSeamlessWtNotifyEvent = NULL;
671 }
672
673 if (g_hSeamlessKmNotifyEvent)
674 {
675 CloseHandle(g_hSeamlessKmNotifyEvent);
676 g_hSeamlessKmNotifyEvent = NULL;
677 }
678}
679
680/**
681 * Main routine for starting / stopping all internal services.
682 *
683 * @returns VBox status code.
684 */
685static int vboxTrayServiceMain(void)
686{
687 int rc = VINF_SUCCESS;
688 VBoxTrayVerbose(2, "Entering main loop\n");
689
690 g_hStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
691 if (g_hStopSem == NULL)
692 {
693 rc = RTErrConvertFromWin32(GetLastError());
694 LogFunc(("CreateEvent for stopping VBoxTray failed, rc=%Rrc\n", rc));
695 }
696 else
697 {
698 /*
699 * Start services listed in the vboxServiceTable.
700 */
701 VBOXTRAYSVCENV svcEnv;
702 svcEnv.hInstance = g_hInstance;
703
704 /* Initializes disp-if to default (XPDM) mode. */
705 VBoxDispIfInit(&svcEnv.dispIf); /* Cannot fail atm. */
706#ifdef VBOX_WITH_WDDM
707 /*
708 * For now the display mode will be adjusted to WDDM mode if needed
709 * on display service initialization when it detects the display driver type.
710 */
711#endif
712 VBoxTrayHlpReportStatus(VBoxGuestFacilityStatus_Init);
713
714 /* Finally start all the built-in services! */
715 rc = vboxTrayServicesStart(&svcEnv);
716 if (RT_SUCCESS(rc))
717 {
718 uint64_t const uNtVersion = RTSystemGetNtVersion();
719 if ( RT_SUCCESS(rc)
720 && uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Only for W2K and up ... */
721 {
722 /* We're ready to create the tooltip balloon.
723 Check in 10 seconds (@todo make seconds configurable) ... */
724 SetTimer(g_hwndToolWindow,
725 TIMERID_VBOXTRAY_CHECK_HOSTVERSION,
726 10 * 1000, /* 10 seconds */
727 NULL /* No timerproc */);
728 }
729
730 if (RT_SUCCESS(rc))
731 {
732 /* Report the host that we're up and running! */
733 VBoxTrayHlpReportStatus(VBoxGuestFacilityStatus_Active);
734 }
735
736 if (RT_SUCCESS(rc))
737 {
738 /* Boost thread priority to make sure we wake up early for seamless window notifications
739 * (not sure if it actually makes any difference though). */
740 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
741
742 /*
743 * Main execution loop
744 * Wait for the stop semaphore to be posted or a window event to arrive
745 */
746
747 HANDLE hWaitEvent[4] = {0};
748 DWORD dwEventCount = 0;
749
750 hWaitEvent[dwEventCount++] = g_hStopSem;
751
752 /* Check if seamless mode is not active and add seamless event to the list */
753 if (0 != g_hSeamlessWtNotifyEvent)
754 {
755 hWaitEvent[dwEventCount++] = g_hSeamlessWtNotifyEvent;
756 }
757
758 if (0 != g_hSeamlessKmNotifyEvent)
759 {
760 hWaitEvent[dwEventCount++] = g_hSeamlessKmNotifyEvent;
761 }
762
763 if (0 != vboxDtGetNotifyEvent())
764 {
765 hWaitEvent[dwEventCount++] = vboxDtGetNotifyEvent();
766 }
767
768 LogFlowFunc(("Number of events to wait in main loop: %ld\n", dwEventCount));
769 while (true)
770 {
771 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
772 waitResult = waitResult - WAIT_OBJECT_0;
773
774 /* Only enable for message debugging, lots of traffic! */
775 //Log(("Wait result = %ld\n", waitResult));
776
777 if (waitResult == 0)
778 {
779 LogFunc(("Event 'Exit' triggered\n"));
780 /* exit */
781 break;
782 }
783 else
784 {
785 BOOL fHandled = FALSE;
786 if (waitResult < RT_ELEMENTS(hWaitEvent))
787 {
788 if (hWaitEvent[waitResult])
789 {
790 if (hWaitEvent[waitResult] == g_hSeamlessWtNotifyEvent)
791 {
792 LogFunc(("Event 'Seamless' triggered\n"));
793
794 /* seamless window notification */
795 VBoxSeamlessCheckWindows(false);
796 fHandled = TRUE;
797 }
798 else if (hWaitEvent[waitResult] == g_hSeamlessKmNotifyEvent)
799 {
800 LogFunc(("Event 'Km Seamless' triggered\n"));
801
802 /* seamless window notification */
803 VBoxSeamlessCheckWindows(true);
804 fHandled = TRUE;
805 }
806 else if (hWaitEvent[waitResult] == vboxDtGetNotifyEvent())
807 {
808 LogFunc(("Event 'Dt' triggered\n"));
809 vboxDtDoCheck();
810 fHandled = TRUE;
811 }
812 }
813 }
814
815 if (!fHandled)
816 {
817 /* timeout or a window message, handle it */
818 MSG msg;
819 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
820 {
821#ifdef DEBUG_andy
822 LogFlowFunc(("PeekMessage %u\n", msg.message));
823#endif
824 if (msg.message == WM_QUIT)
825 {
826 LogFunc(("Terminating ...\n"));
827 SetEvent(g_hStopSem);
828 }
829 TranslateMessage(&msg);
830 DispatchMessage(&msg);
831 }
832 }
833 }
834 }
835 LogFunc(("Returned from main loop, exiting ...\n"));
836 }
837
838 } /* Services started */
839
840 LogFunc(("Waiting for services to stop ...\n"));
841
842 VBoxTrayHlpReportStatus(VBoxGuestFacilityStatus_Terminating);
843
844 vboxTrayServicesStop(&svcEnv);
845
846 CloseHandle(g_hStopSem);
847
848 } /* Stop event created */
849
850 VBoxTrayVerbose(2, "Leaving main loop with %Rrc\n", rc);
851 return rc;
852}
853
854/**
855 * Attaches to a parent console (if any) or creates an own (dedicated) console window.
856 *
857 * @returns VBox status code.
858 */
859static int vboxTrayAttachConsole(void)
860{
861 if (g_fHasConsole) /* Console already attached? Bail out. */
862 return VINF_SUCCESS;
863
864 /* As we run with the WINDOWS subsystem, we need to either attach to or create an own console
865 * to get any stdout / stderr output. */
866 bool fAllocConsole = false;
867 if (!AttachConsole(ATTACH_PARENT_PROCESS))
868 fAllocConsole = true;
869
870 if (fAllocConsole)
871 {
872 if (!AllocConsole())
873 VBoxTrayShowError("Unable to attach to or allocate a console!");
874 /* Continue running. */
875 }
876
877 RTFILE hStdIn;
878 RTFileFromNative(&hStdIn, (RTHCINTPTR)GetStdHandle(STD_INPUT_HANDLE));
879 /** @todo Closing of standard handles not support via IPRT (yet). */
880 RTStrmOpenFileHandle(hStdIn, "r", 0, &g_pStdIn);
881
882 RTFILE hStdOut;
883 RTFileFromNative(&hStdOut, (RTHCINTPTR)GetStdHandle(STD_OUTPUT_HANDLE));
884 /** @todo Closing of standard handles not support via IPRT (yet). */
885 RTStrmOpenFileHandle(hStdOut, "wt", 0, &g_pStdOut);
886
887 RTFILE hStdErr;
888 RTFileFromNative(&hStdErr, (RTHCINTPTR)GetStdHandle(STD_ERROR_HANDLE));
889 RTStrmOpenFileHandle(hStdErr, "wt", 0, &g_pStdErr);
890
891 if (!fAllocConsole) /* When attaching to the parent console, make sure we start on a fresh line. */
892 RTPrintf("\n");
893
894 g_fHasConsole = true;
895
896 return VINF_SUCCESS;
897}
898
899/**
900 * Detaches from the (parent) console.
901 */
902static void vboxTrayDetachConsole()
903{
904 g_fHasConsole = false;
905}
906
907/**
908 * Destroys VBoxTray.
909 *
910 * @returns RTEXITCODE_SUCCESS.
911 */
912static RTEXITCODE vboxTrayDestroy()
913{
914 vboxTrayDetachConsole();
915
916 /* Release instance mutex. */
917 if (g_hMutexAppRunning != NULL)
918 {
919 CloseHandle(g_hMutexAppRunning);
920 g_hMutexAppRunning = NULL;
921 }
922
923 return RTEXITCODE_SUCCESS;
924}
925
926/**
927 * Prints the help to either a message box or a console (if attached).
928 *
929 * @returns RTEXITCODE_SYNTAX.
930 * @param cArgs Number of arguments given via argc.
931 * @param papszArgs Arguments given specified by \a cArgs.
932 */
933static RTEXITCODE vboxTrayPrintHelp(int cArgs, char **papszArgs)
934{
935 RT_NOREF(cArgs);
936
937 char szServices[64] = { 0 };
938 for (size_t i = 0; i < RT_ELEMENTS(g_aServices); i++)
939 {
940 char szName[RTTHREAD_NAME_LEN];
941 int rc2 = RTStrCopy(szName, sizeof(szName), g_aServices[i].pDesc->pszName);
942 RTStrToLower(szName); /* To make it easier for users to recognize the service name via command line. */
943 AssertRCBreak(rc2);
944 if (i > 0)
945 {
946 rc2 = RTStrCat(szServices, sizeof(szServices), ", ");
947 AssertRCBreak(rc2);
948 }
949 rc2 = RTStrCat(szServices, sizeof(szServices), szName);
950 AssertRCBreak(rc2);
951 }
952
953 VBoxTrayShowMsgBox(VBOX_PRODUCT " - " VBOX_VBOXTRAY_TITLE,
954 MB_ICONINFORMATION,
955 VBOX_PRODUCT " %s v%u.%u.%ur%u\n"
956 "Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n"
957 "Command Line Parameters:\n\n"
958 "-d, --debug\n"
959 " Enables debugging mode\n"
960 "-f, --foreground\n"
961 " Enables running in foreground\n"
962 "-l, --logfile <file>\n"
963 " Enables logging to a file\n"
964 "-v, --verbose\n"
965 " Increases verbosity\n"
966 "-V, --version\n"
967 " Displays version number and exit\n"
968 "-?, -h, --help\n"
969 " Displays this help text and exit\n"
970 "\n"
971 "Service parameters:\n\n"
972 "--enable-<service-name>\n"
973 " Enables the given service\n"
974 "--disable-<service-name>\n"
975 " Disables the given service\n"
976 "--only-<service-name>\n"
977 " Only starts the given service\n"
978 "\n"
979 "Examples:\n"
980 " %s -vvv --logfile C:\\Temp\\VBoxTray.log\n"
981 " %s --foreground -vvvv --only-draganddrop\n"
982 "\n"
983 "Available services: %s\n\n",
984 VBOX_VBOXTRAY_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV,
985 papszArgs[0], papszArgs[0], szServices);
986
987 vboxTrayDestroy();
988
989 return RTEXITCODE_SYNTAX;
990}
991
992/**
993 * Main function
994 */
995int main(int cArgs, char **papszArgs)
996{
997 int rc = RTR3InitExe(cArgs, &papszArgs, RTR3INIT_FLAGS_STANDALONE_APP);
998 if (RT_FAILURE(rc))
999 return RTMsgInitFailure(rc);
1000
1001 /* If a debugger is present, we always want to attach a console. */
1002 if (IsDebuggerPresent())
1003 vboxTrayAttachConsole();
1004
1005 /*
1006 * Parse the top level arguments until we find a command.
1007 */
1008 static const RTGETOPTDEF s_aOptions[] =
1009 {
1010 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
1011 { "/debug", 'd', RTGETOPT_REQ_NOTHING },
1012 { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
1013 { "/foreground", 'f', RTGETOPT_REQ_NOTHING },
1014 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1015 { "-help", 'h', RTGETOPT_REQ_NOTHING },
1016 { "/help", 'h', RTGETOPT_REQ_NOTHING },
1017 { "/?", 'h', RTGETOPT_REQ_NOTHING },
1018 { "--logfile", 'l', RTGETOPT_REQ_STRING },
1019 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1020 { "--version", 'V', RTGETOPT_REQ_NOTHING },
1021 };
1022
1023 char szLogFile[RTPATH_MAX] = {0};
1024
1025 RTGETOPTSTATE GetState;
1026 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
1027 if (RT_FAILURE(rc))
1028 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
1029
1030 int ch;
1031 RTGETOPTUNION ValueUnion;
1032 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
1033 {
1034 switch (ch)
1035 {
1036 case 'h':
1037 return vboxTrayPrintHelp(cArgs, papszArgs);
1038
1039 case 'd':
1040 {
1041 /* ignore rc */ vboxTrayAttachConsole();
1042 g_cVerbosity = 4; /* Set verbosity to level 4. */
1043 break;
1044 }
1045
1046 case 'f':
1047 {
1048 /* ignore rc */ vboxTrayAttachConsole();
1049 /* Don't increase verbosity automatically here. */
1050 break;
1051 }
1052
1053 case 'l':
1054 {
1055 if (*ValueUnion.psz == '\0')
1056 szLogFile[0] = '\0';
1057 else
1058 {
1059 rc = RTPathAbs(ValueUnion.psz, szLogFile, sizeof(szLogFile));
1060 if (RT_FAILURE(rc))
1061 {
1062 int rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on log file path: %Rrc (%s)",
1063 rc, ValueUnion.psz);
1064 vboxTrayDestroy();
1065 return rcExit;
1066 }
1067 }
1068 break;
1069 }
1070
1071 case 'v':
1072 g_cVerbosity++;
1073 break;
1074
1075 case 'V':
1076 VBoxTrayShowMsgBox(VBOX_PRODUCT " - " VBOX_VBOXTRAY_TITLE,
1077 MB_ICONINFORMATION,
1078 "%u.%u.%ur%u", VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
1079 return vboxTrayDestroy();
1080
1081 default:
1082 {
1083 const char *psz = ValueUnion.psz;
1084 size_t const cch = strlen(ValueUnion.psz);
1085 bool fFound = false;
1086
1087 if (cch > sizeof("--enable-") && !memcmp(psz, RT_STR_TUPLE("--enable-")))
1088 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
1089 if ((fFound = !RTStrICmp(psz + sizeof("--enable-") - 1, g_aServices[j].pDesc->pszName)))
1090 g_aServices[j].fEnabled = true;
1091
1092 if (cch > sizeof("--disable-") && !memcmp(psz, RT_STR_TUPLE("--disable-")))
1093 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
1094 if ((fFound = !RTStrICmp(psz + sizeof("--disable-") - 1, g_aServices[j].pDesc->pszName)))
1095 g_aServices[j].fEnabled = false;
1096
1097 if (cch > sizeof("--only-") && !memcmp(psz, RT_STR_TUPLE("--only-")))
1098 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
1099 {
1100 g_aServices[j].fEnabled = !RTStrICmp(psz + sizeof("--only-") - 1, g_aServices[j].pDesc->pszName);
1101 if (g_aServices[j].fEnabled)
1102 fFound = true;
1103 }
1104
1105 if (!fFound)
1106 {
1107 rc = vboxTrayServicesLazyPreInit();
1108 if (RT_FAILURE(rc))
1109 break;
1110 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
1111 {
1112 rc = g_aServices[j].pDesc->pfnOption(NULL, cArgs, papszArgs, NULL);
1113 fFound = rc == VINF_SUCCESS;
1114 if (fFound)
1115 break;
1116 if (rc != -1) /* Means not parsed. */
1117 break;
1118 }
1119 }
1120 if (!fFound)
1121 {
1122 RTGetOptPrintError(ch, &ValueUnion); /* Only shown on console. */
1123 return vboxTrayPrintHelp(cArgs, papszArgs);
1124 }
1125
1126 continue;
1127 }
1128 }
1129 }
1130
1131 if (RT_FAILURE(rc))
1132 {
1133 vboxTrayDestroy();
1134 return RTEXITCODE_FAILURE;
1135 }
1136
1137 /**
1138 * VBoxTray already running? Bail out.
1139 *
1140 * Note: Do not use a global namespace ("Global\\") for mutex name here,
1141 * will blow up NT4 compatibility!
1142 */
1143 g_hMutexAppRunning = CreateMutex(NULL, FALSE, VBOX_VBOXTRAY_TITLE);
1144 if ( g_hMutexAppRunning != NULL
1145 && GetLastError() == ERROR_ALREADY_EXISTS)
1146 {
1147 VBoxTrayError(VBOX_VBOXTRAY_TITLE " already running!\n");
1148 return vboxTrayDestroy();
1149 }
1150
1151 /* Set the instance handle. */
1152#ifdef IPRT_NO_CRT
1153 Assert(g_hInstance == NULL); /* Make sure this isn't set before by WinMain(). */
1154 g_hInstance = GetModuleHandleW(NULL);
1155#endif
1156
1157 rc = VbglR3Init();
1158 if (RT_SUCCESS(rc))
1159 {
1160 rc = VBoxTrayLogCreate(szLogFile[0] ? szLogFile : NULL);
1161 if (RT_SUCCESS(rc))
1162 {
1163 VBoxTrayInfo("Verbosity level: %d\n", g_cVerbosity);
1164
1165 rc = vboxTrayCreateToolWindow();
1166 if (RT_SUCCESS(rc))
1167 rc = vboxTrayCreateTrayIcon();
1168
1169 VBoxTrayHlpReportStatus(VBoxGuestFacilityStatus_PreInit);
1170
1171 if (RT_SUCCESS(rc))
1172 {
1173 VBoxCapsInit();
1174
1175 rc = vboxStInit(g_hwndToolWindow);
1176 if (!RT_SUCCESS(rc))
1177 {
1178 LogFlowFunc(("vboxStInit failed, rc=%Rrc\n", rc));
1179 /* ignore the St Init failure. this can happen for < XP win that do not support WTS API
1180 * in that case the session is treated as active connected to the physical console
1181 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1182 Assert(vboxStIsActiveConsole());
1183 }
1184
1185 rc = vboxDtInit();
1186 if (RT_FAILURE(rc))
1187 {
1188 /* ignore the Dt Init failure. this can happen for < XP win that do not support WTS API
1189 * in that case the session is treated as active connected to the physical console
1190 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1191 Assert(vboxDtIsInputDesktop());
1192 }
1193
1194 VBoxAcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, true);
1195
1196 vboxTraySetupSeamless();
1197
1198 rc = vboxTrayServiceMain();
1199 /* Note: Do *not* overwrite rc in the following code, as this acts as the exit code. */
1200
1201 vboxTrayShutdownSeamless();
1202
1203 /* it should be safe to call vboxDtTerm even if vboxStInit above failed */
1204 vboxDtTerm();
1205
1206 /* it should be safe to call vboxStTerm even if vboxStInit above failed */
1207 vboxStTerm();
1208
1209 VBoxCapsTerm();
1210 }
1211
1212 vboxTrayRemoveTrayIcon();
1213 vboxTrayDestroyToolWindow();
1214
1215 if (RT_SUCCESS(rc))
1216
1217 VBoxTrayHlpReportStatus(VBoxGuestFacilityStatus_Terminated);
1218 else
1219 VBoxTrayHlpReportStatus(VBoxGuestFacilityStatus_Failed);
1220
1221 VBoxTrayInfo("VBoxTray terminated with %Rrc\n", rc);
1222
1223 VBoxTrayLogDestroy();
1224 }
1225
1226 VbglR3Term();
1227 }
1228 else
1229 VBoxTrayShowError("VbglR3Init failed: %Rrc\n", rc);
1230
1231 vboxTrayDestroy();
1232
1233 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1234}
1235
1236#ifndef IPRT_NO_CRT
1237int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
1238{
1239 RT_NOREF(hPrevInstance, lpCmdLine, nCmdShow);
1240
1241 g_hInstance = hInstance;
1242
1243 return main(__argc, __argv);
1244}
1245#endif /* IPRT_NO_CRT */
1246
1247/**
1248 * Window procedure for our main tool window.
1249 */
1250static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1251{
1252 LogFlowFunc(("hWnd=%p, uMsg=%u\n", hWnd, uMsg));
1253
1254 switch (uMsg)
1255 {
1256 case WM_CREATE:
1257 {
1258 LogFunc(("Tool window created\n"));
1259
1260 int rc = vboxTrayRegisterGlobalMessages(&g_vboxGlobalMessageTable[0]);
1261 if (RT_FAILURE(rc))
1262 LogFunc(("Error registering global window messages, rc=%Rrc\n", rc));
1263 return 0;
1264 }
1265
1266 case WM_CLOSE:
1267 return 0;
1268
1269 case WM_DESTROY:
1270 {
1271 LogFunc(("Tool window destroyed\n"));
1272 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1273 return 0;
1274 }
1275
1276 case WM_TIMER:
1277 {
1278 if (VBoxCapsCheckTimer(wParam))
1279 return 0;
1280 if (vboxDtCheckTimer(wParam))
1281 return 0;
1282 if (vboxStCheckTimer(wParam))
1283 return 0;
1284
1285 switch (wParam)
1286 {
1287 case TIMERID_VBOXTRAY_CHECK_HOSTVERSION:
1288 {
1289 if (RT_SUCCESS(VBoxCheckHostVersion()))
1290 {
1291 /* After a successful run we don't need to check again. */
1292 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1293 }
1294
1295 return 0;
1296 }
1297
1298 default:
1299 break;
1300 }
1301
1302 break; /* Make sure other timers get processed the usual way! */
1303 }
1304
1305 case WM_VBOXTRAY_TRAY_ICON:
1306 {
1307 switch (LOWORD(lParam))
1308 {
1309 case WM_LBUTTONDBLCLK:
1310 break;
1311 case WM_RBUTTONDOWN:
1312 {
1313 if (!g_cVerbosity) /* Don't show menu when running in non-verbose mode. */
1314 break;
1315
1316 POINT lpCursor;
1317 if (GetCursorPos(&lpCursor))
1318 {
1319 HMENU hContextMenu = CreatePopupMenu();
1320 if (hContextMenu)
1321 {
1322 UINT_PTR uMenuItem = 9999;
1323 UINT fMenuItem = MF_BYPOSITION | MF_STRING;
1324 if (InsertMenuW(hContextMenu, UINT_MAX, fMenuItem, uMenuItem, L"Exit"))
1325 {
1326 SetForegroundWindow(hWnd);
1327
1328 const bool fBlockWhileTracking = true;
1329
1330 UINT fTrack = TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN;
1331
1332 if (fBlockWhileTracking)
1333 fTrack |= TPM_RETURNCMD | TPM_NONOTIFY;
1334
1335 uMsg = TrackPopupMenu(hContextMenu, fTrack, lpCursor.x, lpCursor.y, 0, hWnd, NULL);
1336 if ( uMsg
1337 && fBlockWhileTracking)
1338 {
1339 if (uMsg == uMenuItem)
1340 PostMessage(g_hwndToolWindow, WM_QUIT, 0, 0);
1341 }
1342 else if (!uMsg)
1343 LogFlowFunc(("Tracking popup menu failed with %ld\n", GetLastError()));
1344 }
1345
1346 DestroyMenu(hContextMenu);
1347 }
1348 }
1349 break;
1350 }
1351 }
1352 return 0;
1353 }
1354
1355 case WM_VBOX_SEAMLESS_ENABLE:
1356 {
1357 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1358 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1359 VBoxSeamlessCheckWindows(true);
1360 return 0;
1361 }
1362
1363 case WM_VBOX_SEAMLESS_DISABLE:
1364 {
1365 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1366 return 0;
1367 }
1368
1369 case WM_DISPLAYCHANGE:
1370 ASMAtomicUoWriteU32(&g_fGuestDisplaysChanged, 1);
1371 // No break or return is intentional here.
1372 case WM_VBOX_SEAMLESS_UPDATE:
1373 {
1374 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1375 VBoxSeamlessCheckWindows(true);
1376 return 0;
1377 }
1378
1379 case WM_VBOX_GRAPHICS_SUPPORTED:
1380 {
1381 VBoxGrapicsSetSupported(TRUE);
1382 return 0;
1383 }
1384
1385 case WM_VBOX_GRAPHICS_UNSUPPORTED:
1386 {
1387 VBoxGrapicsSetSupported(FALSE);
1388 return 0;
1389 }
1390
1391 case WM_WTSSESSION_CHANGE:
1392 {
1393 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
1394 if (vboxStHandleEvent(wParam))
1395 {
1396 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
1397 VBoxConsoleEnable(!fOldAllowedState);
1398 }
1399 return 0;
1400 }
1401
1402 default:
1403 {
1404 /* Handle all globally registered window messages. */
1405 if (vboxTrayHandleGlobalMessages(&g_vboxGlobalMessageTable[0], uMsg,
1406 wParam, lParam))
1407 {
1408 return 0; /* We handled the message. @todo Add return value!*/
1409 }
1410 break; /* We did not handle the message, dispatch to DefWndProc. */
1411 }
1412 }
1413
1414 /* Only if message was *not* handled by our switch above, dispatch to DefWindowProc. */
1415 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1416}
1417
1418static void VBoxGrapicsSetSupported(BOOL fSupported)
1419{
1420 VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_GRAPHICS, fSupported);
1421}
1422
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