VirtualBox

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

Last change on this file since 106945 was 106411, checked in by vboxsync, 8 weeks ago

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

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 KB
Line 
1/* $Id: VBoxHelpers.cpp 106411 2024-10-17 07:44:43Z vboxsync $ */
2/** @file
3 * helpers - Guest Additions Service helper functions
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#include <iprt/win/windows.h>
29
30#include <iprt/string.h>
31#include <iprt/alloca.h>
32#include <iprt/stream.h>
33#include <iprt/system.h>
34#include <iprt/utf16.h>
35#include <VBox/Log.h>
36#include <VBox/VBoxGuestLib.h>
37
38#include "VBoxTray.h"
39#include "VBoxTrayInternal.h"
40#include "VBoxHelpers.h"
41
42
43
44static const char *vboxTrayGstFacilityStsToStr(VBoxGuestFacilityStatus faStatus, bool fShort)
45{
46 switch (faStatus)
47 {
48 case VBoxGuestFacilityStatus_Inactive:
49 return fShort ? "inactive" : "not active";
50 case VBoxGuestFacilityStatus_Paused:
51 return fShort ? "paused" : "paused";
52 case VBoxGuestFacilityStatus_PreInit:
53 return fShort ? "preinit" : "pre-initializing";
54 case VBoxGuestFacilityStatus_Init:
55 return fShort ? "init" : "initializing";
56 case VBoxGuestFacilityStatus_Active:
57 return fShort ? "active" : "active/running";
58 case VBoxGuestFacilityStatus_Terminating:
59 return fShort ? "terminating" : "terminating";
60 case VBoxGuestFacilityStatus_Terminated:
61 return fShort ? "terminated" : "terminated";
62 case VBoxGuestFacilityStatus_Failed:
63 return fShort ? "failed" : "failed";
64 case VBoxGuestFacilityStatus_Unknown:
65 default:
66 break;
67 }
68 return "unknown";
69}
70
71int VBoxTrayHlpReportStatus(VBoxGuestFacilityStatus statusCurrent)
72{
73 if (g_cVerbosity)
74 VBoxTrayHlpShowBalloonTip(vboxTrayGstFacilityStsToStr(statusCurrent, false /* fShort */),
75 "Reporting status to host", RT_MS_5SEC);
76
77 int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_VBoxTrayClient,
78 statusCurrent,
79 0 /* Flags */);
80 if (RT_FAILURE(rc))
81 VBoxTrayError("Could not report VBoxTray status '%s' to host, rc=%Rrc\n",
82 vboxTrayGstFacilityStsToStr(statusCurrent, false /* fShort */), rc);
83 return rc;
84}
85
86/**
87 * Attempt to force Windows to reload the cursor image by attaching to the
88 * thread of the window currently under the mouse, hiding the cursor and
89 * showing it again. This could fail to work in any number of ways (no
90 * window under the cursor, the cursor has moved to a different window while
91 * we are processing), but we just accept this, as the cursor will be reloaded
92 * at some point anyway.
93 */
94void hlpReloadCursor(void)
95{
96 POINT mousePos;
97 GetCursorPos(&mousePos);
98
99 DWORD hThread = 0; /* Shut up MSC */
100 DWORD hCurrentThread = 0; /* Ditto */
101 HWND hWin = WindowFromPoint(mousePos);
102 if (hWin)
103 {
104 hThread = GetWindowThreadProcessId(hWin, NULL);
105 hCurrentThread = GetCurrentThreadId();
106 if (hCurrentThread != hThread)
107 AttachThreadInput(hCurrentThread, hThread, TRUE);
108 }
109
110 ShowCursor(false);
111 ShowCursor(true);
112
113 if (hWin && hCurrentThread != hThread)
114 AttachThreadInput(hCurrentThread, hThread, FALSE);
115}
116
117static unsigned hlpNextAdjacentRectXP(RECTL *paRects, unsigned nRects, unsigned uRect)
118{
119 unsigned i;
120 for (i = 0; i < nRects; i++)
121 {
122 if (paRects[uRect].right == paRects[i].left)
123 return i;
124 }
125 return ~0U;
126}
127
128static unsigned hlpNextAdjacentRectXN(RECTL *paRects, unsigned nRects, unsigned uRect)
129{
130 unsigned i;
131 for (i = 0; i < nRects; i++)
132 {
133 if (paRects[uRect].left == paRects[i].right)
134 return i;
135 }
136 return ~0U;
137}
138
139static unsigned hlpNextAdjacentRectYP(RECTL *paRects, unsigned nRects, unsigned uRect)
140{
141 unsigned i;
142 for (i = 0; i < nRects; i++)
143 {
144 if (paRects[uRect].bottom == paRects[i].top)
145 return i;
146 }
147 return ~0U;
148}
149
150static unsigned hlpNextAdjacentRectYN(RECTL *paRects, unsigned nRects, unsigned uRect)
151{
152 unsigned i;
153 for (i = 0; i < nRects; i++)
154 {
155 if (paRects[uRect].top == paRects[i].bottom)
156 return i;
157 }
158 return ~0U;
159}
160
161void hlpResizeRect(RECTL *paRects, unsigned nRects, unsigned uPrimary,
162 unsigned uResized, int iNewWidth, int iNewHeight,
163 int iNewPosX, int iNewPosY)
164{
165 Log4Func(("nRects %d, iPrimary %d, iResized %d, NewWidth %d, NewHeight %d\n", nRects, uPrimary, uResized, iNewWidth, iNewHeight));
166
167 RECTL *paNewRects = (RECTL *)alloca (sizeof (RECTL) * nRects);
168 memcpy (paNewRects, paRects, sizeof (RECTL) * nRects);
169 paNewRects[uResized].right += iNewWidth - (paNewRects[uResized].right - paNewRects[uResized].left);
170 paNewRects[uResized].bottom += iNewHeight - (paNewRects[uResized].bottom - paNewRects[uResized].top);
171 paNewRects[uResized].right += iNewPosX - paNewRects[uResized].left;
172 paNewRects[uResized].bottom += iNewPosY - paNewRects[uResized].top;
173 paNewRects[uResized].left = iNewPosX;
174 paNewRects[uResized].top = iNewPosY;
175
176 /* Verify all pairs of originally adjacent rectangles for all 4 directions.
177 * If the pair has a "good" delta (that is the first rectangle intersects the second)
178 * at a direction and the second rectangle is not primary one (which can not be moved),
179 * move the second rectangle to make it adjacent to the first one.
180 */
181
182 /* X positive. */
183 unsigned iRect;
184 for (iRect = 0; iRect < nRects; iRect++)
185 {
186 /* Find the next adjacent original rect in x positive direction. */
187 unsigned iNextRect = hlpNextAdjacentRectXP(paRects, nRects, iRect);
188 Log4Func(("next %d -> %d\n", iRect, iNextRect));
189
190 if (iNextRect == ~0 || iNextRect == uPrimary)
191 {
192 continue;
193 }
194
195 /* Check whether there is an X intersection between these adjacent rects in the new rectangles
196 * and fix the intersection if delta is "good".
197 */
198 int delta = paNewRects[iRect].right - paNewRects[iNextRect].left;
199
200 if (delta != 0)
201 {
202 Log4Func(("XP intersection right %d left %d, diff %d\n",
203 paNewRects[iRect].right, paNewRects[iNextRect].left,
204 delta));
205
206 paNewRects[iNextRect].left += delta;
207 paNewRects[iNextRect].right += delta;
208 }
209 }
210
211 /* X negative. */
212 for (iRect = 0; iRect < nRects; iRect++)
213 {
214 /* Find the next adjacent original rect in x negative direction. */
215 unsigned iNextRect = hlpNextAdjacentRectXN(paRects, nRects, iRect);
216 Log4Func(("next %d -> %d\n", iRect, iNextRect));
217
218 if (iNextRect == ~0 || iNextRect == uPrimary)
219 {
220 continue;
221 }
222
223 /* Check whether there is an X intersection between these adjacent rects in the new rectangles
224 * and fix the intersection if delta is "good".
225 */
226 int delta = paNewRects[iRect].left - paNewRects[iNextRect].right;
227
228 if (delta != 0)
229 {
230 Log4Func(("XN intersection left %d right %d, diff %d\n",
231 paNewRects[iRect].left, paNewRects[iNextRect].right,
232 delta));
233
234 paNewRects[iNextRect].left += delta;
235 paNewRects[iNextRect].right += delta;
236 }
237 }
238
239 /* Y positive (in the computer sense, top->down). */
240 for (iRect = 0; iRect < nRects; iRect++)
241 {
242 /* Find the next adjacent original rect in y positive direction. */
243 unsigned iNextRect = hlpNextAdjacentRectYP(paRects, nRects, iRect);
244 Log4Func(("next %d -> %d\n", iRect, iNextRect));
245
246 if (iNextRect == ~0 || iNextRect == uPrimary)
247 {
248 continue;
249 }
250
251 /* Check whether there is an Y intersection between these adjacent rects in the new rectangles
252 * and fix the intersection if delta is "good".
253 */
254 int delta = paNewRects[iRect].bottom - paNewRects[iNextRect].top;
255
256 if (delta != 0)
257 {
258 Log4Func(("YP intersection bottom %d top %d, diff %d\n",
259 paNewRects[iRect].bottom, paNewRects[iNextRect].top,
260 delta));
261
262 paNewRects[iNextRect].top += delta;
263 paNewRects[iNextRect].bottom += delta;
264 }
265 }
266
267 /* Y negative (in the computer sense, down->top). */
268 for (iRect = 0; iRect < nRects; iRect++)
269 {
270 /* Find the next adjacent original rect in x negative direction. */
271 unsigned iNextRect = hlpNextAdjacentRectYN(paRects, nRects, iRect);
272 Log4Func(("next %d -> %d\n", iRect, iNextRect));
273
274 if (iNextRect == ~0 || iNextRect == uPrimary)
275 {
276 continue;
277 }
278
279 /* Check whether there is an Y intersection between these adjacent rects in the new rectangles
280 * and fix the intersection if delta is "good".
281 */
282 int delta = paNewRects[iRect].top - paNewRects[iNextRect].bottom;
283
284 if (delta != 0)
285 {
286 Log4Func(("YN intersection top %d bottom %d, diff %d\n",
287 paNewRects[iRect].top, paNewRects[iNextRect].bottom,
288 delta));
289
290 paNewRects[iNextRect].top += delta;
291 paNewRects[iNextRect].bottom += delta;
292 }
293 }
294
295 /* Primary rectangle must remain at 0,0. */
296 int32_t iOffsetX = paNewRects[uPrimary].left;
297 int32_t iOffsetY = paNewRects[uPrimary].top;
298 for (iRect = 0; iRect < nRects; iRect++)
299 {
300 paRects[iRect].left = paNewRects[iRect].left - iOffsetX;
301 paRects[iRect].right = paNewRects[iRect].right - iOffsetX;
302 paRects[iRect].top = paNewRects[iRect].top - iOffsetY;
303 paRects[iRect].bottom = paNewRects[iRect].bottom - iOffsetY;
304 Log4Func((" [%d]: %d,%d %dx%d -> %d,%d %dx%d%s\n",
305 iRect,
306 paRects[iRect].left, paRects[iRect].top,
307 paRects[iRect].right - paRects[iRect].left,
308 paRects[iRect].bottom - paRects[iRect].top,
309 paNewRects[iRect].left, paNewRects[iRect].top,
310 paNewRects[iRect].right - paNewRects[iRect].left,
311 paNewRects[iRect].bottom - paNewRects[iRect].top,
312 iRect == uPrimary? " <- primary": ""));
313 }
314 return;
315}
316
317int VBoxTrayHlpShowBalloonTipEx(HINSTANCE hInst, HWND hWnd, UINT uID,
318 const char *pszMsg, const char *pszTitle,
319 UINT uTimeout, DWORD dwInfoFlags)
320{
321 VBoxTrayVerbose(2, "Showing balloon tip \"%s\": %s\n", pszMsg, pszTitle);
322
323 NOTIFYICONDATA niData;
324 ZeroMemory(&niData, sizeof(NOTIFYICONDATA));
325 niData.cbSize = sizeof(NOTIFYICONDATA);
326 niData.uFlags = NIF_INFO; /* Display a balloon notification. */
327 niData.hWnd = hWnd;
328 niData.uID = uID;
329 /* If not timeout set, set it to 5sec. */
330 if (uTimeout == 0)
331 uTimeout = 5000;
332 niData.uTimeout = uTimeout;
333 /* If no info flag (info, warning, error) set,
334 * set it to info by default. */
335 if (dwInfoFlags == 0)
336 dwInfoFlags = NIIF_INFO;
337 niData.dwInfoFlags = dwInfoFlags;
338
339 /* Is the current OS supported (at least WinXP) for displaying
340 * our own icon and do we actually *want* to display our own stuff? */
341 uint64_t const uNtVersion = RTSystemGetNtVersion();
342 if ( uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)
343 && (dwInfoFlags & NIIF_INFO))
344 {
345 /* Load (or retrieve handle of) the app's icon. */
346 HICON hIcon = LoadIcon(hInst, "IDI_ICON1"); /* see Artwork/win/TemplateR3.rc */
347 if (hIcon)
348 niData.dwInfoFlags = NIIF_USER; /* Use an own notification icon. */
349
350 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 1, 0)) /* WinXP. */
351 {
352 /* Use an own icon instead of the default one. */
353 niData.hIcon = hIcon;
354 }
355 else if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0)) /* Vista and up. */
356 {
357 /* Use an own icon instead of the default one. */
358 niData.dwInfoFlags |= NIIF_LARGE_ICON; /* Use a large icon if available! */
359 niData.hIcon = hIcon;
360 niData.hBalloonIcon = hIcon;
361 }
362 }
363 else
364 {
365 /* This might be a warning, error message or a to old OS. Use the
366 * standard icons provided by Windows (if any). */
367 }
368
369 strcpy(niData.szInfo, pszMsg ? pszMsg : "-");
370 strcpy(niData.szInfoTitle, pszTitle ? pszTitle : "Information");
371
372 if (!Shell_NotifyIcon(NIM_MODIFY, &niData))
373 {
374 VBoxTrayError("Could not show balloon tip (%#x)\n", GetLastError());
375 return VERR_INVALID_HANDLE;
376 }
377
378 return VINF_SUCCESS;
379}
380
381int VBoxTrayHlpShowBalloonTip(const char *pszMsg, const char *pszTitle,
382 UINT uTimeout)
383{
384 return VBoxTrayHlpShowBalloonTipEx(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
385 pszMsg, pszTitle, uTimeout, NIIF_INFO);
386}
387
388/**
389 * Shows a message box with a printf() style formatted string.
390 *
391 * @param pszTitle Title of the message box.
392 * @param uStyle Style of message box to use (see MSDN, MB_ defines).
393 * When 0 is specified, MB_ICONINFORMATION will be used.
394 * @param pszFmt Printf-style format string to show in the message box body.
395 * @param ... Arguments for format string.
396 */
397void VBoxTrayShowMsgBox(const char *pszTitle, UINT uStyle, const char *pszFmt, ...)
398{
399 if (!uStyle)
400 uStyle = MB_ICONINFORMATION;
401
402 char *pszMsg;
403 va_list va;
404 va_start(va, pszFmt);
405 int rc = RTStrAPrintfV(&pszMsg, pszFmt, va);
406 va_end(va);
407 if (rc >= 0)
408 {
409 if (g_fHasConsole)
410 {
411 RTPrintf("%s", pszMsg);
412 }
413 else
414 {
415 PRTUTF16 pwszTitle;
416 rc = RTStrToUtf16(pszTitle, &pwszTitle);
417 if (RT_SUCCESS(rc))
418 {
419 PRTUTF16 pwszMsg;
420 rc = RTStrToUtf16(pszMsg, &pwszMsg);
421 if (RT_SUCCESS(rc))
422 {
423 MessageBoxW(GetDesktopWindow(), pwszMsg, pwszTitle, uStyle);
424 RTUtf16Free(pwszMsg);
425 }
426 else
427 MessageBoxA(GetDesktopWindow(), pszMsg, pszTitle, uStyle);
428 RTUtf16Free(pwszTitle);
429 }
430 }
431 }
432 else /* Should never happen! */
433 AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt));
434 RTStrFree(pszMsg);
435}
436
437/**
438 * Logs a verbose message.
439 *
440 * @param pszFormat The message text.
441 * @param va Format arguments.
442 */
443static void vboxTrayLogV(const char *pszFormat, va_list va)
444{
445#ifdef DEBUG0
446 int rc = RTCritSectEnter(&g_csLog);
447 if (RT_SUCCESS(rc))
448 {
449#endif
450 char *psz = NULL;
451 RTStrAPrintfV(&psz, pszFormat, va);
452
453 AssertPtr(psz);
454 LogRel(("%s", psz));
455
456 RTStrFree(psz);
457#ifdef DEBUG0
458 RTCritSectLeave(&g_csLog);
459 }
460#endif
461}
462
463/**
464 * Logs an information message.
465 *
466 * @param pszFormat The message text.
467 * @param ... Format arguments.
468 */
469void VBoxTrayInfo(const char *pszFormat, ...)
470{
471 va_list va;
472 va_start(va, pszFormat);
473 vboxTrayLogV(pszFormat, va);
474 va_end(va);
475}
476
477/**
478 * Logs an error message.
479 *
480 * @returns RTEXITCODE_FAILURE.
481 * @param pszFormat The message text.
482 * @param ... Format arguments.
483 */
484RTEXITCODE VBoxTrayError(const char *pszFormat, ...)
485{
486 va_list args;
487 va_start(args, pszFormat);
488 char *psz = NULL;
489 RTStrAPrintfV(&psz, pszFormat, args);
490 va_end(args);
491
492 AssertPtr(psz);
493 LogRel(("Error: %s", psz));
494
495 RTStrFree(psz);
496
497 return RTEXITCODE_FAILURE;
498}
499
500/**
501 * Logs a verbose message based on the currently
502 * set global verbosity level.
503 *
504 * @param iLevel Minimum log level required to display this message.
505 * @param pszFormat The message text.
506 * @param ... Format arguments.
507 */
508void VBoxTrayVerbose(unsigned iLevel, const char *pszFormat, ...)
509{
510 if (iLevel <= g_cVerbosity)
511 {
512 va_list va;
513 va_start(va, pszFormat);
514 vboxTrayLogV(pszFormat, va);
515 va_end(va);
516 }
517}
518
519/**
520 * Displays an error message.
521 *
522 * @returns RTEXITCODE_FAILURE.
523 * @param pszFormat The message text.
524 * @param ... Format arguments.
525 */
526RTEXITCODE VBoxTrayShowError(const char *pszFormat, ...)
527{
528 va_list args;
529 va_start(args, pszFormat);
530 char *psz = NULL;
531 RTStrAPrintfV(&psz, pszFormat, args);
532 va_end(args);
533
534 AssertPtr(psz);
535
536 VBoxTrayError("%s", psz);
537 VBoxTrayShowMsgBox(VBOX_VBOXTRAY_TITLE " - Error", MB_OK | MB_ICONERROR, psz);
538
539 RTStrFree(psz);
540
541 return RTEXITCODE_FAILURE;
542}
543
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