VirtualBox

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

Last change on this file since 106945 was 106887, checked in by vboxsync, 5 weeks ago

Additions/VBoxTray: Give a better clue to the user when VBoxTray is unable to start if unable to open a connection to the VBoxGuest driver.

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