VirtualBox

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

Last change on this file was 100204, checked in by vboxsync, 10 months ago

Shared Clipboard: Unified root list entry code to also use the generic list entry code, a lot of updates for the cross OS transfer handling code, more updates for HTTP server transfer handling.

This also changed the handling of how that transfers are being initiated, as we needed to have this for X11: Before, transfers were initiated as soon as on side announced the URI list format -- now we postpone initiating the transfer until the receiving side requests the data as URI list.

bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.4 KB
Line 
1/* $Id: VBoxTray.cpp 100204 2023-06-19 09:11:37Z vboxsync $ */
2/** @file
3 * VBoxTray - Guest Additions Tray Application
4 */
5
6/*
7 * Copyright (C) 2006-2023 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/getopt.h>
56#include <iprt/ldr.h>
57#include <iprt/message.h>
58#include <iprt/path.h>
59#include <iprt/process.h>
60#include <iprt/system.h>
61#include <iprt/time.h>
62#include <iprt/utf16.h>
63
64#include <VBox/log.h>
65#include <VBox/err.h>
66
67
68/*********************************************************************************************************************************
69* Internal Functions *
70*********************************************************************************************************************************/
71static void VBoxGrapicsSetSupported(BOOL fSupported);
72static int vboxTrayCreateTrayIcon(void);
73static LRESULT CALLBACK vboxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
74
75/* Global message handler prototypes. */
76static int vboxTrayGlMsgTaskbarCreated(WPARAM lParam, LPARAM wParam);
77
78
79/*********************************************************************************************************************************
80* Global Variables *
81*********************************************************************************************************************************/
82int g_cVerbosity = 0;
83HANDLE g_hStopSem;
84HANDLE g_hSeamlessWtNotifyEvent = 0;
85HANDLE g_hSeamlessKmNotifyEvent = 0;
86HINSTANCE g_hInstance = NULL;
87HWND g_hwndToolWindow;
88NOTIFYICONDATA g_NotifyIconData;
89
90uint32_t g_fGuestDisplaysChanged = 0;
91
92static PRTLOGGER g_pLoggerRelease = NULL; /**< This is actually the debug logger in DEBUG builds! */
93static uint32_t g_cHistory = 10; /**< Enable log rotation, 10 files. */
94static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /**< Max 1 day per file. */
95static uint64_t g_uHistoryFileSize = 100 * _1M; /**< Max 100MB per file. */
96
97#ifdef DEBUG_andy
98static VBOXSERVICEINFO g_aServices[] =
99{
100 { &g_SvcDescClipboard, NIL_RTTHREAD, NULL, false, false, false, false, true }
101};
102#else
103/**
104 * The details of the services that has been compiled in.
105 */
106static VBOXSERVICEINFO g_aServices[] =
107{
108 { &g_SvcDescDisplay, NIL_RTTHREAD, NULL, false, false, false, false, true },
109#ifdef VBOX_WITH_SHARED_CLIPBOARD
110 { &g_SvcDescClipboard, NIL_RTTHREAD, NULL, false, false, false, false, true },
111#endif
112 { &g_SvcDescSeamless, NIL_RTTHREAD, NULL, false, false, false, false, true },
113 { &g_SvcDescVRDP, NIL_RTTHREAD, NULL, false, false, false, false, true },
114 { &g_SvcDescIPC, NIL_RTTHREAD, NULL, false, false, false, false, true },
115 { &g_SvcDescLA, NIL_RTTHREAD, NULL, false, false, false, false, true },
116#ifdef VBOX_WITH_DRAG_AND_DROP
117 { &g_SvcDescDnD, NIL_RTTHREAD, NULL, false, false, false, false, true }
118#endif
119};
120#endif
121
122/* The global message table. */
123static VBOXGLOBALMESSAGE g_vboxGlobalMessageTable[] =
124{
125 /* Windows specific stuff. */
126 {
127 "TaskbarCreated",
128 vboxTrayGlMsgTaskbarCreated
129 },
130
131 /* VBoxTray specific stuff. */
132 /** @todo Add new messages here! */
133
134 {
135 NULL
136 }
137};
138
139/**
140 * Gets called whenever the Windows main taskbar
141 * get (re-)created. Nice to install our tray icon.
142 *
143 * @return IPRT status code.
144 * @param wParam
145 * @param lParam
146 */
147static int vboxTrayGlMsgTaskbarCreated(WPARAM wParam, LPARAM lParam)
148{
149 RT_NOREF(wParam, lParam);
150 return vboxTrayCreateTrayIcon();
151}
152
153static int vboxTrayCreateTrayIcon(void)
154{
155 HICON hIcon = LoadIcon(g_hInstance, "IDI_ICON1"); /* see Artwork/win/TemplateR3.rc */
156 if (hIcon == NULL)
157 {
158 DWORD dwErr = GetLastError();
159 LogFunc(("Could not load tray icon, error %08X\n", dwErr));
160 return RTErrConvertFromWin32(dwErr);
161 }
162
163 /* Prepare the system tray icon. */
164 RT_ZERO(g_NotifyIconData);
165 g_NotifyIconData.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA);
166 g_NotifyIconData.hWnd = g_hwndToolWindow;
167 g_NotifyIconData.uID = ID_TRAYICON;
168 g_NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
169 g_NotifyIconData.uCallbackMessage = WM_VBOXTRAY_TRAY_ICON;
170 g_NotifyIconData.hIcon = hIcon;
171
172 RTStrPrintf(g_NotifyIconData.szTip, sizeof(g_NotifyIconData.szTip), "%s Guest Additions %d.%d.%dr%d",
173 VBOX_PRODUCT, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
174
175 int rc = VINF_SUCCESS;
176 if (!Shell_NotifyIcon(NIM_ADD, &g_NotifyIconData))
177 {
178 DWORD dwErr = GetLastError();
179 LogFunc(("Could not create tray icon, error=%ld\n", dwErr));
180 rc = RTErrConvertFromWin32(dwErr);
181 RT_ZERO(g_NotifyIconData);
182 }
183
184 if (hIcon)
185 DestroyIcon(hIcon);
186 return rc;
187}
188
189static void vboxTrayRemoveTrayIcon(void)
190{
191 if (g_NotifyIconData.cbSize > 0)
192 {
193 /* Remove the system tray icon and refresh system tray. */
194 Shell_NotifyIcon(NIM_DELETE, &g_NotifyIconData);
195 HWND hTrayWnd = FindWindow("Shell_TrayWnd", NULL); /* We assume we only have one tray atm. */
196 if (hTrayWnd)
197 {
198 HWND hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", NULL);
199 if (hTrayNotifyWnd)
200 SendMessage(hTrayNotifyWnd, WM_PAINT, 0, NULL);
201 }
202 RT_ZERO(g_NotifyIconData);
203 }
204}
205
206/**
207 * The service thread.
208 *
209 * @returns Whatever the worker function returns.
210 * @param ThreadSelf My thread handle.
211 * @param pvUser The service index.
212 */
213static DECLCALLBACK(int) vboxTrayServiceThread(RTTHREAD ThreadSelf, void *pvUser)
214{
215 PVBOXSERVICEINFO pSvc = (PVBOXSERVICEINFO)pvUser;
216 AssertPtr(pSvc);
217
218#ifndef RT_OS_WINDOWS
219 /*
220 * Block all signals for this thread. Only the main thread will handle signals.
221 */
222 sigset_t signalMask;
223 sigfillset(&signalMask);
224 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
225#endif
226
227 int rc = pSvc->pDesc->pfnWorker(pSvc->pInstance, &pSvc->fShutdown);
228 ASMAtomicXchgBool(&pSvc->fShutdown, true);
229 RTThreadUserSignal(ThreadSelf);
230
231 LogFunc(("Worker for '%s' ended with %Rrc\n", pSvc->pDesc->pszName, rc));
232 return rc;
233}
234
235static int vboxTrayServicesStart(PVBOXSERVICEENV pEnv)
236{
237 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
238
239 LogRel(("Starting services ...\n"));
240
241 int rc = VINF_SUCCESS;
242
243 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
244 {
245 PVBOXSERVICEINFO pSvc = &g_aServices[i];
246 LogRel(("Starting service '%s' ...\n", pSvc->pDesc->pszName));
247
248 pSvc->hThread = NIL_RTTHREAD;
249 pSvc->pInstance = NULL;
250 pSvc->fStarted = false;
251 pSvc->fShutdown = false;
252
253 int rc2 = VINF_SUCCESS;
254
255 if (pSvc->pDesc->pfnInit)
256 rc2 = pSvc->pDesc->pfnInit(pEnv, &pSvc->pInstance);
257
258 if (RT_FAILURE(rc2))
259 {
260 switch (rc2)
261 {
262 case VERR_NOT_SUPPORTED:
263 LogRel(("Service '%s' is not supported on this system\n", pSvc->pDesc->pszName));
264 rc2 = VINF_SUCCESS; /* Keep going. */
265 break;
266
267 case VERR_HGCM_SERVICE_NOT_FOUND:
268 LogRel(("Service '%s' is not available on the host\n", pSvc->pDesc->pszName));
269 rc2 = VINF_SUCCESS; /* Keep going. */
270 break;
271
272 default:
273 LogRel(("Failed to initialize service '%s', rc=%Rrc\n", pSvc->pDesc->pszName, rc2));
274 break;
275 }
276 }
277 else
278 {
279 if (pSvc->pDesc->pfnWorker)
280 {
281 rc2 = RTThreadCreate(&pSvc->hThread, vboxTrayServiceThread, pSvc /* pvUser */,
282 0 /* Default stack size */, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, pSvc->pDesc->pszName);
283 if (RT_SUCCESS(rc2))
284 {
285 pSvc->fStarted = true;
286
287 RTThreadUserWait(pSvc->hThread, 30 * 1000 /* Timeout in ms */);
288 if (pSvc->fShutdown)
289 {
290 LogRel(("Service '%s' failed to start!\n", pSvc->pDesc->pszName));
291 rc = VERR_GENERAL_FAILURE;
292 }
293 else
294 LogRel(("Service '%s' started\n", pSvc->pDesc->pszName));
295 }
296 else
297 {
298 LogRel(("Failed to start thread for service '%s': %Rrc\n", rc2));
299 if (pSvc->pDesc->pfnDestroy)
300 pSvc->pDesc->pfnDestroy(pSvc->pInstance);
301 }
302 }
303 }
304
305 if (RT_SUCCESS(rc))
306 rc = rc2;
307 }
308
309 if (RT_SUCCESS(rc))
310 LogRel(("All services started\n"));
311 else
312 LogRel(("Services started, but some with errors\n"));
313
314 LogFlowFuncLeaveRC(rc);
315 return rc;
316}
317
318static int vboxTrayServicesStop(VBOXSERVICEENV *pEnv)
319{
320 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
321
322 LogRel2(("Stopping all services ...\n"));
323
324 /*
325 * Signal all the services.
326 */
327 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
328 ASMAtomicWriteBool(&g_aServices[i].fShutdown, true);
329
330 /*
331 * Do the pfnStop callback on all running services.
332 */
333 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
334 {
335 PVBOXSERVICEINFO pSvc = &g_aServices[i];
336 if ( pSvc->fStarted
337 && pSvc->pDesc->pfnStop)
338 {
339 LogRel2(("Calling stop function for service '%s' ...\n", pSvc->pDesc->pszName));
340 int rc2 = pSvc->pDesc->pfnStop(pSvc->pInstance);
341 if (RT_FAILURE(rc2))
342 LogRel(("Failed to stop service '%s': %Rrc\n", pSvc->pDesc->pszName, rc2));
343 }
344 }
345
346 LogRel2(("All stop functions for services called\n"));
347
348 int rc = VINF_SUCCESS;
349
350 /*
351 * Wait for all the service threads to complete.
352 */
353 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
354 {
355 PVBOXSERVICEINFO pSvc = &g_aServices[i];
356 if (!pSvc->fEnabled) /* Only stop services which were started before. */
357 continue;
358
359 if (pSvc->hThread != NIL_RTTHREAD)
360 {
361 LogRel2(("Waiting for service '%s' to stop ...\n", pSvc->pDesc->pszName));
362 int rc2 = VINF_SUCCESS;
363 for (int j = 0; j < 30; j++) /* Wait 30 seconds in total */
364 {
365 rc2 = RTThreadWait(pSvc->hThread, 1000 /* Wait 1 second */, NULL);
366 if (RT_SUCCESS(rc2))
367 break;
368 }
369 if (RT_FAILURE(rc2))
370 {
371 LogRel(("Service '%s' failed to stop (%Rrc)\n", pSvc->pDesc->pszName, rc2));
372 if (RT_SUCCESS(rc))
373 rc = rc2;
374 }
375 }
376
377 if ( pSvc->pDesc->pfnDestroy
378 && pSvc->pInstance) /* pInstance might be NULL if initialization of a service failed. */
379 {
380 LogRel2(("Terminating service '%s' ...\n", pSvc->pDesc->pszName));
381 pSvc->pDesc->pfnDestroy(pSvc->pInstance);
382 }
383 }
384
385 if (RT_SUCCESS(rc))
386 LogRel(("All services stopped\n"));
387
388 LogFlowFuncLeaveRC(rc);
389 return rc;
390}
391
392static int vboxTrayRegisterGlobalMessages(PVBOXGLOBALMESSAGE pTable)
393{
394 int rc = VINF_SUCCESS;
395 if (pTable == NULL) /* No table to register? Skip. */
396 return rc;
397 while ( pTable->pszName
398 && RT_SUCCESS(rc))
399 {
400 /* Register global accessible window messages. */
401 pTable->uMsgID = RegisterWindowMessage(TEXT(pTable->pszName));
402 if (!pTable->uMsgID)
403 {
404 DWORD dwErr = GetLastError();
405 Log(("Registering global message \"%s\" failed, error = %08X\n", dwErr));
406 rc = RTErrConvertFromWin32(dwErr);
407 }
408
409 /* Advance to next table element. */
410 pTable++;
411 }
412 return rc;
413}
414
415static bool vboxTrayHandleGlobalMessages(PVBOXGLOBALMESSAGE pTable, UINT uMsg,
416 WPARAM wParam, LPARAM lParam)
417{
418 if (pTable == NULL)
419 return false;
420 while (pTable && pTable->pszName)
421 {
422 if (pTable->uMsgID == uMsg)
423 {
424 if (pTable->pfnHandler)
425 pTable->pfnHandler(wParam, lParam);
426 return true;
427 }
428
429 /* Advance to next table element. */
430 pTable++;
431 }
432 return false;
433}
434
435/**
436 * Header/footer callback for the release logger.
437 *
438 * @param pLoggerRelease
439 * @param enmPhase
440 * @param pfnLog
441 */
442static DECLCALLBACK(void) vboxTrayLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
443{
444 /* Some introductory information. */
445 static RTTIMESPEC s_TimeSpec;
446 char szTmp[256];
447 if (enmPhase == RTLOGPHASE_BEGIN)
448 RTTimeNow(&s_TimeSpec);
449 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
450
451 switch (enmPhase)
452 {
453 case RTLOGPHASE_BEGIN:
454 {
455 pfnLog(pLoggerRelease,
456 "VBoxTray %s r%s %s (%s %s) release log\n"
457 "Log opened %s\n",
458 RTBldCfgVersion(), RTBldCfgRevisionStr(), VBOX_BUILD_TARGET,
459 __DATE__, __TIME__, szTmp);
460
461 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
462 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
463 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
464 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
465 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
466 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
467 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
468 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
469 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
470 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
471 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
472
473 /* the package type is interesting for Linux distributions */
474 char szExecName[RTPATH_MAX];
475 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
476 pfnLog(pLoggerRelease,
477 "Executable: %s\n"
478 "Process ID: %u\n"
479 "Package type: %s"
480#ifdef VBOX_OSE
481 " (OSE)"
482#endif
483 "\n",
484 pszExecName ? pszExecName : "unknown",
485 RTProcSelf(),
486 VBOX_PACKAGE_STRING);
487 break;
488 }
489
490 case RTLOGPHASE_PREROTATE:
491 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
492 break;
493
494 case RTLOGPHASE_POSTROTATE:
495 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
496 break;
497
498 case RTLOGPHASE_END:
499 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
500 break;
501
502 default:
503 /* nothing */;
504 }
505}
506
507/**
508 * Creates the default release logger outputting to the specified file.
509 *
510 * @return IPRT status code.
511 * @param pszLogFile Path to log file to use.
512 */
513static int vboxTrayLogCreate(const char *pszLogFile)
514{
515 /* Create release (or debug) logger (stdout + file). */
516 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
517 static const char s_szEnvVarPfx[] = "VBOXTRAY_RELEASE_LOG";
518
519 RTERRINFOSTATIC ErrInfo;
520 int rc = RTLogCreateEx(&g_pLoggerRelease, s_szEnvVarPfx,
521 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_USECRLF,
522 "all.e", RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX,
523 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT,
524 vboxTrayLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
525 NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
526 RTErrInfoInitStatic(&ErrInfo), "%s", pszLogFile ? pszLogFile : "");
527 if (RT_SUCCESS(rc))
528 {
529 /* Register this logger as the release logger. */
530 RTLogRelSetDefaultInstance(g_pLoggerRelease);
531
532 /* Register this logger as the _debug_ logger. */
533 RTLogSetDefaultInstance(g_pLoggerRelease);
534
535 /* All groups we want to enable logging for VBoxTray. */
536 const char *apszGroups[] = { "guest_dnd", "shared_clipboard" };
537 char szGroupSettings[_1K];
538
539 szGroupSettings[0] = '\0';
540
541 for (size_t i = 0; i < RT_ELEMENTS(apszGroups); i++)
542 {
543 if (i > 0)
544 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), "+");
545 if (RT_SUCCESS(rc))
546 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), apszGroups[i]);
547 if (RT_FAILURE(rc))
548 break;
549
550 switch (g_cVerbosity)
551 {
552 case 1:
553 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), ".e.l");
554 break;
555
556 case 2:
557 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), ".e.l.l2");
558 break;
559
560 case 3:
561 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), ".e.l.l2.l3");
562 break;
563
564 case 4:
565 RT_FALL_THROUGH();
566 default:
567 rc = RTStrCat(szGroupSettings, sizeof(szGroupSettings), ".e.l.l2.l3.f");
568 break;
569 }
570
571 if (RT_FAILURE(rc))
572 break;
573 }
574
575 LogRel(("Verbose log settings are: %s\n", szGroupSettings));
576
577 if (RT_SUCCESS(rc))
578 rc = RTLogGroupSettings(g_pLoggerRelease, szGroupSettings);
579 if (RT_FAILURE(rc))
580 RTMsgError("Setting log group settings failed, rc=%Rrc\n", rc);
581
582 /* Explicitly flush the log in case of VBOXTRAY_RELEASE_LOG=buffered. */
583 RTLogFlush(g_pLoggerRelease);
584 }
585 else
586 VBoxTrayShowError(ErrInfo.szMsg);
587
588 return rc;
589}
590
591static void vboxTrayLogDestroy(void)
592{
593 /* Only want to destroy the release logger before calling exit(). The debug
594 logger can be useful after that point... */
595 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
596}
597
598/**
599 * Displays an error message.
600 *
601 * @returns RTEXITCODE_FAILURE.
602 * @param pszFormat The message text.
603 * @param ... Format arguments.
604 */
605RTEXITCODE VBoxTrayShowError(const char *pszFormat, ...)
606{
607 va_list args;
608 va_start(args, pszFormat);
609 char *psz = NULL;
610 RTStrAPrintfV(&psz, pszFormat, args);
611 va_end(args);
612
613 AssertPtr(psz);
614 LogRel(("Error: %s", psz));
615
616 MessageBox(GetDesktopWindow(), psz, "VBoxTray - Error", MB_OK | MB_ICONERROR);
617
618 RTStrFree(psz);
619
620 return RTEXITCODE_FAILURE;
621}
622
623static void vboxTrayDestroyToolWindow(void)
624{
625 if (g_hwndToolWindow)
626 {
627 Log(("Destroying tool window ...\n"));
628
629 /* Destroy the tool window. */
630 DestroyWindow(g_hwndToolWindow);
631 g_hwndToolWindow = NULL;
632
633 UnregisterClass("VBoxTrayToolWndClass", g_hInstance);
634 }
635}
636
637static int vboxTrayCreateToolWindow(void)
638{
639 DWORD dwErr = ERROR_SUCCESS;
640
641 /* Create a custom window class. */
642 WNDCLASSEX wc = { 0 };
643 wc.cbSize = sizeof(WNDCLASSEX);
644 wc.style = CS_NOCLOSE;
645 wc.lpfnWndProc = (WNDPROC)vboxToolWndProc;
646 wc.hInstance = g_hInstance;
647 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
648 wc.lpszClassName = "VBoxTrayToolWndClass";
649
650 if (!RegisterClassEx(&wc))
651 {
652 dwErr = GetLastError();
653 Log(("Registering invisible tool window failed, error = %08X\n", dwErr));
654 }
655 else
656 {
657 /*
658 * Create our (invisible) tool window.
659 * Note: The window name ("VBoxTrayToolWnd") and class ("VBoxTrayToolWndClass") is
660 * needed for posting globally registered messages to VBoxTray and must not be
661 * changed! Otherwise things get broken!
662 *
663 */
664 g_hwndToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
665 "VBoxTrayToolWndClass", "VBoxTrayToolWnd",
666 WS_POPUPWINDOW,
667 -200, -200, 100, 100, NULL, NULL, g_hInstance, NULL);
668 if (!g_hwndToolWindow)
669 {
670 dwErr = GetLastError();
671 Log(("Creating invisible tool window failed, error = %08X\n", dwErr));
672 }
673 else
674 {
675 /* Reload the cursor(s). */
676 hlpReloadCursor();
677
678 Log(("Invisible tool window handle = %p\n", g_hwndToolWindow));
679 }
680 }
681
682 if (dwErr != ERROR_SUCCESS)
683 vboxTrayDestroyToolWindow();
684 return RTErrConvertFromWin32(dwErr);
685}
686
687static int vboxTraySetupSeamless(void)
688{
689 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore. */
690 SECURITY_ATTRIBUTES SecAttr;
691 DWORD dwErr = ERROR_SUCCESS;
692 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
693 BOOL fRC;
694
695 SecAttr.nLength = sizeof(SecAttr);
696 SecAttr.bInheritHandle = FALSE;
697 SecAttr.lpSecurityDescriptor = &secDesc;
698 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
699 fRC = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
700 if (!fRC)
701 {
702 dwErr = GetLastError();
703 Log(("SetSecurityDescriptorDacl failed with last error = %08X\n", dwErr));
704 }
705 else
706 {
707 /* For Vista and up we need to change the integrity of the security descriptor, too. */
708 uint64_t const uNtVersion = RTSystemGetNtVersion();
709 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
710 {
711 BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize);
712 *(void **)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA =
713 RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorA");
714 Log(("pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %p\n",
715 RT_CB_LOG_CAST(pfnConvertStringSecurityDescriptorToSecurityDescriptorA)));
716 if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA)
717 {
718 PSECURITY_DESCRIPTOR pSD;
719 PACL pSacl = NULL;
720 BOOL fSaclPresent = FALSE;
721 BOOL fSaclDefaulted = FALSE;
722
723 fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */
724 SDDL_REVISION_1, &pSD, NULL);
725 if (!fRC)
726 {
727 dwErr = GetLastError();
728 Log(("ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr));
729 }
730 else
731 {
732 fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted);
733 if (!fRC)
734 {
735 dwErr = GetLastError();
736 Log(("GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
737 }
738 else
739 {
740 fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE);
741 if (!fRC)
742 {
743 dwErr = GetLastError();
744 Log(("SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
745 }
746 }
747 }
748 }
749 }
750
751 if ( dwErr == ERROR_SUCCESS
752 && uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Only for W2K and up ... */
753 {
754 g_hSeamlessWtNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_WT_EVENT_NAME);
755 if (g_hSeamlessWtNotifyEvent == NULL)
756 {
757 dwErr = GetLastError();
758 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
759 }
760
761 g_hSeamlessKmNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
762 if (g_hSeamlessKmNotifyEvent == NULL)
763 {
764 dwErr = GetLastError();
765 Log(("CreateEvent for Seamless failed, last error = %08X\n", dwErr));
766 }
767 }
768 }
769 return RTErrConvertFromWin32(dwErr);
770}
771
772static void vboxTrayShutdownSeamless(void)
773{
774 if (g_hSeamlessWtNotifyEvent)
775 {
776 CloseHandle(g_hSeamlessWtNotifyEvent);
777 g_hSeamlessWtNotifyEvent = NULL;
778 }
779
780 if (g_hSeamlessKmNotifyEvent)
781 {
782 CloseHandle(g_hSeamlessKmNotifyEvent);
783 g_hSeamlessKmNotifyEvent = NULL;
784 }
785}
786
787static int vboxTrayServiceMain(void)
788{
789 int rc = VINF_SUCCESS;
790 LogFunc(("Entering vboxTrayServiceMain\n"));
791
792 g_hStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
793 if (g_hStopSem == NULL)
794 {
795 rc = RTErrConvertFromWin32(GetLastError());
796 LogFunc(("CreateEvent for stopping VBoxTray failed, rc=%Rrc\n", rc));
797 }
798 else
799 {
800 /*
801 * Start services listed in the vboxServiceTable.
802 */
803 VBOXSERVICEENV svcEnv;
804 svcEnv.hInstance = g_hInstance;
805
806 /* Initializes disp-if to default (XPDM) mode. */
807 VBoxDispIfInit(&svcEnv.dispIf); /* Cannot fail atm. */
808 #ifdef VBOX_WITH_WDDM
809 /*
810 * For now the display mode will be adjusted to WDDM mode if needed
811 * on display service initialization when it detects the display driver type.
812 */
813 #endif
814
815 /* Finally start all the built-in services! */
816 rc = vboxTrayServicesStart(&svcEnv);
817 if (RT_FAILURE(rc))
818 {
819 /* Terminate service if something went wrong. */
820 vboxTrayServicesStop(&svcEnv);
821 }
822 else
823 {
824 uint64_t const uNtVersion = RTSystemGetNtVersion();
825 rc = vboxTrayCreateTrayIcon();
826 if ( RT_SUCCESS(rc)
827 && uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Only for W2K and up ... */
828 {
829 /* We're ready to create the tooltip balloon.
830 Check in 10 seconds (@todo make seconds configurable) ... */
831 SetTimer(g_hwndToolWindow,
832 TIMERID_VBOXTRAY_CHECK_HOSTVERSION,
833 10 * 1000, /* 10 seconds */
834 NULL /* No timerproc */);
835 }
836
837 if (RT_SUCCESS(rc))
838 {
839 /* Report the host that we're up and running! */
840 hlpReportStatus(VBoxGuestFacilityStatus_Active);
841 }
842
843 if (RT_SUCCESS(rc))
844 {
845 /* Boost thread priority to make sure we wake up early for seamless window notifications
846 * (not sure if it actually makes any difference though). */
847 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
848
849 /*
850 * Main execution loop
851 * Wait for the stop semaphore to be posted or a window event to arrive
852 */
853
854 HANDLE hWaitEvent[4] = {0};
855 DWORD dwEventCount = 0;
856
857 hWaitEvent[dwEventCount++] = g_hStopSem;
858
859 /* Check if seamless mode is not active and add seamless event to the list */
860 if (0 != g_hSeamlessWtNotifyEvent)
861 {
862 hWaitEvent[dwEventCount++] = g_hSeamlessWtNotifyEvent;
863 }
864
865 if (0 != g_hSeamlessKmNotifyEvent)
866 {
867 hWaitEvent[dwEventCount++] = g_hSeamlessKmNotifyEvent;
868 }
869
870 if (0 != vboxDtGetNotifyEvent())
871 {
872 hWaitEvent[dwEventCount++] = vboxDtGetNotifyEvent();
873 }
874
875 LogFlowFunc(("Number of events to wait in main loop: %ld\n", dwEventCount));
876 while (true)
877 {
878 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
879 waitResult = waitResult - WAIT_OBJECT_0;
880
881 /* Only enable for message debugging, lots of traffic! */
882 //Log(("Wait result = %ld\n", waitResult));
883
884 if (waitResult == 0)
885 {
886 LogFunc(("Event 'Exit' triggered\n"));
887 /* exit */
888 break;
889 }
890 else
891 {
892 BOOL fHandled = FALSE;
893 if (waitResult < RT_ELEMENTS(hWaitEvent))
894 {
895 if (hWaitEvent[waitResult])
896 {
897 if (hWaitEvent[waitResult] == g_hSeamlessWtNotifyEvent)
898 {
899 LogFunc(("Event 'Seamless' triggered\n"));
900
901 /* seamless window notification */
902 VBoxSeamlessCheckWindows(false);
903 fHandled = TRUE;
904 }
905 else if (hWaitEvent[waitResult] == g_hSeamlessKmNotifyEvent)
906 {
907 LogFunc(("Event 'Km Seamless' triggered\n"));
908
909 /* seamless window notification */
910 VBoxSeamlessCheckWindows(true);
911 fHandled = TRUE;
912 }
913 else if (hWaitEvent[waitResult] == vboxDtGetNotifyEvent())
914 {
915 LogFunc(("Event 'Dt' triggered\n"));
916 vboxDtDoCheck();
917 fHandled = TRUE;
918 }
919 }
920 }
921
922 if (!fHandled)
923 {
924 /* timeout or a window message, handle it */
925 MSG msg;
926 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
927 {
928#ifdef DEBUG_andy
929 LogFlowFunc(("PeekMessage %u\n", msg.message));
930#endif
931 if (msg.message == WM_QUIT)
932 {
933 LogFunc(("Terminating ...\n"));
934 SetEvent(g_hStopSem);
935 }
936 TranslateMessage(&msg);
937 DispatchMessage(&msg);
938 }
939 }
940 }
941 }
942 LogFunc(("Returned from main loop, exiting ...\n"));
943 }
944 LogFunc(("Waiting for services to stop ...\n"));
945 vboxTrayServicesStop(&svcEnv);
946 } /* Services started */
947 CloseHandle(g_hStopSem);
948 } /* Stop event created */
949
950 vboxTrayRemoveTrayIcon();
951
952 LogFunc(("Leaving with rc=%Rrc\n", rc));
953 return rc;
954}
955
956/**
957 * Main function
958 */
959int main(int cArgs, char **papszArgs)
960{
961 int rc = RTR3InitExe(cArgs, &papszArgs, RTR3INIT_FLAGS_STANDALONE_APP);
962 if (RT_FAILURE(rc))
963 return RTMsgInitFailure(rc);
964
965 /*
966 * Parse the top level arguments until we find a command.
967 */
968 static const RTGETOPTDEF s_aOptions[] =
969 {
970 { "--help", 'h', RTGETOPT_REQ_NOTHING },
971 { "-help", 'h', RTGETOPT_REQ_NOTHING },
972 { "/help", 'h', RTGETOPT_REQ_NOTHING },
973 { "/?", 'h', RTGETOPT_REQ_NOTHING },
974 { "--logfile", 'l', RTGETOPT_REQ_STRING },
975 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
976 { "--version", 'V', RTGETOPT_REQ_NOTHING },
977 };
978
979 char szLogFile[RTPATH_MAX] = {0};
980
981 RTGETOPTSTATE GetState;
982 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
983 if (RT_FAILURE(rc))
984 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
985
986 int ch;
987 RTGETOPTUNION ValueUnion;
988 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
989 {
990 switch (ch)
991 {
992 case 'h':
993 hlpShowMessageBox(VBOX_PRODUCT " - " VBOX_VBOXTRAY_TITLE,
994 MB_ICONINFORMATION,
995 "-- " VBOX_PRODUCT " %s v%u.%u.%ur%u --\n\n"
996 "Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n"
997 "Command Line Parameters:\n\n"
998 "-l, --logfile <file>\n"
999 " Enables logging to a file\n"
1000 "-v, --verbose\n"
1001 " Increases verbosity\n"
1002 "-V, --version\n"
1003 " Displays version number and exit\n"
1004 "-?, -h, --help\n"
1005 " Displays this help text and exit\n"
1006 "\n"
1007 "Examples:\n"
1008 " %s -vvv\n",
1009 VBOX_VBOXTRAY_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV,
1010 papszArgs[0], papszArgs[0]);
1011 return RTEXITCODE_SUCCESS;
1012
1013 case 'l':
1014 if (*ValueUnion.psz == '\0')
1015 szLogFile[0] = '\0';
1016 else
1017 {
1018 rc = RTPathAbs(ValueUnion.psz, szLogFile, sizeof(szLogFile));
1019 if (RT_FAILURE(rc))
1020 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on log file path: %Rrc (%s)",
1021 rc, ValueUnion.psz);
1022 }
1023 break;
1024
1025 case 'v':
1026 g_cVerbosity++;
1027 break;
1028
1029 case 'V':
1030 hlpShowMessageBox(VBOX_VBOXTRAY_TITLE, MB_ICONINFORMATION,
1031 "Version: %u.%u.%ur%u",
1032 VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
1033 return RTEXITCODE_SUCCESS;
1034
1035 default:
1036 rc = RTGetOptPrintError(ch, &ValueUnion);
1037 break;
1038 }
1039 }
1040
1041 /* Note: Do not use a global namespace ("Global\\") for mutex name here,
1042 * will blow up NT4 compatibility! */
1043 HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, VBOX_VBOXTRAY_TITLE);
1044 if ( hMutexAppRunning != NULL
1045 && GetLastError() == ERROR_ALREADY_EXISTS)
1046 {
1047 /* VBoxTray already running? Bail out. */
1048 CloseHandle (hMutexAppRunning);
1049 hMutexAppRunning = NULL;
1050 return RTEXITCODE_SUCCESS;
1051 }
1052
1053 rc = vboxTrayLogCreate(szLogFile[0] ? szLogFile : NULL);
1054 if (RT_SUCCESS(rc))
1055 {
1056 LogRel(("Verbosity level: %d\n", g_cVerbosity));
1057
1058 rc = VbglR3Init();
1059 if (RT_SUCCESS(rc))
1060 {
1061 /* Log the major windows NT version: */
1062 uint64_t const uNtVersion = RTSystemGetNtVersion();
1063 LogRel(("Windows version %u.%u build %u (uNtVersion=%#RX64)\n", RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVersion),
1064 RTSYSTEM_NT_VERSION_GET_MINOR(uNtVersion), RTSYSTEM_NT_VERSION_GET_BUILD(uNtVersion), uNtVersion ));
1065
1066 /* Set the instance handle. */
1067#ifdef IPRT_NO_CRT
1068 Assert(g_hInstance == NULL); /* Make sure this isn't set before by WinMain(). */
1069 g_hInstance = GetModuleHandleW(NULL);
1070#endif
1071 hlpReportStatus(VBoxGuestFacilityStatus_Init);
1072 rc = vboxTrayCreateToolWindow();
1073 if (RT_SUCCESS(rc))
1074 {
1075 VBoxCapsInit();
1076
1077 rc = vboxStInit(g_hwndToolWindow);
1078 if (!RT_SUCCESS(rc))
1079 {
1080 LogFlowFunc(("vboxStInit failed, rc=%Rrc\n", rc));
1081 /* ignore the St Init failure. this can happen for < XP win that do not support WTS API
1082 * in that case the session is treated as active connected to the physical console
1083 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1084 Assert(vboxStIsActiveConsole());
1085 }
1086
1087 rc = vboxDtInit();
1088 if (!RT_SUCCESS(rc))
1089 {
1090 LogFlowFunc(("vboxDtInit failed, rc=%Rrc\n", rc));
1091 /* ignore the Dt Init failure. this can happen for < XP win that do not support WTS API
1092 * in that case the session is treated as active connected to the physical console
1093 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
1094 Assert(vboxDtIsInputDesktop());
1095 }
1096
1097 rc = VBoxAcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, true);
1098 if (!RT_SUCCESS(rc))
1099 LogFlowFunc(("VBoxAcquireGuestCaps failed with rc=%Rrc, ignoring ...\n", rc));
1100
1101 rc = vboxTraySetupSeamless(); /** @todo r=andy Do we really want to be this critical for the whole application? */
1102 if (RT_SUCCESS(rc))
1103 {
1104 rc = vboxTrayServiceMain();
1105 if (RT_SUCCESS(rc))
1106 hlpReportStatus(VBoxGuestFacilityStatus_Terminating);
1107 vboxTrayShutdownSeamless();
1108 }
1109
1110 /* it should be safe to call vboxDtTerm even if vboxStInit above failed */
1111 vboxDtTerm();
1112
1113 /* it should be safe to call vboxStTerm even if vboxStInit above failed */
1114 vboxStTerm();
1115
1116 VBoxCapsTerm();
1117
1118 vboxTrayDestroyToolWindow();
1119 }
1120 if (RT_SUCCESS(rc))
1121 hlpReportStatus(VBoxGuestFacilityStatus_Terminated);
1122 else
1123 {
1124 LogRel(("Error while starting, rc=%Rrc\n", rc));
1125 hlpReportStatus(VBoxGuestFacilityStatus_Failed);
1126 }
1127
1128 LogRel(("Ended\n"));
1129 VbglR3Term();
1130 }
1131 else
1132 LogRel(("VbglR3Init failed: %Rrc\n", rc));
1133 }
1134
1135 /* Release instance mutex. */
1136 if (hMutexAppRunning != NULL)
1137 {
1138 CloseHandle(hMutexAppRunning);
1139 hMutexAppRunning = NULL;
1140 }
1141
1142 vboxTrayLogDestroy();
1143
1144 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1145}
1146
1147#ifndef IPRT_NO_CRT
1148int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
1149{
1150 RT_NOREF(hPrevInstance, lpCmdLine, nCmdShow);
1151
1152 g_hInstance = hInstance;
1153
1154 return main(__argc, __argv);
1155}
1156#endif /* IPRT_NO_CRT */
1157
1158/**
1159 * Window procedure for our main tool window.
1160 */
1161static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1162{
1163 LogFlowFunc(("hWnd=%p, uMsg=%u\n", hWnd, uMsg));
1164
1165 switch (uMsg)
1166 {
1167 case WM_CREATE:
1168 {
1169 LogFunc(("Tool window created\n"));
1170
1171 int rc = vboxTrayRegisterGlobalMessages(&g_vboxGlobalMessageTable[0]);
1172 if (RT_FAILURE(rc))
1173 LogFunc(("Error registering global window messages, rc=%Rrc\n", rc));
1174 return 0;
1175 }
1176
1177 case WM_CLOSE:
1178 return 0;
1179
1180 case WM_DESTROY:
1181 {
1182 LogFunc(("Tool window destroyed\n"));
1183 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1184 return 0;
1185 }
1186
1187 case WM_TIMER:
1188 {
1189 if (VBoxCapsCheckTimer(wParam))
1190 return 0;
1191 if (vboxDtCheckTimer(wParam))
1192 return 0;
1193 if (vboxStCheckTimer(wParam))
1194 return 0;
1195
1196 switch (wParam)
1197 {
1198 case TIMERID_VBOXTRAY_CHECK_HOSTVERSION:
1199 if (RT_SUCCESS(VBoxCheckHostVersion()))
1200 {
1201 /* After successful run we don't need to check again. */
1202 KillTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
1203 }
1204 return 0;
1205
1206 default:
1207 break;
1208 }
1209
1210 break; /* Make sure other timers get processed the usual way! */
1211 }
1212
1213 case WM_VBOXTRAY_TRAY_ICON:
1214 {
1215 switch (LOWORD(lParam))
1216 {
1217 case WM_LBUTTONDBLCLK:
1218 break;
1219 case WM_RBUTTONDOWN:
1220 {
1221 if (!g_cVerbosity) /* Don't show menu when running in non-verbose mode. */
1222 break;
1223
1224 POINT lpCursor;
1225 if (GetCursorPos(&lpCursor))
1226 {
1227 HMENU hContextMenu = CreatePopupMenu();
1228 if (hContextMenu)
1229 {
1230 UINT_PTR uMenuItem = 9999;
1231 UINT fMenuItem = MF_BYPOSITION | MF_STRING;
1232 if (InsertMenuW(hContextMenu, UINT_MAX, fMenuItem, uMenuItem, L"Exit"))
1233 {
1234 SetForegroundWindow(hWnd);
1235
1236 const bool fBlockWhileTracking = true;
1237
1238 UINT fTrack = TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN;
1239
1240 if (fBlockWhileTracking)
1241 fTrack |= TPM_RETURNCMD | TPM_NONOTIFY;
1242
1243 uMsg = TrackPopupMenu(hContextMenu, fTrack, lpCursor.x, lpCursor.y, 0, hWnd, NULL);
1244 if ( uMsg
1245 && fBlockWhileTracking)
1246 {
1247 if (uMsg == uMenuItem)
1248 PostMessage(g_hwndToolWindow, WM_QUIT, 0, 0);
1249 }
1250 else if (!uMsg)
1251 LogFlowFunc(("Tracking popup menu failed with %ld\n", GetLastError()));
1252 }
1253
1254 DestroyMenu(hContextMenu);
1255 }
1256 }
1257 break;
1258 }
1259 }
1260 return 0;
1261 }
1262
1263 case WM_VBOX_SEAMLESS_ENABLE:
1264 {
1265 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1266 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1267 VBoxSeamlessCheckWindows(true);
1268 return 0;
1269 }
1270
1271 case WM_VBOX_SEAMLESS_DISABLE:
1272 {
1273 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1274 return 0;
1275 }
1276
1277 case WM_DISPLAYCHANGE:
1278 ASMAtomicUoWriteU32(&g_fGuestDisplaysChanged, 1);
1279 // No break or return is intentional here.
1280 case WM_VBOX_SEAMLESS_UPDATE:
1281 {
1282 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1283 VBoxSeamlessCheckWindows(true);
1284 return 0;
1285 }
1286
1287 case WM_VBOX_GRAPHICS_SUPPORTED:
1288 {
1289 VBoxGrapicsSetSupported(TRUE);
1290 return 0;
1291 }
1292
1293 case WM_VBOX_GRAPHICS_UNSUPPORTED:
1294 {
1295 VBoxGrapicsSetSupported(FALSE);
1296 return 0;
1297 }
1298
1299 case WM_WTSSESSION_CHANGE:
1300 {
1301 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
1302 if (vboxStHandleEvent(wParam))
1303 {
1304 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
1305 VBoxConsoleEnable(!fOldAllowedState);
1306 }
1307 return 0;
1308 }
1309
1310 default:
1311 {
1312 /* Handle all globally registered window messages. */
1313 if (vboxTrayHandleGlobalMessages(&g_vboxGlobalMessageTable[0], uMsg,
1314 wParam, lParam))
1315 {
1316 return 0; /* We handled the message. @todo Add return value!*/
1317 }
1318 break; /* We did not handle the message, dispatch to DefWndProc. */
1319 }
1320 }
1321
1322 /* Only if message was *not* handled by our switch above, dispatch to DefWindowProc. */
1323 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1324}
1325
1326static void VBoxGrapicsSetSupported(BOOL fSupported)
1327{
1328 VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_GRAPHICS, fSupported);
1329}
1330
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use