Don't stop the search after the first driver found. Next ones may be better
[reactos.git] / reactos / dll / win32 / newdev / newdev.c
1 /*
2 * New device installer (newdev.dll)
3 *
4 * Copyright 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
5 * 2005 Christoph von Wittich (Christoph@ActiveVB.de)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #define YDEBUG
23 #include "newdev_private.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(newdev);
26
27 /* Global variables */
28 HINSTANCE hDllInstance;
29
30 /*
31 * @implemented
32 */
33 BOOL WINAPI
34 UpdateDriverForPlugAndPlayDevicesW(
35 IN HWND hwndParent,
36 IN LPCWSTR HardwareId,
37 IN LPCWSTR FullInfPath,
38 IN DWORD InstallFlags,
39 OUT PBOOL bRebootRequired OPTIONAL)
40 {
41 DEVINSTDATA DevInstData;
42 DWORD i;
43 LPWSTR Buffer = NULL;
44 DWORD BufferSize;
45 LPCWSTR CurrentHardwareId; /* Pointer into Buffer */
46 BOOL FoundHardwareId, FoundAtLeastOneDevice = FALSE;
47 BOOL ret = FALSE;
48
49 DevInstData.hDevInfo = INVALID_HANDLE_VALUE;
50
51 TRACE("UpdateDriverForPlugAndPlayDevicesW(%p %S %S 0x%lx %p)\n",
52 hwndParent, HardwareId, FullInfPath, InstallFlags, bRebootRequired);
53
54 /* FIXME: InstallFlags bRebootRequired ignored! */
55
56 /* Check flags */
57 if (InstallFlags & ~(INSTALLFLAG_FORCE | INSTALLFLAG_READONLY | INSTALLFLAG_NONINTERACTIVE))
58 {
59 DPRINT("Unknown flags: 0x%08lx\n", InstallFlags & ~(INSTALLFLAG_FORCE | INSTALLFLAG_READONLY | INSTALLFLAG_NONINTERACTIVE));
60 SetLastError(ERROR_INVALID_FLAGS);
61 goto cleanup;
62 }
63
64 /* Enumerate all devices of the system */
65 DevInstData.hDevInfo = SetupDiGetClassDevsW(NULL, NULL, hwndParent, DIGCF_ALLCLASSES | DIGCF_PRESENT);
66 if (DevInstData.hDevInfo == INVALID_HANDLE_VALUE)
67 goto cleanup;
68 DevInstData.devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
69 for (i = 0; ; i++)
70 {
71 if (!SetupDiEnumDeviceInfo(DevInstData.hDevInfo, i, &DevInstData.devInfoData))
72 {
73 if (GetLastError() != ERROR_NO_MORE_ITEMS)
74 {
75 TRACE("SetupDiEnumDeviceInfo() failed with error 0x%lx\n", GetLastError());
76 goto cleanup;
77 }
78 /* This error was expected */
79 break;
80 }
81
82 /* Get Hardware ID */
83 HeapFree(GetProcessHeap(), 0, Buffer);
84 Buffer = NULL;
85 BufferSize = 0;
86 while (!SetupDiGetDeviceRegistryPropertyW(
87 DevInstData.hDevInfo,
88 &DevInstData.devInfoData,
89 SPDRP_HARDWAREID,
90 NULL,
91 (PBYTE)Buffer,
92 BufferSize,
93 &BufferSize))
94 {
95 if (GetLastError() == ERROR_FILE_NOT_FOUND)
96 {
97 Buffer = NULL;
98 break;
99 }
100 else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
101 {
102 TRACE("SetupDiGetDeviceRegistryPropertyW() failed with error 0x%lx\n", GetLastError());
103 goto cleanup;
104 }
105 /* This error was expected */
106 HeapFree(GetProcessHeap(), 0, Buffer);
107 Buffer = HeapAlloc(GetProcessHeap(), 0, BufferSize);
108 if (!Buffer)
109 {
110 TRACE("HeapAlloc() failed\n", GetLastError());
111 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
112 goto cleanup;
113 }
114 }
115 if (Buffer == NULL)
116 continue;
117
118 /* Check if we match the given hardware ID */
119 FoundHardwareId = FALSE;
120 for (CurrentHardwareId = Buffer; *CurrentHardwareId != UNICODE_NULL; CurrentHardwareId += wcslen(CurrentHardwareId) + 1)
121 {
122 if (wcscmp(CurrentHardwareId, HardwareId) == 0)
123 {
124 FoundHardwareId = TRUE;
125 break;
126 }
127 }
128 if (!FoundHardwareId)
129 continue;
130
131 /* We need to try to update the driver of this device */
132
133 /* Get Instance ID */
134 HeapFree(GetProcessHeap(), 0, Buffer);
135 Buffer = NULL;
136 if (SetupDiGetDeviceInstanceIdW(DevInstData.hDevInfo, &DevInstData.devInfoData, NULL, 0, &BufferSize))
137 {
138 /* Error, as the output buffer should be too small */
139 SetLastError(ERROR_GEN_FAILURE);
140 goto cleanup;
141 }
142 else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
143 {
144 TRACE("SetupDiGetDeviceInstanceIdW() failed with error 0x%lx\n", GetLastError());
145 goto cleanup;
146 }
147 else if ((Buffer = HeapAlloc(GetProcessHeap(), 0, BufferSize)) == NULL)
148 {
149 TRACE("HeapAlloc() failed\n", GetLastError());
150 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
151 goto cleanup;
152 }
153 else if (!SetupDiGetDeviceInstanceIdW(DevInstData.hDevInfo, &DevInstData.devInfoData, Buffer, BufferSize, NULL))
154 {
155 TRACE("SetupDiGetDeviceInstanceIdW() failed with error 0x%lx\n", GetLastError());
156 goto cleanup;
157 }
158 TRACE("Trying to update the driver of %S\n", Buffer);
159
160 /* Search driver in the specified .inf file */
161 if (!SearchDriver(&DevInstData, NULL, FullInfPath))
162 {
163 TRACE("SearchDriver() failed with error 0x%lx\n", GetLastError());
164 continue;
165 }
166
167 /* FIXME: HACK! We shouldn't check of ERROR_PRIVILEGE_NOT_HELD */
168 //if (!InstallCurrentDriver(&DevInstData))
169 if (!InstallCurrentDriver(&DevInstData) && GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
170 {
171 TRACE("InstallCurrentDriver() failed with error 0x%lx\n", GetLastError());
172 continue;
173 }
174
175 FoundAtLeastOneDevice = TRUE;
176 }
177
178 if (FoundAtLeastOneDevice)
179 {
180 SetLastError(NO_ERROR);
181 ret = TRUE;
182 }
183 else
184 {
185 TRACE("No device found with HardwareID %S\n", HardwareId);
186 SetLastError(ERROR_NO_SUCH_DEVINST);
187 }
188
189 cleanup:
190 if (DevInstData.hDevInfo != INVALID_HANDLE_VALUE)
191 SetupDiDestroyDeviceInfoList(DevInstData.hDevInfo);
192 HeapFree(GetProcessHeap(), 0, Buffer);
193 return ret;
194 }
195
196 /*
197 * @implemented
198 */
199 BOOL WINAPI
200 UpdateDriverForPlugAndPlayDevicesA(
201 IN HWND hwndParent,
202 IN LPCSTR HardwareId,
203 IN LPCSTR FullInfPath,
204 IN DWORD InstallFlags,
205 OUT PBOOL bRebootRequired OPTIONAL)
206 {
207 BOOL Result;
208 LPWSTR HardwareIdW = NULL;
209 LPWSTR FullInfPathW = NULL;
210
211 int len = MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, NULL, 0);
212 HardwareIdW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
213 if (!HardwareIdW)
214 {
215 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
216 return FALSE;
217 }
218 MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, HardwareIdW, len);
219
220 len = MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, NULL, 0);
221 FullInfPathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
222 if (!FullInfPathW)
223 {
224 HeapFree(GetProcessHeap(), 0, HardwareIdW);
225 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
226 return FALSE;
227 }
228 MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, FullInfPathW, len);
229
230 Result = UpdateDriverForPlugAndPlayDevicesW(
231 hwndParent,
232 HardwareIdW,
233 FullInfPathW,
234 InstallFlags,
235 bRebootRequired);
236
237 HeapFree(GetProcessHeap(), 0, HardwareIdW);
238 HeapFree(GetProcessHeap(), 0, FullInfPathW);
239
240 return Result;
241 }
242
243 /* Directory and InfFile MUST NOT be specified simultaneously */
244 BOOL
245 SearchDriver(
246 IN PDEVINSTDATA DevInstData,
247 IN LPCTSTR Directory OPTIONAL,
248 IN LPCTSTR InfFile OPTIONAL)
249 {
250 SP_DEVINSTALL_PARAMS DevInstallParams = {0,};
251 BOOL ret;
252
253 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
254 if (!SetupDiGetDeviceInstallParams(DevInstData->hDevInfo, &DevInstData->devInfoData, &DevInstallParams))
255 {
256 TRACE("SetupDiGetDeviceInstallParams() failed with error 0x%lx\n", GetLastError());
257 return FALSE;
258 }
259 DevInstallParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS;
260
261 if (InfFile)
262 {
263 DevInstallParams.Flags |= DI_ENUMSINGLEINF;
264 _tcsncpy(DevInstallParams.DriverPath, InfFile, MAX_PATH);
265 }
266 else if (Directory)
267 {
268 DevInstallParams.Flags &= ~DI_ENUMSINGLEINF;
269 _tcsncpy(DevInstallParams.DriverPath, Directory, MAX_PATH);
270 }
271 else
272 *DevInstallParams.DriverPath = _T('\0');
273
274 ret = SetupDiSetDeviceInstallParams(
275 DevInstData->hDevInfo,
276 &DevInstData->devInfoData,
277 &DevInstallParams);
278 if (!ret)
279 {
280 TRACE("SetupDiSetDeviceInstallParams() failed with error 0x%lx\n", GetLastError());
281 return FALSE;
282 }
283
284 ret = SetupDiBuildDriverInfoList(
285 DevInstData->hDevInfo,
286 &DevInstData->devInfoData,
287 SPDIT_COMPATDRIVER);
288 if (!ret)
289 {
290 TRACE("SetupDiBuildDriverInfoList() failed with error 0x%lx\n", GetLastError());
291 return FALSE;
292 }
293
294 DevInstData->drvInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
295 ret = SetupDiEnumDriverInfo(
296 DevInstData->hDevInfo,
297 &DevInstData->devInfoData,
298 SPDIT_COMPATDRIVER,
299 0,
300 &DevInstData->drvInfoData);
301 if (!ret)
302 {
303 if (GetLastError() == ERROR_NO_MORE_ITEMS)
304 return FALSE;
305 TRACE("SetupDiEnumDriverInfo() failed with error 0x%lx\n", GetLastError());
306 return FALSE;
307 }
308
309 return TRUE;
310 }
311
312 static BOOL
313 IsDots(IN LPCTSTR str)
314 {
315 if(_tcscmp(str, _T(".")) && _tcscmp(str, _T(".."))) return FALSE;
316 return TRUE;
317 }
318
319 static LPTSTR
320 GetFileExt(IN LPTSTR FileName)
321 {
322 if (FileName == 0)
323 return _T("");
324
325 int i = _tcsclen(FileName);
326 while ((i >= 0) && (FileName[i] != _T('.')))
327 i--;
328
329 FileName = _tcslwr(FileName);
330
331 if (i >= 0)
332 return &FileName[i];
333 else
334 return _T("");
335 }
336
337 BOOL
338 SearchDriverRecursive(
339 IN PDEVINSTDATA DevInstData,
340 IN LPCTSTR Path)
341 {
342 WIN32_FIND_DATA wfd;
343 TCHAR DirPath[MAX_PATH];
344 TCHAR FileName[MAX_PATH];
345 TCHAR FullPath[MAX_PATH];
346 TCHAR LastDirPath[MAX_PATH] = _T("");
347 TCHAR PathWithPattern[MAX_PATH];
348 BOOL ok = TRUE;
349 BOOL retval = FALSE;
350 HANDLE hFindFile = INVALID_HANDLE_VALUE;
351
352 _tcscpy(DirPath, Path);
353
354 if (DirPath[_tcsclen(DirPath) - 1] != '\\')
355 _tcscat(DirPath, _T("\\"));
356
357 _tcscpy(PathWithPattern, DirPath);
358 _tcscat(PathWithPattern, _T("\\*"));
359
360 for (hFindFile = FindFirstFile(PathWithPattern, &wfd);
361 ok && hFindFile != INVALID_HANDLE_VALUE;
362 ok = FindNextFile(hFindFile, &wfd))
363 {
364
365 _tcscpy(FileName, wfd.cFileName);
366 if (IsDots(FileName))
367 continue;
368
369 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
370 {
371 /* Recursive search */
372 _tcscpy(FullPath, DirPath);
373 _tcscat(FullPath, FileName);
374 if (SearchDriverRecursive(DevInstData, FullPath))
375 {
376 retval = TRUE;
377 /* We continue the search for a better driver */
378 }
379 }
380 else
381 {
382 LPCTSTR pszExtension = GetFileExt(FileName);
383
384 if ((_tcscmp(pszExtension, _T(".inf")) == 0) && (_tcscmp(LastDirPath, DirPath) != 0))
385 {
386 _tcscpy(LastDirPath, DirPath);
387
388 if (_tcsclen(DirPath) > MAX_PATH)
389 /* Path is too long to be searched */
390 continue;
391
392 if (SearchDriver(DevInstData, DirPath, NULL))
393 {
394 retval = TRUE;
395 /* We continue the search for a better driver */
396 }
397
398 }
399 }
400 }
401
402 if (hFindFile != INVALID_HANDLE_VALUE)
403 FindClose(hFindFile);
404 return retval;
405 }
406
407 BOOL
408 InstallCurrentDriver(
409 IN PDEVINSTDATA DevInstData)
410 {
411 BOOL ret;
412
413 TRACE("Installing driver %S: %S\n", DevInstData->drvInfoData.MfgName, DevInstData->drvInfoData.Description);
414
415 ret = SetupDiCallClassInstaller(
416 DIF_SELECTBESTCOMPATDRV,
417 DevInstData->hDevInfo,
418 &DevInstData->devInfoData);
419 if (!ret)
420 {
421 TRACE("SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV) failed with error 0x%lx\n", GetLastError());
422 return FALSE;
423 }
424
425 ret = SetupDiCallClassInstaller(
426 DIF_ALLOW_INSTALL,
427 DevInstData->hDevInfo,
428 &DevInstData->devInfoData);
429 if (!ret)
430 {
431 TRACE("SetupDiCallClassInstaller(DIF_ALLOW_INSTALL) failed with error 0x%lx\n", GetLastError());
432 return FALSE;
433 }
434
435 ret = SetupDiCallClassInstaller(
436 DIF_NEWDEVICEWIZARD_PREANALYZE,
437 DevInstData->hDevInfo,
438 &DevInstData->devInfoData);
439 if (!ret)
440 {
441 TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_PREANALYZE) failed with error 0x%lx\n", GetLastError());
442 return FALSE;
443 }
444
445 ret = SetupDiCallClassInstaller(
446 DIF_NEWDEVICEWIZARD_POSTANALYZE,
447 DevInstData->hDevInfo,
448 &DevInstData->devInfoData);
449 if (!ret)
450 {
451 TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_POSTANALYZE) failed with error 0x%lx\n", GetLastError());
452 return FALSE;
453 }
454
455 ret = SetupDiCallClassInstaller(
456 DIF_INSTALLDEVICEFILES,
457 DevInstData->hDevInfo,
458 &DevInstData->devInfoData);
459 if (!ret)
460 {
461 TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) failed with error 0x%lx\n", GetLastError());
462 return FALSE;
463 }
464
465 ret = SetupDiCallClassInstaller(
466 DIF_REGISTER_COINSTALLERS,
467 DevInstData->hDevInfo,
468 &DevInstData->devInfoData);
469 if (!ret)
470 {
471 TRACE("SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) failed with error 0x%lx\n", GetLastError());
472 return FALSE;
473 }
474
475 ret = SetupDiCallClassInstaller(
476 DIF_INSTALLINTERFACES,
477 DevInstData->hDevInfo,
478 &DevInstData->devInfoData);
479 if (!ret)
480 {
481 TRACE("SetupDiCallClassInstaller(DIF_INSTALLINTERFACES) failed with error 0x%lx\n", GetLastError());
482 return FALSE;
483 }
484
485 ret = SetupDiCallClassInstaller(
486 DIF_INSTALLDEVICE,
487 DevInstData->hDevInfo,
488 &DevInstData->devInfoData);
489 if (!ret)
490 {
491 TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed with error 0x%lx\n", GetLastError());
492 return FALSE;
493 }
494
495 ret = SetupDiCallClassInstaller(
496 DIF_NEWDEVICEWIZARD_FINISHINSTALL,
497 DevInstData->hDevInfo,
498 &DevInstData->devInfoData);
499 if (!ret)
500 {
501 TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL) failed with error 0x%lx\n", GetLastError());
502 return FALSE;
503 }
504
505 ret = SetupDiCallClassInstaller(
506 DIF_DESTROYPRIVATEDATA,
507 DevInstData->hDevInfo,
508 &DevInstData->devInfoData);
509 if (!ret)
510 {
511 TRACE("SetupDiCallClassInstaller(DIF_DESTROYPRIVATEDATA) failed with error 0x%lx\n", GetLastError());
512 return FALSE;
513 }
514
515 return TRUE;
516 }
517
518 /*
519 * @implemented
520 */
521 BOOL WINAPI
522 DevInstallW(
523 IN HWND hWndParent,
524 IN HINSTANCE hInstance,
525 IN LPCWSTR InstanceId,
526 IN INT Show)
527 {
528 PDEVINSTDATA DevInstData = NULL;
529 BOOL ret;
530 DWORD config_flags;
531 BOOL retval = FALSE;
532
533 if (!IsUserAdmin())
534 {
535 /* XP kills the process... */
536 ExitProcess(ERROR_ACCESS_DENIED);
537 }
538
539 DevInstData = HeapAlloc(GetProcessHeap(), 0, sizeof(DEVINSTDATA));
540 if (!DevInstData)
541 {
542 TRACE("HeapAlloc() failed\n");
543 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
544 goto cleanup;
545 }
546
547 /* Clear devinst data */
548 ZeroMemory(DevInstData, sizeof(DEVINSTDATA));
549 DevInstData->devInfoData.cbSize = 0; /* Tell if the devInfoData is valid */
550
551 /* Fill devinst data */
552 DevInstData->hDevInfo = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL);
553 if (DevInstData->hDevInfo == INVALID_HANDLE_VALUE)
554 {
555 TRACE("SetupDiCreateDeviceInfoListExW() failed with error 0x%lx\n", GetLastError());
556 goto cleanup;
557 }
558
559 DevInstData->devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
560 ret = SetupDiOpenDeviceInfoW(
561 DevInstData->hDevInfo,
562 InstanceId,
563 NULL,
564 0, /* Open flags */
565 &DevInstData->devInfoData);
566 if (!ret)
567 {
568 TRACE("SetupDiOpenDeviceInfoW() failed with error 0x%lx (InstanceId %S)\n", GetLastError(), InstanceId);
569 DevInstData->devInfoData.cbSize = 0;
570 goto cleanup;
571 }
572
573 SetLastError(ERROR_GEN_FAILURE);
574 ret = SetupDiGetDeviceRegistryProperty(
575 DevInstData->hDevInfo,
576 &DevInstData->devInfoData,
577 SPDRP_DEVICEDESC,
578 &DevInstData->regDataType,
579 NULL, 0,
580 &DevInstData->requiredSize);
581
582 if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && DevInstData->regDataType == REG_SZ)
583 {
584 DevInstData->buffer = HeapAlloc(GetProcessHeap(), 0, DevInstData->requiredSize);
585 if (!DevInstData->buffer)
586 {
587 TRACE("HeapAlloc() failed\n");
588 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
589 }
590 else
591 {
592 ret = SetupDiGetDeviceRegistryProperty(
593 DevInstData->hDevInfo,
594 &DevInstData->devInfoData,
595 SPDRP_DEVICEDESC,
596 &DevInstData->regDataType,
597 DevInstData->buffer, DevInstData->requiredSize,
598 &DevInstData->requiredSize);
599 }
600 }
601 if (!ret)
602 {
603 TRACE("SetupDiGetDeviceRegistryProperty() failed with error 0x%lx (InstanceId %S)\n", GetLastError(), InstanceId);
604 goto cleanup;
605 }
606
607 if (SetupDiGetDeviceRegistryProperty(
608 DevInstData->hDevInfo,
609 &DevInstData->devInfoData,
610 SPDRP_CONFIGFLAGS,
611 NULL,
612 (BYTE *)&config_flags,
613 sizeof(config_flags),
614 NULL))
615 {
616 if (config_flags & CONFIGFLAG_FAILEDINSTALL)
617 {
618 /* The device is disabled */
619 retval = TRUE;
620 goto cleanup;
621 }
622 }
623
624 TRACE("Installing %S (%S)\n", DevInstData->buffer, InstanceId);
625
626 /* Search driver in default location */
627 if (SearchDriver(DevInstData, NULL, NULL))
628 {
629 /* Driver found ; install it */
630 retval = InstallCurrentDriver(DevInstData);
631 goto cleanup;
632 }
633 else if (Show == SW_HIDE)
634 {
635 /* We can't show the wizard. Fail the install */
636 goto cleanup;
637 }
638
639 /* Prepare the wizard, and display it */
640 retval = DisplayWizard(DevInstData, hWndParent, IDD_WELCOMEPAGE);
641
642 cleanup:
643 if (DevInstData)
644 {
645 if (DevInstData->devInfoData.cbSize != 0)
646 {
647 if (!SetupDiDestroyDriverInfoList(DevInstData->hDevInfo, &DevInstData->devInfoData, SPDIT_COMPATDRIVER))
648 TRACE("SetupDiDestroyDriverInfoList() failed with error 0x%lx\n", GetLastError());
649 }
650 if (DevInstData->hDevInfo != INVALID_HANDLE_VALUE)
651 {
652 if (!SetupDiDestroyDeviceInfoList(DevInstData->hDevInfo))
653 TRACE("SetupDiDestroyDeviceInfoList() failed with error 0x%lx\n", GetLastError());
654 }
655 HeapFree(GetProcessHeap(), 0, DevInstData->buffer);
656 HeapFree(GetProcessHeap(), 0, DevInstData);
657 }
658
659 return retval;
660 }
661
662 /*
663 * @unimplemented
664 */
665 BOOL WINAPI
666 ClientSideInstallW(
667 IN HWND hWndOwner,
668 IN DWORD dwUnknownFlags,
669 IN LPWSTR lpNamedPipeName)
670 {
671 /* NOTE: pNamedPipeName is in the format:
672 * "\\.\pipe\PNP_Device_Install_Pipe_0.{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
673 */
674 FIXME("Stub\n");
675 return FALSE;
676 }
677
678 BOOL WINAPI
679 DllMain(
680 IN HINSTANCE hInstance,
681 IN DWORD dwReason,
682 IN LPVOID lpReserved)
683 {
684 if (dwReason == DLL_PROCESS_ATTACH)
685 {
686 INITCOMMONCONTROLSEX InitControls;
687
688 DisableThreadLibraryCalls(hInstance);
689
690 InitControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
691 InitControls.dwICC = ICC_PROGRESS_CLASS;
692 InitCommonControlsEx(&InitControls);
693 hDllInstance = hInstance;
694 }
695
696 return TRUE;
697 }