mirror of
https://github.com/reactos/reactos.git
synced 2024-08-22 03:12:51 +00:00
1053 lines
30 KiB
C
1053 lines
30 KiB
C
/*
|
|
* New device installer (newdev.dll)
|
|
*
|
|
* Copyright 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
|
|
* 2005 Christoph von Wittich (Christoph@ActiveVB.de)
|
|
* 2009 Colin Finck (colin@reactos.org)
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "newdev_private.h"
|
|
|
|
#include <stdio.h>
|
|
#include <winnls.h>
|
|
|
|
/* Global variables */
|
|
HINSTANCE hDllInstance;
|
|
|
|
static BOOL
|
|
SearchDriver(
|
|
IN PDEVINSTDATA DevInstData,
|
|
IN LPCWSTR Directory OPTIONAL,
|
|
IN LPCWSTR InfFile OPTIONAL);
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
UpdateDriverForPlugAndPlayDevicesW(
|
|
IN HWND hwndParent,
|
|
IN LPCWSTR HardwareId,
|
|
IN LPCWSTR FullInfPath,
|
|
IN DWORD InstallFlags,
|
|
OUT PBOOL bRebootRequired OPTIONAL)
|
|
{
|
|
DEVINSTDATA DevInstData;
|
|
DWORD i;
|
|
LPWSTR Buffer = NULL;
|
|
DWORD BufferSize;
|
|
LPCWSTR CurrentHardwareId; /* Pointer into Buffer */
|
|
BOOL FoundHardwareId, FoundAtLeastOneDevice = FALSE;
|
|
BOOL ret = FALSE;
|
|
|
|
DevInstData.hDevInfo = INVALID_HANDLE_VALUE;
|
|
|
|
TRACE("UpdateDriverForPlugAndPlayDevicesW(%p %s %s 0x%x %p)\n",
|
|
hwndParent, debugstr_w(HardwareId), debugstr_w(FullInfPath), InstallFlags, bRebootRequired);
|
|
|
|
/* FIXME: InstallFlags bRebootRequired ignored! */
|
|
|
|
/* Check flags */
|
|
if (InstallFlags & ~(INSTALLFLAG_FORCE | INSTALLFLAG_READONLY | INSTALLFLAG_NONINTERACTIVE))
|
|
{
|
|
TRACE("Unknown flags: 0x%08lx\n", InstallFlags & ~(INSTALLFLAG_FORCE | INSTALLFLAG_READONLY | INSTALLFLAG_NONINTERACTIVE));
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Enumerate all devices of the system */
|
|
DevInstData.hDevInfo = SetupDiGetClassDevsW(NULL, NULL, hwndParent, DIGCF_ALLCLASSES | DIGCF_PRESENT);
|
|
if (DevInstData.hDevInfo == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
DevInstData.devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
for (i = 0; ; i++)
|
|
{
|
|
if (!SetupDiEnumDeviceInfo(DevInstData.hDevInfo, i, &DevInstData.devInfoData))
|
|
{
|
|
if (GetLastError() != ERROR_NO_MORE_ITEMS)
|
|
{
|
|
TRACE("SetupDiEnumDeviceInfo() failed with error 0x%x\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
/* This error was expected */
|
|
break;
|
|
}
|
|
|
|
/* Get Hardware ID */
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
Buffer = NULL;
|
|
BufferSize = 0;
|
|
while (!SetupDiGetDeviceRegistryPropertyW(
|
|
DevInstData.hDevInfo,
|
|
&DevInstData.devInfoData,
|
|
SPDRP_HARDWAREID,
|
|
NULL,
|
|
(PBYTE)Buffer,
|
|
BufferSize,
|
|
&BufferSize))
|
|
{
|
|
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
Buffer = NULL;
|
|
break;
|
|
}
|
|
else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
TRACE("SetupDiGetDeviceRegistryPropertyW() failed with error 0x%x\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
/* This error was expected */
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
Buffer = HeapAlloc(GetProcessHeap(), 0, BufferSize);
|
|
if (!Buffer)
|
|
{
|
|
TRACE("HeapAlloc() failed\n", GetLastError());
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (Buffer == NULL)
|
|
continue;
|
|
|
|
/* Check if we match the given hardware ID */
|
|
FoundHardwareId = FALSE;
|
|
for (CurrentHardwareId = Buffer; *CurrentHardwareId != UNICODE_NULL; CurrentHardwareId += wcslen(CurrentHardwareId) + 1)
|
|
{
|
|
if (wcscmp(CurrentHardwareId, HardwareId) == 0)
|
|
{
|
|
FoundHardwareId = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!FoundHardwareId)
|
|
continue;
|
|
|
|
/* We need to try to update the driver of this device */
|
|
|
|
/* Get Instance ID */
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
Buffer = NULL;
|
|
if (SetupDiGetDeviceInstanceIdW(DevInstData.hDevInfo, &DevInstData.devInfoData, NULL, 0, &BufferSize))
|
|
{
|
|
/* Error, as the output buffer should be too small */
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto cleanup;
|
|
}
|
|
else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
TRACE("SetupDiGetDeviceInstanceIdW() failed with error 0x%x\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
else if ((Buffer = HeapAlloc(GetProcessHeap(), 0, BufferSize * sizeof(WCHAR))) == NULL)
|
|
{
|
|
TRACE("HeapAlloc() failed\n", GetLastError());
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
else if (!SetupDiGetDeviceInstanceIdW(DevInstData.hDevInfo, &DevInstData.devInfoData, Buffer, BufferSize, NULL))
|
|
{
|
|
TRACE("SetupDiGetDeviceInstanceIdW() failed with error 0x%x\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
TRACE("Trying to update the driver of %s\n", debugstr_w(Buffer));
|
|
|
|
/* Search driver in the specified .inf file */
|
|
if (!SearchDriver(&DevInstData, NULL, FullInfPath))
|
|
{
|
|
TRACE("SearchDriver() failed with error 0x%x\n", GetLastError());
|
|
continue;
|
|
}
|
|
|
|
/* FIXME: HACK! We shouldn't check of ERROR_PRIVILEGE_NOT_HELD */
|
|
//if (!InstallCurrentDriver(&DevInstData))
|
|
if (!InstallCurrentDriver(&DevInstData) && GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
|
|
{
|
|
TRACE("InstallCurrentDriver() failed with error 0x%x\n", GetLastError());
|
|
continue;
|
|
}
|
|
|
|
FoundAtLeastOneDevice = TRUE;
|
|
}
|
|
|
|
if (FoundAtLeastOneDevice)
|
|
{
|
|
SetLastError(NO_ERROR);
|
|
ret = TRUE;
|
|
}
|
|
else
|
|
{
|
|
TRACE("No device found with HardwareID %s\n", debugstr_w(HardwareId));
|
|
SetLastError(ERROR_NO_SUCH_DEVINST);
|
|
}
|
|
|
|
cleanup:
|
|
if (DevInstData.hDevInfo != INVALID_HANDLE_VALUE)
|
|
SetupDiDestroyDeviceInfoList(DevInstData.hDevInfo);
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
UpdateDriverForPlugAndPlayDevicesA(
|
|
IN HWND hwndParent,
|
|
IN LPCSTR HardwareId,
|
|
IN LPCSTR FullInfPath,
|
|
IN DWORD InstallFlags,
|
|
OUT PBOOL bRebootRequired OPTIONAL)
|
|
{
|
|
BOOL Result;
|
|
LPWSTR HardwareIdW = NULL;
|
|
LPWSTR FullInfPathW = NULL;
|
|
|
|
int len = MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, NULL, 0);
|
|
HardwareIdW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
if (!HardwareIdW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, HardwareIdW, len);
|
|
|
|
len = MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, NULL, 0);
|
|
FullInfPathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
if (!FullInfPathW)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, HardwareIdW);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, FullInfPathW, len);
|
|
|
|
Result = UpdateDriverForPlugAndPlayDevicesW(
|
|
hwndParent,
|
|
HardwareIdW,
|
|
FullInfPathW,
|
|
InstallFlags,
|
|
bRebootRequired);
|
|
|
|
HeapFree(GetProcessHeap(), 0, HardwareIdW);
|
|
HeapFree(GetProcessHeap(), 0, FullInfPathW);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/* Directory and InfFile MUST NOT be specified simultaneously */
|
|
static BOOL
|
|
SearchDriver(
|
|
IN PDEVINSTDATA DevInstData,
|
|
IN LPCWSTR Directory OPTIONAL,
|
|
IN LPCWSTR InfFile OPTIONAL)
|
|
{
|
|
SP_DEVINSTALL_PARAMS_W DevInstallParams = {0,};
|
|
BOOL ret;
|
|
|
|
DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
|
|
if (!SetupDiGetDeviceInstallParamsW(DevInstData->hDevInfo, &DevInstData->devInfoData, &DevInstallParams))
|
|
{
|
|
TRACE("SetupDiGetDeviceInstallParams() failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
DevInstallParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS;
|
|
|
|
if (InfFile)
|
|
{
|
|
DevInstallParams.Flags |= DI_ENUMSINGLEINF;
|
|
wcsncpy(DevInstallParams.DriverPath, InfFile, MAX_PATH);
|
|
}
|
|
else if (Directory)
|
|
{
|
|
DevInstallParams.Flags &= ~DI_ENUMSINGLEINF;
|
|
wcsncpy(DevInstallParams.DriverPath, Directory, MAX_PATH);
|
|
}
|
|
else
|
|
{
|
|
DevInstallParams.Flags &= ~DI_ENUMSINGLEINF;
|
|
*DevInstallParams.DriverPath = '\0';
|
|
}
|
|
|
|
ret = SetupDiSetDeviceInstallParamsW(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
&DevInstallParams);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiSetDeviceInstallParams() failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiBuildDriverInfoList(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
SPDIT_COMPATDRIVER);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiBuildDriverInfoList() failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
DevInstData->drvInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
|
|
ret = SetupDiEnumDriverInfoW(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
SPDIT_COMPATDRIVER,
|
|
0,
|
|
&DevInstData->drvInfoData);
|
|
if (!ret)
|
|
{
|
|
if (GetLastError() == ERROR_NO_MORE_ITEMS)
|
|
return FALSE;
|
|
TRACE("SetupDiEnumDriverInfo() failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
IsDots(IN LPCWSTR str)
|
|
{
|
|
if(wcscmp(str, L".") && wcscmp(str, L"..")) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static LPCWSTR
|
|
GetFileExt(IN LPWSTR FileName)
|
|
{
|
|
LPCWSTR Dot;
|
|
|
|
Dot = wcsrchr(FileName, '.');
|
|
if (!Dot)
|
|
return L"";
|
|
|
|
return Dot;
|
|
}
|
|
|
|
static BOOL
|
|
SearchDriverRecursive(
|
|
IN PDEVINSTDATA DevInstData,
|
|
IN LPCWSTR Path)
|
|
{
|
|
WIN32_FIND_DATAW wfd;
|
|
WCHAR DirPath[MAX_PATH];
|
|
WCHAR FileName[MAX_PATH];
|
|
WCHAR FullPath[MAX_PATH];
|
|
WCHAR LastDirPath[MAX_PATH] = L"";
|
|
WCHAR PathWithPattern[MAX_PATH];
|
|
BOOL ok = TRUE;
|
|
BOOL retval = FALSE;
|
|
HANDLE hFindFile = INVALID_HANDLE_VALUE;
|
|
|
|
wcscpy(DirPath, Path);
|
|
|
|
if (DirPath[wcslen(DirPath) - 1] != '\\')
|
|
wcscat(DirPath, L"\\");
|
|
|
|
wcscpy(PathWithPattern, DirPath);
|
|
wcscat(PathWithPattern, L"\\*");
|
|
|
|
for (hFindFile = FindFirstFileW(PathWithPattern, &wfd);
|
|
ok && hFindFile != INVALID_HANDLE_VALUE;
|
|
ok = FindNextFileW(hFindFile, &wfd))
|
|
{
|
|
|
|
wcscpy(FileName, wfd.cFileName);
|
|
if (IsDots(FileName))
|
|
continue;
|
|
|
|
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
/* Recursive search */
|
|
wcscpy(FullPath, DirPath);
|
|
wcscat(FullPath, FileName);
|
|
if (SearchDriverRecursive(DevInstData, FullPath))
|
|
{
|
|
retval = TRUE;
|
|
/* We continue the search for a better driver */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LPCWSTR pszExtension = GetFileExt(FileName);
|
|
|
|
if ((_wcsicmp(pszExtension, L".inf") == 0) && (wcscmp(LastDirPath, DirPath) != 0))
|
|
{
|
|
wcscpy(LastDirPath, DirPath);
|
|
|
|
if (wcslen(DirPath) > MAX_PATH)
|
|
/* Path is too long to be searched */
|
|
continue;
|
|
|
|
if (SearchDriver(DevInstData, DirPath, NULL))
|
|
{
|
|
retval = TRUE;
|
|
/* We continue the search for a better driver */
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hFindFile != INVALID_HANDLE_VALUE)
|
|
FindClose(hFindFile);
|
|
return retval;
|
|
}
|
|
|
|
BOOL
|
|
ScanFoldersForDriver(
|
|
IN PDEVINSTDATA DevInstData)
|
|
{
|
|
BOOL result;
|
|
|
|
/* Search in default location */
|
|
result = SearchDriver(DevInstData, NULL, NULL);
|
|
|
|
if (DevInstData->CustomSearchPath)
|
|
{
|
|
/* Search only in specified paths */
|
|
/* We need to check all specified directories to be
|
|
* sure to find the best driver for the device.
|
|
*/
|
|
LPCWSTR Path;
|
|
for (Path = DevInstData->CustomSearchPath; *Path != '\0'; Path += wcslen(Path) + 1)
|
|
{
|
|
TRACE("Search driver in %s\n", debugstr_w(Path));
|
|
if (wcslen(Path) == 2 && Path[1] == ':')
|
|
{
|
|
if (SearchDriverRecursive(DevInstData, Path))
|
|
result = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (SearchDriver(DevInstData, Path, NULL))
|
|
result = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOL
|
|
PrepareFoldersToScan(
|
|
IN PDEVINSTDATA DevInstData,
|
|
IN BOOL IncludeRemovableDevices,
|
|
IN BOOL IncludeCustomPath,
|
|
IN HWND hwndCombo OPTIONAL)
|
|
{
|
|
WCHAR drive[] = {'?',':',0};
|
|
DWORD dwDrives = 0;
|
|
DWORD i;
|
|
UINT nType;
|
|
DWORD CustomTextLength = 0;
|
|
DWORD LengthNeeded = 0;
|
|
LPWSTR Buffer;
|
|
|
|
/* Calculate length needed to store the search paths */
|
|
if (IncludeRemovableDevices)
|
|
{
|
|
dwDrives = GetLogicalDrives();
|
|
for (drive[0] = 'A', i = 1; drive[0] <= 'Z'; drive[0]++, i <<= 1)
|
|
{
|
|
if (dwDrives & i)
|
|
{
|
|
nType = GetDriveTypeW(drive);
|
|
if (nType == DRIVE_REMOVABLE || nType == DRIVE_CDROM)
|
|
{
|
|
LengthNeeded += 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (IncludeCustomPath)
|
|
{
|
|
CustomTextLength = 1 + ComboBox_GetTextLength(hwndCombo);
|
|
LengthNeeded += CustomTextLength;
|
|
}
|
|
|
|
/* Allocate space for search paths */
|
|
HeapFree(GetProcessHeap(), 0, DevInstData->CustomSearchPath);
|
|
DevInstData->CustomSearchPath = Buffer = HeapAlloc(
|
|
GetProcessHeap(),
|
|
0,
|
|
(LengthNeeded + 1) * sizeof(WCHAR));
|
|
if (!Buffer)
|
|
{
|
|
TRACE("HeapAlloc() failed\n");
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Fill search paths */
|
|
if (IncludeRemovableDevices)
|
|
{
|
|
for (drive[0] = 'A', i = 1; drive[0] <= 'Z'; drive[0]++, i <<= 1)
|
|
{
|
|
if (dwDrives & i)
|
|
{
|
|
nType = GetDriveTypeW(drive);
|
|
if (nType == DRIVE_REMOVABLE || nType == DRIVE_CDROM)
|
|
{
|
|
Buffer += 1 + swprintf(Buffer, drive);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (IncludeCustomPath)
|
|
{
|
|
Buffer += 1 + GetWindowTextW(hwndCombo, Buffer, CustomTextLength);
|
|
}
|
|
*Buffer = '\0';
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
InstallCurrentDriver(
|
|
IN PDEVINSTDATA DevInstData)
|
|
{
|
|
BOOL ret;
|
|
|
|
TRACE("Installing driver %s: %s\n",
|
|
debugstr_w(DevInstData->drvInfoData.MfgName),
|
|
debugstr_w(DevInstData->drvInfoData.Description));
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_SELECTBESTCOMPATDRV,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_ALLOW_INSTALL,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_ALLOW_INSTALL) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_NEWDEVICEWIZARD_PREANALYZE,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_PREANALYZE) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_NEWDEVICEWIZARD_POSTANALYZE,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_POSTANALYZE) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_INSTALLDEVICEFILES,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_REGISTER_COINSTALLERS,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_INSTALLINTERFACES,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_INSTALLINTERFACES) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_INSTALLDEVICE,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_NEWDEVICEWIZARD_FINISHINSTALL,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCallClassInstaller(
|
|
DIF_DESTROYPRIVATEDATA,
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiCallClassInstaller(DIF_DESTROYPRIVATEDATA) failed with error 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
DevInstallW(
|
|
IN HWND hWndParent,
|
|
IN HINSTANCE hInstance,
|
|
IN LPCWSTR InstanceId,
|
|
IN INT Show)
|
|
{
|
|
PDEVINSTDATA DevInstData = NULL;
|
|
BOOL ret;
|
|
DWORD config_flags;
|
|
BOOL retval = FALSE;
|
|
|
|
TRACE("(%p, %p, %s, %d)\n", hWndParent, hInstance, debugstr_w(InstanceId), Show);
|
|
|
|
if (!IsUserAdmin())
|
|
{
|
|
/* XP kills the process... */
|
|
ExitProcess(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
DevInstData = HeapAlloc(GetProcessHeap(), 0, sizeof(DEVINSTDATA));
|
|
if (!DevInstData)
|
|
{
|
|
TRACE("HeapAlloc() failed\n");
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Clear devinst data */
|
|
ZeroMemory(DevInstData, sizeof(DEVINSTDATA));
|
|
DevInstData->devInfoData.cbSize = 0; /* Tell if the devInfoData is valid */
|
|
|
|
/* Fill devinst data */
|
|
DevInstData->hDevInfo = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL);
|
|
if (DevInstData->hDevInfo == INVALID_HANDLE_VALUE)
|
|
{
|
|
TRACE("SetupDiCreateDeviceInfoListExW() failed with error 0x%x\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
DevInstData->devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
ret = SetupDiOpenDeviceInfoW(
|
|
DevInstData->hDevInfo,
|
|
InstanceId,
|
|
NULL,
|
|
0, /* Open flags */
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiOpenDeviceInfoW() failed with error 0x%x (InstanceId %s)\n",
|
|
GetLastError(), debugstr_w(InstanceId));
|
|
DevInstData->devInfoData.cbSize = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
ret = SetupDiGetDeviceRegistryProperty(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
SPDRP_DEVICEDESC,
|
|
&DevInstData->regDataType,
|
|
NULL, 0,
|
|
&DevInstData->requiredSize);
|
|
|
|
if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && DevInstData->regDataType == REG_SZ)
|
|
{
|
|
DevInstData->buffer = HeapAlloc(GetProcessHeap(), 0, DevInstData->requiredSize);
|
|
if (!DevInstData->buffer)
|
|
{
|
|
TRACE("HeapAlloc() failed\n");
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
else
|
|
{
|
|
ret = SetupDiGetDeviceRegistryPropertyW(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
SPDRP_DEVICEDESC,
|
|
&DevInstData->regDataType,
|
|
DevInstData->buffer, DevInstData->requiredSize,
|
|
&DevInstData->requiredSize);
|
|
}
|
|
}
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiGetDeviceRegistryProperty() failed with error 0x%x (InstanceId %s)\n",
|
|
GetLastError(), debugstr_w(InstanceId));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (SetupDiGetDeviceRegistryPropertyW(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
SPDRP_CONFIGFLAGS,
|
|
NULL,
|
|
(BYTE *)&config_flags,
|
|
sizeof(config_flags),
|
|
NULL))
|
|
{
|
|
if (config_flags & CONFIGFLAG_FAILEDINSTALL)
|
|
{
|
|
/* The device is disabled */
|
|
TRACE("Device is disabled\n");
|
|
retval = TRUE;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
TRACE("Installing %s (%s)\n", debugstr_w((PCWSTR)DevInstData->buffer), debugstr_w(InstanceId));
|
|
|
|
/* Search driver in default location and removable devices */
|
|
if (!PrepareFoldersToScan(DevInstData, FALSE, FALSE, NULL))
|
|
{
|
|
TRACE("PrepareFoldersToScan() failed with error 0x%lx\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
if (ScanFoldersForDriver(DevInstData))
|
|
{
|
|
/* Driver found ; install it */
|
|
retval = InstallCurrentDriver(DevInstData);
|
|
TRACE("InstallCurrentDriver() returned %d\n", retval);
|
|
if (retval && Show != SW_HIDE)
|
|
{
|
|
/* Should we display the 'Need to reboot' page? */
|
|
SP_DEVINSTALL_PARAMS installParams;
|
|
installParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
&installParams))
|
|
{
|
|
if (installParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))
|
|
{
|
|
TRACE("Displaying 'Reboot' wizard page\n");
|
|
retval = DisplayWizard(DevInstData, hWndParent, IDD_NEEDREBOOT);
|
|
}
|
|
}
|
|
}
|
|
goto cleanup;
|
|
}
|
|
else if (Show == SW_HIDE)
|
|
{
|
|
/* We can't show the wizard. Fail the install */
|
|
TRACE("No wizard\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Prepare the wizard, and display it */
|
|
TRACE("Need to show install wizard\n");
|
|
retval = DisplayWizard(DevInstData, hWndParent, IDD_WELCOMEPAGE);
|
|
|
|
cleanup:
|
|
if (DevInstData)
|
|
{
|
|
if (DevInstData->devInfoData.cbSize != 0)
|
|
{
|
|
if (!SetupDiDestroyDriverInfoList(DevInstData->hDevInfo, &DevInstData->devInfoData, SPDIT_COMPATDRIVER))
|
|
TRACE("SetupDiDestroyDriverInfoList() failed with error 0x%lx\n", GetLastError());
|
|
}
|
|
if (DevInstData->hDevInfo != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!SetupDiDestroyDeviceInfoList(DevInstData->hDevInfo))
|
|
TRACE("SetupDiDestroyDeviceInfoList() failed with error 0x%lx\n", GetLastError());
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, DevInstData->buffer);
|
|
HeapFree(GetProcessHeap(), 0, DevInstData);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
InstallDevInstEx(
|
|
IN HWND hWndParent,
|
|
IN LPCWSTR InstanceId,
|
|
IN BOOL bUpdate,
|
|
OUT LPDWORD lpReboot,
|
|
IN DWORD Unknown)
|
|
{
|
|
PDEVINSTDATA DevInstData = NULL;
|
|
BOOL ret;
|
|
BOOL retval = FALSE;
|
|
|
|
TRACE("InstllDevInstEx(%p, %s, %d, %p, %lx)\n",
|
|
hWndParent, debugstr_w(InstanceId), bUpdate, lpReboot, Unknown);
|
|
|
|
DevInstData = HeapAlloc(GetProcessHeap(), 0, sizeof(DEVINSTDATA));
|
|
if (!DevInstData)
|
|
{
|
|
TRACE("HeapAlloc() failed\n");
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Clear devinst data */
|
|
ZeroMemory(DevInstData, sizeof(DEVINSTDATA));
|
|
DevInstData->devInfoData.cbSize = 0; /* Tell if the devInfoData is valid */
|
|
DevInstData->bUpdate = bUpdate;
|
|
|
|
/* Fill devinst data */
|
|
DevInstData->hDevInfo = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL);
|
|
if (DevInstData->hDevInfo == INVALID_HANDLE_VALUE)
|
|
{
|
|
TRACE("SetupDiCreateDeviceInfoListExW() failed with error 0x%x\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
DevInstData->devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
ret = SetupDiOpenDeviceInfoW(
|
|
DevInstData->hDevInfo,
|
|
InstanceId,
|
|
NULL,
|
|
0, /* Open flags */
|
|
&DevInstData->devInfoData);
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiOpenDeviceInfoW() failed with error 0x%x (InstanceId %s)\n",
|
|
GetLastError(), debugstr_w(InstanceId));
|
|
DevInstData->devInfoData.cbSize = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
ret = SetupDiGetDeviceRegistryProperty(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
SPDRP_DEVICEDESC,
|
|
&DevInstData->regDataType,
|
|
NULL, 0,
|
|
&DevInstData->requiredSize);
|
|
|
|
if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && DevInstData->regDataType == REG_SZ)
|
|
{
|
|
DevInstData->buffer = HeapAlloc(GetProcessHeap(), 0, DevInstData->requiredSize);
|
|
if (!DevInstData->buffer)
|
|
{
|
|
TRACE("HeapAlloc() failed\n");
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
else
|
|
{
|
|
ret = SetupDiGetDeviceRegistryPropertyW(
|
|
DevInstData->hDevInfo,
|
|
&DevInstData->devInfoData,
|
|
SPDRP_DEVICEDESC,
|
|
&DevInstData->regDataType,
|
|
DevInstData->buffer, DevInstData->requiredSize,
|
|
&DevInstData->requiredSize);
|
|
}
|
|
}
|
|
|
|
if (!ret)
|
|
{
|
|
TRACE("SetupDiGetDeviceRegistryProperty() failed with error 0x%x (InstanceId %s)\n",
|
|
GetLastError(), debugstr_w(InstanceId));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Prepare the wizard, and display it */
|
|
TRACE("Need to show install wizard\n");
|
|
retval = DisplayWizard(DevInstData, hWndParent, IDD_WELCOMEPAGE);
|
|
|
|
cleanup:
|
|
if (DevInstData)
|
|
{
|
|
if (DevInstData->devInfoData.cbSize != 0)
|
|
{
|
|
if (!SetupDiDestroyDriverInfoList(DevInstData->hDevInfo, &DevInstData->devInfoData, SPDIT_COMPATDRIVER))
|
|
TRACE("SetupDiDestroyDriverInfoList() failed with error 0x%lx\n", GetLastError());
|
|
}
|
|
if (DevInstData->hDevInfo != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!SetupDiDestroyDeviceInfoList(DevInstData->hDevInfo))
|
|
TRACE("SetupDiDestroyDeviceInfoList() failed with error 0x%lx\n", GetLastError());
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, DevInstData->buffer);
|
|
HeapFree(GetProcessHeap(), 0, DevInstData);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
InstallDevInst(
|
|
IN HWND hWndParent,
|
|
IN LPCWSTR InstanceId,
|
|
IN BOOL bUpdate,
|
|
OUT LPDWORD lpReboot)
|
|
{
|
|
return InstallDevInstEx(hWndParent, InstanceId, bUpdate, lpReboot, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
ClientSideInstallW(
|
|
IN HWND hWndOwner,
|
|
IN HINSTANCE hInstance,
|
|
IN LPWSTR lpNamedPipeName,
|
|
IN INT Show)
|
|
{
|
|
BOOL ReturnValue = FALSE;
|
|
BOOL ShowWizard;
|
|
DWORD BytesRead;
|
|
DWORD Value;
|
|
HANDLE hPipe = INVALID_HANDLE_VALUE;
|
|
PWSTR DeviceInstance = NULL;
|
|
PWSTR InstallEventName = NULL;
|
|
HANDLE hInstallEvent;
|
|
|
|
/* Open the pipe */
|
|
hPipe = CreateFileW(lpNamedPipeName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if(hPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
ERR("CreateFileW failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Read the data. Some is just included for compatibility with Windows right now and not yet used by ReactOS.
|
|
See umpnpmgr for more details. */
|
|
if(!ReadFile(hPipe, &Value, sizeof(Value), &BytesRead, NULL))
|
|
{
|
|
ERR("ReadFile failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
InstallEventName = (PWSTR)HeapAlloc(GetProcessHeap(), 0, Value);
|
|
|
|
if(!ReadFile(hPipe, InstallEventName, Value, &BytesRead, NULL))
|
|
{
|
|
ERR("ReadFile failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
/* I couldn't figure out what the following value means under Windows XP.
|
|
Therefore I used it in umpnpmgr to pass the ShowWizard variable. */
|
|
if(!ReadFile(hPipe, &ShowWizard, sizeof(ShowWizard), &BytesRead, NULL))
|
|
{
|
|
ERR("ReadFile failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Next one is again size in bytes of the following string */
|
|
if(!ReadFile(hPipe, &Value, sizeof(Value), &BytesRead, NULL))
|
|
{
|
|
ERR("ReadFile failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
DeviceInstance = (PWSTR)HeapAlloc(GetProcessHeap(), 0, Value);
|
|
|
|
if(!ReadFile(hPipe, DeviceInstance, Value, &BytesRead, NULL))
|
|
{
|
|
ERR("ReadFile failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
ReturnValue = DevInstallW(NULL, NULL, DeviceInstance, ShowWizard ? SW_SHOWNOACTIVATE : SW_HIDE);
|
|
if(!ReturnValue)
|
|
{
|
|
ERR("DevInstallW failed with error %lu\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
hInstallEvent = CreateEventW(NULL, TRUE, FALSE, InstallEventName);
|
|
if(!hInstallEvent)
|
|
{
|
|
TRACE("CreateEventW('%ls') failed with error %lu\n", InstallEventName, GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
SetEvent(hInstallEvent);
|
|
CloseHandle(hInstallEvent);
|
|
|
|
cleanup:
|
|
if(hPipe != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hPipe);
|
|
|
|
if(InstallEventName)
|
|
HeapFree(GetProcessHeap(), 0, InstallEventName);
|
|
|
|
if(DeviceInstance)
|
|
HeapFree(GetProcessHeap(), 0, DeviceInstance);
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
DllMain(
|
|
IN HINSTANCE hInstance,
|
|
IN DWORD dwReason,
|
|
IN LPVOID lpReserved)
|
|
{
|
|
if (dwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
INITCOMMONCONTROLSEX InitControls;
|
|
|
|
DisableThreadLibraryCalls(hInstance);
|
|
|
|
InitControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
|
InitControls.dwICC = ICC_PROGRESS_CLASS;
|
|
InitCommonControlsEx(&InitControls);
|
|
hDllInstance = hInstance;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|