VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp@ 34079

Last change on this file since 34079 was 33895, checked in by vboxsync, 14 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.5 KB
Line 
1/* $Id: VBoxServiceVMInfo-win.cpp 33895 2010-11-09 12:41:28Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host, Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2009-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0502
23# undef _WIN32_WINNT
24# define _WIN32_WINNT 0x0502 /* CachedRemoteInteractive in recent SDKs. */
25#endif
26#include <Windows.h>
27#include <wtsapi32.h> /* For WTS* calls. */
28#include <psapi.h> /* EnumProcesses. */
29#include <Ntsecapi.h> /* Needed for process security information. */
30
31#include <iprt/assert.h>
32#include <iprt/mem.h>
33#include <iprt/thread.h>
34#include <iprt/string.h>
35#include <iprt/semaphore.h>
36#include <iprt/system.h>
37#include <iprt/time.h>
38#include <VBox/VBoxGuestLib.h>
39#include "VBoxServiceInternal.h"
40#include "VBoxServiceUtils.h"
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46/** Structure for storing the looked up user information. */
47typedef struct
48{
49 WCHAR wszUser[_MAX_PATH];
50 WCHAR wszAuthenticationPackage[_MAX_PATH];
51 WCHAR wszLogonDomain[_MAX_PATH];
52} VBOXSERVICEVMINFOUSER, *PVBOXSERVICEVMINFOUSER;
53
54/** Structure for the file information lookup. */
55typedef struct
56{
57 char *pszFilePath;
58 char *pszFileName;
59} VBOXSERVICEVMINFOFILE, *PVBOXSERVICEVMINFOFILE;
60
61/** Structure for process information lookup. */
62typedef struct
63{
64 DWORD id;
65 LUID luid;
66} VBOXSERVICEVMINFOPROC, *PVBOXSERVICEVMINFOPROC;
67
68
69/*******************************************************************************
70* Prototypes
71*******************************************************************************/
72bool VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, VBOXSERVICEVMINFOPROC const *paProcs, DWORD cProcs);
73bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER a_pUserInfo, PLUID a_pSession);
74int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppProc, DWORD *pdwCount);
75void VBoxServiceVMInfoWinProcessesFree(PVBOXSERVICEVMINFOPROC paProcs);
76
77
78
79#ifndef TARGET_NT4
80
81/**
82 * Fills in more data for a process.
83 *
84 * @returns VBox status code.
85 * @param pProc The process structure to fill data into.
86 * @param tkClass The kind of token information to get.
87 */
88static int VBoxServiceVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pProc,
89 TOKEN_INFORMATION_CLASS tkClass)
90{
91 AssertPtr(pProc);
92 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pProc->id);
93 if (h == NULL)
94 return RTErrConvertFromWin32(GetLastError());
95
96 int rc = VERR_NO_MEMORY;
97 HANDLE hToken;
98 if (OpenProcessToken(h, TOKEN_QUERY, &hToken))
99 {
100 void *pvTokenInfo = NULL;
101 DWORD dwTokenInfoSize;
102 switch (tkClass)
103 {
104 case TokenStatistics:
105 dwTokenInfoSize = sizeof(TOKEN_STATISTICS);
106 pvTokenInfo = RTMemAlloc(dwTokenInfoSize);
107 break;
108
109 /** @todo Implement more token classes here. */
110
111 default:
112 VBoxServiceError("Token class not implemented: %ld", tkClass);
113 rc = VERR_NOT_IMPLEMENTED;
114 break;
115 }
116
117 if (pvTokenInfo)
118 {
119 DWORD dwRetLength;
120 if (GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
121 {
122 switch (tkClass)
123 {
124 case TokenStatistics:
125 {
126 TOKEN_STATISTICS *pStats = (TOKEN_STATISTICS*)pvTokenInfo;
127 pProc->luid = pStats->AuthenticationId;
128 /** @todo Add more information of TOKEN_STATISTICS as needed. */
129 break;
130 }
131
132 default:
133 /* Should never get here! */
134 break;
135 }
136 rc = VINF_SUCCESS;
137 }
138 else
139 rc = RTErrConvertFromWin32(GetLastError());
140 RTMemFree(pvTokenInfo);
141 }
142 CloseHandle(hToken);
143 }
144 else
145 rc = RTErrConvertFromWin32(GetLastError());
146 CloseHandle(h);
147 return rc;
148}
149
150
151/**
152 * Enumerate all the processes in the system and get the logon user IDs for
153 * them.
154 *
155 * @returns VBox status code.
156 * @param ppaProcs Where to return the process snapshot. This must be
157 * freed by calling VBoxServiceVMInfoWinProcessesFree.
158 *
159 * @param pcProcs Where to store the returned process count.
160 */
161int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDWORD pcProcs)
162{
163 AssertPtr(ppaProcs);
164 AssertPtr(pcProcs);
165
166 /*
167 * Call EnumProcesses with an increasingly larger buffer until it all fits
168 * or we think something is screwed up.
169 */
170 DWORD cProcesses = 64;
171 PDWORD paPids = NULL;
172 int rc = VINF_SUCCESS;
173 do
174 {
175 /* Allocate / grow the buffer first. */
176 cProcesses *= 2;
177 void *pvNew = RTMemRealloc(paPids, cProcesses * sizeof(DWORD));
178 if (!pvNew)
179 {
180 rc = VERR_NO_MEMORY;
181 break;
182 }
183 paPids = (PDWORD)pvNew;
184
185 /* Query the processes. Not the cbRet == buffer size means there could be more work to be done. */
186 DWORD cbRet;
187 if (!EnumProcesses(paPids, cProcesses * sizeof(DWORD), &cbRet))
188 {
189 rc = RTErrConvertFromWin32(GetLastError());
190 break;
191 }
192 if (cbRet < cProcesses * sizeof(DWORD))
193 {
194 cProcesses = cbRet / sizeof(DWORD);
195 break;
196 }
197 } while (cProcesses <= 32768); /* Should be enough; see: http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx */
198 if (RT_SUCCESS(rc))
199 {
200 /*
201 * Allocate out process structures and fill data into them.
202 * We currently only try lookup their LUID's.
203 */
204 PVBOXSERVICEVMINFOPROC paProcs;
205 paProcs = (PVBOXSERVICEVMINFOPROC)RTMemAllocZ(cProcesses * sizeof(VBOXSERVICEVMINFOPROC));
206 if (paProcs)
207 {
208 for (DWORD i = 0; i < cProcesses; i++)
209 {
210 paProcs[i].id = paPids[i];
211 rc = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenStatistics);
212 if (RT_FAILURE(rc))
213 {
214 /* Because some processes cannot be opened/parsed on
215 Windows, we should not consider to be this an error here. */
216 rc = VINF_SUCCESS;
217 }
218 }
219
220 /* Save number of processes */
221 if (RT_SUCCESS(rc))
222 {
223 *pcProcs = cProcesses;
224 *ppaProcs = paProcs;
225 }
226 else
227 RTMemFree(paProcs);
228 }
229 else
230 rc = VERR_NO_MEMORY;
231 }
232
233 RTMemFree(paPids);
234 return rc;
235}
236
237/**
238 * Frees the process structures returned by
239 * VBoxServiceVMInfoWinProcessesEnumerate() before.
240 *
241 * @param paProcs What
242 */
243void VBoxServiceVMInfoWinProcessesFree(PVBOXSERVICEVMINFOPROC paProcs)
244{
245 RTMemFree(paProcs);
246}
247
248/**
249 * Determines whether the specified session has processes on the system.
250 *
251 * @returns true if it has, false if it doesn't.
252 * @param pSession The session.
253 * @param paProcs The process snapshot.
254 * @param cProcs The number of processes in the snaphot.
255 */
256bool VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, VBOXSERVICEVMINFOPROC const *paProcs, DWORD cProcs)
257{
258 AssertPtr(pSession);
259
260 if (!cProcs) /* To be on the safe side. */
261 return false;
262 AssertPtr(paProcs);
263
264 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
265 NTSTATUS rcNt = LsaGetLogonSessionData(pSession, &pSessionData);
266 if (rcNt != STATUS_SUCCESS)
267 {
268 VBoxServiceError("Could not get logon session data! rcNt=%#x", rcNt);
269 return false;
270 }
271 AssertPtrReturn(pSessionData, false);
272
273 /*
274 * Even if a user seems to be logged in, it could be a stale/orphaned logon
275 * session. So check if we have some processes bound to it by comparing the
276 * session <-> process LUIDs.
277 */
278 for (DWORD i = 0; i < cProcs; i++)
279 {
280 /*VBoxServiceVerbose(3, "%ld:%ld <-> %ld:%ld\n",
281 paProcs[i].luid.HighPart, paProcs[i].luid.LowPart,
282 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);*/
283 if ( paProcs[i].luid.HighPart == pSessionData->LogonId.HighPart
284 && paProcs[i].luid.LowPart == pSessionData->LogonId.LowPart)
285 {
286 VBoxServiceVerbose(3, "Users: Session %ld:%ld has active processes\n",
287 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);
288 LsaFreeReturnBuffer(pSessionData);
289 return true;
290 }
291 }
292 LsaFreeReturnBuffer(pSessionData);
293 return false;
294}
295
296
297/**
298 * Save and noisy string copy.
299 *
300 * @param pwszDst Destination buffer.
301 * @param cbDst Size in bytes - not WCHAR count!
302 * @param pSrc Source string.
303 * @param pszWhat What this is. For the log.
304 */
305static void VBoxServiceVMInfoWinSafeCopy(PWCHAR pwszDst, size_t cbDst, LSA_UNICODE_STRING const *pSrc, const char *pszWhat)
306{
307 Assert(RT_ALIGN(cbDst, sizeof(WCHAR)) == cbDst);
308
309 size_t cbCopy = pSrc->Length;
310 if (cbCopy + sizeof(WCHAR) > cbDst)
311 {
312 VBoxServiceVerbose(0, "%s is too long - %u bytes, buffer %u bytes! It will be truncated.\n",
313 pszWhat, cbCopy, cbDst);
314 cbCopy = cbDst - sizeof(WCHAR);
315 }
316 if (cbCopy)
317 memcpy(pwszDst, pSrc->Buffer, cbCopy);
318 pwszDst[cbCopy / sizeof(WCHAR)] = '\0';
319}
320
321
322/**
323 * Detects whether a user is logged on.
324 *
325 * @returns true if logged in, false if not (or error).
326 * @param a_pUserInfo Where to return the user information.
327 * @param a_pSession The session to check.
328 */
329bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER a_pUserInfo, PLUID a_pSession)
330{
331 AssertPtr(a_pUserInfo);
332 if (!a_pSession)
333 return false;
334
335 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
336 NTSTATUS rcNt = LsaGetLogonSessionData(a_pSession, &pSessionData);
337 if (rcNt != STATUS_SUCCESS)
338 {
339 ULONG ulError = LsaNtStatusToWinError(rcNt);
340 /* Skip session data which is not valid anymore because it may have been
341 * already terminated. */
342 if (ulError != ERROR_NO_SUCH_LOGON_SESSION)
343 VBoxServiceError("VMInfo/Users: LsaGetLogonSessionData failed, LSA error %u\n", ulError);
344 if (pSessionData)
345 LsaFreeReturnBuffer(pSessionData);
346 return false;
347 }
348 if (!pSessionData)
349 {
350 VBoxServiceError("VMInfo/Users: Invalid logon session data!\n");
351 return false;
352 }
353
354 /*
355 * Only handle users which can login interactively or logged in
356 * remotely over native RDP.
357 */
358 bool fFoundUser = false;
359 DWORD dwErr = NO_ERROR;
360 if ( IsValidSid(pSessionData->Sid)
361 && ( (SECURITY_LOGON_TYPE)pSessionData->LogonType == Interactive
362 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == RemoteInteractive
363 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == CachedInteractive
364 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == CachedRemoteInteractive))
365 {
366 VBoxServiceVerbose(3, "VMInfo/Users: Session data: Name=%ls, Len=%d, SID=%s, LogonID=%ld,%ld\n",
367 pSessionData->UserName.Buffer,
368 pSessionData->UserName.Length,
369 pSessionData->Sid != NULL ? "1" : "0",
370 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);
371
372 /*
373 * Copy out relevant data.
374 */
375 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszUser, sizeof(a_pUserInfo->wszUser),
376 &pSessionData->UserName, "User name");
377 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszAuthenticationPackage, sizeof(a_pUserInfo->wszAuthenticationPackage),
378 &pSessionData->AuthenticationPackage, "Authentication pkg name");
379 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszLogonDomain, sizeof(a_pUserInfo->wszLogonDomain),
380 &pSessionData->LogonDomain, "Logon domain name");
381
382 TCHAR szOwnerName[_MAX_PATH] = { 0 };
383 DWORD dwOwnerNameSize = sizeof(szOwnerName);
384 TCHAR szDomainName[_MAX_PATH] = { 0 };
385 DWORD dwDomainNameSize = sizeof(szDomainName);
386 SID_NAME_USE enmOwnerType = SidTypeInvalid;
387 if (!LookupAccountSid(NULL,
388 pSessionData->Sid,
389 szOwnerName,
390 &dwOwnerNameSize,
391 szDomainName,
392 &dwDomainNameSize,
393 &enmOwnerType))
394 {
395 DWORD dwErr = GetLastError();
396 /*
397 * If a network time-out prevents the function from finding the name or
398 * if a SID that does not have a corresponding account name (such as a
399 * logon SID that identifies a logon session), we get ERROR_NONE_MAPPED
400 * here that we just skip.
401 */
402 if (dwErr != ERROR_NONE_MAPPED)
403 VBoxServiceError("VMInfo/Users: Failed looking up account info for user '%ls': %ld!\n",
404 a_pUserInfo->wszUser, dwErr);
405 }
406 else
407 {
408 if (enmOwnerType == SidTypeUser) /* Only recognize users; we don't care about the rest! */
409 {
410 VBoxServiceVerbose(3, "VMInfo/Users: Account User=%ls, Session=%ld, LUID=%ld,%ld, AuthPkg=%ls, Domain=%ls\n",
411 a_pUserInfo->wszUser, pSessionData->Session, pSessionData->LogonId.HighPart,
412 pSessionData->LogonId.LowPart, a_pUserInfo->wszAuthenticationPackage,
413 a_pUserInfo->wszLogonDomain);
414
415 /* Detect RDP sessions as well. */
416 LPTSTR pBuffer = NULL;
417 DWORD cbRet = 0;
418 int iState = 0;
419 if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
420 pSessionData->Session,
421 WTSConnectState,
422 &pBuffer,
423 &cbRet))
424 {
425 if (cbRet)
426 iState = *pBuffer;
427 VBoxServiceVerbose(3, "VMInfo/Users: Account User=%ls, WTSConnectState=%d\n",
428 a_pUserInfo->wszUser, iState);
429 if ( iState == WTSActive /* User logged on to WinStation. */
430 || iState == WTSShadow /* Shadowing another WinStation. */
431 || iState == WTSDisconnected) /* WinStation logged on without client. */
432 {
433 /** @todo On Vista and W2K, always "old" user name are still
434 * there. Filter out the old one! */
435 VBoxServiceVerbose(3, "VMInfo/Users: Account User=%ls is logged in via TCS/RDP. State=%d\n",
436 a_pUserInfo->wszUser, iState);
437 fFoundUser = true;
438 }
439 if (pBuffer)
440 WTSFreeMemory(pBuffer);
441 }
442 else
443 {
444 VBoxServiceVerbose(3, "VMInfo/Users: Account User=%ls, WTSConnectState returned %ld\n",
445 a_pUserInfo->wszUser, GetLastError());
446
447 /*
448 * Terminal services don't run (for example in W2K,
449 * nothing to worry about ...). ... or is on the Vista
450 * fast user switching page!
451 */
452 fFoundUser = true;
453 }
454 }
455 }
456 }
457
458 LsaFreeReturnBuffer(pSessionData);
459 return fFoundUser;
460}
461
462
463/**
464 * Retrieves the currently logged in users and stores their names along with the
465 * user count.
466 *
467 * @returns VBox status code.
468 * @param ppszUserList Where to store the user list (separated by commas).
469 * Must be freed with RTStrFree().
470 * @param pcUsersInList Where to store the number of users in the list.
471 */
472int VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList)
473{
474 PLUID paSessions = NULL;
475 ULONG cSession = 0;
476
477 /* This function can report stale or orphaned interactive logon sessions
478 of already logged off users (especially in Windows 2000). */
479 NTSTATUS rcNt = LsaEnumerateLogonSessions(&cSession, &paSessions);
480 if (rcNt != STATUS_SUCCESS)
481 {
482 ULONG rcWin = LsaNtStatusToWinError(rcNt);
483
484 /* If we're about to shutdown when we were in the middle of enumerating the logon
485 sessions, skip the error to not confuse the user with an unnecessary log message. */
486 if (rcWin == ERROR_SHUTDOWN_IN_PROGRESS)
487 {
488 VBoxServiceVerbose(3, "VMInfo/Users: Shutdown in progress ...\n");
489 rcWin = ERROR_SUCCESS;
490 }
491 else
492 VBoxServiceError("VMInfo/Users: LsaEnumerate failed with %lu\n", rcWin);
493 return RTErrConvertFromWin32(rcWin);
494 }
495 VBoxServiceVerbose(3, "VMInfo/Users: Found %ld users\n", cSession);
496
497 PVBOXSERVICEVMINFOPROC paProcs;
498 DWORD cProcs;
499 int rc = VBoxServiceVMInfoWinProcessesEnumerate(&paProcs, &cProcs);
500 if (RT_SUCCESS(rc))
501 {
502 *pcUsersInList = 0;
503 for (ULONG i = 0; i < cSession; i++)
504 {
505 VBOXSERVICEVMINFOUSER UserInfo;
506 if ( VBoxServiceVMInfoWinIsLoggedIn(&UserInfo, &paSessions[i])
507 && VBoxServiceVMInfoWinSessionHasProcesses(&paSessions[i], paProcs, cProcs))
508 {
509 if (*pcUsersInList > 0)
510 {
511 rc = RTStrAAppend(ppszUserList, ",");
512 AssertRCBreakStmt(rc, RTStrFree(*ppszUserList));
513 }
514
515 *pcUsersInList += 1;
516
517 char *pszTemp;
518 int rc2 = RTUtf16ToUtf8(UserInfo.wszUser, &pszTemp);
519 if (RT_SUCCESS(rc2))
520 {
521 rc = RTStrAAppend(ppszUserList, pszTemp);
522 RTMemFree(pszTemp);
523 }
524 else
525 rc = RTStrAAppend(ppszUserList, "<string-conversion-error>");
526 AssertRCBreakStmt(rc, RTStrFree(*ppszUserList));
527 }
528 }
529 VBoxServiceVMInfoWinProcessesFree(paProcs);
530 }
531 LsaFreeReturnBuffer(paSessions);
532 return rc;
533}
534
535#endif /* TARGET_NT4 */
536
537int VBoxServiceWinGetComponentVersions(uint32_t uClientID)
538{
539 int rc;
540 char szSysDir[_MAX_PATH] = {0};
541 char szWinDir[_MAX_PATH] = {0};
542 char szDriversDir[_MAX_PATH + 32] = {0};
543
544 /* ASSUME: szSysDir and szWinDir and derivatives are always ASCII compatible. */
545 GetSystemDirectory(szSysDir, _MAX_PATH);
546 GetWindowsDirectory(szWinDir, _MAX_PATH);
547 RTStrPrintf(szDriversDir, sizeof(szDriversDir), "%s\\drivers", szSysDir);
548#ifdef RT_ARCH_AMD64
549 char szSysWowDir[_MAX_PATH + 32] = {0};
550 RTStrPrintf(szSysWowDir, sizeof(szSysWowDir), "%s\\SysWow64", szWinDir);
551#endif
552
553 /* The file information table. */
554#ifndef TARGET_NT4
555 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
556 {
557 { szSysDir, "VBoxControl.exe" },
558 { szSysDir, "VBoxHook.dll" },
559 { szSysDir, "VBoxDisp.dll" },
560 { szSysDir, "VBoxMRXNP.dll" },
561 { szSysDir, "VBoxService.exe" },
562 { szSysDir, "VBoxTray.exe" },
563 { szSysDir, "VBoxGINA.dll" },
564 { szSysDir, "VBoxCredProv.dll" },
565
566 /* On 64-bit we don't yet have the OpenGL DLLs in native format.
567 So just enumerate the 32-bit files in the SYSWOW directory. */
568# ifdef RT_ARCH_AMD64
569 { szSysWowDir, "VBoxOGLarrayspu.dll" },
570 { szSysWowDir, "VBoxOGLcrutil.dll" },
571 { szSysWowDir, "VBoxOGLerrorspu.dll" },
572 { szSysWowDir, "VBoxOGLpackspu.dll" },
573 { szSysWowDir, "VBoxOGLpassthroughspu.dll" },
574 { szSysWowDir, "VBoxOGLfeedbackspu.dll" },
575 { szSysWowDir, "VBoxOGL.dll" },
576# else /* !RT_ARCH_AMD64 */
577 { szSysDir, "VBoxOGLarrayspu.dll" },
578 { szSysDir, "VBoxOGLcrutil.dll" },
579 { szSysDir, "VBoxOGLerrorspu.dll" },
580 { szSysDir, "VBoxOGLpackspu.dll" },
581 { szSysDir, "VBoxOGLpassthroughspu.dll" },
582 { szSysDir, "VBoxOGLfeedbackspu.dll" },
583 { szSysDir, "VBoxOGL.dll" },
584# endif /* !RT_ARCH_AMD64 */
585
586 { szDriversDir, "VBoxGuest.sys" },
587 { szDriversDir, "VBoxMouse.sys" },
588 { szDriversDir, "VBoxSF.sys" },
589 { szDriversDir, "VBoxVideo.sys" },
590 };
591
592#else /* TARGET_NT4 */
593 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
594 {
595 { szSysDir, "VBoxControl.exe" },
596 { szSysDir, "VBoxHook.dll" },
597 { szSysDir, "VBoxDisp.dll" },
598 { szSysDir, "VBoxServiceNT.exe" },
599 { szSysDir, "VBoxTray.exe" },
600
601 { szDriversDir, "VBoxGuestNT.sys" },
602 { szDriversDir, "VBoxMouseNT.sys" },
603 { szDriversDir, "VBoxVideo.sys" },
604 };
605#endif /* TARGET_NT4 */
606
607 for (unsigned i = 0; i < RT_ELEMENTS(aVBoxFiles); i++)
608 {
609 char szVer[128];
610 VBoxServiceGetFileVersionString(aVBoxFiles[i].pszFilePath, aVBoxFiles[i].pszFileName, szVer, sizeof(szVer));
611 char szPropPath[256];
612 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestAdd/Components/%s", aVBoxFiles[i].pszFileName);
613 rc = VBoxServiceWritePropF(uClientID, szPropPath, "%s", szVer);
614 }
615
616 return VINF_SUCCESS;
617}
618
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