VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Installer/VBoxGuestDrvInst.cpp@ 96603

Last change on this file since 96603 was 96603, checked in by vboxsync, 2 years ago

Add/NT/Inst: Fixed some bugs in the NT4 video driver installation helper. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.7 KB
Line 
1/* $Id: VBoxGuestDrvInst.cpp 96603 2022-09-05 18:12:44Z vboxsync $ */
2/** @file
3 * instdrvmain - Install guest drivers on NT4
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 <iprt/win/windows.h>
33#include <iprt/win/setupapi.h>
34#include <regstr.h>
35#include <DEVGUID.h>
36
37#include <iprt/path.h>
38#include <iprt/string.h>
39#include <iprt/utf16.h>
40
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46/** The video service name. */
47#define VBOXGUEST_VIDEO_NAME "VBoxVideo"
48
49/** The video inf file name */
50#define VBOXGUEST_VIDEO_INF_NAME "VBoxVideo.inf"
51
52
53/*
54 * A few error messaging functions that avoids dragging in printf-like stuff.
55 */
56
57static RTEXITCODE ErrorMsg(const char *pszMsg)
58{
59 HANDLE const hStdOut = GetStdHandle(STD_ERROR_HANDLE);
60 DWORD cbIgn;
61 WriteFile(hStdOut, RT_STR_TUPLE("error: "), &cbIgn, NULL);
62 WriteFile(hStdOut, pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
63 WriteFile(hStdOut, RT_STR_TUPLE("\r\n"), &cbIgn, NULL);
64 return RTEXITCODE_FAILURE;
65}
66
67
68static RTEXITCODE ErrorMsgErr(const char *pszMsg, DWORD dwErr, const char *pszErrIntro, size_t cchErrIntro, bool fSigned)
69{
70 HANDLE const hStdOut = GetStdHandle(STD_ERROR_HANDLE);
71 DWORD cbIgn;
72 WriteFile(hStdOut, RT_STR_TUPLE("error: "), &cbIgn, NULL);
73 WriteFile(hStdOut, pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
74 WriteFile(hStdOut, pszErrIntro, (DWORD)cchErrIntro, &cbIgn, NULL);
75 char szVal[128];
76 ssize_t cchVal = RTStrFormatU32(szVal, sizeof(szVal), dwErr, 10, 0, 0, fSigned ? RTSTR_F_VALSIGNED : 0);
77 WriteFile(hStdOut, szVal, (DWORD)cchVal, &cbIgn, NULL);
78 WriteFile(hStdOut, RT_STR_TUPLE("/"), &cbIgn, NULL);
79 cchVal = RTStrFormatU32(szVal, sizeof(szVal), dwErr, 16, 0, 0, RTSTR_F_SPECIAL);
80 WriteFile(hStdOut, szVal, (DWORD)cchVal, &cbIgn, NULL);
81 WriteFile(hStdOut, RT_STR_TUPLE(")\r\n"), &cbIgn, NULL);
82 return RTEXITCODE_FAILURE;
83}
84
85
86static RTEXITCODE ErrorMsgLastErr(const char *pszMsg)
87{
88 return ErrorMsgErr(pszMsg, GetLastError(), RT_STR_TUPLE(" (last error "), false);
89}
90
91
92static RTEXITCODE ErrorMsgLStatus(const char *pszMsg, LSTATUS lrc)
93{
94 return ErrorMsgErr(pszMsg, (DWORD)lrc, RT_STR_TUPLE(" ("), true);
95}
96
97
98
99/**
100 * Inner NT4 video driver installation function.
101 *
102 * This can normally return immediately on errors as the parent will do the
103 * cleaning up.
104 */
105static RTEXITCODE InstallNt4VideoDriverInner(WCHAR const * const pwszDriverDir, HDEVINFO hDevInfo, HINF *phInf)
106{
107 /*
108 * Get the first found driver - our Inf file only contains one so this is ok.
109 *
110 * Note! We must use the V1 structure here as it is the only NT4 recognizes.
111 * There are four versioned structures:
112 * - SP_ALTPLATFORM_INFO
113 * - SP_DRVINFO_DATA_W
114 * - SP_BACKUP_QUEUE_PARAMS_W
115 * - SP_INF_SIGNER_INFO_W,
116 * but we only make use of SP_DRVINFO_DATA_W.
117 */
118 SetLastError(NO_ERROR);
119 SP_DRVINFO_DATA_V1_W drvInfoData = { sizeof(drvInfoData) };
120 if (!SetupDiEnumDriverInfoW(hDevInfo, NULL, SPDIT_CLASSDRIVER, 0, &drvInfoData))
121 return ErrorMsgLastErr("SetupDiEnumDriverInfoW");
122
123 /*
124 * Get necessary driver details
125 */
126 union
127 {
128 SP_DRVINFO_DETAIL_DATA_W s;
129 uint64_t au64Padding[(sizeof(SP_DRVINFO_DETAIL_DATA_W) + 256) / sizeof(uint64_t)];
130 } DriverInfoDetailData = { { sizeof(DriverInfoDetailData.s) } };
131 DWORD cbReqSize = NULL;
132 if ( !SetupDiGetDriverInfoDetailW(hDevInfo, NULL, &drvInfoData,
133 &DriverInfoDetailData.s, sizeof(DriverInfoDetailData), &cbReqSize)
134 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
135 return ErrorMsgLastErr("SetupDiGetDriverInfoDetailW");
136
137 HINF hInf = *phInf = SetupOpenInfFileW(DriverInfoDetailData.s.InfFileName, NULL, INF_STYLE_WIN4, NULL);
138 if (hInf == INVALID_HANDLE_VALUE)
139 return ErrorMsgLastErr("SetupOpenInfFileW");
140
141 /*
142 * First install the service.
143 */
144 WCHAR wszServiceSection[LINE_LEN];
145 int rc = RTUtf16Copy(wszServiceSection, RT_ELEMENTS(wszServiceSection), DriverInfoDetailData.s.SectionName);
146 if (RT_SUCCESS(rc))
147 rc = RTUtf16CatAscii(wszServiceSection, RT_ELEMENTS(wszServiceSection), ".Services");
148 if (RT_FAILURE(rc))
149 return ErrorMsg("wszServiceSection too small");
150
151 INFCONTEXT SvcCtx;
152 if (!SetupFindFirstLineW(hInf, wszServiceSection, NULL, &SvcCtx))
153 return ErrorMsgLastErr("SetupFindFirstLine"); /* impossible... */
154
155 /*
156 * Get the name
157 */
158 WCHAR wszServiceData[LINE_LEN] = {0};
159 if (!SetupGetStringFieldW(&SvcCtx, 1, wszServiceData, RT_ELEMENTS(wszServiceData), NULL))
160 return ErrorMsgLastErr("SetupGetStringFieldW");
161
162 WCHAR wszDevInstanceId[LINE_LEN];
163 rc = RTUtf16CopyAscii(wszDevInstanceId, RT_ELEMENTS(wszDevInstanceId), "Root\\LEGACY_");
164 if (RT_SUCCESS(rc))
165 rc = RTUtf16Cat(wszDevInstanceId, RT_ELEMENTS(wszDevInstanceId), wszServiceData);
166 if (RT_SUCCESS(rc))
167 rc = RTUtf16CatAscii(wszDevInstanceId, RT_ELEMENTS(wszDevInstanceId), "\\0000");
168 if (RT_FAILURE(rc))
169 return ErrorMsg("wszDevInstanceId too small");
170
171 /*
172 * ...
173 */
174 SP_DEVINFO_DATA deviceInfoData = { sizeof(deviceInfoData) };
175 /* Check for existing first. */
176 BOOL fDevInfoOkay = SetupDiOpenDeviceInfoW(hDevInfo, wszDevInstanceId, NULL, 0, &deviceInfoData);
177 if (!fDevInfoOkay)
178 {
179 /* Okay, try create a new device info element. */
180 if (SetupDiCreateDeviceInfoW(hDevInfo, wszDevInstanceId, (LPGUID)&GUID_DEVCLASS_DISPLAY,
181 NULL, // Do we need a description here?
182 NULL, // No user interface
183 0, &deviceInfoData))
184 {
185 if (SetupDiRegisterDeviceInfo(hDevInfo, &deviceInfoData, 0, NULL, NULL, NULL))
186 fDevInfoOkay = TRUE;
187 else
188 return ErrorMsgLastErr("SetupDiRegisterDeviceInfo"); /** @todo Original code didn't return here. */
189 }
190 else
191 return ErrorMsgLastErr("SetupDiCreateDeviceInfoW"); /** @todo Original code didn't return here. */
192 }
193 if (fDevInfoOkay) /** @todo if not needed if it's okay to fail on failure above */
194 {
195 /* We created a new key in the registry */ /* bogus... */
196
197 /*
198 * Redo the install parameter thing with deviceInfoData.
199 */
200 SP_DEVINSTALL_PARAMS_W DeviceInstallParams = { sizeof(DeviceInstallParams) };
201 if (!SetupDiGetDeviceInstallParamsW(hDevInfo, &deviceInfoData, &DeviceInstallParams))
202 return ErrorMsgLastErr("SetupDiGetDeviceInstallParamsW(#2)"); /** @todo Original code didn't return here. */
203
204 DeviceInstallParams.cbSize = sizeof(DeviceInstallParams);
205 DeviceInstallParams.Flags |= DI_NOFILECOPY /* We did our own file copying */
206 | DI_DONOTCALLCONFIGMG
207 | DI_ENUMSINGLEINF; /* .DriverPath specifies an inf file */
208 rc = RTUtf16Copy(DeviceInstallParams.DriverPath, RT_ELEMENTS(DeviceInstallParams.DriverPath), pwszDriverDir);
209 if (RT_SUCCESS(rc))
210 rc = RTUtf16CatAscii(DeviceInstallParams.DriverPath, RT_ELEMENTS(DeviceInstallParams.DriverPath),
211 VBOXGUEST_VIDEO_INF_NAME);
212 if (RT_FAILURE(rc))
213 return ErrorMsg("Install dir too deep (long)");
214
215 if (!SetupDiSetDeviceInstallParamsW(hDevInfo, &deviceInfoData, &DeviceInstallParams))
216 return ErrorMsgLastErr("SetupDiSetDeviceInstallParamsW(#2)"); /** @todo Original code didn't return here. */
217
218 if (!SetupDiBuildDriverInfoList(hDevInfo, &deviceInfoData, SPDIT_CLASSDRIVER))
219 return ErrorMsgLastErr("SetupDiBuildDriverInfoList(#2)");
220
221 /*
222 * Repeat the query at the start of the function.
223 */
224 drvInfoData.cbSize = sizeof(drvInfoData);
225 if (!SetupDiEnumDriverInfoW(hDevInfo, &deviceInfoData, SPDIT_CLASSDRIVER, 0, &drvInfoData))
226 return ErrorMsgLastErr("SetupDiEnumDriverInfoW(#2)");
227
228 /*
229 * ...
230 */
231 if (!SetupDiSetSelectedDriverW(hDevInfo, &deviceInfoData, &drvInfoData))
232 return ErrorMsgLastErr("SetupDiSetSelectedDriverW(#2)");
233
234 if (!SetupDiInstallDevice(hDevInfo, &deviceInfoData))
235 return ErrorMsgLastErr("SetupDiInstallDevice(#2)");
236 }
237
238 /*
239 * Make sure the device is enabled.
240 */
241 DWORD fConfig = 0;
242 if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData, SPDRP_CONFIGFLAGS,
243 NULL, (LPBYTE)&fConfig, sizeof(DWORD), NULL))
244 {
245 if (fConfig & CONFIGFLAG_DISABLED)
246 {
247 fConfig &= ~CONFIGFLAG_DISABLED;
248 if (!SetupDiSetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData, SPDRP_CONFIGFLAGS,
249 (LPBYTE)&fConfig, sizeof(fConfig)))
250 ErrorMsg("SetupDiSetDeviceRegistryPropertyW");
251 }
252 }
253 else
254 ErrorMsg("SetupDiGetDeviceRegistryPropertyW");
255
256 /*
257 * Open the service key.
258 */
259 WCHAR wszSvcRegKey[LINE_LEN + 64];
260 rc = RTUtf16CopyAscii(wszSvcRegKey, RT_ELEMENTS(wszSvcRegKey), "System\\CurrentControlSet\\Services\\");
261 if (RT_SUCCESS(rc))
262 rc = RTUtf16Cat(wszSvcRegKey, RT_ELEMENTS(wszSvcRegKey), wszServiceData);
263 if (RT_SUCCESS(rc))
264 rc = RTUtf16CatAscii(wszSvcRegKey, RT_ELEMENTS(wszSvcRegKey), "\\Device0"); /* We only have one device. */
265 if (RT_FAILURE(rc))
266 return ErrorMsg("Service key name too long");
267
268 DWORD dwIgn;
269 HKEY hKey = NULL;
270 LSTATUS lrc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wszSvcRegKey, 0, NULL, REG_OPTION_NON_VOLATILE,
271 KEY_READ | KEY_WRITE, NULL, &hKey, &dwIgn);
272 if (lrc == ERROR_SUCCESS)
273 {
274 /*
275 * Insert service description.
276 */
277 lrc = RegSetValueExW(hKey, L"Device Description", 0, REG_SZ, (LPBYTE)DriverInfoDetailData.s.DrvDescription,
278 (DWORD)((RTUtf16Len(DriverInfoDetailData.s.DrvDescription) + 1) * sizeof(WCHAR)));
279 if (lrc != ERROR_SUCCESS)
280 ErrorMsgLStatus("RegSetValueExW", lrc);
281
282 /*
283 * Execute the SoftwareSettings section of the INF-file or something like that.
284 */
285 BOOL fOkay = FALSE;
286 WCHAR wszSoftwareSection[LINE_LEN + 32];
287 rc = RTUtf16Copy(wszSoftwareSection, RT_ELEMENTS(wszSoftwareSection), wszServiceData);
288 if (RT_SUCCESS(rc))
289 rc = RTUtf16CatAscii(wszSoftwareSection, RT_ELEMENTS(wszSoftwareSection), ".SoftwareSettings");
290 if (RT_SUCCESS(rc))
291 {
292 if (SetupInstallFromInfSectionW(NULL, hInf, wszSoftwareSection, SPINST_REGISTRY, hKey,
293 NULL, 0, NULL, NULL, NULL, NULL))
294 fOkay = TRUE;
295 else
296 ErrorMsgLastErr("SetupInstallFromInfSectionW");
297 }
298 else
299 ErrorMsg("Software settings section name too long");
300 RegCloseKey(hKey);
301 if (!fOkay)
302 return RTEXITCODE_FAILURE;
303 }
304 else
305 ErrorMsgLStatus("RegCreateKeyExW/Service", lrc);
306
307 /*
308 * Install OpenGL stuff.
309 */
310 lrc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers", 0, NULL,
311 REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &dwIgn);
312 if (lrc == ERROR_SUCCESS)
313 {
314 /* Do installation here if ever necessary. Currently there is no OpenGL stuff */
315 RegCloseKey(hKey);
316 }
317 else
318 ErrorMsgLStatus("RegCreateKeyExW/OpenGLDrivers", lrc);
319
320#if 0
321 /* If this key is inserted into the registry, windows will show the desktop
322 applet on next boot. We decide in the installer if we want that so the code
323 is disabled here. */
324 lrc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers\\NewDisplay",
325 0, NULL, REG_OPTION_NON_VOLATILE,
326 KEY_READ | KEY_WRITE, NULL, &hHey, &dwIgn)
327 if (lrc == ERROR_SUCCESS)
328 RegCloseKey(hHey);
329 else
330 ErrorMsgLStatus("RegCreateKeyExW/NewDisplay", lrc);
331#endif
332
333 /*
334 * We must reboot at some point
335 */
336 lrc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers\\RebootNecessary", 0, NULL,
337 REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &dwIgn);
338 if (lrc == ERROR_SUCCESS)
339 RegCloseKey(hKey);
340 else
341 ErrorMsgLStatus("RegCreateKeyExW/RebootNecessary", lrc);
342
343 return RTEXITCODE_SUCCESS;
344}
345
346
347
348/**
349 * Install the VBox video driver.
350 *
351 * @param pwszDriverDir The base directory where we find the INF.
352 */
353static RTEXITCODE InstallNt4VideoDriver(WCHAR const * const pwszDriverDir)
354{
355 /*
356 * Create an empty list
357 */
358 HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList((LPGUID)&GUID_DEVCLASS_DISPLAY, NULL);
359 if (hDevInfo == INVALID_HANDLE_VALUE)
360 return ErrorMsgLastErr("SetupDiCreateDeviceInfoList");
361
362 /*
363 * Get the default install parameters.
364 */
365 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
366 SP_DEVINSTALL_PARAMS_W DeviceInstallParams = { sizeof(DeviceInstallParams) };
367 if (SetupDiGetDeviceInstallParamsW(hDevInfo, NULL, &DeviceInstallParams))
368 {
369 /*
370 * Insert our install parameters and update hDevInfo with them.
371 */
372 DeviceInstallParams.cbSize = sizeof(DeviceInstallParams);
373 DeviceInstallParams.Flags |= DI_NOFILECOPY /* We did our own file copying */
374 | DI_DONOTCALLCONFIGMG
375 | DI_ENUMSINGLEINF; /* .DriverPath specifies an inf file */
376 int rc = RTUtf16Copy(DeviceInstallParams.DriverPath, RT_ELEMENTS(DeviceInstallParams.DriverPath), pwszDriverDir);
377 if (RT_SUCCESS(rc))
378 rc = RTUtf16CatAscii(DeviceInstallParams.DriverPath, RT_ELEMENTS(DeviceInstallParams.DriverPath),
379 VBOXGUEST_VIDEO_INF_NAME);
380 if (RT_SUCCESS(rc))
381 {
382 if (SetupDiSetDeviceInstallParamsW(hDevInfo, NULL, &DeviceInstallParams))
383 {
384 /*
385 * Read the drivers from the INF-file.
386 */
387 if (SetupDiBuildDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER))
388 {
389 HINF hInf = NULL;
390 rcExit = InstallNt4VideoDriverInner(pwszDriverDir, hDevInfo, &hInf);
391
392 if (hInf)
393 SetupCloseInfFile(hInf);
394 SetupDiDestroyDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER);
395 }
396 else
397 ErrorMsgLastErr("SetupDiBuildDriverInfoList");
398 }
399 else
400 ErrorMsgLastErr("SetupDiSetDeviceInstallParamsW");
401
402 }
403 else
404 ErrorMsg("Install dir too deep (long)");
405 SetupDiDestroyDeviceInfoList(hDevInfo);
406 }
407 else
408 ErrorMsgLastErr("SetupDiGetDeviceInstallParams"); /** @todo Original code didn't return here. */
409 SetupDiDestroyDeviceInfoList(hDevInfo);
410 return rcExit;
411}
412
413
414/**
415 * Video driver uninstallation will be added later if necessary.
416 */
417static RTEXITCODE UninstallDrivers(void)
418{
419 return ErrorMsg("Uninstall not implemented");
420}
421
422
423static RTEXITCODE displayHelpAndExit(char *pszProgName)
424{
425 HANDLE const hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
426 DWORD cbIgn;
427 WriteFile(hStdOut, RT_STR_TUPLE("Installs VirtualBox Guest Additions Graphics Drivers for Windows NT 4.0\r\n"
428 "\r\n"
429 "Syntax: "), &cbIgn, NULL);
430 WriteFile(hStdOut, pszProgName, (DWORD)strlen(pszProgName), &cbIgn, NULL);
431 WriteFile(hStdOut, RT_STR_TUPLE("</i|/u>\r\n"
432 "\r\n"
433 "Options:\r\n"
434 " /i - Install draphics drivers\r\n"
435 " /u - Uninstall draphics drivers (not implemented)\r\n"
436 ), &cbIgn, NULL);
437 return RTEXITCODE_SYNTAX;
438}
439
440
441static bool IsNt4(void)
442{
443 OSVERSIONINFOW VerInfo = { sizeof(VerInfo), 0 };
444 GetVersionExW(&VerInfo);
445 return VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT
446 && VerInfo.dwMajorVersion == 4;
447}
448
449
450int main(int argc, char **argv)
451{
452 /*
453 * "Parse" arguments
454 */
455 if (argc != 2)
456 {
457 if (argc > 2)
458 ErrorMsg("Too many parameter. Expected only one.");
459 return displayHelpAndExit(argv[0]);
460 }
461
462 bool fInstall = true;
463 const char *pszArg = argv[1];
464 if (RTStrICmpAscii(pszArg, "/i") == 0)
465 fInstall = true;
466 else if (RTStrICmpAscii(pszArg, "/u") == 0)
467 fInstall = false;
468 else
469 {
470 ErrorMsg("Unknown parameter (only known parameters are '/i' and '/u')");
471 ErrorMsg(pszArg);
472 return displayHelpAndExit(argv[0]);
473 }
474
475 /*
476 * This program is only for installing drivers on NT4.
477 */
478 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
479 if (IsNt4())
480 {
481 /*
482 * Derive the installation directory from the executable location.
483 * Just strip the filename and use the path.
484 */
485 WCHAR wszInstallDir[MAX_PATH];
486 DWORD cwcInstallDir = GetModuleFileNameW(GetModuleHandle(NULL), &wszInstallDir[0], RT_ELEMENTS(wszInstallDir));
487 if (cwcInstallDir > 0)
488 {
489 while (cwcInstallDir > 0 && !RTPATH_IS_SEP(wszInstallDir[cwcInstallDir - 1]))
490 cwcInstallDir--;
491 if (!cwcInstallDir) /* paranoia^3 */
492 {
493 wszInstallDir[cwcInstallDir++] = '.';
494 wszInstallDir[cwcInstallDir++] = '\\';
495 }
496 wszInstallDir[cwcInstallDir] = '\0';
497
498 /*
499 * Do the install/uninstall.
500 */
501 if (fInstall)
502 rcExit = InstallNt4VideoDriver(wszInstallDir);
503 else
504 rcExit = UninstallDrivers();
505
506 /*
507 * Summary message.
508 */
509 if (rcExit != RTEXITCODE_SUCCESS)
510 ErrorMsg("Some failure occurred during driver installation");
511 }
512 else
513 ErrorMsgLastErr("GetModuleFileNameW failed!");
514 }
515 else
516 ErrorMsg("This program only runs on NT4");
517 return rcExit;
518}
519
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