Fix search of driver on removable media
[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.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 /* FIXME: 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 break;
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 goto cleanup;
396 }
397
398 }
399 }
400 }
401
402 cleanup:
403 if (hFindFile != INVALID_HANDLE_VALUE)
404 FindClose(hFindFile);
405 return retval;
406 }
407
408 BOOL
409 InstallCurrentDriver(
410 IN PDEVINSTDATA DevInstData)
411 {
412 BOOL ret;
413
414 TRACE("Installing driver %S: %S\n", DevInstData->drvInfoData.MfgName, DevInstData->drvInfoData.Description);
415
416 ret = SetupDiCallClassInstaller(
417 DIF_SELECTBESTCOMPATDRV,
418 DevInstData->hDevInfo,
419 &DevInstData->devInfoData);
420 if (!ret)
421 {
422 TRACE("SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV) failed with error 0x%lx\n", GetLastError());
423 return FALSE;
424 }
425
426 ret = SetupDiCallClassInstaller(
427 DIF_ALLOW_INSTALL,
428 DevInstData->hDevInfo,
429 &DevInstData->devInfoData);
430 if (!ret)
431 {
432 TRACE("SetupDiCallClassInstaller(DIF_ALLOW_INSTALL) failed with error 0x%lx\n", GetLastError());
433 return FALSE;
434 }
435
436 ret = SetupDiCallClassInstaller(
437 DIF_NEWDEVICEWIZARD_PREANALYZE,
438 DevInstData->hDevInfo,
439 &DevInstData->devInfoData);
440 if (!ret)
441 {
442 TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_PREANALYZE) failed with error 0x%lx\n", GetLastError());
443 return FALSE;
444 }
445
446 ret = SetupDiCallClassInstaller(
447 DIF_NEWDEVICEWIZARD_POSTANALYZE,
448 DevInstData->hDevInfo,
449 &DevInstData->devInfoData);
450 if (!ret)
451 {
452 TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_POSTANALYZE) failed with error 0x%lx\n", GetLastError());
453 return FALSE;
454 }
455
456 ret = SetupDiCallClassInstaller(
457 DIF_INSTALLDEVICEFILES,
458 DevInstData->hDevInfo,
459 &DevInstData->devInfoData);
460 if (!ret)
461 {
462 TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) failed with error 0x%lx\n", GetLastError());
463 return FALSE;
464 }
465
466 ret = SetupDiCallClassInstaller(
467 DIF_REGISTER_COINSTALLERS,
468 DevInstData->hDevInfo,
469 &DevInstData->devInfoData);
470 if (!ret)
471 {
472 TRACE("SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) failed with error 0x%lx\n", GetLastError());
473 return FALSE;
474 }
475
476 ret = SetupDiCallClassInstaller(
477 DIF_INSTALLINTERFACES,
478 DevInstData->hDevInfo,
479 &DevInstData->devInfoData);
480 if (!ret)
481 {
482 TRACE("SetupDiCallClassInstaller(DIF_INSTALLINTERFACES) failed with error 0x%lx\n", GetLastError());
483 return FALSE;
484 }
485
486 ret = SetupDiCallClassInstaller(
487 DIF_INSTALLDEVICE,
488 DevInstData->hDevInfo,
489 &DevInstData->devInfoData);
490 if (!ret)
491 {
492 TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed with error 0x%lx\n", GetLastError());
493 return FALSE;
494 }
495
496 ret = SetupDiCallClassInstaller(
497 DIF_NEWDEVICEWIZARD_FINISHINSTALL,
498 DevInstData->hDevInfo,
499 &DevInstData->devInfoData);
500 if (!ret)
501 {
502 TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL) failed with error 0x%lx\n", GetLastError());
503 return FALSE;
504 }
505
506 ret = SetupDiCallClassInstaller(
507 DIF_DESTROYPRIVATEDATA,
508 DevInstData->hDevInfo,
509 &DevInstData->devInfoData);
510 if (!ret)
511 {
512 TRACE("SetupDiCallClassInstaller(DIF_DESTROYPRIVATEDATA) failed with error 0x%lx\n", GetLastError());
513 return FALSE;
514 }
515
516 return TRUE;
517 }
518
519 /*
520 * @implemented
521 */
522 BOOL WINAPI
523 DevInstallW(
524 IN HWND hWndParent,
525 IN HINSTANCE hInstance,
526 IN LPCWSTR InstanceId,
527 IN INT Show)
528 {
529 PDEVINSTDATA DevInstData = NULL;
530 BOOL ret;
531 DWORD config_flags;
532 BOOL retval = FALSE;
533
534 if (!IsUserAdmin())
535 {
536 /* XP kills the process... */
537 ExitProcess(ERROR_ACCESS_DENIED);
538 }
539
540 DevInstData = HeapAlloc(GetProcessHeap(), 0, sizeof(DEVINSTDATA));
541 if (!DevInstData)
542 {
543 TRACE("HeapAlloc() failed\n");
544 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
545 goto cleanup;
546 }
547
548 /* Clear devinst data */
549 ZeroMemory(DevInstData, sizeof(DEVINSTDATA));
550 DevInstData->devInfoData.cbSize = 0; /* Tell if the devInfoData is valid */
551
552 /* Fill devinst data */
553 DevInstData->hDevInfo = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL);
554 if (DevInstData->hDevInfo == INVALID_HANDLE_VALUE)
555 {
556 TRACE("SetupDiCreateDeviceInfoListExW() failed with error 0x%lx\n", GetLastError());
557 goto cleanup;
558 }
559
560 DevInstData->devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
561 ret = SetupDiOpenDeviceInfoW(
562 DevInstData->hDevInfo,
563 InstanceId,
564 NULL,
565 0, /* Open flags */
566 &DevInstData->devInfoData);
567 if (!ret)
568 {
569 TRACE("SetupDiOpenDeviceInfoW() failed with error 0x%lx (InstanceId %S)\n", GetLastError(), InstanceId);
570 DevInstData->devInfoData.cbSize = 0;
571 goto cleanup;
572 }
573
574 SetLastError(ERROR_GEN_FAILURE);
575 ret = SetupDiGetDeviceRegistryProperty(
576 DevInstData->hDevInfo,
577 &DevInstData->devInfoData,
578 SPDRP_DEVICEDESC,
579 &DevInstData->regDataType,
580 NULL, 0,
581 &DevInstData->requiredSize);
582
583 if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && DevInstData->regDataType == REG_SZ)
584 {
585 DevInstData->buffer = HeapAlloc(GetProcessHeap(), 0, DevInstData->requiredSize);
586 if (!DevInstData->buffer)
587 {
588 TRACE("HeapAlloc() failed\n");
589 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
590 }
591 else
592 {
593 ret = SetupDiGetDeviceRegistryProperty(
594 DevInstData->hDevInfo,
595 &DevInstData->devInfoData,
596 SPDRP_DEVICEDESC,
597 &DevInstData->regDataType,
598 DevInstData->buffer, DevInstData->requiredSize,
599 &DevInstData->requiredSize);
600 }
601 }
602 if (!ret)
603 {
604 TRACE("SetupDiGetDeviceRegistryProperty() failed with error 0x%lx (InstanceId %S)\n", GetLastError(), InstanceId);
605 goto cleanup;
606 }
607
608 if (SetupDiGetDeviceRegistryProperty(
609 DevInstData->hDevInfo,
610 &DevInstData->devInfoData,
611 SPDRP_CONFIGFLAGS,
612 NULL,
613 (BYTE *)&config_flags,
614 sizeof(config_flags),
615 NULL))
616 {
617 if (config_flags & CONFIGFLAG_FAILEDINSTALL)
618 {
619 /* The device is disabled */
620 retval = TRUE;
621 goto cleanup;
622 }
623 }
624
625 TRACE("Installing %S (%S)\n", DevInstData->buffer, InstanceId);
626
627 /* Search driver in default location */
628 if (SearchDriver(DevInstData, NULL, NULL))
629 {
630 /* Driver found ; install it */
631 retval = InstallCurrentDriver(DevInstData);
632 goto cleanup;
633 }
634 else if (Show == SW_HIDE)
635 {
636 /* We can't show the wizard. Fail the install */
637 goto cleanup;
638 }
639
640 /* Prepare the wizard, and display it */
641 retval = DisplayWizard(DevInstData, hWndParent, IDD_WELCOMEPAGE);
642
643 cleanup:
644 if (DevInstData)
645 {
646 if (DevInstData->devInfoData.cbSize != 0)
647 {
648 if (!SetupDiDestroyDriverInfoList(DevInstData->hDevInfo, &DevInstData->devInfoData, SPDIT_COMPATDRIVER))
649 TRACE("SetupDiDestroyDriverInfoList() failed with error 0x%lx\n", GetLastError());
650 }
651 if (DevInstData->hDevInfo != INVALID_HANDLE_VALUE)
652 {
653 if (!SetupDiDestroyDeviceInfoList(DevInstData->hDevInfo))
654 TRACE("SetupDiDestroyDeviceInfoList() failed with error 0x%lx\n", GetLastError());
655 }
656 HeapFree(GetProcessHeap(), 0, DevInstData->buffer);
657 HeapFree(GetProcessHeap(), 0, DevInstData);
658 }
659
660 return retval;
661 }
662
663 /*
664 * @unimplemented
665 */
666 BOOL WINAPI
667 ClientSideInstallW(
668 IN HWND hWndOwner,
669 IN DWORD dwUnknownFlags,
670 IN LPWSTR lpNamedPipeName)
671 {
672 /* NOTE: pNamedPipeName is in the format:
673 * "\\.\pipe\PNP_Device_Install_Pipe_0.{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
674 */
675 FIXME("Stub\n");
676 return FALSE;
677 }
678
679 BOOL WINAPI
680 DllMain(
681 IN HINSTANCE hInstance,
682 IN DWORD dwReason,
683 IN LPVOID lpReserved)
684 {
685 if (dwReason == DLL_PROCESS_ATTACH)
686 {
687 INITCOMMONCONTROLSEX InitControls;
688
689 DisableThreadLibraryCalls(hInstance);
690
691 InitControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
692 InitControls.dwICC = ICC_PROGRESS_CLASS;
693 InitCommonControlsEx(&InitControls);
694 hDllInstance = hInstance;
695 }
696
697 return TRUE;
698 }