VirtualBox

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

Last change on this file since 106466 was 106466, checked in by vboxsync, 4 months ago

Additions/VBoxTray: More cleanup (renaming). ​bugref:10763

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