mirror of
https://github.com/reactos/reactos.git
synced 2024-08-20 18:35:32 +00:00
![Andreas Maier](/assets/img/avatar_default.png)
Rename SetupDiSetDeviceRegistryPropertyW to IntSetupDiSetDeviceRegistryPropertyAW and use it for both (A/W). The FIXME in the A-Version was already implemented in the W-Version. CORE-14173.
5967 lines
193 KiB
C
5967 lines
193 KiB
C
/*
|
|
* SetupAPI device installer
|
|
*
|
|
* Copyright 2000 Andreas Mohr for CodeWeavers
|
|
* 2005-2006 Hervé Poussineau (hpoussin@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 St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "setupapi_private.h"
|
|
|
|
/* Unicode constants */
|
|
static const WCHAR BackSlash[] = {'\\',0};
|
|
static const WCHAR DateFormat[] = {'%','u','-','%','u','-','%','u',0};
|
|
static const WCHAR DotCoInstallers[] = {'.','C','o','I','n','s','t','a','l','l','e','r','s',0};
|
|
static const WCHAR DotHW[] = {'.','H','W',0};
|
|
static const WCHAR DotServices[] = {'.','S','e','r','v','i','c','e','s',0};
|
|
static const WCHAR InfDirectory[] = {'i','n','f','\\',0};
|
|
static const WCHAR InstanceKeyFormat[] = {'%','0','4','l','u',0};
|
|
static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
|
|
static const WCHAR VersionFormat[] = {'%','u','.','%','u','.','%','u','.','%','u',0};
|
|
|
|
static const WCHAR REGSTR_DRIVER_DATE[] = {'D','r','i','v','e','r','D','a','t','e',0};
|
|
static const WCHAR REGSTR_DRIVER_DATE_DATA[] = {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
|
|
static const WCHAR REGSTR_DRIVER_VERSION[] = {'D','r','i','v','e','r','V','e','r','s','i','o','n',0};
|
|
static const WCHAR REGSTR_SECURITY[] = {'S','e','c','u','r','i','t','y',0};
|
|
static const WCHAR REGSTR_UI_NUMBER_DESC_FORMAT[] = {'U','I','N','u','m','b','e','r','D','e','s','c','F','o','r','m','a','t',0};
|
|
|
|
typedef DWORD
|
|
(CALLBACK* CLASS_INSTALL_PROC) (
|
|
IN DI_FUNCTION InstallFunction,
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL);
|
|
typedef BOOL
|
|
(WINAPI* DEFAULT_CLASS_INSTALL_PROC) (
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN OUT PSP_DEVINFO_DATA DeviceInfoData);
|
|
typedef DWORD
|
|
(CALLBACK* COINSTALLER_PROC) (
|
|
IN DI_FUNCTION InstallFunction,
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
|
|
IN OUT PCOINSTALLER_CONTEXT_DATA Context);
|
|
|
|
struct CoInstallerElement
|
|
{
|
|
LIST_ENTRY ListEntry;
|
|
|
|
HMODULE Module;
|
|
COINSTALLER_PROC Function;
|
|
BOOL DoPostProcessing;
|
|
PVOID PrivateData;
|
|
};
|
|
|
|
struct GetSectionCallbackInfo
|
|
{
|
|
PSP_ALTPLATFORM_INFO PlatformInfo;
|
|
BYTE ProductType;
|
|
WORD SuiteMask;
|
|
DWORD PrefixLength;
|
|
WCHAR BestSection[LINE_LEN + 1];
|
|
DWORD BestScore1, BestScore2, BestScore3, BestScore4, BestScore5;
|
|
};
|
|
|
|
|
|
|
|
static void SETUPDI_GuidToString(const GUID *guid, LPWSTR guidStr)
|
|
{
|
|
static const WCHAR fmt[] = {'{','%','0','8','X','-','%','0','4','X','-',
|
|
'%','0','4','X','-','%','0','2','X','%','0','2','X','-','%','0','2',
|
|
'X','%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X','%',
|
|
'0','2','X','}',0};
|
|
|
|
sprintfW(guidStr, fmt, guid->Data1, guid->Data2, guid->Data3,
|
|
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
|
|
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
|
|
}
|
|
|
|
static DWORD
|
|
GetErrorCodeFromCrCode(const IN CONFIGRET cr)
|
|
{
|
|
switch (cr)
|
|
{
|
|
case CR_ACCESS_DENIED: return ERROR_ACCESS_DENIED;
|
|
case CR_BUFFER_SMALL: return ERROR_INSUFFICIENT_BUFFER;
|
|
case CR_CALL_NOT_IMPLEMENTED: return ERROR_CALL_NOT_IMPLEMENTED;
|
|
case CR_FAILURE: return ERROR_GEN_FAILURE;
|
|
case CR_INVALID_DATA: return ERROR_INVALID_USER_BUFFER;
|
|
case CR_INVALID_DEVICE_ID: return ERROR_INVALID_PARAMETER;
|
|
case CR_INVALID_MACHINENAME: return ERROR_INVALID_COMPUTERNAME;
|
|
case CR_INVALID_DEVNODE: return ERROR_INVALID_PARAMETER;
|
|
case CR_INVALID_FLAG: return ERROR_INVALID_FLAGS;
|
|
case CR_INVALID_POINTER: return ERROR_INVALID_PARAMETER;
|
|
case CR_INVALID_PROPERTY: return ERROR_INVALID_PARAMETER;
|
|
case CR_NO_SUCH_DEVNODE: return ERROR_FILE_NOT_FOUND;
|
|
case CR_NO_SUCH_REGISTRY_KEY: return ERROR_FILE_NOT_FOUND;
|
|
case CR_NO_SUCH_VALUE: return ERROR_FILE_NOT_FOUND;
|
|
case CR_OUT_OF_MEMORY: return ERROR_NOT_ENOUGH_MEMORY;
|
|
case CR_REGISTRY_ERROR: return ERROR_GEN_FAILURE;
|
|
case CR_ALREADY_SUCH_DEVINST: return ERROR_DEVINST_ALREADY_EXISTS;
|
|
case CR_SUCCESS: return ERROR_SUCCESS;
|
|
default: return ERROR_GEN_FAILURE;
|
|
}
|
|
|
|
/* Does not happen */
|
|
}
|
|
|
|
/* Lower scores are best ones */
|
|
static BOOL
|
|
CheckSectionValid(
|
|
IN LPCWSTR SectionName,
|
|
IN PSP_ALTPLATFORM_INFO PlatformInfo,
|
|
IN BYTE ProductType,
|
|
IN WORD SuiteMask,
|
|
OUT PDWORD ScorePlatform,
|
|
OUT PDWORD ScoreMajorVersion,
|
|
OUT PDWORD ScoreMinorVersion,
|
|
OUT PDWORD ScoreProductType,
|
|
OUT PDWORD ScoreSuiteMask)
|
|
{
|
|
LPWSTR Section = NULL;
|
|
//LPCWSTR pExtensionPlatform;
|
|
LPCWSTR pExtensionArchitecture;
|
|
LPWSTR Fields[6];
|
|
DWORD i;
|
|
BOOL ret = FALSE;
|
|
|
|
//static const WCHAR ExtensionPlatformNone[] = {'.',0};
|
|
static const WCHAR ExtensionPlatformNT[] = {'.','N','T',0};
|
|
static const WCHAR ExtensionPlatformWindows[] = {'.','W','i','n',0};
|
|
|
|
static const WCHAR ExtensionArchitectureNone[] = {0};
|
|
static const WCHAR ExtensionArchitecturealpha[] = {'a','l','p','h','a',0};
|
|
static const WCHAR ExtensionArchitectureamd64[] = {'A','M','D','6','4',0};
|
|
static const WCHAR ExtensionArchitectureia64[] = {'I','A','6','4',0};
|
|
static const WCHAR ExtensionArchitecturemips[] = {'m','i','p','s',0};
|
|
static const WCHAR ExtensionArchitectureppc[] = {'p','p','c',0};
|
|
static const WCHAR ExtensionArchitecturex86[] = {'x','8','6',0};
|
|
|
|
TRACE("%s %p 0x%x 0x%x\n",
|
|
debugstr_w(SectionName), PlatformInfo, ProductType, SuiteMask);
|
|
|
|
*ScorePlatform = *ScoreMajorVersion = *ScoreMinorVersion = *ScoreProductType = *ScoreSuiteMask = 0;
|
|
|
|
Section = pSetupDuplicateString(SectionName);
|
|
if (!Section)
|
|
{
|
|
TRACE("pSetupDuplicateString() failed\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Set various extensions values */
|
|
switch (PlatformInfo->Platform)
|
|
{
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
//pExtensionPlatform = ExtensionPlatformWindows;
|
|
break;
|
|
case VER_PLATFORM_WIN32_NT:
|
|
//pExtensionPlatform = ExtensionPlatformNT;
|
|
break;
|
|
default:
|
|
ERR("Unknown platform 0x%lx\n", PlatformInfo->Platform);
|
|
//pExtensionPlatform = ExtensionPlatformNone;
|
|
break;
|
|
}
|
|
switch (PlatformInfo->ProcessorArchitecture)
|
|
{
|
|
case PROCESSOR_ARCHITECTURE_ALPHA:
|
|
pExtensionArchitecture = ExtensionArchitecturealpha;
|
|
break;
|
|
case PROCESSOR_ARCHITECTURE_AMD64:
|
|
pExtensionArchitecture = ExtensionArchitectureamd64;
|
|
break;
|
|
case PROCESSOR_ARCHITECTURE_IA64:
|
|
pExtensionArchitecture = ExtensionArchitectureia64;
|
|
break;
|
|
case PROCESSOR_ARCHITECTURE_INTEL:
|
|
pExtensionArchitecture = ExtensionArchitecturex86;
|
|
break;
|
|
case PROCESSOR_ARCHITECTURE_MIPS:
|
|
pExtensionArchitecture = ExtensionArchitecturemips;
|
|
break;
|
|
case PROCESSOR_ARCHITECTURE_PPC:
|
|
pExtensionArchitecture = ExtensionArchitectureppc;
|
|
break;
|
|
default:
|
|
ERR("Unknown processor architecture 0x%x\n", PlatformInfo->ProcessorArchitecture);
|
|
case PROCESSOR_ARCHITECTURE_UNKNOWN:
|
|
pExtensionArchitecture = ExtensionArchitectureNone;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Field[0] Platform
|
|
* Field[1] Architecture
|
|
* Field[2] Major version
|
|
* Field[3] Minor version
|
|
* Field[4] Product type
|
|
* Field[5] Suite mask
|
|
* Remark: these fields may be NULL if the information is not provided
|
|
*/
|
|
Fields[0] = Section;
|
|
if (Fields[0] == NULL)
|
|
{
|
|
TRACE("No extension found\n");
|
|
*ScorePlatform = *ScoreMajorVersion = *ScoreMinorVersion = *ScoreProductType = *ScoreSuiteMask = ULONG_MAX;
|
|
ret = TRUE;
|
|
goto cleanup;
|
|
}
|
|
Fields[1] = Fields[0] + 1;
|
|
Fields[2] = Fields[3] = Fields[4] = Fields[5] = NULL;
|
|
for (i = 2; Fields[i - 1] != NULL && i < 6; i++)
|
|
{
|
|
Fields[i] = wcschr(Fields[i - 1], '.');
|
|
if (Fields[i])
|
|
{
|
|
Fields[i]++;
|
|
*(Fields[i] - 1) = UNICODE_NULL;
|
|
}
|
|
}
|
|
/* Take care of first 2 fields */
|
|
if (strncmpiW(Fields[0], ExtensionPlatformWindows, strlenW(ExtensionPlatformWindows)) == 0)
|
|
{
|
|
if (PlatformInfo->Platform != VER_PLATFORM_WIN32_WINDOWS)
|
|
{
|
|
TRACE("Mismatch on platform field\n");
|
|
goto cleanup;
|
|
}
|
|
Fields[1] += wcslen(ExtensionPlatformWindows) - 1;
|
|
}
|
|
else if (strncmpiW(Fields[0], ExtensionPlatformNT, strlenW(ExtensionPlatformNT)) == 0)
|
|
{
|
|
if (PlatformInfo->Platform != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
TRACE("Mismatch on platform field\n");
|
|
goto cleanup;
|
|
}
|
|
Fields[1] += wcslen(ExtensionPlatformNT) - 1;
|
|
}
|
|
else
|
|
{
|
|
/* No platform specified */
|
|
*ScorePlatform |= 0x02;
|
|
}
|
|
if (strcmpiW(Fields[1], ExtensionArchitectureNone) == 0)
|
|
{
|
|
/* No architecture specified */
|
|
*ScorePlatform |= 0x01;
|
|
}
|
|
else if (strcmpiW(Fields[1], pExtensionArchitecture) != 0)
|
|
{
|
|
TRACE("Mismatch on architecture field ('%s' and '%s')\n",
|
|
debugstr_w(Fields[1]), debugstr_w(pExtensionArchitecture));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Check if informations are matching */
|
|
if (Fields[2] && *Fields[2])
|
|
{
|
|
DWORD MajorVersion, MinorVersion = 0;
|
|
MajorVersion = strtoulW(Fields[2], NULL, 0);
|
|
if ((MajorVersion == 0 || MajorVersion == ULONG_MAX) &&
|
|
(errno == ERANGE || errno == EINVAL))
|
|
{
|
|
TRACE("Wrong MajorVersion ('%s')\n", debugstr_w(Fields[2]));
|
|
goto cleanup;
|
|
}
|
|
if (Fields[3] && *Fields[3])
|
|
{
|
|
MinorVersion = strtoulW(Fields[3], NULL, 0);
|
|
if ((MinorVersion == 0 || MinorVersion == ULONG_MAX) &&
|
|
(errno == ERANGE || errno == EINVAL))
|
|
{
|
|
TRACE("Wrong MinorVersion ('%s')\n", debugstr_w(Fields[3]));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (PlatformInfo->MajorVersion < MajorVersion ||
|
|
(PlatformInfo->MajorVersion == MajorVersion && PlatformInfo->MinorVersion < MinorVersion))
|
|
{
|
|
TRACE("Mismatch on version field (%lu.%lu and %lu.%lu)\n",
|
|
MajorVersion, MinorVersion, PlatformInfo->MajorVersion, PlatformInfo->MinorVersion);
|
|
goto cleanup;
|
|
}
|
|
*ScoreMajorVersion = MajorVersion - PlatformInfo->MajorVersion;
|
|
if (MajorVersion == PlatformInfo->MajorVersion)
|
|
*ScoreMinorVersion = MinorVersion - PlatformInfo->MinorVersion;
|
|
else
|
|
*ScoreMinorVersion = MinorVersion;
|
|
}
|
|
else if (Fields[3] && *Fields[3])
|
|
{
|
|
TRACE("Minor version found without major version\n");
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
*ScoreMajorVersion = PlatformInfo->MajorVersion;
|
|
*ScoreMinorVersion = PlatformInfo->MinorVersion;
|
|
}
|
|
|
|
if (Fields[4] && *Fields[4])
|
|
{
|
|
DWORD CurrentProductType;
|
|
CurrentProductType = strtoulW(Fields[4], NULL, 0);
|
|
if ((CurrentProductType == 0 || CurrentProductType == ULONG_MAX) &&
|
|
(errno == ERANGE || errno == EINVAL))
|
|
{
|
|
TRACE("Wrong Product type ('%s')\n", debugstr_w(Fields[4]));
|
|
goto cleanup;
|
|
}
|
|
if (CurrentProductType != ProductType)
|
|
{
|
|
TRACE("Mismatch on product type (0x%08lx and 0x%08x)\n",
|
|
CurrentProductType, ProductType);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else
|
|
*ScoreProductType = 1;
|
|
|
|
if (Fields[5] && *Fields[5])
|
|
{
|
|
DWORD CurrentSuiteMask;
|
|
CurrentSuiteMask = strtoulW(Fields[5], NULL, 0);
|
|
if ((CurrentSuiteMask == 0 || CurrentSuiteMask == ULONG_MAX) &&
|
|
(errno == ERANGE || errno == EINVAL))
|
|
{
|
|
TRACE("Wrong Suite mask ('%s')\n", debugstr_w(Fields[5]));
|
|
goto cleanup;
|
|
}
|
|
if ((CurrentSuiteMask & ~SuiteMask) != 0)
|
|
{
|
|
TRACE("Mismatch on suite mask (0x%08lx and 0x%08x)\n",
|
|
CurrentSuiteMask, SuiteMask);
|
|
goto cleanup;
|
|
}
|
|
*ScoreSuiteMask = SuiteMask & ~CurrentSuiteMask;
|
|
}
|
|
else
|
|
*ScoreSuiteMask = SuiteMask;
|
|
|
|
ret = TRUE;
|
|
|
|
cleanup:
|
|
MyFree(Section);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL
|
|
GetSectionCallback(
|
|
IN LPCWSTR SectionName,
|
|
IN PVOID Context)
|
|
{
|
|
struct GetSectionCallbackInfo *info = Context;
|
|
DWORD Score1, Score2, Score3, Score4, Score5;
|
|
BOOL ret;
|
|
|
|
if (SectionName[info->PrefixLength] != '.')
|
|
return TRUE;
|
|
|
|
ret = CheckSectionValid(
|
|
&SectionName[info->PrefixLength],
|
|
info->PlatformInfo,
|
|
info->ProductType,
|
|
info->SuiteMask,
|
|
&Score1, &Score2, &Score3, &Score4, &Score5);
|
|
if (!ret)
|
|
{
|
|
TRACE("Section %s not compatible\n", debugstr_w(SectionName));
|
|
return TRUE;
|
|
}
|
|
if (Score1 > info->BestScore1) goto done;
|
|
if (Score1 < info->BestScore1) goto bettersection;
|
|
if (Score2 > info->BestScore2) goto done;
|
|
if (Score2 < info->BestScore2) goto bettersection;
|
|
if (Score3 > info->BestScore3) goto done;
|
|
if (Score3 < info->BestScore3) goto bettersection;
|
|
if (Score4 > info->BestScore4) goto done;
|
|
if (Score4 < info->BestScore4) goto bettersection;
|
|
if (Score5 > info->BestScore5) goto done;
|
|
if (Score5 < info->BestScore5) goto bettersection;
|
|
goto done;
|
|
|
|
bettersection:
|
|
strcpyW(info->BestSection, SectionName);
|
|
info->BestScore1 = Score1;
|
|
info->BestScore2 = Score2;
|
|
info->BestScore3 = Score3;
|
|
info->BestScore4 = Score4;
|
|
info->BestScore5 = Score5;
|
|
|
|
done:
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetActualSectionToInstallExW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiGetActualSectionToInstallExW(
|
|
IN HINF InfHandle,
|
|
IN PCWSTR InfSectionName,
|
|
IN PSP_ALTPLATFORM_INFO AlternatePlatformInfo OPTIONAL,
|
|
OUT PWSTR InfSectionWithExt OPTIONAL,
|
|
IN DWORD InfSectionWithExtSize,
|
|
OUT PDWORD RequiredSize OPTIONAL,
|
|
OUT PWSTR* Extension OPTIONAL,
|
|
IN PVOID Reserved)
|
|
{
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %s %p %p %lu %p %p %p\n", InfHandle, debugstr_w(InfSectionName),
|
|
AlternatePlatformInfo, InfSectionWithExt, InfSectionWithExtSize,
|
|
RequiredSize, Extension, Reserved);
|
|
|
|
if (!InfHandle || InfHandle == (HINF)INVALID_HANDLE_VALUE)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (!InfSectionName)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (AlternatePlatformInfo && AlternatePlatformInfo->cbSize != sizeof(SP_ALTPLATFORM_INFO))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else if (Reserved != NULL)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else
|
|
{
|
|
static SP_ALTPLATFORM_INFO CurrentPlatform = { 0, };
|
|
static BYTE CurrentProductType = 0;
|
|
static WORD CurrentSuiteMask = 0;
|
|
PSP_ALTPLATFORM_INFO pPlatformInfo = &CurrentPlatform;
|
|
struct GetSectionCallbackInfo CallbackInfo;
|
|
DWORD dwFullLength;
|
|
BYTE ProductType;
|
|
WORD SuiteMask;
|
|
|
|
/* Fill platform info if needed */
|
|
if (AlternatePlatformInfo)
|
|
{
|
|
pPlatformInfo = AlternatePlatformInfo;
|
|
ProductType = 0;
|
|
SuiteMask = 0;
|
|
}
|
|
else
|
|
{
|
|
if (CurrentPlatform.cbSize != sizeof(SP_ALTPLATFORM_INFO))
|
|
{
|
|
/* That's the first time we go here. We need to fill in the structure */
|
|
SYSTEM_INFO SystemInfo;
|
|
GetSystemInfo(&SystemInfo);
|
|
CurrentPlatform.cbSize = sizeof(SP_ALTPLATFORM_INFO);
|
|
CurrentPlatform.Platform = OsVersionInfo.dwPlatformId;
|
|
CurrentPlatform.MajorVersion = OsVersionInfo.dwMajorVersion;
|
|
CurrentPlatform.MinorVersion = OsVersionInfo.dwMinorVersion;
|
|
CurrentPlatform.ProcessorArchitecture = SystemInfo.wProcessorArchitecture;
|
|
CurrentPlatform.Reserved = 0;
|
|
CurrentProductType = OsVersionInfo.wProductType;
|
|
CurrentSuiteMask = OsVersionInfo.wSuiteMask;
|
|
}
|
|
ProductType = CurrentProductType;
|
|
SuiteMask = CurrentSuiteMask;
|
|
}
|
|
|
|
CallbackInfo.PlatformInfo = pPlatformInfo;
|
|
CallbackInfo.ProductType = ProductType;
|
|
CallbackInfo.SuiteMask = SuiteMask;
|
|
CallbackInfo.PrefixLength = strlenW(InfSectionName);
|
|
CallbackInfo.BestScore1 = ULONG_MAX;
|
|
CallbackInfo.BestScore2 = ULONG_MAX;
|
|
CallbackInfo.BestScore3 = ULONG_MAX;
|
|
CallbackInfo.BestScore4 = ULONG_MAX;
|
|
CallbackInfo.BestScore5 = ULONG_MAX;
|
|
strcpyW(CallbackInfo.BestSection, InfSectionName);
|
|
TRACE("EnumerateSectionsStartingWith(InfSectionName = %S)\n", InfSectionName);
|
|
if (!EnumerateSectionsStartingWith(
|
|
InfHandle,
|
|
InfSectionName,
|
|
GetSectionCallback,
|
|
&CallbackInfo))
|
|
{
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto done;
|
|
}
|
|
TRACE("CallbackInfo.BestSection = %S\n", CallbackInfo.BestSection);
|
|
|
|
dwFullLength = lstrlenW(CallbackInfo.BestSection);
|
|
if (RequiredSize != NULL)
|
|
*RequiredSize = dwFullLength + 1;
|
|
|
|
if (InfSectionWithExtSize > 0)
|
|
{
|
|
if (InfSectionWithExtSize < dwFullLength + 1)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
goto done;
|
|
}
|
|
strcpyW(InfSectionWithExt, CallbackInfo.BestSection);
|
|
if (Extension)
|
|
{
|
|
DWORD dwLength = lstrlenW(InfSectionName);
|
|
*Extension = (dwLength == dwFullLength) ? NULL : &InfSectionWithExt[dwLength];
|
|
}
|
|
}
|
|
|
|
ret = TRUE;
|
|
}
|
|
|
|
done:
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreateDeviceInfo(
|
|
IN struct DeviceInfoSet *list,
|
|
IN LPCWSTR InstancePath,
|
|
IN LPCGUID pClassGuid,
|
|
OUT struct DeviceInfo **pDeviceInfo)
|
|
{
|
|
DWORD size;
|
|
CONFIGRET cr;
|
|
struct DeviceInfo *deviceInfo;
|
|
|
|
*pDeviceInfo = NULL;
|
|
|
|
size = FIELD_OFFSET(struct DeviceInfo, Data) + (strlenW(InstancePath) + 1) * sizeof(WCHAR);
|
|
deviceInfo = HeapAlloc(GetProcessHeap(), 0, size);
|
|
if (!deviceInfo)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
ZeroMemory(deviceInfo, size);
|
|
|
|
cr = CM_Locate_DevNode_ExW(&deviceInfo->dnDevInst, (DEVINSTID_W)InstancePath, CM_LOCATE_DEVNODE_PHANTOM, list->hMachine);
|
|
if (cr != CR_SUCCESS)
|
|
{
|
|
SetLastError(GetErrorCodeFromCrCode(cr));
|
|
return FALSE;
|
|
}
|
|
|
|
deviceInfo->set = list;
|
|
deviceInfo->InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
|
|
strcpyW(deviceInfo->Data, InstancePath);
|
|
deviceInfo->instanceId = deviceInfo->Data;
|
|
deviceInfo->UniqueId = strrchrW(deviceInfo->Data, '\\');
|
|
deviceInfo->DeviceDescription = NULL;
|
|
memcpy(&deviceInfo->ClassGuid, pClassGuid, sizeof(GUID));
|
|
deviceInfo->CreationFlags = 0;
|
|
InitializeListHead(&deviceInfo->DriverListHead);
|
|
InitializeListHead(&deviceInfo->InterfaceListHead);
|
|
|
|
*pDeviceInfo = deviceInfo;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static BOOL
|
|
DestroyClassInstallParams(struct ClassInstallParams* installParams)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, installParams->PropChangeParams);
|
|
HeapFree(GetProcessHeap(), 0, installParams->AddPropertyPageData);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
DestroyDeviceInfo(struct DeviceInfo *deviceInfo)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
struct DriverInfoElement *driverInfo;
|
|
struct DeviceInterface *deviceInterface;
|
|
|
|
while (!IsListEmpty(&deviceInfo->DriverListHead))
|
|
{
|
|
ListEntry = RemoveHeadList(&deviceInfo->DriverListHead);
|
|
driverInfo = CONTAINING_RECORD(ListEntry, struct DriverInfoElement, ListEntry);
|
|
if (!DestroyDriverInfoElement(driverInfo))
|
|
return FALSE;
|
|
}
|
|
while (!IsListEmpty(&deviceInfo->InterfaceListHead))
|
|
{
|
|
ListEntry = RemoveHeadList(&deviceInfo->InterfaceListHead);
|
|
deviceInterface = CONTAINING_RECORD(ListEntry, struct DeviceInterface, ListEntry);
|
|
if (!DestroyDeviceInterface(deviceInterface))
|
|
return FALSE;
|
|
}
|
|
DestroyClassInstallParams(&deviceInfo->ClassInstallParams);
|
|
if (deviceInfo->hmodDevicePropPageProvider)
|
|
FreeLibrary(deviceInfo->hmodDevicePropPageProvider);
|
|
return HeapFree(GetProcessHeap(), 0, deviceInfo);
|
|
}
|
|
|
|
static BOOL
|
|
DestroyDeviceInfoSet(struct DeviceInfoSet* list)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
struct DeviceInfo *deviceInfo;
|
|
|
|
while (!IsListEmpty(&list->ListHead))
|
|
{
|
|
ListEntry = RemoveHeadList(&list->ListHead);
|
|
deviceInfo = CONTAINING_RECORD(ListEntry, struct DeviceInfo, ListEntry);
|
|
if (!DestroyDeviceInfo(deviceInfo))
|
|
return FALSE;
|
|
}
|
|
if (list->HKLM != HKEY_LOCAL_MACHINE)
|
|
RegCloseKey(list->HKLM);
|
|
CM_Disconnect_Machine(list->hMachine);
|
|
DestroyClassInstallParams(&list->ClassInstallParams);
|
|
if (list->hmodClassPropPageProvider)
|
|
FreeLibrary(list->hmodClassPropPageProvider);
|
|
return HeapFree(GetProcessHeap(), 0, list);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiBuildClassInfoList (SETUPAPI.@)
|
|
*
|
|
* Returns a list of setup class GUIDs that identify the classes
|
|
* that are installed on a local machine.
|
|
*
|
|
* PARAMS
|
|
* Flags [I] control exclusion of classes from the list.
|
|
* ClassGuidList [O] pointer to a GUID-typed array that receives a list of setup class GUIDs.
|
|
* ClassGuidListSize [I] The number of GUIDs in the array (ClassGuidList).
|
|
* RequiredSize [O] pointer, which receives the number of GUIDs that are returned.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE.
|
|
* Failure: FALSE.
|
|
*/
|
|
BOOL WINAPI SetupDiBuildClassInfoList(
|
|
DWORD Flags,
|
|
LPGUID ClassGuidList,
|
|
DWORD ClassGuidListSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
TRACE("\n");
|
|
return SetupDiBuildClassInfoListExW(Flags, ClassGuidList,
|
|
ClassGuidListSize, RequiredSize,
|
|
NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiBuildClassInfoListExA (SETUPAPI.@)
|
|
*
|
|
* Returns a list of setup class GUIDs that identify the classes
|
|
* that are installed on a local or remote machine.
|
|
*
|
|
* PARAMS
|
|
* Flags [I] control exclusion of classes from the list.
|
|
* ClassGuidList [O] pointer to a GUID-typed array that receives a list of setup class GUIDs.
|
|
* ClassGuidListSize [I] The number of GUIDs in the array (ClassGuidList).
|
|
* RequiredSize [O] pointer, which receives the number of GUIDs that are returned.
|
|
* MachineName [I] name of a remote machine.
|
|
* Reserved [I] must be NULL.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE.
|
|
* Failure: FALSE.
|
|
*/
|
|
BOOL WINAPI SetupDiBuildClassInfoListExA(
|
|
DWORD Flags,
|
|
LPGUID ClassGuidList,
|
|
DWORD ClassGuidListSize,
|
|
PDWORD RequiredSize,
|
|
LPCSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
LPWSTR MachineNameW = NULL;
|
|
BOOL bResult;
|
|
|
|
TRACE("0x%lx %p %lu %p %s %p\n", Flags, ClassGuidList,
|
|
ClassGuidListSize, RequiredSize, debugstr_a(MachineName), Reserved);
|
|
|
|
if (MachineName)
|
|
{
|
|
MachineNameW = pSetupMultiByteToUnicode(MachineName, CP_ACP);
|
|
if (MachineNameW == NULL) return FALSE;
|
|
}
|
|
|
|
bResult = SetupDiBuildClassInfoListExW(Flags, ClassGuidList,
|
|
ClassGuidListSize, RequiredSize,
|
|
MachineNameW, Reserved);
|
|
|
|
MyFree(MachineNameW);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiBuildClassInfoListExW (SETUPAPI.@)
|
|
*
|
|
* Returns a list of setup class GUIDs that identify the classes
|
|
* that are installed on a local or remote machine.
|
|
*
|
|
* PARAMS
|
|
* Flags [I] control exclusion of classes from the list.
|
|
* ClassGuidList [O] pointer to a GUID-typed array that receives a list of setup class GUIDs.
|
|
* ClassGuidListSize [I] The number of GUIDs in the array (ClassGuidList).
|
|
* RequiredSize [O] pointer, which receives the number of GUIDs that are returned.
|
|
* MachineName [I] name of a remote machine.
|
|
* Reserved [I] must be NULL.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE.
|
|
* Failure: FALSE.
|
|
*/
|
|
BOOL WINAPI SetupDiBuildClassInfoListExW(
|
|
DWORD Flags,
|
|
LPGUID ClassGuidList,
|
|
DWORD ClassGuidListSize,
|
|
PDWORD RequiredSize,
|
|
LPCWSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
WCHAR szKeyName[40];
|
|
HKEY hClassesKey = INVALID_HANDLE_VALUE;
|
|
HKEY hClassKey;
|
|
DWORD dwLength;
|
|
DWORD dwIndex;
|
|
LONG lError;
|
|
DWORD dwGuidListIndex = 0;
|
|
|
|
TRACE("0x%lx %p %lu %p %s %p\n", Flags, ClassGuidList,
|
|
ClassGuidListSize, RequiredSize, debugstr_w(MachineName), Reserved);
|
|
|
|
if (!RequiredSize)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
else if (!ClassGuidList && ClassGuidListSize > 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
hClassesKey = SetupDiOpenClassRegKeyExW(NULL,
|
|
KEY_ENUMERATE_SUB_KEYS,
|
|
DIOCR_INSTALLER,
|
|
MachineName,
|
|
Reserved);
|
|
if (hClassesKey == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (dwIndex = 0; ; dwIndex++)
|
|
{
|
|
dwLength = 40;
|
|
lError = RegEnumKeyExW(hClassesKey,
|
|
dwIndex,
|
|
szKeyName,
|
|
&dwLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
TRACE("RegEnumKeyExW() returns %d\n", lError);
|
|
if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA)
|
|
{
|
|
TRACE("Key name: %s\n", debugstr_w(szKeyName));
|
|
|
|
if (RegOpenKeyExW(hClassesKey,
|
|
szKeyName,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hClassKey))
|
|
{
|
|
RegCloseKey(hClassesKey);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!RegQueryValueExW(hClassKey,
|
|
REGSTR_VAL_NOUSECLASS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
TRACE("'NoUseClass' value found!\n");
|
|
RegCloseKey(hClassKey);
|
|
continue;
|
|
}
|
|
|
|
if ((Flags & DIBCI_NOINSTALLCLASS) &&
|
|
(!RegQueryValueExW(hClassKey,
|
|
REGSTR_VAL_NOINSTALLCLASS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL)))
|
|
{
|
|
TRACE("'NoInstallClass' value found!\n");
|
|
RegCloseKey(hClassKey);
|
|
continue;
|
|
}
|
|
|
|
if ((Flags & DIBCI_NODISPLAYCLASS) &&
|
|
(!RegQueryValueExW(hClassKey,
|
|
REGSTR_VAL_NODISPLAYCLASS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL)))
|
|
{
|
|
TRACE("'NoDisplayClass' value found!\n");
|
|
RegCloseKey(hClassKey);
|
|
continue;
|
|
}
|
|
|
|
RegCloseKey(hClassKey);
|
|
|
|
TRACE("Guid: %s\n", debugstr_w(szKeyName));
|
|
if (dwGuidListIndex < ClassGuidListSize)
|
|
{
|
|
if (szKeyName[0] == '{' && szKeyName[37] == '}')
|
|
{
|
|
szKeyName[37] = 0;
|
|
}
|
|
TRACE("Guid: %p\n", &szKeyName[1]);
|
|
|
|
UuidFromStringW(&szKeyName[1],
|
|
&ClassGuidList[dwGuidListIndex]);
|
|
}
|
|
|
|
dwGuidListIndex++;
|
|
}
|
|
|
|
if (lError != ERROR_SUCCESS)
|
|
break;
|
|
}
|
|
|
|
RegCloseKey(hClassesKey);
|
|
|
|
if (RequiredSize != NULL)
|
|
*RequiredSize = dwGuidListIndex;
|
|
|
|
if (ClassGuidListSize < dwGuidListIndex)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiClassGuidsFromNameA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiClassGuidsFromNameA(
|
|
LPCSTR ClassName,
|
|
LPGUID ClassGuidList,
|
|
DWORD ClassGuidListSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
return SetupDiClassGuidsFromNameExA(ClassName, ClassGuidList,
|
|
ClassGuidListSize, RequiredSize,
|
|
NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiClassGuidsFromNameW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiClassGuidsFromNameW(
|
|
LPCWSTR ClassName,
|
|
LPGUID ClassGuidList,
|
|
DWORD ClassGuidListSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
return SetupDiClassGuidsFromNameExW(ClassName, ClassGuidList,
|
|
ClassGuidListSize, RequiredSize,
|
|
NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiClassGuidsFromNameExA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiClassGuidsFromNameExA(
|
|
LPCSTR ClassName,
|
|
LPGUID ClassGuidList,
|
|
DWORD ClassGuidListSize,
|
|
PDWORD RequiredSize,
|
|
LPCSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
LPWSTR ClassNameW = NULL;
|
|
LPWSTR MachineNameW = NULL;
|
|
BOOL bResult;
|
|
|
|
TRACE("%s %p %lu %p %s %p\n", debugstr_a(ClassName), ClassGuidList,
|
|
ClassGuidListSize, RequiredSize, debugstr_a(MachineName), Reserved);
|
|
|
|
if (!ClassName)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
ClassNameW = pSetupMultiByteToUnicode(ClassName, CP_ACP);
|
|
if (ClassNameW == NULL)
|
|
return FALSE;
|
|
|
|
if (MachineName)
|
|
{
|
|
MachineNameW = pSetupMultiByteToUnicode(MachineName, CP_ACP);
|
|
if (MachineNameW == NULL)
|
|
{
|
|
MyFree(ClassNameW);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bResult = SetupDiClassGuidsFromNameExW(ClassNameW, ClassGuidList,
|
|
ClassGuidListSize, RequiredSize,
|
|
MachineNameW, Reserved);
|
|
|
|
MyFree(MachineNameW);
|
|
MyFree(ClassNameW);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiClassGuidsFromNameExW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiClassGuidsFromNameExW(
|
|
LPCWSTR ClassName,
|
|
LPGUID ClassGuidList,
|
|
DWORD ClassGuidListSize,
|
|
PDWORD RequiredSize,
|
|
LPCWSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
WCHAR szKeyName[40];
|
|
WCHAR szClassName[MAX_CLASS_NAME_LEN];
|
|
HKEY hClassesKey;
|
|
HKEY hClassKey;
|
|
DWORD dwLength;
|
|
DWORD dwIndex;
|
|
LONG lError;
|
|
DWORD dwGuidListIndex = 0;
|
|
|
|
TRACE("%s %p %lu %p %s %p\n", debugstr_w(ClassName), ClassGuidList,
|
|
ClassGuidListSize, RequiredSize, debugstr_w(MachineName), Reserved);
|
|
|
|
if (!ClassName || !RequiredSize)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (!ClassGuidList && ClassGuidListSize > 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
*RequiredSize = 0;
|
|
|
|
hClassesKey = SetupDiOpenClassRegKeyExW(NULL,
|
|
KEY_ENUMERATE_SUB_KEYS,
|
|
DIOCR_INSTALLER,
|
|
MachineName,
|
|
Reserved);
|
|
if (hClassesKey == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (dwIndex = 0; ; dwIndex++)
|
|
{
|
|
dwLength = 40;
|
|
lError = RegEnumKeyExW(hClassesKey,
|
|
dwIndex,
|
|
szKeyName,
|
|
&dwLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
TRACE("RegEnumKeyExW() returns %d\n", lError);
|
|
if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA)
|
|
{
|
|
TRACE("Key name: %p\n", szKeyName);
|
|
|
|
if (RegOpenKeyExW(hClassesKey,
|
|
szKeyName,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hClassKey))
|
|
{
|
|
RegCloseKey(hClassesKey);
|
|
return FALSE;
|
|
}
|
|
|
|
dwLength = MAX_CLASS_NAME_LEN * sizeof(WCHAR);
|
|
if (!RegQueryValueExW(hClassKey,
|
|
REGSTR_VAL_CLASS,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szClassName,
|
|
&dwLength))
|
|
{
|
|
TRACE("Class name: %p\n", szClassName);
|
|
|
|
if (strcmpiW(szClassName, ClassName) == 0)
|
|
{
|
|
TRACE("Found matching class name\n");
|
|
|
|
TRACE("Guid: %p\n", szKeyName);
|
|
if (dwGuidListIndex < ClassGuidListSize)
|
|
{
|
|
if (szKeyName[0] == '{' && szKeyName[37] == '}')
|
|
{
|
|
szKeyName[37] = 0;
|
|
}
|
|
TRACE("Guid: %p\n", &szKeyName[1]);
|
|
|
|
UuidFromStringW(&szKeyName[1],
|
|
&ClassGuidList[dwGuidListIndex]);
|
|
}
|
|
|
|
dwGuidListIndex++;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hClassKey);
|
|
}
|
|
|
|
if (lError != ERROR_SUCCESS)
|
|
break;
|
|
}
|
|
|
|
RegCloseKey(hClassesKey);
|
|
|
|
if (RequiredSize != NULL)
|
|
*RequiredSize = dwGuidListIndex;
|
|
|
|
if (ClassGuidListSize < dwGuidListIndex)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiClassNameFromGuidA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiClassNameFromGuidA(
|
|
const GUID* ClassGuid,
|
|
PSTR ClassName,
|
|
DWORD ClassNameSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
return SetupDiClassNameFromGuidExA(ClassGuid, ClassName,
|
|
ClassNameSize, RequiredSize,
|
|
NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiClassNameFromGuidW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiClassNameFromGuidW(
|
|
const GUID* ClassGuid,
|
|
PWSTR ClassName,
|
|
DWORD ClassNameSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
return SetupDiClassNameFromGuidExW(ClassGuid, ClassName,
|
|
ClassNameSize, RequiredSize,
|
|
NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiClassNameFromGuidExA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiClassNameFromGuidExA(
|
|
const GUID* ClassGuid,
|
|
PSTR ClassName,
|
|
DWORD ClassNameSize,
|
|
PDWORD RequiredSize,
|
|
PCSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
WCHAR ClassNameW[MAX_CLASS_NAME_LEN];
|
|
LPWSTR MachineNameW = NULL;
|
|
BOOL ret;
|
|
|
|
if (MachineName)
|
|
MachineNameW = pSetupMultiByteToUnicode(MachineName, CP_ACP);
|
|
ret = SetupDiClassNameFromGuidExW(ClassGuid, ClassNameW, MAX_CLASS_NAME_LEN,
|
|
RequiredSize, MachineNameW, Reserved);
|
|
if (ret)
|
|
{
|
|
int len = WideCharToMultiByte(CP_ACP, 0, ClassNameW, -1, ClassName,
|
|
ClassNameSize, NULL, NULL);
|
|
if (len == 0 || len > ClassNameSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
ret = FALSE;
|
|
}
|
|
}
|
|
MyFree(MachineNameW);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiClassNameFromGuidExW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiClassNameFromGuidExW(
|
|
const GUID* ClassGuid,
|
|
PWSTR ClassName,
|
|
DWORD ClassNameSize,
|
|
PDWORD RequiredSize,
|
|
PCWSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwLength;
|
|
DWORD dwRegType;
|
|
LONG rc;
|
|
PWSTR Buffer;
|
|
|
|
TRACE("%s %p %lu %p %s %p\n", debugstr_guid(ClassGuid), ClassName,
|
|
ClassNameSize, RequiredSize, debugstr_w(MachineName), Reserved);
|
|
|
|
/* Make sure there's a GUID */
|
|
if (ClassGuid == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_CLASS); /* On Vista: ERROR_INVALID_USER_BUFFER */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Make sure there's a real buffer when there's a size */
|
|
if ((ClassNameSize > 0) && (ClassName == NULL))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER); /* On Vista: ERROR_INVALID_USER_BUFFER */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Open the key for the GUID */
|
|
hKey = SetupDiOpenClassRegKeyExW(ClassGuid, KEY_QUERY_VALUE, DIOCR_INSTALLER, MachineName, Reserved);
|
|
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
/* Retrieve the class name data and close the key */
|
|
rc = QueryRegistryValue(hKey, REGSTR_VAL_CLASS, (LPBYTE *) &Buffer, &dwRegType, &dwLength);
|
|
RegCloseKey(hKey);
|
|
|
|
/* Make sure we got the data */
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Make sure the data is a string */
|
|
if (dwRegType != REG_SZ)
|
|
{
|
|
MyFree(Buffer);
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Determine the length of the class name */
|
|
dwLength /= sizeof(WCHAR);
|
|
|
|
if ((dwLength == 0) || (Buffer[dwLength - 1] != UNICODE_NULL))
|
|
/* Count the null-terminator */
|
|
dwLength++;
|
|
|
|
/* Inform the caller about the class name */
|
|
if ((ClassName != NULL) && (dwLength <= ClassNameSize))
|
|
{
|
|
memcpy(ClassName, Buffer, (dwLength - 1) * sizeof(WCHAR));
|
|
ClassName[dwLength - 1] = UNICODE_NULL;
|
|
}
|
|
|
|
/* Inform the caller about the required size */
|
|
if (RequiredSize != NULL)
|
|
*RequiredSize = dwLength;
|
|
|
|
/* Clean up the buffer */
|
|
MyFree(Buffer);
|
|
|
|
/* Make sure the buffer was large enough */
|
|
if ((ClassName == NULL) || (dwLength > ClassNameSize))
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInfoList (SETUPAPI.@)
|
|
*/
|
|
HDEVINFO WINAPI
|
|
SetupDiCreateDeviceInfoList(const GUID *ClassGuid,
|
|
HWND hwndParent)
|
|
{
|
|
return SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent, NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInfoListExA (SETUPAPI.@)
|
|
*/
|
|
HDEVINFO WINAPI
|
|
SetupDiCreateDeviceInfoListExA(const GUID *ClassGuid,
|
|
HWND hwndParent,
|
|
PCSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
LPWSTR MachineNameW = NULL;
|
|
HDEVINFO hDevInfo;
|
|
|
|
TRACE("%s %p %s %p\n", debugstr_guid(ClassGuid), hwndParent,
|
|
debugstr_a(MachineName), Reserved);
|
|
|
|
if (MachineName)
|
|
{
|
|
MachineNameW = pSetupMultiByteToUnicode(MachineName, CP_ACP);
|
|
if (MachineNameW == NULL)
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
hDevInfo = SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent,
|
|
MachineNameW, Reserved);
|
|
|
|
MyFree(MachineNameW);
|
|
|
|
return hDevInfo;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInfoListExW (SETUPAPI.@)
|
|
*
|
|
* Create an empty DeviceInfoSet list.
|
|
*
|
|
* PARAMS
|
|
* ClassGuid [I] if not NULL only devices with GUID ClassGuid are associated
|
|
* with this list.
|
|
* hwndParent [I] hwnd needed for interface related actions.
|
|
* MachineName [I] name of machine to create emtpy DeviceInfoSet list, if NULL
|
|
* local registry will be used.
|
|
* Reserved [I] must be NULL
|
|
*
|
|
* RETURNS
|
|
* Success: empty list.
|
|
* Failure: INVALID_HANDLE_VALUE.
|
|
*/
|
|
HDEVINFO WINAPI
|
|
SetupDiCreateDeviceInfoListExW(const GUID *ClassGuid,
|
|
HWND hwndParent,
|
|
PCWSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
struct DeviceInfoSet *list = NULL;
|
|
DWORD size = FIELD_OFFSET(struct DeviceInfoSet, szData);
|
|
DWORD rc;
|
|
CONFIGRET cr;
|
|
HDEVINFO ret = INVALID_HANDLE_VALUE;
|
|
|
|
TRACE("%s %p %s %p\n", debugstr_guid(ClassGuid), hwndParent,
|
|
debugstr_w(MachineName), Reserved);
|
|
|
|
if (MachineName != NULL)
|
|
{
|
|
SIZE_T len = strlenW(MachineName);
|
|
if (len >= SP_MAX_MACHINENAME_LENGTH - 4)
|
|
{
|
|
SetLastError(ERROR_INVALID_MACHINENAME);
|
|
goto cleanup;
|
|
}
|
|
if(len > 0)
|
|
size += (len + 3) * sizeof(WCHAR);
|
|
else
|
|
MachineName = NULL;
|
|
}
|
|
|
|
if (Reserved != NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
list = MyMalloc(size);
|
|
if (!list)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
ZeroMemory(list, FIELD_OFFSET(struct DeviceInfoSet, szData));
|
|
|
|
list->magic = SETUP_DEVICE_INFO_SET_MAGIC;
|
|
memcpy(&list->ClassGuid,
|
|
ClassGuid ? ClassGuid : &GUID_NULL,
|
|
sizeof(list->ClassGuid));
|
|
list->InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
|
|
list->InstallParams.Flags |= DI_CLASSINSTALLPARAMS;
|
|
list->InstallParams.hwndParent = hwndParent;
|
|
if (MachineName)
|
|
{
|
|
rc = RegConnectRegistryW(MachineName, HKEY_LOCAL_MACHINE, &list->HKLM);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(ERROR_INVALID_MACHINENAME);
|
|
goto cleanup;
|
|
}
|
|
|
|
list->szData[0] = list->szData[1] = '\\';
|
|
strcpyW(list->szData + 2, MachineName);
|
|
list->MachineName = list->szData;
|
|
}
|
|
else
|
|
{
|
|
list->HKLM = HKEY_LOCAL_MACHINE;
|
|
list->MachineName = NULL;
|
|
}
|
|
cr = CM_Connect_MachineW(list->MachineName, &list->hMachine);
|
|
if (cr != CR_SUCCESS)
|
|
{
|
|
SetLastError(GetErrorCodeFromCrCode(cr));
|
|
goto cleanup;
|
|
}
|
|
InitializeListHead(&list->DriverListHead);
|
|
InitializeListHead(&list->ListHead);
|
|
|
|
return (HDEVINFO)list;
|
|
|
|
cleanup:
|
|
if (ret == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (list)
|
|
{
|
|
if (list->HKLM != NULL && list->HKLM != HKEY_LOCAL_MACHINE)
|
|
RegCloseKey(list->HKLM);
|
|
MyFree(list);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDevRegKeyA (SETUPAPI.@)
|
|
*/
|
|
HKEY WINAPI SetupDiCreateDevRegKeyA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Scope,
|
|
DWORD HwProfile,
|
|
DWORD KeyType,
|
|
HINF InfHandle,
|
|
PCSTR InfSectionName)
|
|
{
|
|
PWSTR InfSectionNameW = NULL;
|
|
HKEY key;
|
|
|
|
TRACE("%p %p %d %d %d %p %s\n", DeviceInfoSet, DeviceInfoData, Scope,
|
|
HwProfile, KeyType, InfHandle, debugstr_a(InfSectionName));
|
|
|
|
if (InfHandle)
|
|
{
|
|
if (!InfSectionName)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
else
|
|
{
|
|
InfSectionNameW = pSetupMultiByteToUnicode(InfSectionName, CP_ACP);
|
|
if (InfSectionNameW == NULL) return INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
key = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, Scope,
|
|
HwProfile, KeyType, InfHandle, InfSectionNameW);
|
|
MyFree(InfSectionNameW);
|
|
return key;
|
|
}
|
|
|
|
static HKEY
|
|
OpenHardwareProfileKey(
|
|
IN HKEY HKLM,
|
|
IN DWORD HwProfile,
|
|
IN DWORD samDesired);
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDevRegKeyW (SETUPAPI.@)
|
|
*/
|
|
HKEY WINAPI SetupDiCreateDevRegKeyW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Scope,
|
|
DWORD HwProfile,
|
|
DWORD KeyType,
|
|
HINF InfHandle,
|
|
PCWSTR InfSectionName)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
HKEY key = INVALID_HANDLE_VALUE;
|
|
LPWSTR lpGuidString = NULL;
|
|
LPWSTR DriverKey = NULL; /* {GUID}\Index */
|
|
LPWSTR pDeviceInstance; /* Points into DriverKey, on the Index field */
|
|
DWORD Index; /* Index used in the DriverKey name */
|
|
DWORD dwSize;
|
|
DWORD Disposition;
|
|
DWORD rc;
|
|
HKEY hHWProfileKey = INVALID_HANDLE_VALUE;
|
|
HKEY hEnumKey = NULL;
|
|
HKEY hClassKey = NULL;
|
|
HKEY hDeviceKey = INVALID_HANDLE_VALUE;
|
|
HKEY hKey = NULL;
|
|
HKEY RootKey;
|
|
|
|
TRACE("%p %p %lu %lu %lu %p %s\n", DeviceInfoSet, DeviceInfoData, Scope,
|
|
HwProfile, KeyType, InfHandle, debugstr_w(InfSectionName));
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
|
|
|| !DeviceInfoData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (Scope != DICS_FLAG_GLOBAL && Scope != DICS_FLAG_CONFIGSPECIFIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (KeyType != DIREG_DEV && KeyType != DIREG_DRV)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (InfHandle && !InfSectionName)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (!InfHandle && InfSectionName)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (Scope == DICS_FLAG_GLOBAL)
|
|
RootKey = set->HKLM;
|
|
else /* Scope == DICS_FLAG_CONFIGSPECIFIC */
|
|
{
|
|
hHWProfileKey = OpenHardwareProfileKey(set->HKLM, HwProfile, KEY_CREATE_SUB_KEY);
|
|
if (hHWProfileKey == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
RootKey = hHWProfileKey;
|
|
}
|
|
|
|
if (KeyType == DIREG_DEV)
|
|
{
|
|
struct DeviceInfo *deviceInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
|
|
|
|
rc = RegCreateKeyExW(
|
|
RootKey,
|
|
REGSTR_PATH_SYSTEMENUM,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_CREATE_SUB_KEY,
|
|
NULL,
|
|
&hEnumKey,
|
|
NULL);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
rc = RegCreateKeyExW(
|
|
hEnumKey,
|
|
deviceInfo->instanceId,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
#if _WIN32_WINNT >= 0x502
|
|
KEY_READ | KEY_WRITE,
|
|
#else
|
|
KEY_ALL_ACCESS,
|
|
#endif
|
|
NULL,
|
|
&hKey,
|
|
NULL);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else /* KeyType == DIREG_DRV */
|
|
{
|
|
/* Open device key, to read Driver value */
|
|
hDeviceKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, Scope, HwProfile, DIREG_DEV, KEY_QUERY_VALUE | KEY_SET_VALUE);
|
|
if (hDeviceKey == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
|
|
rc = RegOpenKeyExW(RootKey, REGSTR_PATH_CLASS_NT, 0, KEY_CREATE_SUB_KEY, &hClassKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = RegQueryValueExW(hDeviceKey, REGSTR_VAL_DRIVER, NULL, NULL, NULL, &dwSize);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
/* Create a new driver key */
|
|
|
|
if (UuidToStringW((UUID*)&DeviceInfoData->ClassGuid, &lpGuidString) != RPC_S_OK)
|
|
goto cleanup;
|
|
|
|
/* The driver key is in \System\CurrentControlSet\Control\Class\{GUID}\Index */
|
|
DriverKey = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpGuidString) + 7) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
|
|
if (!DriverKey)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
|
|
DriverKey[0] = '{';
|
|
strcpyW(&DriverKey[1], lpGuidString);
|
|
pDeviceInstance = &DriverKey[strlenW(DriverKey)];
|
|
*pDeviceInstance++ = '}';
|
|
*pDeviceInstance++ = '\\';
|
|
|
|
/* Try all values for Index between 0 and 9999 */
|
|
Index = 0;
|
|
while (Index <= 9999)
|
|
{
|
|
sprintfW(pDeviceInstance, InstanceKeyFormat, Index);
|
|
rc = RegCreateKeyExW(hClassKey,
|
|
DriverKey,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
#if _WIN32_WINNT >= 0x502
|
|
KEY_READ | KEY_WRITE,
|
|
#else
|
|
KEY_ALL_ACCESS,
|
|
#endif
|
|
NULL,
|
|
&hKey,
|
|
&Disposition);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
if (Disposition == REG_CREATED_NEW_KEY)
|
|
break;
|
|
RegCloseKey(hKey);
|
|
hKey = NULL;
|
|
Index++;
|
|
}
|
|
|
|
if (Index > 9999)
|
|
{
|
|
/* Unable to create more than 9999 devices within the same class */
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Write the new Driver value */
|
|
rc = RegSetValueExW(hDeviceKey, REGSTR_VAL_DRIVER, 0, REG_SZ, (const BYTE *)DriverKey, (strlenW(DriverKey) + 1) * sizeof(WCHAR));
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Open the existing driver key */
|
|
|
|
DriverKey = HeapAlloc(GetProcessHeap(), 0, dwSize);
|
|
if (!DriverKey)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = RegQueryValueExW(hDeviceKey, REGSTR_VAL_DRIVER, NULL, NULL, (LPBYTE)DriverKey, &dwSize);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = RegCreateKeyExW(hClassKey,
|
|
DriverKey,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
#if _WIN32_WINNT >= 0x502
|
|
KEY_READ | KEY_WRITE,
|
|
#else
|
|
KEY_ALL_ACCESS,
|
|
#endif
|
|
NULL,
|
|
&hKey,
|
|
&Disposition);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Do installation of the specified section */
|
|
if (InfHandle)
|
|
{
|
|
FIXME("Need to install section %s in file %p\n",
|
|
debugstr_w(InfSectionName), InfHandle);
|
|
}
|
|
key = hKey;
|
|
|
|
cleanup:
|
|
if (lpGuidString)
|
|
RpcStringFreeW(&lpGuidString);
|
|
HeapFree(GetProcessHeap(), 0, DriverKey);
|
|
if (hHWProfileKey != INVALID_HANDLE_VALUE)
|
|
RegCloseKey(hHWProfileKey);
|
|
if (hEnumKey != NULL)
|
|
RegCloseKey(hEnumKey);
|
|
if (hClassKey != NULL)
|
|
RegCloseKey(hClassKey);
|
|
if (hDeviceKey != INVALID_HANDLE_VALUE)
|
|
RegCloseKey(hDeviceKey);
|
|
if (hKey != NULL && hKey != key)
|
|
RegCloseKey(hKey);
|
|
|
|
TRACE("Returning 0x%p\n", key);
|
|
return key;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInfoA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiCreateDeviceInfoA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PCSTR DeviceName,
|
|
CONST GUID *ClassGuid,
|
|
PCSTR DeviceDescription,
|
|
HWND hwndParent,
|
|
DWORD CreationFlags,
|
|
PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
BOOL ret;
|
|
LPWSTR DeviceNameW = NULL;
|
|
LPWSTR DeviceDescriptionW = NULL;
|
|
|
|
TRACE("\n");
|
|
|
|
if (DeviceName)
|
|
{
|
|
DeviceNameW = pSetupMultiByteToUnicode(DeviceName, CP_ACP);
|
|
if (DeviceNameW == NULL) return FALSE;
|
|
}
|
|
if (DeviceDescription)
|
|
{
|
|
DeviceDescriptionW = pSetupMultiByteToUnicode(DeviceDescription, CP_ACP);
|
|
if (DeviceDescriptionW == NULL)
|
|
{
|
|
MyFree(DeviceNameW);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ret = SetupDiCreateDeviceInfoW(DeviceInfoSet, DeviceNameW, ClassGuid, DeviceDescriptionW,
|
|
hwndParent, CreationFlags, DeviceInfoData);
|
|
|
|
MyFree(DeviceNameW);
|
|
MyFree(DeviceDescriptionW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInfoW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiCreateDeviceInfoW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PCWSTR DeviceName,
|
|
CONST GUID *ClassGuid,
|
|
PCWSTR DeviceDescription,
|
|
HWND hwndParent,
|
|
DWORD CreationFlags,
|
|
PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
struct DeviceInfo *deviceInfo = NULL;
|
|
BOOL ret = FALSE;
|
|
CONFIGRET cr;
|
|
DEVINST RootDevInst;
|
|
DEVINST DevInst;
|
|
WCHAR GenInstanceId[MAX_DEVICE_ID_LEN];
|
|
|
|
TRACE("%p %s %s %s %p %x %p\n", DeviceInfoSet, debugstr_w(DeviceName),
|
|
debugstr_guid(ClassGuid), debugstr_w(DeviceDescription),
|
|
hwndParent, CreationFlags, DeviceInfoData);
|
|
|
|
if (!DeviceName)
|
|
{
|
|
SetLastError(ERROR_INVALID_DEVINST_NAME);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!ClassGuid)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!IsEqualGUID(&set->ClassGuid, &GUID_NULL) &&
|
|
!IsEqualGUID(ClassGuid, &set->ClassGuid))
|
|
{
|
|
SetLastError(ERROR_CLASS_MISMATCH);
|
|
return FALSE;
|
|
}
|
|
if (CreationFlags & ~(DICD_GENERATE_ID | DICD_INHERIT_CLASSDRVS))
|
|
{
|
|
TRACE("Unknown flags: 0x%08lx\n", CreationFlags & ~(DICD_GENERATE_ID | DICD_INHERIT_CLASSDRVS));
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the root device instance */
|
|
cr = CM_Locate_DevInst_ExW(&RootDevInst,
|
|
NULL,
|
|
CM_LOCATE_DEVINST_NORMAL,
|
|
set->hMachine);
|
|
if (cr != CR_SUCCESS)
|
|
{
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Create the new device instance */
|
|
cr = CM_Create_DevInst_ExW(&DevInst,
|
|
(DEVINSTID)DeviceName,
|
|
RootDevInst,
|
|
(CreationFlags & DICD_GENERATE_ID) ?
|
|
CM_CREATE_DEVINST_GENERATE_ID : 0,
|
|
set->hMachine);
|
|
if (cr != CR_SUCCESS)
|
|
{
|
|
SetLastError(GetErrorCodeFromCrCode(cr));
|
|
return FALSE;
|
|
}
|
|
|
|
if (CreationFlags & DICD_GENERATE_ID)
|
|
{
|
|
/* Grab the actual instance ID that was created */
|
|
cr = CM_Get_Device_ID_Ex(DevInst,
|
|
GenInstanceId,
|
|
MAX_DEVICE_ID_LEN,
|
|
0,
|
|
set->hMachine);
|
|
if (cr != CR_SUCCESS)
|
|
{
|
|
SetLastError(GetErrorCodeFromCrCode(cr));
|
|
return FALSE;
|
|
}
|
|
|
|
DeviceName = GenInstanceId;
|
|
TRACE("Using generated instance ID: %s\n", debugstr_w(DeviceName));
|
|
}
|
|
|
|
if (CreateDeviceInfo(set, DeviceName, ClassGuid, &deviceInfo))
|
|
{
|
|
InsertTailList(&set->ListHead, &deviceInfo->ListEntry);
|
|
|
|
if (!DeviceInfoData)
|
|
ret = TRUE;
|
|
else
|
|
{
|
|
if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
{
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
}
|
|
else
|
|
{
|
|
memcpy(&DeviceInfoData->ClassGuid, ClassGuid, sizeof(GUID));
|
|
DeviceInfoData->DevInst = deviceInfo->dnDevInst;
|
|
DeviceInfoData->Reserved = (ULONG_PTR)deviceInfo;
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == FALSE)
|
|
{
|
|
if (deviceInfo != NULL)
|
|
{
|
|
/* Remove deviceInfo from List */
|
|
RemoveEntryList(&deviceInfo->ListEntry);
|
|
|
|
/* Destroy deviceInfo */
|
|
DestroyDeviceInfo(deviceInfo);
|
|
}
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiRegisterDeviceInfo (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiRegisterDeviceInfo(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Flags,
|
|
PSP_DETSIG_CMPPROC CompareProc,
|
|
PVOID CompareContext,
|
|
PSP_DEVINFO_DATA DupDeviceInfoData)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
WCHAR DevInstId[MAX_DEVICE_ID_LEN];
|
|
DEVINST ParentDevInst;
|
|
CONFIGRET cr;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
TRACE("%p %p %08x %p %p %p\n", DeviceInfoSet, DeviceInfoData, Flags,
|
|
CompareProc, CompareContext, DupDeviceInfoData);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
|
|
|| !DeviceInfoData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Flags & ~SPRDI_FIND_DUPS)
|
|
{
|
|
TRACE("Unknown flags: 0x%08lx\n", Flags & ~SPRDI_FIND_DUPS);
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Flags & SPRDI_FIND_DUPS)
|
|
{
|
|
FIXME("Unimplemented codepath!\n");
|
|
}
|
|
|
|
CM_Get_Device_ID_Ex(DeviceInfoData->DevInst,
|
|
DevInstId,
|
|
MAX_DEVICE_ID_LEN,
|
|
0,
|
|
set->hMachine);
|
|
|
|
CM_Get_Parent_Ex(&ParentDevInst,
|
|
DeviceInfoData->DevInst,
|
|
0,
|
|
set->hMachine);
|
|
|
|
cr = CM_Create_DevInst_Ex(&DeviceInfoData->DevInst,
|
|
DevInstId,
|
|
ParentDevInst,
|
|
CM_CREATE_DEVINST_NORMAL | CM_CREATE_DEVINST_DO_NOT_INSTALL,
|
|
set->hMachine);
|
|
if (cr != CR_SUCCESS &&
|
|
cr != CR_ALREADY_SUCH_DEVINST)
|
|
{
|
|
dwError = ERROR_NO_SUCH_DEVINST;
|
|
}
|
|
|
|
SetLastError(dwError);
|
|
|
|
return (dwError == ERROR_SUCCESS);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiEnumDeviceInfo (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiEnumDeviceInfo(
|
|
HDEVINFO devinfo,
|
|
DWORD index,
|
|
PSP_DEVINFO_DATA info)
|
|
{
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %d %p\n", devinfo, index, info);
|
|
|
|
if(info==NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (devinfo && devinfo != INVALID_HANDLE_VALUE)
|
|
{
|
|
struct DeviceInfoSet *list = (struct DeviceInfoSet *)devinfo;
|
|
if (list->magic == SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
if (info->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
PLIST_ENTRY ItemList = list->ListHead.Flink;
|
|
while (ItemList != &list->ListHead && index-- > 0)
|
|
ItemList = ItemList->Flink;
|
|
if (ItemList == &list->ListHead)
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
else
|
|
{
|
|
struct DeviceInfo *DevInfo = CONTAINING_RECORD(ItemList, struct DeviceInfo, ListEntry);
|
|
memcpy(&info->ClassGuid,
|
|
&DevInfo->ClassGuid,
|
|
sizeof(GUID));
|
|
info->DevInst = DevInfo->dnDevInst;
|
|
info->Reserved = (ULONG_PTR)DevInfo;
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
}
|
|
else
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInstanceIdA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceInstanceIdA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
PSTR DeviceInstanceId,
|
|
DWORD DeviceInstanceIdSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DWORD size;
|
|
PWSTR instanceId;
|
|
|
|
TRACE("%p %p %p %d %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstanceId,
|
|
DeviceInstanceIdSize, RequiredSize);
|
|
|
|
if (!DeviceInstanceId && DeviceInstanceIdSize > 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
ret = SetupDiGetDeviceInstanceIdW(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
NULL,
|
|
0,
|
|
&size);
|
|
if (!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
return FALSE;
|
|
instanceId = MyMalloc(size * sizeof(WCHAR));
|
|
if (instanceId)
|
|
{
|
|
ret = SetupDiGetDeviceInstanceIdW(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
instanceId,
|
|
size,
|
|
&size);
|
|
if (ret)
|
|
{
|
|
int len = WideCharToMultiByte(CP_ACP, 0, instanceId, -1,
|
|
DeviceInstanceId,
|
|
DeviceInstanceIdSize, NULL, NULL);
|
|
|
|
if (!len)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (len > DeviceInstanceIdSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
ret = FALSE;
|
|
}
|
|
if (RequiredSize)
|
|
*RequiredSize = len;
|
|
}
|
|
}
|
|
MyFree(instanceId);
|
|
}
|
|
else
|
|
{
|
|
if (RequiredSize)
|
|
*RequiredSize = size;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
ret = FALSE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInstanceIdW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceInstanceIdW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
PWSTR DeviceInstanceId,
|
|
DWORD DeviceInstanceIdSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
struct DeviceInfo *devInfo;
|
|
|
|
TRACE("%p %p %p %d %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstanceId,
|
|
DeviceInstanceIdSize, RequiredSize);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
|
|
|| !DeviceInfoData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
|
|
if (!DeviceInstanceId && DeviceInstanceIdSize > 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (DeviceInstanceId && DeviceInstanceIdSize == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
TRACE("instance ID: %s\n", debugstr_w(devInfo->instanceId));
|
|
if (DeviceInstanceIdSize < lstrlenW(devInfo->instanceId) + 1)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
if (RequiredSize)
|
|
*RequiredSize = lstrlenW(devInfo->instanceId) + 1;
|
|
return FALSE;
|
|
}
|
|
lstrcpyW(DeviceInstanceId, devInfo->instanceId);
|
|
if (RequiredSize)
|
|
*RequiredSize = lstrlenW(devInfo->instanceId) + 1;
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetActualSectionToInstallA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetActualSectionToInstallA(
|
|
HINF InfHandle,
|
|
PCSTR InfSectionName,
|
|
PSTR InfSectionWithExt,
|
|
DWORD InfSectionWithExtSize,
|
|
PDWORD RequiredSize,
|
|
PSTR *Extension)
|
|
{
|
|
return SetupDiGetActualSectionToInstallExA(InfHandle, InfSectionName,
|
|
NULL, InfSectionWithExt, InfSectionWithExtSize, RequiredSize,
|
|
Extension, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetActualSectionToInstallW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetActualSectionToInstallW(
|
|
HINF InfHandle,
|
|
PCWSTR InfSectionName,
|
|
PWSTR InfSectionWithExt,
|
|
DWORD InfSectionWithExtSize,
|
|
PDWORD RequiredSize,
|
|
PWSTR *Extension)
|
|
{
|
|
return SetupDiGetActualSectionToInstallExW(InfHandle, InfSectionName,
|
|
NULL, InfSectionWithExt, InfSectionWithExtSize, RequiredSize,
|
|
Extension, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetActualSectionToInstallExA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiGetActualSectionToInstallExA(
|
|
IN HINF InfHandle,
|
|
IN PCSTR InfSectionName,
|
|
IN PSP_ALTPLATFORM_INFO AlternatePlatformInfo OPTIONAL,
|
|
OUT PSTR InfSectionWithExt OPTIONAL,
|
|
IN DWORD InfSectionWithExtSize,
|
|
OUT PDWORD RequiredSize OPTIONAL,
|
|
OUT PSTR* Extension OPTIONAL,
|
|
IN PVOID Reserved)
|
|
{
|
|
LPWSTR InfSectionNameW = NULL;
|
|
LPWSTR InfSectionWithExtW = NULL;
|
|
PWSTR ExtensionW;
|
|
BOOL bResult = FALSE;
|
|
|
|
TRACE("\n");
|
|
|
|
if (InfSectionName)
|
|
{
|
|
InfSectionNameW = pSetupMultiByteToUnicode(InfSectionName, CP_ACP);
|
|
if (InfSectionNameW == NULL)
|
|
goto cleanup;
|
|
}
|
|
if (InfSectionWithExt)
|
|
{
|
|
InfSectionWithExtW = MyMalloc(InfSectionWithExtSize * sizeof(WCHAR));
|
|
if (InfSectionWithExtW == NULL)
|
|
goto cleanup;
|
|
}
|
|
|
|
bResult = SetupDiGetActualSectionToInstallExW(
|
|
InfHandle, InfSectionNameW, AlternatePlatformInfo,
|
|
InfSectionWithExt ? InfSectionWithExtW : NULL,
|
|
InfSectionWithExtSize,
|
|
RequiredSize,
|
|
Extension ? &ExtensionW : NULL,
|
|
Reserved);
|
|
|
|
if (bResult && InfSectionWithExt)
|
|
{
|
|
bResult = WideCharToMultiByte(CP_ACP, 0, InfSectionWithExtW, -1, InfSectionWithExt,
|
|
InfSectionWithExtSize, NULL, NULL) != 0;
|
|
}
|
|
if (bResult && Extension)
|
|
{
|
|
if (ExtensionW == NULL)
|
|
*Extension = NULL;
|
|
else
|
|
*Extension = &InfSectionWithExt[ExtensionW - InfSectionWithExtW];
|
|
}
|
|
|
|
cleanup:
|
|
MyFree(InfSectionNameW);
|
|
MyFree(InfSectionWithExtW);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetClassDescriptionA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetClassDescriptionA(
|
|
const GUID* ClassGuid,
|
|
PSTR ClassDescription,
|
|
DWORD ClassDescriptionSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
return SetupDiGetClassDescriptionExA(ClassGuid, ClassDescription,
|
|
ClassDescriptionSize,
|
|
RequiredSize, NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetClassDescriptionW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetClassDescriptionW(
|
|
const GUID* ClassGuid,
|
|
PWSTR ClassDescription,
|
|
DWORD ClassDescriptionSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
return SetupDiGetClassDescriptionExW(ClassGuid, ClassDescription,
|
|
ClassDescriptionSize,
|
|
RequiredSize, NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetClassDescriptionExA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetClassDescriptionExA(
|
|
const GUID* ClassGuid,
|
|
PSTR ClassDescription,
|
|
DWORD ClassDescriptionSize,
|
|
PDWORD RequiredSize,
|
|
PCSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
PWCHAR ClassDescriptionW = NULL;
|
|
LPWSTR MachineNameW = NULL;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%s %p %lu %p %s %p\n", debugstr_guid(ClassGuid), ClassDescription,
|
|
ClassDescriptionSize, RequiredSize, debugstr_a(MachineName), Reserved);
|
|
|
|
if (ClassDescriptionSize > 0)
|
|
{
|
|
ClassDescriptionW = MyMalloc(ClassDescriptionSize * sizeof(WCHAR));
|
|
if (!ClassDescriptionW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (MachineName)
|
|
{
|
|
MachineNameW = pSetupMultiByteToUnicode(MachineName, CP_ACP);
|
|
if (!MachineNameW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ret = SetupDiGetClassDescriptionExW(ClassGuid, ClassDescriptionW,
|
|
ClassDescriptionSize * sizeof(WCHAR), RequiredSize, MachineNameW, Reserved);
|
|
if (ret)
|
|
{
|
|
DWORD len = (DWORD)WideCharToMultiByte(CP_ACP, 0, ClassDescriptionW, -1, ClassDescription,
|
|
ClassDescriptionSize, NULL, NULL);
|
|
if (len == 0 || len > ClassDescriptionSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
ret = FALSE;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
MyFree(ClassDescriptionW);
|
|
MyFree(MachineNameW);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetClassDescriptionExW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetClassDescriptionExW(
|
|
const GUID* ClassGuid,
|
|
PWSTR ClassDescription,
|
|
DWORD ClassDescriptionSize,
|
|
PDWORD RequiredSize,
|
|
PCWSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwLength;
|
|
DWORD dwRegType;
|
|
LONG rc;
|
|
PWSTR Buffer;
|
|
|
|
TRACE("%s %p %lu %p %s %p\n", debugstr_guid(ClassGuid), ClassDescription,
|
|
ClassDescriptionSize, RequiredSize, debugstr_w(MachineName), Reserved);
|
|
|
|
/* Make sure there's a GUID */
|
|
if (!ClassGuid)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Make sure there's a real buffer when there's a size */
|
|
if (!ClassDescription && ClassDescriptionSize > 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Open the key for the GUID */
|
|
hKey = SetupDiOpenClassRegKeyExW(ClassGuid,
|
|
KEY_QUERY_VALUE,
|
|
DIOCR_INSTALLER,
|
|
MachineName,
|
|
Reserved);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
/* Retrieve the class description data and close the key */
|
|
rc = QueryRegistryValue(hKey, NULL, (LPBYTE *) &Buffer, &dwRegType, &dwLength);
|
|
RegCloseKey(hKey);
|
|
|
|
/* Make sure we got the data */
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Make sure the data is a string */
|
|
if (dwRegType != REG_SZ)
|
|
{
|
|
MyFree(Buffer);
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Determine the length of the class description */
|
|
dwLength /= sizeof(WCHAR);
|
|
|
|
/* Count the null-terminator if none is present */
|
|
if ((dwLength == 0) || (Buffer[dwLength - 1] != UNICODE_NULL))
|
|
dwLength++;
|
|
|
|
/* Inform the caller about the class description */
|
|
if ((ClassDescription != NULL) && (dwLength <= ClassDescriptionSize))
|
|
{
|
|
memcpy(ClassDescription, Buffer, (dwLength - 1) * sizeof(WCHAR));
|
|
ClassDescription[dwLength - 1] = UNICODE_NULL;
|
|
}
|
|
|
|
/* Inform the caller about the required size */
|
|
if (RequiredSize != NULL)
|
|
*RequiredSize = dwLength;
|
|
|
|
/* Clean up the buffer */
|
|
MyFree(Buffer);
|
|
|
|
/* Make sure the buffer was large enough */
|
|
if ((ClassDescription == NULL) || (dwLength > ClassDescriptionSize))
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetClassDevsA (SETUPAPI.@)
|
|
*/
|
|
HDEVINFO WINAPI SetupDiGetClassDevsA(
|
|
CONST GUID *class,
|
|
LPCSTR enumstr,
|
|
HWND parent,
|
|
DWORD flags)
|
|
{
|
|
return SetupDiGetClassDevsExA(class, enumstr, parent,
|
|
flags, NULL, NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetClassDevsExA (SETUPAPI.@)
|
|
*/
|
|
HDEVINFO WINAPI SetupDiGetClassDevsExA(
|
|
const GUID *class,
|
|
PCSTR enumstr,
|
|
HWND parent,
|
|
DWORD flags,
|
|
HDEVINFO deviceset,
|
|
PCSTR machine,
|
|
PVOID reserved)
|
|
{
|
|
HDEVINFO ret;
|
|
LPWSTR enumstrW = NULL, machineW = NULL;
|
|
|
|
if (enumstr)
|
|
{
|
|
enumstrW = pSetupMultiByteToUnicode(enumstr, CP_ACP);
|
|
if (!enumstrW)
|
|
{
|
|
ret = INVALID_HANDLE_VALUE;
|
|
goto end;
|
|
}
|
|
}
|
|
if (machine)
|
|
{
|
|
machineW = pSetupMultiByteToUnicode(machine, CP_ACP);
|
|
if (!machineW)
|
|
{
|
|
MyFree(enumstrW);
|
|
ret = INVALID_HANDLE_VALUE;
|
|
goto end;
|
|
}
|
|
}
|
|
ret = SetupDiGetClassDevsExW(class, enumstrW, parent, flags, deviceset,
|
|
machineW, reserved);
|
|
MyFree(enumstrW);
|
|
MyFree(machineW);
|
|
|
|
end:
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetClassDevsW (SETUPAPI.@)
|
|
*/
|
|
HDEVINFO WINAPI SetupDiGetClassDevsW(
|
|
CONST GUID *class,
|
|
LPCWSTR enumstr,
|
|
HWND parent,
|
|
DWORD flags)
|
|
{
|
|
return SetupDiGetClassDevsExW(class, enumstr, parent, flags, NULL, NULL,
|
|
NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetClassDevsExW (SETUPAPI.@)
|
|
*/
|
|
HDEVINFO WINAPI SetupDiGetClassDevsExW(
|
|
CONST GUID *class,
|
|
PCWSTR enumstr,
|
|
HWND parent,
|
|
DWORD flags,
|
|
HDEVINFO deviceset,
|
|
PCWSTR machine,
|
|
PVOID reserved)
|
|
{
|
|
HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
|
|
struct DeviceInfoSet *list;
|
|
CONST GUID *pClassGuid;
|
|
LONG rc;
|
|
HDEVINFO set = INVALID_HANDLE_VALUE;
|
|
|
|
TRACE("%s %s %p 0x%08x %p %s %p\n", debugstr_guid(class),
|
|
debugstr_w(enumstr), parent, flags, deviceset, debugstr_w(machine),
|
|
reserved);
|
|
|
|
if (!(flags & DIGCF_ALLCLASSES) && !class)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
/* Create the deviceset if not set */
|
|
if (deviceset)
|
|
{
|
|
list = (struct DeviceInfoSet *)deviceset;
|
|
if (list->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
goto cleanup;
|
|
}
|
|
hDeviceInfo = deviceset;
|
|
}
|
|
else
|
|
{
|
|
hDeviceInfo = SetupDiCreateDeviceInfoListExW(
|
|
flags & (DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES) ? NULL : class,
|
|
NULL, machine, NULL);
|
|
if (hDeviceInfo == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
list = (struct DeviceInfoSet *)hDeviceInfo;
|
|
}
|
|
|
|
if (flags & DIGCF_PROFILE)
|
|
FIXME(": flag DIGCF_PROFILE ignored\n");
|
|
|
|
if (flags & DIGCF_DEVICEINTERFACE)
|
|
{
|
|
if (!class)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto cleanup;
|
|
}
|
|
rc = SETUP_CreateInterfaceList(list, machine, class, enumstr, flags & DIGCF_PRESENT);
|
|
}
|
|
else
|
|
{
|
|
/* Determine which class(es) should be included in the deviceset */
|
|
if (flags & DIGCF_ALLCLASSES)
|
|
{
|
|
/* The caller wants all classes. Check if
|
|
* the deviceset limits us to one class */
|
|
if (IsEqualIID(&list->ClassGuid, &GUID_NULL))
|
|
pClassGuid = NULL;
|
|
else
|
|
pClassGuid = &list->ClassGuid;
|
|
}
|
|
else if (class)
|
|
{
|
|
/* The caller wants one class. Check if it matches deviceset class */
|
|
if (IsEqualIID(&list->ClassGuid, class)
|
|
|| IsEqualIID(&list->ClassGuid, &GUID_NULL))
|
|
{
|
|
pClassGuid = class;
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else if (!IsEqualIID(&list->ClassGuid, &GUID_NULL))
|
|
{
|
|
/* No class specified. Try to use the one of the deviceset */
|
|
if (IsEqualIID(&list->ClassGuid, &GUID_NULL))
|
|
pClassGuid = &list->ClassGuid;
|
|
else
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto cleanup;
|
|
}
|
|
rc = SETUP_CreateDevicesList(list, machine, pClassGuid, enumstr);
|
|
}
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
set = hDeviceInfo;
|
|
|
|
cleanup:
|
|
if (!deviceset && hDeviceInfo != INVALID_HANDLE_VALUE && hDeviceInfo != set)
|
|
SetupDiDestroyDeviceInfoList(hDeviceInfo);
|
|
return set;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInfoListDetailA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceInfoListDetailA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_LIST_DETAIL_DATA_A DevInfoData )
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, DevInfoData);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DevInfoData ||
|
|
DevInfoData->cbSize != sizeof(SP_DEVINFO_LIST_DETAIL_DATA_A))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
memcpy(&DevInfoData->ClassGuid, &set->ClassGuid, sizeof(GUID));
|
|
DevInfoData->RemoteMachineHandle = set->hMachine;
|
|
if (set->MachineName)
|
|
{
|
|
FIXME("Stub\n");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
else
|
|
DevInfoData->RemoteMachineName[0] = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInfoListDetailW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceInfoListDetailW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_LIST_DETAIL_DATA_W DevInfoData )
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, DevInfoData);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DevInfoData ||
|
|
DevInfoData->cbSize != sizeof(SP_DEVINFO_LIST_DETAIL_DATA_W))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
memcpy(&DevInfoData->ClassGuid, &set->ClassGuid, sizeof(GUID));
|
|
DevInfoData->RemoteMachineHandle = set->hMachine;
|
|
if (set->MachineName)
|
|
strcpyW(DevInfoData->RemoteMachineName, set->MachineName + 2);
|
|
else
|
|
DevInfoData->RemoteMachineName[0] = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInterfaceA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiCreateDeviceInterfaceA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
const GUID *InterfaceClassGuid,
|
|
PCSTR ReferenceString,
|
|
DWORD CreationFlags,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
|
|
{
|
|
BOOL ret;
|
|
LPWSTR ReferenceStringW = NULL;
|
|
|
|
TRACE("%p %p %s %s %08x %p\n", DeviceInfoSet, DeviceInfoData,
|
|
debugstr_guid(InterfaceClassGuid), debugstr_a(ReferenceString),
|
|
CreationFlags, DeviceInterfaceData);
|
|
|
|
if (ReferenceString)
|
|
{
|
|
ReferenceStringW = pSetupMultiByteToUnicode(ReferenceString, CP_ACP);
|
|
if (ReferenceStringW == NULL) return FALSE;
|
|
}
|
|
|
|
ret = SetupDiCreateDeviceInterfaceW(DeviceInfoSet, DeviceInfoData,
|
|
InterfaceClassGuid, ReferenceStringW, CreationFlags,
|
|
DeviceInterfaceData);
|
|
|
|
MyFree(ReferenceStringW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInterfaceW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiCreateDeviceInterfaceW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
const GUID *InterfaceClassGuid,
|
|
PCWSTR ReferenceString,
|
|
DWORD CreationFlags,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
TRACE("%p %p %s %s %08x %p\n", DeviceInfoSet, DeviceInfoData,
|
|
debugstr_guid(InterfaceClassGuid), debugstr_w(ReferenceString),
|
|
CreationFlags, DeviceInterfaceData);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
|
|
|| !DeviceInfoData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (!InterfaceClassGuid)
|
|
{
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
FIXME("%p %p %s %s %08x %p\n", DeviceInfoSet, DeviceInfoData,
|
|
debugstr_guid(InterfaceClassGuid), debugstr_w(ReferenceString),
|
|
CreationFlags, DeviceInterfaceData);
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInterfaceRegKeyA (SETUPAPI.@)
|
|
*/
|
|
HKEY WINAPI SetupDiCreateDeviceInterfaceRegKeyA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
|
|
DWORD Reserved,
|
|
REGSAM samDesired,
|
|
HINF InfHandle,
|
|
PCSTR InfSectionName)
|
|
{
|
|
HKEY key;
|
|
PWSTR InfSectionNameW = NULL;
|
|
|
|
TRACE("%p %p %d %08x %p %p\n", DeviceInfoSet, DeviceInterfaceData, Reserved,
|
|
samDesired, InfHandle, InfSectionName);
|
|
if (InfHandle)
|
|
{
|
|
if (!InfSectionName)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
InfSectionNameW = pSetupMultiByteToUnicode(InfSectionName, CP_ACP);
|
|
if (!InfSectionNameW)
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
key = SetupDiCreateDeviceInterfaceRegKeyW(DeviceInfoSet,
|
|
DeviceInterfaceData, Reserved, samDesired, InfHandle,
|
|
InfSectionNameW);
|
|
MyFree(InfSectionNameW);
|
|
return key;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCreateDeviceInterfaceRegKeyW (SETUPAPI.@)
|
|
*/
|
|
HKEY WINAPI SetupDiCreateDeviceInterfaceRegKeyW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
|
|
DWORD Reserved,
|
|
REGSAM samDesired,
|
|
HINF InfHandle,
|
|
PCWSTR InfSectionName)
|
|
{
|
|
HKEY hKey, hDevKey;
|
|
LPWSTR SymbolicLink;
|
|
DWORD Length, Index;
|
|
LONG rc;
|
|
WCHAR bracedGuidString[39];
|
|
struct DeviceInterface *DevItf;
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
|
|
TRACE("%p %p %d %08x %p %p\n", DeviceInfoSet, DeviceInterfaceData, Reserved,
|
|
samDesired, InfHandle, InfSectionName);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
|
|
set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (!DeviceInterfaceData ||
|
|
DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA) ||
|
|
!DeviceInterfaceData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (InfHandle && !InfSectionName)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
hKey = SetupDiOpenClassRegKeyExW(&DeviceInterfaceData->InterfaceClassGuid, samDesired, DIOCR_INTERFACE, NULL, NULL);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
{
|
|
hKey = SetupDiOpenClassRegKeyExW(NULL, samDesired, DIOCR_INTERFACE, NULL, NULL);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
SETUPDI_GuidToString(&DeviceInterfaceData->InterfaceClassGuid, bracedGuidString);
|
|
|
|
if (RegCreateKeyExW(hKey, bracedGuidString, 0, NULL, 0, samDesired, NULL, &hDevKey, NULL) != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
RegCloseKey(hKey);
|
|
hKey = hDevKey;
|
|
}
|
|
|
|
DevItf = (struct DeviceInterface *)DeviceInterfaceData->Reserved;
|
|
|
|
Length = (wcslen(DevItf->SymbolicLink)+1) * sizeof(WCHAR);
|
|
SymbolicLink = HeapAlloc(GetProcessHeap(), 0, Length);
|
|
if (!SymbolicLink)
|
|
{
|
|
RegCloseKey(hKey);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
wcscpy(SymbolicLink, DevItf->SymbolicLink);
|
|
|
|
Index = 0;
|
|
while(SymbolicLink[Index])
|
|
{
|
|
if (SymbolicLink[Index] == L'\\')
|
|
{
|
|
SymbolicLink[Index] = L'#';
|
|
}
|
|
Index++;
|
|
}
|
|
|
|
rc = RegCreateKeyExW(hKey, SymbolicLink, 0, NULL, 0, samDesired, NULL, &hDevKey, NULL);
|
|
|
|
RegCloseKey(hKey);
|
|
HeapFree(GetProcessHeap(), 0, SymbolicLink);
|
|
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
if (InfHandle && InfSectionName)
|
|
{
|
|
if (!SetupInstallFromInfSection(NULL /*FIXME */,
|
|
InfHandle,
|
|
InfSectionName,
|
|
SPINST_INIFILES | SPINST_REGISTRY | SPINST_INI2REG | SPINST_FILES | SPINST_BITREG | SPINST_REGSVR | SPINST_UNREGSVR | SPINST_PROFILEITEMS | SPINST_COPYINF,
|
|
hDevKey,
|
|
NULL,
|
|
0,
|
|
set->SelectedDevice->InstallParams.InstallMsgHandler,
|
|
set->SelectedDevice->InstallParams.InstallMsgHandlerContext,
|
|
INVALID_HANDLE_VALUE,
|
|
NULL))
|
|
{
|
|
RegCloseKey(hDevKey);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetLastError(rc);
|
|
return hDevKey;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiDeleteDeviceInterfaceRegKey (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiDeleteDeviceInterfaceRegKey(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
|
|
DWORD Reserved)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p %d\n", DeviceInfoSet, DeviceInterfaceData, Reserved);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
|
|
set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInterfaceData ||
|
|
DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA) ||
|
|
!DeviceInterfaceData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
FIXME("%p %p %d\n", DeviceInfoSet, DeviceInterfaceData, Reserved);
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiEnumDeviceInterfaces (SETUPAPI.@)
|
|
*
|
|
* PARAMS
|
|
* DeviceInfoSet [I] Set of devices from which to enumerate
|
|
* interfaces
|
|
* DeviceInfoData [I] (Optional) If specified, a specific device
|
|
* instance from which to enumerate interfaces.
|
|
* If it isn't specified, all interfaces for all
|
|
* devices in the set are enumerated.
|
|
* InterfaceClassGuid [I] The interface class to enumerate.
|
|
* MemberIndex [I] An index of the interface instance to enumerate.
|
|
* A caller should start with MemberIndex set to 0,
|
|
* and continue until the function fails with
|
|
* ERROR_NO_MORE_ITEMS.
|
|
* DeviceInterfaceData [I/O] Returns an enumerated interface. Its cbSize
|
|
* member must be set to
|
|
* sizeof(SP_DEVICE_INTERFACE_DATA).
|
|
*
|
|
* RETURNS
|
|
* Success: non-zero value.
|
|
* Failure: FALSE. Call GetLastError() for more info.
|
|
*/
|
|
BOOL WINAPI SetupDiEnumDeviceInterfaces(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
CONST GUID * InterfaceClassGuid,
|
|
DWORD MemberIndex,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p, %p, %s, %d, %p\n", DeviceInfoSet, DeviceInfoData,
|
|
debugstr_guid(InterfaceClassGuid), MemberIndex, DeviceInterfaceData);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
|
|
set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (DeviceInfoData && (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA) ||
|
|
!DeviceInfoData->Reserved))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInterfaceData ||
|
|
DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (DeviceInfoData)
|
|
{
|
|
struct DeviceInfo *devInfo =
|
|
(struct DeviceInfo *)DeviceInfoData->Reserved;
|
|
BOOL found = FALSE;
|
|
PLIST_ENTRY InterfaceListEntry = devInfo->InterfaceListHead.Flink;
|
|
while (InterfaceListEntry != &devInfo->InterfaceListHead && !found)
|
|
{
|
|
struct DeviceInterface *DevItf = CONTAINING_RECORD(InterfaceListEntry, struct DeviceInterface, ListEntry);
|
|
if (!IsEqualIID(&DevItf->InterfaceClassGuid, InterfaceClassGuid))
|
|
{
|
|
InterfaceListEntry = InterfaceListEntry->Flink;
|
|
continue;
|
|
}
|
|
if (MemberIndex-- == 0)
|
|
{
|
|
/* return this item */
|
|
memcpy(&DeviceInterfaceData->InterfaceClassGuid,
|
|
&DevItf->InterfaceClassGuid,
|
|
sizeof(GUID));
|
|
DeviceInterfaceData->Flags = DevItf->Flags;
|
|
DeviceInterfaceData->Reserved = (ULONG_PTR)DevItf;
|
|
found = TRUE;
|
|
ret = TRUE;
|
|
}
|
|
InterfaceListEntry = InterfaceListEntry->Flink;
|
|
}
|
|
if (!found)
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
}
|
|
else
|
|
{
|
|
BOOL found = FALSE;
|
|
PLIST_ENTRY ItemList = set->ListHead.Flink;
|
|
while (ItemList != &set->ListHead && !found)
|
|
{
|
|
PLIST_ENTRY InterfaceListEntry;
|
|
struct DeviceInfo *devInfo =
|
|
CONTAINING_RECORD(ItemList, struct DeviceInfo, ListEntry);
|
|
InterfaceListEntry = devInfo->InterfaceListHead.Flink;
|
|
while (InterfaceListEntry != &devInfo->InterfaceListHead && !found)
|
|
{
|
|
struct DeviceInterface *DevItf = CONTAINING_RECORD(InterfaceListEntry, struct DeviceInterface, ListEntry);
|
|
if (!IsEqualIID(&DevItf->InterfaceClassGuid, InterfaceClassGuid))
|
|
{
|
|
InterfaceListEntry = InterfaceListEntry->Flink;
|
|
continue;
|
|
}
|
|
if (MemberIndex-- == 0)
|
|
{
|
|
/* return this item */
|
|
memcpy(&DeviceInterfaceData->InterfaceClassGuid,
|
|
&DevItf->InterfaceClassGuid,
|
|
sizeof(GUID));
|
|
DeviceInterfaceData->Flags = DevItf->Flags;
|
|
DeviceInterfaceData->Reserved = (ULONG_PTR)DevItf;
|
|
found = TRUE;
|
|
ret = TRUE;
|
|
}
|
|
InterfaceListEntry = InterfaceListEntry->Flink;
|
|
}
|
|
ItemList = ItemList->Flink;
|
|
|
|
}
|
|
if (!found)
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiDestroyDeviceInfoList (SETUPAPI.@)
|
|
*
|
|
* Destroy a DeviceInfoList and free all used memory of the list.
|
|
*
|
|
* PARAMS
|
|
* devinfo [I] DeviceInfoList pointer to list to destroy
|
|
*
|
|
* RETURNS
|
|
* Success: non zero value.
|
|
* Failure: zero value.
|
|
*/
|
|
BOOL WINAPI SetupDiDestroyDeviceInfoList(HDEVINFO devinfo)
|
|
{
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p\n", devinfo);
|
|
if (devinfo && devinfo != INVALID_HANDLE_VALUE)
|
|
{
|
|
struct DeviceInfoSet *list = (struct DeviceInfoSet *)devinfo;
|
|
|
|
if (list->magic == SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
ret = DestroyDeviceInfoSet(list);
|
|
}
|
|
}
|
|
|
|
if (ret == FALSE)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInterfaceDetailA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceInterfaceDetailA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData,
|
|
DWORD DeviceInterfaceDetailDataSize,
|
|
PDWORD RequiredSize,
|
|
PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailDataW = NULL;
|
|
DWORD sizeW = 0, bytesNeeded;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("(%p, %p, %p, %d, %p, %p)\n", DeviceInfoSet,
|
|
DeviceInterfaceData, DeviceInterfaceDetailData,
|
|
DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
|
|
set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInterfaceData ||
|
|
DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA) ||
|
|
!DeviceInterfaceData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (DeviceInterfaceDetailData && (DeviceInterfaceDetailData->cbSize != sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A)))
|
|
{
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
if((DeviceInterfaceDetailDataSize != 0) &&
|
|
(DeviceInterfaceDetailDataSize < (FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath) + sizeof(CHAR))))
|
|
{
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!DeviceInterfaceDetailData && DeviceInterfaceDetailDataSize)
|
|
{
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (DeviceInterfaceDetailData != NULL)
|
|
{
|
|
sizeW = FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath)
|
|
+ (DeviceInterfaceDetailDataSize - FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath)) * sizeof(WCHAR);
|
|
DeviceInterfaceDetailDataW = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)MyMalloc(sizeW);
|
|
if (!DeviceInterfaceDetailDataW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
DeviceInterfaceDetailDataW->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
|
}
|
|
if (!DeviceInterfaceDetailData || (DeviceInterfaceDetailData && DeviceInterfaceDetailDataW))
|
|
{
|
|
ret = SetupDiGetDeviceInterfaceDetailW(
|
|
DeviceInfoSet,
|
|
DeviceInterfaceData,
|
|
DeviceInterfaceDetailDataW,
|
|
sizeW,
|
|
&sizeW,
|
|
DeviceInfoData);
|
|
bytesNeeded = (sizeW - FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath)) / sizeof(WCHAR)
|
|
+ FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath);
|
|
if (RequiredSize)
|
|
*RequiredSize = bytesNeeded;
|
|
if (ret && DeviceInterfaceDetailData && DeviceInterfaceDetailDataSize >= bytesNeeded)
|
|
{
|
|
if (!WideCharToMultiByte(
|
|
CP_ACP, 0,
|
|
DeviceInterfaceDetailDataW->DevicePath, -1,
|
|
DeviceInterfaceDetailData->DevicePath, DeviceInterfaceDetailDataSize - FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath),
|
|
NULL, NULL))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
}
|
|
}
|
|
MyFree(DeviceInterfaceDetailDataW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInterfaceDetailW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceInterfaceDetailW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData,
|
|
DWORD DeviceInterfaceDetailDataSize,
|
|
PDWORD RequiredSize,
|
|
PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("(%p, %p, %p, %d, %p, %p)\n", DeviceInfoSet,
|
|
DeviceInterfaceData, DeviceInterfaceDetailData,
|
|
DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
|
|
set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInterfaceData ||
|
|
DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA) ||
|
|
!DeviceInterfaceData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (DeviceInterfaceDetailData && DeviceInterfaceDetailData->cbSize != sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W))
|
|
{
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInterfaceDetailData && DeviceInterfaceDetailDataSize)
|
|
{
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
return FALSE;
|
|
}
|
|
if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if ((DeviceInterfaceDetailData != NULL)
|
|
&& (DeviceInterfaceDetailDataSize < (FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath)) + sizeof(WCHAR)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
struct DeviceInterface *deviceInterface = (struct DeviceInterface *)DeviceInterfaceData->Reserved;
|
|
LPCWSTR devName = deviceInterface->SymbolicLink;
|
|
DWORD sizeRequired = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) +
|
|
(lstrlenW(devName) + 1) * sizeof(WCHAR);
|
|
|
|
if (sizeRequired > DeviceInterfaceDetailDataSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
if (RequiredSize)
|
|
*RequiredSize = sizeRequired;
|
|
}
|
|
else
|
|
{
|
|
strcpyW(DeviceInterfaceDetailData->DevicePath, devName);
|
|
TRACE("DevicePath is %s\n", debugstr_w(DeviceInterfaceDetailData->DevicePath));
|
|
if (DeviceInfoData)
|
|
{
|
|
memcpy(&DeviceInfoData->ClassGuid,
|
|
&deviceInterface->DeviceInfo->ClassGuid,
|
|
sizeof(GUID));
|
|
DeviceInfoData->DevInst = deviceInterface->DeviceInfo->dnDevInst;
|
|
DeviceInfoData->Reserved = (ULONG_PTR)deviceInterface->DeviceInfo;
|
|
}
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
struct PropertyMapEntry
|
|
{
|
|
DWORD regType;
|
|
LPCSTR nameA;
|
|
LPCWSTR nameW;
|
|
};
|
|
|
|
static struct PropertyMapEntry PropertyMap[] = {
|
|
{ REG_SZ, "DeviceDesc", REGSTR_VAL_DEVDESC },
|
|
{ REG_MULTI_SZ, "HardwareId", REGSTR_VAL_HARDWAREID },
|
|
{ REG_MULTI_SZ, "CompatibleIDs", REGSTR_VAL_COMPATIBLEIDS },
|
|
{ 0, NULL, NULL }, /* SPDRP_UNUSED0 */
|
|
{ REG_SZ, "Service", REGSTR_VAL_SERVICE },
|
|
{ 0, NULL, NULL }, /* SPDRP_UNUSED1 */
|
|
{ 0, NULL, NULL }, /* SPDRP_UNUSED2 */
|
|
{ REG_SZ, "Class", REGSTR_VAL_CLASS },
|
|
{ REG_SZ, "ClassGUID", REGSTR_VAL_CLASSGUID },
|
|
{ REG_SZ, "Driver", REGSTR_VAL_DRIVER },
|
|
{ REG_DWORD, "ConfigFlags", REGSTR_VAL_CONFIGFLAGS },
|
|
{ REG_SZ, "Mfg", REGSTR_VAL_MFG },
|
|
{ REG_SZ, "FriendlyName", REGSTR_VAL_FRIENDLYNAME },
|
|
{ REG_SZ, "LocationInformation", REGSTR_VAL_LOCATION_INFORMATION },
|
|
{ 0, NULL, NULL }, /* SPDRP_PHYSICAL_DEVICE_OBJECT_NAME */
|
|
{ REG_DWORD, "Capabilities", REGSTR_VAL_CAPABILITIES },
|
|
{ REG_DWORD, "UINumber", REGSTR_VAL_UI_NUMBER },
|
|
{ REG_MULTI_SZ, "UpperFilters", REGSTR_VAL_UPPERFILTERS },
|
|
{ REG_MULTI_SZ, "LowerFilters", REGSTR_VAL_LOWERFILTERS },
|
|
{ 0, NULL, NULL }, /* SPDRP_BUSTYPEGUID */
|
|
{ 0, NULL, NULL }, /* SPDRP_LEGACYBUSTYPE */
|
|
{ 0, NULL, NULL }, /* SPDRP_BUSNUMBER */
|
|
{ 0, NULL, NULL }, /* SPDRP_ENUMERATOR_NAME */
|
|
{ REG_BINARY, "Security", REGSTR_SECURITY },
|
|
{ 0, NULL, NULL }, /* SPDRP_SECURITY_SDS */
|
|
{ 0, NULL, NULL }, /* SPDRP_DEVTYPE */
|
|
{ 0, NULL, NULL }, /* SPDRP_EXCLUSIVE */
|
|
{ 0, NULL, NULL }, /* SPDRP_CHARACTERISTICS */
|
|
{ 0, NULL, NULL }, /* SPDRP_ADDRESS */
|
|
{ REG_SZ, "UINumberDescFormat", REGSTR_UI_NUMBER_DESC_FORMAT },
|
|
{ 0, NULL, NULL }, /* SPDRP_DEVICE_POWER_DATA */
|
|
{ 0, NULL, NULL }, /* SPDRP_REMOVAL_POLICY */
|
|
{ 0, NULL, NULL }, /* SPDRP_REMOVAL_POLICY_HW_DEFAULT */
|
|
{ 0, NULL, NULL }, /* SPDRP_REMOVAL_POLICY_OVERRIDE */
|
|
{ 0, NULL, NULL }, /* SPDRP_INSTALL_STATE */
|
|
};
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceRegistryPropertyA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Property,
|
|
PDWORD PropertyRegDataType,
|
|
PBYTE PropertyBuffer,
|
|
DWORD PropertyBufferSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
BOOL ret;
|
|
BOOL bIsStringProperty;
|
|
DWORD RegType;
|
|
DWORD RequiredSizeA, RequiredSizeW;
|
|
DWORD PropertyBufferSizeW = 0;
|
|
PBYTE PropertyBufferW = NULL;
|
|
|
|
TRACE("%p %p %d %p %p %d %p\n", DeviceInfoSet, DeviceInfoData,
|
|
Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize,
|
|
RequiredSize);
|
|
|
|
if (PropertyBufferSize != 0)
|
|
{
|
|
PropertyBufferSizeW = PropertyBufferSize * 2;
|
|
PropertyBufferW = HeapAlloc(GetProcessHeap(), 0, PropertyBufferSizeW);
|
|
if (!PropertyBufferW)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ret = SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
Property,
|
|
&RegType,
|
|
PropertyBufferW,
|
|
PropertyBufferSizeW,
|
|
&RequiredSizeW);
|
|
|
|
if (ret || GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
bIsStringProperty = (RegType == REG_SZ || RegType == REG_MULTI_SZ || RegType == REG_EXPAND_SZ);
|
|
|
|
if (bIsStringProperty)
|
|
RequiredSizeA = RequiredSizeW / sizeof(WCHAR);
|
|
else
|
|
RequiredSizeA = RequiredSizeW;
|
|
if (RequiredSize)
|
|
*RequiredSize = RequiredSizeA;
|
|
if (PropertyRegDataType)
|
|
*PropertyRegDataType = RegType;
|
|
}
|
|
|
|
if (!ret)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, PropertyBufferW);
|
|
return ret;
|
|
}
|
|
|
|
if (RequiredSizeA <= PropertyBufferSize)
|
|
{
|
|
if (bIsStringProperty && PropertyBufferSize > 0)
|
|
{
|
|
if (WideCharToMultiByte(CP_ACP, 0, (LPWSTR)PropertyBufferW, RequiredSizeW / sizeof(WCHAR), (LPSTR)PropertyBuffer, PropertyBufferSize, NULL, NULL) == 0)
|
|
{
|
|
/* Last error is already set by WideCharToMultiByte */
|
|
ret = FALSE;
|
|
}
|
|
}
|
|
else
|
|
memcpy(PropertyBuffer, PropertyBufferW, RequiredSizeA);
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
ret = FALSE;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, PropertyBufferW);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceRegistryPropertyW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Property,
|
|
PDWORD PropertyRegDataType,
|
|
PBYTE PropertyBuffer,
|
|
DWORD PropertyBufferSize,
|
|
PDWORD RequiredSize)
|
|
{
|
|
BOOL ret = FALSE;
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
struct DeviceInfo *devInfo;
|
|
|
|
TRACE("%p %p %d %p %p %d %p\n", DeviceInfoSet, DeviceInfoData,
|
|
Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize,
|
|
RequiredSize);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
|
|
|| !DeviceInfoData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
|
|
if (Property < sizeof(PropertyMap) / sizeof(PropertyMap[0])
|
|
&& PropertyMap[Property].nameW)
|
|
{
|
|
DWORD size = PropertyBufferSize;
|
|
HKEY hKey;
|
|
LONG l;
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
l = RegQueryValueExW(hKey, PropertyMap[Property].nameW,
|
|
NULL, PropertyRegDataType, PropertyBuffer, &size);
|
|
RegCloseKey(hKey);
|
|
|
|
if (RequiredSize)
|
|
*RequiredSize = size;
|
|
switch(l) {
|
|
case ERROR_SUCCESS:
|
|
if (PropertyBuffer != NULL || size == 0)
|
|
ret = TRUE;
|
|
else
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
break;
|
|
case ERROR_MORE_DATA:
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
break;
|
|
default:
|
|
SetLastError(l);
|
|
}
|
|
}
|
|
else if (Property == SPDRP_PHYSICAL_DEVICE_OBJECT_NAME)
|
|
{
|
|
DWORD required = (strlenW(devInfo->Data) + 1) * sizeof(WCHAR);
|
|
|
|
if (PropertyRegDataType)
|
|
*PropertyRegDataType = REG_SZ;
|
|
if (RequiredSize)
|
|
*RequiredSize = required;
|
|
if (PropertyBufferSize >= required)
|
|
{
|
|
strcpyW((LPWSTR)PropertyBuffer, devInfo->Data);
|
|
ret = TRUE;
|
|
}
|
|
else
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
else
|
|
{
|
|
ERR("Property 0x%lx not implemented\n", Property);
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Internal for SetupDiSetDeviceRegistryPropertyA/W
|
|
*/
|
|
BOOL WINAPI IntSetupDiSetDeviceRegistryPropertyAW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Property,
|
|
const BYTE *PropertyBuffer,
|
|
DWORD PropertyBufferSize,
|
|
BOOL isAnsi)
|
|
{
|
|
BOOL ret = FALSE;
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
|
|
TRACE("%p %p %d %p %d\n", DeviceInfoSet, DeviceInfoData, Property,
|
|
PropertyBuffer, PropertyBufferSize);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
|
|
|| !DeviceInfoData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (Property < sizeof(PropertyMap) / sizeof(PropertyMap[0])
|
|
&& PropertyMap[Property].nameW
|
|
&& PropertyMap[Property].nameA)
|
|
{
|
|
HKEY hKey;
|
|
LONG l;
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_SET_VALUE);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
/* Write new data */
|
|
if (isAnsi)
|
|
{
|
|
l = RegSetValueExA(
|
|
hKey, PropertyMap[Property].nameA, 0,
|
|
PropertyMap[Property].regType, PropertyBuffer,
|
|
PropertyBufferSize);
|
|
}
|
|
else
|
|
{
|
|
l = RegSetValueExW(
|
|
hKey, PropertyMap[Property].nameW, 0,
|
|
PropertyMap[Property].regType, PropertyBuffer,
|
|
PropertyBufferSize);
|
|
}
|
|
if (!l)
|
|
ret = TRUE;
|
|
else
|
|
SetLastError(l);
|
|
RegCloseKey(hKey);
|
|
}
|
|
else
|
|
{
|
|
ERR("Property 0x%lx not implemented\n", Property);
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
/***********************************************************************
|
|
* SetupDiSetDeviceRegistryPropertyA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiSetDeviceRegistryPropertyA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Property,
|
|
const BYTE *PropertyBuffer,
|
|
DWORD PropertyBufferSize)
|
|
{
|
|
return IntSetupDiSetDeviceRegistryPropertyAW(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
Property,
|
|
PropertyBuffer,
|
|
PropertyBufferSize,
|
|
TRUE);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiSetDeviceRegistryPropertyW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiSetDeviceRegistryPropertyW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Property,
|
|
const BYTE *PropertyBuffer,
|
|
DWORD PropertyBufferSize)
|
|
{
|
|
return IntSetupDiSetDeviceRegistryPropertyAW(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
Property,
|
|
PropertyBuffer,
|
|
PropertyBufferSize,
|
|
FALSE);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiInstallClassA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiInstallClassA(
|
|
HWND hwndParent,
|
|
PCSTR InfFileName,
|
|
DWORD Flags,
|
|
HSPFILEQ FileQueue)
|
|
{
|
|
return SetupDiInstallClassExA(hwndParent, InfFileName, Flags, FileQueue, NULL, NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiInstallClassExA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiInstallClassExA(
|
|
IN HWND hwndParent OPTIONAL,
|
|
IN PCSTR InfFileName OPTIONAL,
|
|
IN DWORD Flags,
|
|
IN HSPFILEQ FileQueue OPTIONAL,
|
|
IN CONST GUID *InterfaceClassGuid OPTIONAL,
|
|
IN PVOID Reserved1,
|
|
IN PVOID Reserved2)
|
|
{
|
|
PWSTR InfFileNameW = NULL;
|
|
BOOL Result;
|
|
|
|
if (!InfFileName)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
InfFileNameW = pSetupMultiByteToUnicode(InfFileName, CP_ACP);
|
|
if (InfFileNameW == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
Result = SetupDiInstallClassExW(hwndParent, InfFileNameW, Flags,
|
|
FileQueue, InterfaceClassGuid, Reserved1, Reserved2);
|
|
|
|
MyFree(InfFileNameW);
|
|
|
|
return Result;
|
|
}
|
|
|
|
HKEY SETUP_CreateClassKey(HINF hInf)
|
|
{
|
|
WCHAR FullBuffer[MAX_PATH];
|
|
WCHAR Buffer[MAX_PATH];
|
|
DWORD RequiredSize;
|
|
HKEY hClassKey;
|
|
DWORD Disposition;
|
|
|
|
/* Obtain the Class GUID for this class */
|
|
if (!SetupGetLineTextW(NULL,
|
|
hInf,
|
|
Version,
|
|
REGSTR_VAL_CLASSGUID,
|
|
Buffer,
|
|
sizeof(Buffer) / sizeof(WCHAR),
|
|
&RequiredSize))
|
|
{
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
/* Build the corresponding registry key name */
|
|
lstrcpyW(FullBuffer, REGSTR_PATH_CLASS_NT);
|
|
lstrcatW(FullBuffer, BackSlash);
|
|
lstrcatW(FullBuffer, Buffer);
|
|
|
|
/* Obtain the Class name for this class */
|
|
if (!SetupGetLineTextW(NULL,
|
|
hInf,
|
|
Version,
|
|
REGSTR_VAL_CLASS,
|
|
Buffer,
|
|
sizeof(Buffer) / sizeof(WCHAR),
|
|
&RequiredSize))
|
|
{
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
/* Try to open or create the registry key */
|
|
TRACE("Opening class key %s\n", debugstr_w(FullBuffer));
|
|
#if 0 // I keep this for reference...
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
FullBuffer,
|
|
0,
|
|
KEY_SET_VALUE,
|
|
&hClassKey))
|
|
{
|
|
/* Use RegCreateKeyExW */
|
|
}
|
|
#endif
|
|
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE,
|
|
FullBuffer,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE,
|
|
NULL,
|
|
&hClassKey,
|
|
&Disposition))
|
|
{
|
|
ERR("RegCreateKeyExW(%s) failed\n", debugstr_w(FullBuffer));
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (Disposition == REG_CREATED_NEW_KEY)
|
|
TRACE("The class key %s was successfully created\n", debugstr_w(FullBuffer));
|
|
else
|
|
TRACE("The class key %s was successfully opened\n", debugstr_w(FullBuffer));
|
|
|
|
TRACE( "setting value %s to %s\n", debugstr_w(REGSTR_VAL_CLASS), debugstr_w(Buffer) );
|
|
if (RegSetValueExW(hClassKey,
|
|
REGSTR_VAL_CLASS,
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)Buffer,
|
|
RequiredSize * sizeof(WCHAR)))
|
|
{
|
|
RegCloseKey(hClassKey);
|
|
RegDeleteKeyW(HKEY_LOCAL_MACHINE,
|
|
FullBuffer);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return hClassKey;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiInstallClassW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiInstallClassW(
|
|
HWND hwndParent,
|
|
PCWSTR InfFileName,
|
|
DWORD Flags,
|
|
HSPFILEQ FileQueue)
|
|
{
|
|
return SetupDiInstallClassExW(hwndParent, InfFileName, Flags, FileQueue, NULL, NULL, NULL);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetupDiOpenClassRegKey (SETUPAPI.@)
|
|
*/
|
|
HKEY WINAPI SetupDiOpenClassRegKey(
|
|
const GUID* ClassGuid,
|
|
REGSAM samDesired)
|
|
{
|
|
return SetupDiOpenClassRegKeyExW(ClassGuid, samDesired,
|
|
DIOCR_INSTALLER, NULL, NULL);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetupDiOpenClassRegKeyExA (SETUPAPI.@)
|
|
*/
|
|
HKEY WINAPI SetupDiOpenClassRegKeyExA(
|
|
const GUID* ClassGuid,
|
|
REGSAM samDesired,
|
|
DWORD Flags,
|
|
PCSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
PWSTR MachineNameW = NULL;
|
|
HKEY hKey;
|
|
|
|
TRACE("%s 0x%lx 0x%lx %s %p\n", debugstr_guid(ClassGuid), samDesired,
|
|
Flags, debugstr_a(MachineName), Reserved);
|
|
|
|
if (MachineName)
|
|
{
|
|
MachineNameW = pSetupMultiByteToUnicode(MachineName, CP_ACP);
|
|
if (MachineNameW == NULL)
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
hKey = SetupDiOpenClassRegKeyExW(ClassGuid, samDesired,
|
|
Flags, MachineNameW, Reserved);
|
|
|
|
MyFree(MachineNameW);
|
|
|
|
return hKey;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetupDiOpenClassRegKeyExW (SETUPAPI.@)
|
|
*/
|
|
HKEY WINAPI SetupDiOpenClassRegKeyExW(
|
|
const GUID* ClassGuid,
|
|
REGSAM samDesired,
|
|
DWORD Flags,
|
|
PCWSTR MachineName,
|
|
PVOID Reserved)
|
|
{
|
|
HKEY HKLM;
|
|
HKEY hClassesKey;
|
|
HKEY key;
|
|
LPCWSTR lpKeyName;
|
|
LONG l;
|
|
|
|
TRACE("%s 0x%lx 0x%lx %s %p\n", debugstr_guid(ClassGuid), samDesired,
|
|
Flags, debugstr_w(MachineName), Reserved);
|
|
|
|
if (MachineName != NULL)
|
|
{
|
|
l = RegConnectRegistryW(MachineName, HKEY_LOCAL_MACHINE, &HKLM);
|
|
if (l != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(l);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
else
|
|
HKLM = HKEY_LOCAL_MACHINE;
|
|
|
|
if (Flags == DIOCR_INSTALLER)
|
|
{
|
|
lpKeyName = REGSTR_PATH_CLASS_NT;
|
|
}
|
|
else if (Flags == DIOCR_INTERFACE)
|
|
{
|
|
lpKeyName = REGSTR_PATH_DEVICE_CLASSES;
|
|
}
|
|
else
|
|
{
|
|
ERR("Invalid Flags parameter!\n");
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
if (MachineName != NULL) RegCloseKey(HKLM);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (!ClassGuid)
|
|
{
|
|
if ((l = RegOpenKeyExW(HKLM,
|
|
lpKeyName,
|
|
0,
|
|
samDesired,
|
|
&hClassesKey)))
|
|
{
|
|
SetLastError(ERROR_INVALID_CLASS);
|
|
hClassesKey = INVALID_HANDLE_VALUE;
|
|
}
|
|
if (MachineName != NULL)
|
|
RegCloseKey(HKLM);
|
|
key = hClassesKey;
|
|
}
|
|
else
|
|
{
|
|
WCHAR bracedGuidString[39];
|
|
|
|
SETUPDI_GuidToString(ClassGuid, bracedGuidString);
|
|
|
|
if (!(l = RegOpenKeyExW(HKLM,
|
|
lpKeyName,
|
|
0,
|
|
samDesired,
|
|
&hClassesKey)))
|
|
{
|
|
if (MachineName != NULL)
|
|
RegCloseKey(HKLM);
|
|
|
|
if ((l = RegOpenKeyExW(hClassesKey,
|
|
bracedGuidString,
|
|
0,
|
|
samDesired,
|
|
&key)))
|
|
{
|
|
SetLastError(l);
|
|
key = INVALID_HANDLE_VALUE;
|
|
}
|
|
RegCloseKey(hClassesKey);
|
|
}
|
|
else
|
|
{
|
|
if (MachineName != NULL) RegCloseKey(HKLM);
|
|
SetLastError(l);
|
|
key = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiOpenDeviceInterfaceW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiOpenDeviceInterfaceW(
|
|
HDEVINFO DeviceInfoSet,
|
|
PCWSTR DevicePath,
|
|
DWORD OpenFlags,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
|
|
{
|
|
struct DeviceInfoSet * list;
|
|
PCWSTR pEnd;
|
|
DWORD dwLength, dwError, dwIndex, dwKeyName, dwSubIndex;
|
|
CLSID ClassId;
|
|
WCHAR Buffer[MAX_PATH + 1];
|
|
WCHAR SymBuffer[MAX_PATH + 1];
|
|
WCHAR InstancePath[MAX_PATH + 1];
|
|
HKEY hKey, hDevKey, hSymKey;
|
|
struct DeviceInfo * deviceInfo;
|
|
struct DeviceInterface *deviceInterface;
|
|
BOOL Ret;
|
|
PLIST_ENTRY ItemList;
|
|
PLIST_ENTRY InterfaceListEntry;
|
|
|
|
TRACE("%p %s %08x %p\n",
|
|
DeviceInfoSet, debugstr_w(DevicePath), OpenFlags, DeviceInterfaceData);
|
|
|
|
|
|
if (DeviceInterfaceData && DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
list = (struct DeviceInfoSet * )DeviceInfoSet;
|
|
|
|
dwLength = wcslen(DevicePath);
|
|
if (dwLength < 39)
|
|
{
|
|
/* path must be at least a guid length + L'\0' */
|
|
SetLastError(ERROR_BAD_PATHNAME);
|
|
return FALSE;
|
|
}
|
|
|
|
if (DevicePath[0] != L'\\' ||
|
|
DevicePath[1] != L'\\' ||
|
|
(DevicePath[2] != L'?' && DevicePath[2] != L'.') ||
|
|
DevicePath[3] != L'\\')
|
|
{
|
|
/* invalid formatted path */
|
|
SetLastError(ERROR_BAD_PATHNAME);
|
|
return FALSE;
|
|
}
|
|
|
|
/* check for reference strings */
|
|
pEnd = wcschr(&DevicePath[4], L'\\');
|
|
if (!pEnd)
|
|
{
|
|
/* no reference string */
|
|
pEnd = DevicePath + dwLength;
|
|
}
|
|
|
|
/* copy guid */
|
|
wcscpy(Buffer, pEnd - 37);
|
|
Buffer[36] = L'\0';
|
|
|
|
dwError = UuidFromStringW(Buffer, &ClassId);
|
|
if (dwError != NOERROR)
|
|
{
|
|
/* invalid formatted path */
|
|
SetLastError(ERROR_BAD_PATHNAME);
|
|
return FALSE;
|
|
}
|
|
|
|
hKey = SetupDiOpenClassRegKeyExW(&ClassId, KEY_READ, DIOCR_INTERFACE, list->MachineName, NULL);
|
|
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
{
|
|
/* invalid device class */
|
|
return FALSE;
|
|
}
|
|
|
|
ItemList = list->ListHead.Flink;
|
|
while (ItemList != &list->ListHead)
|
|
{
|
|
deviceInfo = CONTAINING_RECORD(ItemList, struct DeviceInfo, ListEntry);
|
|
InterfaceListEntry = deviceInfo->InterfaceListHead.Flink;
|
|
while (InterfaceListEntry != &deviceInfo->InterfaceListHead)
|
|
{
|
|
deviceInterface = CONTAINING_RECORD(InterfaceListEntry, struct DeviceInterface, ListEntry);
|
|
if (!IsEqualIID(&deviceInterface->InterfaceClassGuid, &ClassId))
|
|
{
|
|
InterfaceListEntry = InterfaceListEntry->Flink;
|
|
continue;
|
|
}
|
|
|
|
if (!wcsicmp(deviceInterface->SymbolicLink, DevicePath))
|
|
{
|
|
if (DeviceInterfaceData)
|
|
{
|
|
DeviceInterfaceData->Reserved = (ULONG_PTR)deviceInterface;
|
|
DeviceInterfaceData->Flags = deviceInterface->Flags;
|
|
CopyMemory(&DeviceInterfaceData->InterfaceClassGuid, &ClassId, sizeof(GUID));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
dwIndex = 0;
|
|
do
|
|
{
|
|
Buffer[0] = 0;
|
|
dwKeyName = sizeof(Buffer) / sizeof(WCHAR);
|
|
dwError = RegEnumKeyExW(hKey, dwIndex, Buffer, &dwKeyName, NULL, NULL, NULL, NULL);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
break;
|
|
|
|
if (RegOpenKeyExW(hKey, Buffer, 0, KEY_READ, &hDevKey) != ERROR_SUCCESS)
|
|
break;
|
|
|
|
dwSubIndex = 0;
|
|
InstancePath[0] = 0;
|
|
dwKeyName = sizeof(InstancePath);
|
|
|
|
dwError = RegQueryValueExW(hDevKey, L"DeviceInstance", NULL, NULL, (LPBYTE)InstancePath, &dwKeyName);
|
|
|
|
while(TRUE)
|
|
{
|
|
Buffer[0] = 0;
|
|
dwKeyName = sizeof(Buffer) / sizeof(WCHAR);
|
|
dwError = RegEnumKeyExW(hDevKey, dwSubIndex, Buffer, &dwKeyName, NULL, NULL, NULL, NULL);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
break;
|
|
|
|
dwError = RegOpenKeyExW(hDevKey, Buffer, 0, KEY_READ, &hSymKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
break;
|
|
|
|
/* query for symbolic link */
|
|
dwKeyName = sizeof(SymBuffer);
|
|
SymBuffer[0] = L'\0';
|
|
dwError = RegQueryValueExW(hSymKey, L"SymbolicLink", NULL, NULL, (LPBYTE)SymBuffer, &dwKeyName);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hSymKey);
|
|
break;
|
|
}
|
|
|
|
if (!wcsicmp(SymBuffer, DevicePath))
|
|
{
|
|
Ret = CreateDeviceInfo(list, InstancePath, &ClassId, &deviceInfo);
|
|
RegCloseKey(hSymKey);
|
|
RegCloseKey(hDevKey);
|
|
RegCloseKey(hKey);
|
|
|
|
if (Ret)
|
|
{
|
|
deviceInterface = HeapAlloc(GetProcessHeap(), 0, sizeof(struct DeviceInterface) + (wcslen(SymBuffer) + 1) * sizeof(WCHAR));
|
|
if (deviceInterface)
|
|
{
|
|
|
|
CopyMemory(&deviceInterface->InterfaceClassGuid, &ClassId, sizeof(GUID));
|
|
deviceInterface->DeviceInfo = deviceInfo;
|
|
deviceInterface->Flags = SPINT_ACTIVE; //FIXME
|
|
|
|
wcscpy(deviceInterface->SymbolicLink, SymBuffer);
|
|
|
|
InsertTailList(&deviceInfo->InterfaceListHead, &deviceInterface->ListEntry);
|
|
InsertTailList(&list->ListHead, &deviceInfo->ListEntry);
|
|
|
|
|
|
if (DeviceInterfaceData)
|
|
{
|
|
DeviceInterfaceData->Reserved = (ULONG_PTR)deviceInterface;
|
|
DeviceInterfaceData->Flags = deviceInterface->Flags;
|
|
CopyMemory(&DeviceInterfaceData->InterfaceClassGuid, &ClassId, sizeof(GUID));
|
|
}
|
|
else
|
|
{
|
|
Ret = FALSE;
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, deviceInfo);
|
|
Ret = FALSE;
|
|
}
|
|
return Ret;
|
|
}
|
|
RegCloseKey(hSymKey);
|
|
dwSubIndex++;
|
|
}
|
|
|
|
RegCloseKey(hDevKey);
|
|
dwIndex++;
|
|
} while(TRUE);
|
|
|
|
RegCloseKey(hKey);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiOpenDeviceInterfaceA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiOpenDeviceInterfaceA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PCSTR DevicePath,
|
|
DWORD OpenFlags,
|
|
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
|
|
{
|
|
LPWSTR DevicePathW = NULL;
|
|
BOOL bResult;
|
|
|
|
TRACE("%p %s %08lx %p\n", DeviceInfoSet, debugstr_a(DevicePath), OpenFlags, DeviceInterfaceData);
|
|
|
|
DevicePathW = pSetupMultiByteToUnicode(DevicePath, CP_ACP);
|
|
if (DevicePathW == NULL)
|
|
return FALSE;
|
|
|
|
bResult = SetupDiOpenDeviceInterfaceW(DeviceInfoSet,
|
|
DevicePathW, OpenFlags, DeviceInterfaceData);
|
|
|
|
MyFree(DevicePathW);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiSetClassInstallParamsA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiSetClassInstallParamsA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
PSP_CLASSINSTALL_HEADER ClassInstallParams,
|
|
DWORD ClassInstallParamsSize)
|
|
{
|
|
FIXME("%p %p %x %u\n",DeviceInfoSet, DeviceInfoData,
|
|
ClassInstallParams->InstallFunction, ClassInstallParamsSize);
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL WINAPI
|
|
IntSetupDiRegisterDeviceInfo(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN OUT PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
return SetupDiRegisterDeviceInfo(DeviceInfoSet, DeviceInfoData, 0, NULL, NULL, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiCallClassInstaller (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiCallClassInstaller(
|
|
DI_FUNCTION InstallFunction,
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%u %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (((struct DeviceInfoSet *)DeviceInfoSet)->HKLM != HKEY_LOCAL_MACHINE)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
SP_DEVINSTALL_PARAMS_W InstallParams;
|
|
#define CLASS_COINSTALLER 0x1
|
|
#define DEVICE_COINSTALLER 0x2
|
|
#define CLASS_INSTALLER 0x4
|
|
UCHAR CanHandle = 0;
|
|
DEFAULT_CLASS_INSTALL_PROC DefaultHandler = NULL;
|
|
|
|
switch (InstallFunction)
|
|
{
|
|
case DIF_ADDPROPERTYPAGE_ADVANCED:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_ADDREMOTEPROPERTYPAGE_ADVANCED:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_ALLOW_INSTALL:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_DETECT:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_DESTROYPRIVATEDATA:
|
|
CanHandle = CLASS_INSTALLER;
|
|
break;
|
|
case DIF_INSTALLDEVICE:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiInstallDevice;
|
|
break;
|
|
case DIF_INSTALLDEVICEFILES:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiInstallDriverFiles;
|
|
break;
|
|
case DIF_INSTALLINTERFACES:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiInstallDeviceInterfaces;
|
|
break;
|
|
case DIF_NEWDEVICEWIZARD_FINISHINSTALL:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_NEWDEVICEWIZARD_POSTANALYZE:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_NEWDEVICEWIZARD_PREANALYZE:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_NEWDEVICEWIZARD_PRESELECT:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_NEWDEVICEWIZARD_SELECT:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_POWERMESSAGEWAKE:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_PROPERTYCHANGE:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiChangeState;
|
|
break;
|
|
case DIF_REGISTER_COINSTALLERS:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiRegisterCoDeviceInstallers;
|
|
break;
|
|
case DIF_REGISTERDEVICE:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = IntSetupDiRegisterDeviceInfo;
|
|
break;
|
|
case DIF_REMOVE:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiRemoveDevice;
|
|
break;
|
|
case DIF_SELECTBESTCOMPATDRV:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiSelectBestCompatDrv;
|
|
break;
|
|
case DIF_SELECTDEVICE:
|
|
CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiSelectDevice;
|
|
break;
|
|
case DIF_TROUBLESHOOTER:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
break;
|
|
case DIF_UNREMOVE:
|
|
CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
|
|
DefaultHandler = SetupDiUnremoveDevice;
|
|
break;
|
|
default:
|
|
ERR("Install function %u not supported\n", InstallFunction);
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
|
|
if (!SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams))
|
|
/* Don't process this call, as a parameter is invalid */
|
|
CanHandle = 0;
|
|
|
|
if (CanHandle != 0)
|
|
{
|
|
LIST_ENTRY ClassCoInstallersListHead;
|
|
LIST_ENTRY DeviceCoInstallersListHead;
|
|
HMODULE ClassInstallerLibrary = NULL;
|
|
CLASS_INSTALL_PROC ClassInstaller = NULL;
|
|
COINSTALLER_CONTEXT_DATA Context;
|
|
PLIST_ENTRY ListEntry;
|
|
HKEY hKey;
|
|
DWORD dwRegType, dwLength;
|
|
DWORD rc = NO_ERROR;
|
|
|
|
InitializeListHead(&ClassCoInstallersListHead);
|
|
InitializeListHead(&DeviceCoInstallersListHead);
|
|
|
|
if (CanHandle & DEVICE_COINSTALLER)
|
|
{
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
|
|
if (hKey != INVALID_HANDLE_VALUE)
|
|
{
|
|
rc = RegQueryValueExW(hKey, REGSTR_VAL_COINSTALLERS_32, NULL, &dwRegType, NULL, &dwLength);
|
|
if (rc == ERROR_SUCCESS && dwRegType == REG_MULTI_SZ)
|
|
{
|
|
LPWSTR KeyBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);
|
|
if (KeyBuffer != NULL)
|
|
{
|
|
rc = RegQueryValueExW(hKey, REGSTR_VAL_COINSTALLERS_32, NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
LPWSTR ptr;
|
|
for (ptr = KeyBuffer; *ptr; ptr += strlenW(ptr) + 1)
|
|
{
|
|
/* Add coinstaller to DeviceCoInstallersListHead list */
|
|
struct CoInstallerElement *coinstaller;
|
|
TRACE("Got device coinstaller '%s'\n", debugstr_w(ptr));
|
|
coinstaller = HeapAlloc(GetProcessHeap(), 0, sizeof(struct CoInstallerElement));
|
|
if (!coinstaller)
|
|
continue;
|
|
ZeroMemory(coinstaller, sizeof(struct CoInstallerElement));
|
|
if (GetFunctionPointer(ptr, &coinstaller->Module, (PVOID*)&coinstaller->Function) == ERROR_SUCCESS)
|
|
InsertTailList(&DeviceCoInstallersListHead, &coinstaller->ListEntry);
|
|
else
|
|
HeapFree(GetProcessHeap(), 0, coinstaller);
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, KeyBuffer);
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
if (CanHandle & CLASS_COINSTALLER)
|
|
{
|
|
rc = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_CODEVICEINSTALLERS,
|
|
0, /* Options */
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
LPWSTR lpGuidString;
|
|
if (UuidToStringW((UUID*)&DeviceInfoData->ClassGuid, &lpGuidString) == RPC_S_OK)
|
|
{
|
|
rc = RegQueryValueExW(hKey, lpGuidString, NULL, &dwRegType, NULL, &dwLength);
|
|
if (rc == ERROR_SUCCESS && dwRegType == REG_MULTI_SZ)
|
|
{
|
|
LPWSTR KeyBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);
|
|
if (KeyBuffer != NULL)
|
|
{
|
|
rc = RegQueryValueExW(hKey, lpGuidString, NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
LPWSTR ptr;
|
|
for (ptr = KeyBuffer; *ptr; ptr += strlenW(ptr) + 1)
|
|
{
|
|
/* Add coinstaller to ClassCoInstallersListHead list */
|
|
struct CoInstallerElement *coinstaller;
|
|
TRACE("Got class coinstaller '%s'\n", debugstr_w(ptr));
|
|
coinstaller = HeapAlloc(GetProcessHeap(), 0, sizeof(struct CoInstallerElement));
|
|
if (!coinstaller)
|
|
continue;
|
|
ZeroMemory(coinstaller, sizeof(struct CoInstallerElement));
|
|
if (GetFunctionPointer(ptr, &coinstaller->Module, (PVOID*)&coinstaller->Function) == ERROR_SUCCESS)
|
|
InsertTailList(&ClassCoInstallersListHead, &coinstaller->ListEntry);
|
|
else
|
|
HeapFree(GetProcessHeap(), 0, coinstaller);
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, KeyBuffer);
|
|
}
|
|
}
|
|
RpcStringFreeW(&lpGuidString);
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
if ((CanHandle & CLASS_INSTALLER) && !(InstallParams.FlagsEx & DI_FLAGSEX_CI_FAILED))
|
|
{
|
|
hKey = SetupDiOpenClassRegKey(&DeviceInfoData->ClassGuid, KEY_QUERY_VALUE);
|
|
if (hKey != INVALID_HANDLE_VALUE)
|
|
{
|
|
rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, &dwRegType, NULL, &dwLength);
|
|
if (rc == ERROR_SUCCESS && dwRegType == REG_SZ)
|
|
{
|
|
LPWSTR KeyBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);
|
|
if (KeyBuffer != NULL)
|
|
{
|
|
rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
/* Get ClassInstaller function pointer */
|
|
TRACE("Got class installer '%s'\n", debugstr_w(KeyBuffer));
|
|
if (GetFunctionPointer(KeyBuffer, &ClassInstallerLibrary, (PVOID*)&ClassInstaller) != ERROR_SUCCESS)
|
|
{
|
|
InstallParams.FlagsEx |= DI_FLAGSEX_CI_FAILED;
|
|
SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, KeyBuffer);
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
/* Call Class co-installers */
|
|
Context.PostProcessing = FALSE;
|
|
rc = NO_ERROR;
|
|
ListEntry = ClassCoInstallersListHead.Flink;
|
|
while (rc == NO_ERROR && ListEntry != &ClassCoInstallersListHead)
|
|
{
|
|
struct CoInstallerElement *coinstaller;
|
|
coinstaller = CONTAINING_RECORD(ListEntry, struct CoInstallerElement, ListEntry);
|
|
rc = (*coinstaller->Function)(InstallFunction, DeviceInfoSet, DeviceInfoData, &Context);
|
|
coinstaller->PrivateData = Context.PrivateData;
|
|
if (rc == ERROR_DI_POSTPROCESSING_REQUIRED)
|
|
{
|
|
coinstaller->DoPostProcessing = TRUE;
|
|
rc = NO_ERROR;
|
|
}
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
/* Call Device co-installers */
|
|
ListEntry = DeviceCoInstallersListHead.Flink;
|
|
while (rc == NO_ERROR && ListEntry != &DeviceCoInstallersListHead)
|
|
{
|
|
struct CoInstallerElement *coinstaller;
|
|
coinstaller = CONTAINING_RECORD(ListEntry, struct CoInstallerElement, ListEntry);
|
|
rc = (*coinstaller->Function)(InstallFunction, DeviceInfoSet, DeviceInfoData, &Context);
|
|
coinstaller->PrivateData = Context.PrivateData;
|
|
if (rc == ERROR_DI_POSTPROCESSING_REQUIRED)
|
|
{
|
|
coinstaller->DoPostProcessing = TRUE;
|
|
rc = NO_ERROR;
|
|
}
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
/* Call Class installer */
|
|
if (ClassInstaller)
|
|
{
|
|
rc = (*ClassInstaller)(InstallFunction, DeviceInfoSet, DeviceInfoData);
|
|
FreeFunctionPointer(ClassInstallerLibrary, ClassInstaller);
|
|
}
|
|
else
|
|
rc = ERROR_DI_DO_DEFAULT;
|
|
|
|
/* Call default handler */
|
|
if (rc == ERROR_DI_DO_DEFAULT)
|
|
{
|
|
if (DefaultHandler && !(InstallParams.Flags & DI_NODI_DEFAULTACTION))
|
|
{
|
|
if ((*DefaultHandler)(DeviceInfoSet, DeviceInfoData))
|
|
rc = NO_ERROR;
|
|
else
|
|
rc = GetLastError();
|
|
}
|
|
else
|
|
rc = NO_ERROR;
|
|
}
|
|
|
|
/* Call Class co-installers that required postprocessing */
|
|
Context.PostProcessing = TRUE;
|
|
ListEntry = ClassCoInstallersListHead.Flink;
|
|
while (ListEntry != &ClassCoInstallersListHead)
|
|
{
|
|
struct CoInstallerElement *coinstaller;
|
|
coinstaller = CONTAINING_RECORD(ListEntry, struct CoInstallerElement, ListEntry);
|
|
if (coinstaller->DoPostProcessing)
|
|
{
|
|
Context.InstallResult = rc;
|
|
Context.PrivateData = coinstaller->PrivateData;
|
|
rc = (*coinstaller->Function)(InstallFunction, DeviceInfoSet, DeviceInfoData, &Context);
|
|
}
|
|
FreeFunctionPointer(coinstaller->Module, coinstaller->Function);
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
/* Call Device co-installers that required postprocessing */
|
|
ListEntry = DeviceCoInstallersListHead.Flink;
|
|
while (ListEntry != &DeviceCoInstallersListHead)
|
|
{
|
|
struct CoInstallerElement *coinstaller;
|
|
coinstaller = CONTAINING_RECORD(ListEntry, struct CoInstallerElement, ListEntry);
|
|
if (coinstaller->DoPostProcessing)
|
|
{
|
|
Context.InstallResult = rc;
|
|
Context.PrivateData = coinstaller->PrivateData;
|
|
rc = (*coinstaller->Function)(InstallFunction, DeviceInfoSet, DeviceInfoData, &Context);
|
|
}
|
|
FreeFunctionPointer(coinstaller->Module, coinstaller->Function);
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
/* Free allocated memory */
|
|
while (!IsListEmpty(&ClassCoInstallersListHead))
|
|
{
|
|
ListEntry = RemoveHeadList(&ClassCoInstallersListHead);
|
|
HeapFree(GetProcessHeap(), 0, CONTAINING_RECORD(ListEntry, struct CoInstallerElement, ListEntry));
|
|
}
|
|
while (!IsListEmpty(&DeviceCoInstallersListHead))
|
|
{
|
|
ListEntry = RemoveHeadList(&DeviceCoInstallersListHead);
|
|
HeapFree(GetProcessHeap(), 0, CONTAINING_RECORD(ListEntry, struct CoInstallerElement, ListEntry));
|
|
}
|
|
|
|
ret = (rc == NO_ERROR);
|
|
}
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInstallParamsA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiGetDeviceInstallParamsA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
PSP_DEVINSTALL_PARAMS_A DeviceInstallParams)
|
|
{
|
|
SP_DEVINSTALL_PARAMS_W deviceInstallParamsW;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams);
|
|
|
|
if (DeviceInstallParams == NULL)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInstallParams->cbSize != sizeof(SP_DEVINSTALL_PARAMS_A))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
deviceInstallParamsW.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
|
|
ret = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &deviceInstallParamsW);
|
|
|
|
if (ret)
|
|
{
|
|
/* Do W->A conversion */
|
|
memcpy(
|
|
DeviceInstallParams,
|
|
&deviceInstallParamsW,
|
|
FIELD_OFFSET(SP_DEVINSTALL_PARAMS_W, DriverPath));
|
|
if (WideCharToMultiByte(CP_ACP, 0, deviceInstallParamsW.DriverPath, -1,
|
|
DeviceInstallParams->DriverPath, MAX_PATH, NULL, NULL) == 0)
|
|
{
|
|
DeviceInstallParams->DriverPath[0] = '\0';
|
|
ret = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInfoListClass (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiGetDeviceInfoListClass(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
OUT LPGUID ClassGuid)
|
|
{
|
|
struct DeviceInfoSet *list;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, ClassGuid);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (IsEqualIID(&list->ClassGuid, &GUID_NULL))
|
|
SetLastError(ERROR_NO_ASSOCIATED_CLASS);
|
|
else
|
|
{
|
|
memcpy(&ClassGuid, &list->ClassGuid, sizeof(GUID));
|
|
|
|
ret = TRUE;
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetDeviceInstallParamsW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiGetDeviceInstallParamsW(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
|
|
OUT PSP_DEVINSTALL_PARAMS_W DeviceInstallParams)
|
|
{
|
|
struct DeviceInfoSet *list;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else if (!DeviceInstallParams)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInstallParams->cbSize != sizeof(SP_DEVINSTALL_PARAMS_W))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
PSP_DEVINSTALL_PARAMS_W Source;
|
|
|
|
if (DeviceInfoData)
|
|
Source = &((struct DeviceInfo *)DeviceInfoData->Reserved)->InstallParams;
|
|
else
|
|
Source = &list->InstallParams;
|
|
|
|
ret = TRUE;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
memcpy(DeviceInstallParams, Source, Source->cbSize);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(_SEH2_GetExceptionCode()));
|
|
ret = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL
|
|
CheckDeviceInstallParameters(
|
|
IN PSP_DEVINSTALL_PARAMS_W DeviceInstallParams)
|
|
{
|
|
DWORD SupportedFlags =
|
|
DI_NOVCP | /* 0x00000008 */
|
|
DI_DIDCOMPAT | /* 0x00000010 */
|
|
DI_DIDCLASS | /* 0x00000020 */
|
|
DI_NEEDRESTART | /* 0x00000080 */
|
|
DI_NEEDREBOOT | /* 0x00000100 */
|
|
DI_RESOURCEPAGE_ADDED | /* 0x00002000 */
|
|
DI_PROPERTIES_CHANGE | /* 0x00004000 */
|
|
DI_ENUMSINGLEINF | /* 0x00010000 */
|
|
DI_DONOTCALLCONFIGMG | /* 0x00020000 */
|
|
DI_CLASSINSTALLPARAMS | /* 0x00100000 */
|
|
DI_NODI_DEFAULTACTION | /* 0x00200000 */
|
|
DI_QUIETINSTALL | /* 0x00800000 */
|
|
DI_NOFILECOPY | /* 0x01000000 */
|
|
DI_DRIVERPAGE_ADDED; /* 0x04000000 */
|
|
DWORD SupportedFlagsEx =
|
|
DI_FLAGSEX_CI_FAILED | /* 0x00000004 */
|
|
DI_FLAGSEX_DIDINFOLIST | /* 0x00000010 */
|
|
DI_FLAGSEX_DIDCOMPATINFO | /* 0x00000020 */
|
|
DI_FLAGSEX_ALLOWEXCLUDEDDRVS | /* 0x00000800 */
|
|
DI_FLAGSEX_NO_DRVREG_MODIFY | /* 0x00008000 */
|
|
DI_FLAGSEX_INSTALLEDDRIVER; /* 0x04000000 */
|
|
BOOL ret = FALSE;
|
|
|
|
/* FIXME: add support for more flags */
|
|
|
|
/* FIXME: DI_CLASSINSTALLPARAMS flag is not correctly used.
|
|
* It should be checked before accessing to other values
|
|
* of the SP_DEVINSTALL_PARAMS structure */
|
|
|
|
if (DeviceInstallParams->Flags & ~SupportedFlags)
|
|
{
|
|
FIXME("Unknown Flags: 0x%08lx\n", DeviceInstallParams->Flags & ~SupportedFlags);
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
}
|
|
else if (DeviceInstallParams->FlagsEx & ~SupportedFlagsEx)
|
|
{
|
|
FIXME("Unknown FlagsEx: 0x%08lx\n", DeviceInstallParams->FlagsEx & ~SupportedFlagsEx);
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
}
|
|
else if ((DeviceInstallParams->Flags & DI_NOVCP)
|
|
&& (DeviceInstallParams->FileQueue == NULL || DeviceInstallParams->FileQueue == (HSPFILEQ)INVALID_HANDLE_VALUE))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
/* FIXME: check Reserved field */
|
|
ret = TRUE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiSetDeviceInstallParamsW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiSetDeviceInstallParamsW(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
|
|
IN PSP_DEVINSTALL_PARAMS_W DeviceInstallParams)
|
|
{
|
|
struct DeviceInfoSet *list;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else if (!DeviceInstallParams)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInstallParams->cbSize != sizeof(SP_DEVINSTALL_PARAMS_W))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else if (CheckDeviceInstallParameters(DeviceInstallParams))
|
|
{
|
|
PSP_DEVINSTALL_PARAMS_W Destination;
|
|
|
|
if (DeviceInfoData)
|
|
Destination = &((struct DeviceInfo *)DeviceInfoData->Reserved)->InstallParams;
|
|
else
|
|
Destination = &list->InstallParams;
|
|
memcpy(Destination, DeviceInstallParams, DeviceInstallParams->cbSize);
|
|
ret = TRUE;
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiSetDeviceInstallParamsW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiSetDeviceInstallParamsA(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
PSP_DEVINSTALL_PARAMS_A DeviceInstallParams)
|
|
{
|
|
SP_DEVINSTALL_PARAMS_W deviceInstallParamsW;
|
|
int len = 0;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams);
|
|
|
|
if (DeviceInstallParams == NULL)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInstallParams->cbSize < sizeof(SP_DEVINSTALL_PARAMS_A))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
memcpy(&deviceInstallParamsW, DeviceInstallParams, FIELD_OFFSET(SP_DEVINSTALL_PARAMS_A, DriverPath));
|
|
deviceInstallParamsW.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
|
|
len = MultiByteToWideChar(CP_ACP, 0, DeviceInstallParams->DriverPath, -1, NULL, 0);
|
|
if (!len)
|
|
{
|
|
ERR("DrivePath is NULL\n");
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, DeviceInstallParams->DriverPath, -1, deviceInstallParamsW.DriverPath, len);
|
|
ret = SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &deviceInstallParamsW);
|
|
}
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static HKEY
|
|
OpenHardwareProfileKey(
|
|
IN HKEY HKLM,
|
|
IN DWORD HwProfile,
|
|
IN DWORD samDesired)
|
|
{
|
|
HKEY hHWProfilesKey = NULL;
|
|
HKEY hHWProfileKey = NULL;
|
|
HKEY ret = INVALID_HANDLE_VALUE;
|
|
LONG rc;
|
|
|
|
rc = RegOpenKeyExW(HKLM,
|
|
REGSTR_PATH_HWPROFILES,
|
|
0,
|
|
READ_CONTROL,
|
|
&hHWProfilesKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
if (HwProfile == 0)
|
|
{
|
|
rc = RegOpenKeyExW(hHWProfilesKey,
|
|
REGSTR_KEY_CURRENT,
|
|
0,
|
|
KEY_CREATE_SUB_KEY,
|
|
&hHWProfileKey);
|
|
}
|
|
else
|
|
{
|
|
WCHAR subKey[5];
|
|
snprintfW(subKey, 4, InstanceKeyFormat, HwProfile);
|
|
subKey[4] = '\0';
|
|
rc = RegOpenKeyExW(hHWProfilesKey,
|
|
subKey,
|
|
0,
|
|
KEY_CREATE_SUB_KEY,
|
|
&hHWProfileKey);
|
|
}
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
ret = hHWProfileKey;
|
|
|
|
cleanup:
|
|
if (hHWProfilesKey != NULL)
|
|
RegCloseKey(hHWProfilesKey);
|
|
if (hHWProfileKey != NULL && hHWProfileKey != ret)
|
|
RegCloseKey(hHWProfileKey);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL
|
|
IsDeviceInfoInDeviceInfoSet(
|
|
struct DeviceInfoSet *deviceInfoSet,
|
|
struct DeviceInfo *deviceInfo)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
ListEntry = deviceInfoSet->ListHead.Flink;
|
|
while (ListEntry != &deviceInfoSet->ListHead)
|
|
{
|
|
if (deviceInfo == CONTAINING_RECORD(ListEntry, struct DeviceInfo, ListEntry))
|
|
return TRUE;
|
|
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiDeleteDeviceInfo (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiDeleteDeviceInfo(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
struct DeviceInfoSet *deviceInfoSet;
|
|
struct DeviceInfo *deviceInfo = (struct DeviceInfo *)DeviceInfoData;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if ((deviceInfoSet = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else if (!IsDeviceInfoInDeviceInfoSet(deviceInfoSet, deviceInfo))
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else
|
|
{
|
|
RemoveEntryList(&deviceInfo->ListEntry);
|
|
DestroyDeviceInfo(deviceInfo);
|
|
ret = TRUE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetupDiOpenDeviceInfoA (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiOpenDeviceInfoA(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PCSTR DeviceInstanceId,
|
|
IN HWND hwndParent OPTIONAL,
|
|
IN DWORD OpenFlags,
|
|
OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
|
|
{
|
|
LPWSTR DeviceInstanceIdW = NULL;
|
|
BOOL bResult;
|
|
|
|
TRACE("%p %s %p %lx %p\n", DeviceInfoSet, DeviceInstanceId, hwndParent, OpenFlags, DeviceInfoData);
|
|
|
|
DeviceInstanceIdW = pSetupMultiByteToUnicode(DeviceInstanceId, CP_ACP);
|
|
if (DeviceInstanceIdW == NULL)
|
|
return FALSE;
|
|
|
|
bResult = SetupDiOpenDeviceInfoW(DeviceInfoSet,
|
|
DeviceInstanceIdW, hwndParent, OpenFlags, DeviceInfoData);
|
|
|
|
MyFree(DeviceInstanceIdW);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetupDiOpenDeviceInfoW (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiOpenDeviceInfoW(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PCWSTR DeviceInstanceId,
|
|
IN HWND hwndParent OPTIONAL,
|
|
IN DWORD OpenFlags,
|
|
OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
|
|
{
|
|
struct DeviceInfoSet *list;
|
|
HKEY hEnumKey, hKey = NULL;
|
|
DWORD rc, dwSize;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %s %p %lx %p\n",
|
|
DeviceInfoSet, debugstr_w(DeviceInstanceId),
|
|
hwndParent, OpenFlags, DeviceInfoData);
|
|
|
|
if (OpenFlags & DIOD_CANCEL_REMOVE)
|
|
FIXME("DIOD_CANCEL_REMOVE flag not implemented\n");
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (!DeviceInstanceId)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (OpenFlags & ~(DIOD_CANCEL_REMOVE | DIOD_INHERIT_CLASSDRVS))
|
|
{
|
|
TRACE("Unknown flags: 0x%08lx\n", OpenFlags & ~(DIOD_CANCEL_REMOVE | DIOD_INHERIT_CLASSDRVS));
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
}
|
|
else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
struct DeviceInfo *deviceInfo = NULL;
|
|
/* Search if device already exists in DeviceInfoSet.
|
|
* If yes, return the existing element
|
|
* If no, create a new element using information in registry
|
|
*/
|
|
PLIST_ENTRY ItemList = list->ListHead.Flink;
|
|
while (ItemList != &list->ListHead)
|
|
{
|
|
deviceInfo = CONTAINING_RECORD(ItemList, struct DeviceInfo, ListEntry);
|
|
if (!wcscmp(deviceInfo->instanceId, DeviceInstanceId))
|
|
break;
|
|
deviceInfo = NULL;
|
|
ItemList = ItemList->Flink;
|
|
}
|
|
|
|
if (deviceInfo)
|
|
{
|
|
/* good one found */
|
|
ret = TRUE;
|
|
}
|
|
else
|
|
{
|
|
GUID ClassGUID;
|
|
WCHAR szClassGuid[MAX_GUID_STRING_LEN];
|
|
|
|
/* Open supposed registry key */
|
|
rc = RegOpenKeyExW(
|
|
list->HKLM,
|
|
REGSTR_PATH_SYSTEMENUM,
|
|
0, /* Options */
|
|
READ_CONTROL,
|
|
&hEnumKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
rc = RegOpenKeyExW(
|
|
hEnumKey,
|
|
DeviceInstanceId,
|
|
0, /* Options */
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
RegCloseKey(hEnumKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
if (rc == ERROR_FILE_NOT_FOUND)
|
|
rc = ERROR_NO_SUCH_DEVINST;
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
ClassGUID = GUID_NULL;
|
|
dwSize = MAX_GUID_STRING_LEN * sizeof(WCHAR);
|
|
|
|
if (RegQueryValueExW(hKey,
|
|
REGSTR_VAL_CLASSGUID,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szClassGuid,
|
|
&dwSize) == ERROR_SUCCESS)
|
|
{
|
|
szClassGuid[MAX_GUID_STRING_LEN - 2] = UNICODE_NULL;
|
|
|
|
/* Convert a string to a ClassGuid */
|
|
UuidFromStringW(&szClassGuid[1], &ClassGUID);
|
|
}
|
|
|
|
if (!CreateDeviceInfo(list, DeviceInstanceId, &ClassGUID, &deviceInfo))
|
|
goto cleanup;
|
|
|
|
InsertTailList(&list->ListHead, &deviceInfo->ListEntry);
|
|
|
|
ret = TRUE;
|
|
}
|
|
|
|
if (ret && deviceInfo && DeviceInfoData)
|
|
{
|
|
memcpy(&DeviceInfoData->ClassGuid, &deviceInfo->ClassGuid, sizeof(GUID));
|
|
DeviceInfoData->DevInst = deviceInfo->dnDevInst;
|
|
DeviceInfoData->Reserved = (ULONG_PTR)deviceInfo;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetupDiGetSelectedDevice (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiGetSelectedDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
OUT PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
struct DeviceInfoSet *list;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (list->SelectedDevice == NULL)
|
|
SetLastError(ERROR_NO_DEVICE_SELECTED);
|
|
else if (!DeviceInfoData)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
memcpy(&DeviceInfoData->ClassGuid,
|
|
&list->SelectedDevice->ClassGuid,
|
|
sizeof(GUID));
|
|
DeviceInfoData->DevInst = list->SelectedDevice->dnDevInst;
|
|
DeviceInfoData->Reserved = (ULONG_PTR)list->SelectedDevice;
|
|
ret = TRUE;
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetupDiSetSelectedDevice (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiSetSelectedDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
struct DeviceInfoSet *list;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (!DeviceInfoData)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else if (DeviceInfoData->Reserved == 0)
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
list->SelectedDevice = (struct DeviceInfo *)DeviceInfoData->Reserved;
|
|
ret = TRUE;
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Return the current hardware profile id, or -1 if error */
|
|
static DWORD
|
|
SETUPAPI_GetCurrentHwProfile(
|
|
IN HDEVINFO DeviceInfoSet)
|
|
{
|
|
HKEY hKey = NULL;
|
|
DWORD dwRegType, dwLength;
|
|
DWORD hwProfile;
|
|
LONG rc;
|
|
DWORD ret = (DWORD)-1;
|
|
|
|
rc = RegOpenKeyExW(((struct DeviceInfoSet *)DeviceInfoSet)->HKLM,
|
|
REGSTR_PATH_IDCONFIGDB,
|
|
0, /* Options */
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
dwLength = sizeof(DWORD);
|
|
rc = RegQueryValueExW(hKey,
|
|
REGSTR_VAL_CURRENTCONFIG,
|
|
NULL,
|
|
&dwRegType,
|
|
(LPBYTE)&hwProfile, &dwLength);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
else if (dwRegType != REG_DWORD || dwLength != sizeof(DWORD))
|
|
{
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = hwProfile;
|
|
|
|
cleanup:
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static BOOL
|
|
ResetDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
#ifndef __WINESRC__
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
struct DeviceInfo *deviceInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
|
|
CONFIGRET cr;
|
|
|
|
cr = CM_Enable_DevNode_Ex(deviceInfo->dnDevInst, 0, set->hMachine);
|
|
if (cr != CR_SUCCESS)
|
|
{
|
|
SetLastError(GetErrorCodeFromCrCode(cr));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
#else
|
|
FIXME("Stub: ResetDevice(%p %p)\n", DeviceInfoSet, DeviceInfoData);
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
|
|
static BOOL StopDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
FIXME("Stub: StopDevice(%p %p)\n", DeviceInfoSet, DeviceInfoData);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiChangeState (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiChangeState(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN OUT PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
PSP_PROPCHANGE_PARAMS PropChange;
|
|
HKEY hKey = INVALID_HANDLE_VALUE;
|
|
LPCWSTR RegistryValueName;
|
|
DWORD dwConfigFlags, dwLength, dwRegType;
|
|
LONG rc;
|
|
BOOL ret = FALSE;
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
|
|
|
|
if (!DeviceInfoData)
|
|
PropChange = ((struct DeviceInfoSet *)DeviceInfoSet)->ClassInstallParams.PropChangeParams;
|
|
else
|
|
PropChange = ((struct DeviceInfo *)DeviceInfoData->Reserved)->ClassInstallParams.PropChangeParams;
|
|
if (!PropChange)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (PropChange->Scope == DICS_FLAG_GLOBAL)
|
|
RegistryValueName = REGSTR_VAL_CONFIGFLAGS;
|
|
else
|
|
RegistryValueName = REGSTR_VAL_CSCONFIGFLAGS;
|
|
|
|
switch (PropChange->StateChange)
|
|
{
|
|
case DICS_ENABLE:
|
|
case DICS_DISABLE:
|
|
{
|
|
/* Enable/disable device in registry */
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, PropChange->Scope, PropChange->HwProfile, DIREG_DEV, KEY_QUERY_VALUE | KEY_SET_VALUE);
|
|
if (hKey == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
|
|
hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, PropChange->Scope, PropChange->HwProfile, DIREG_DEV, NULL, NULL);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
break;
|
|
dwLength = sizeof(DWORD);
|
|
rc = RegQueryValueExW(
|
|
hKey,
|
|
RegistryValueName,
|
|
NULL,
|
|
&dwRegType,
|
|
(LPBYTE)&dwConfigFlags, &dwLength);
|
|
if (rc == ERROR_FILE_NOT_FOUND)
|
|
dwConfigFlags = 0;
|
|
else if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
else if (dwRegType != REG_DWORD || dwLength != sizeof(DWORD))
|
|
{
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto cleanup;
|
|
}
|
|
if (PropChange->StateChange == DICS_ENABLE)
|
|
dwConfigFlags &= ~(PropChange->Scope == DICS_FLAG_GLOBAL ? CONFIGFLAG_DISABLED : CSCONFIGFLAG_DISABLED);
|
|
else
|
|
dwConfigFlags |= (PropChange->Scope == DICS_FLAG_GLOBAL ? CONFIGFLAG_DISABLED : CSCONFIGFLAG_DISABLED);
|
|
rc = RegSetValueExW(
|
|
hKey,
|
|
RegistryValueName,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwConfigFlags, sizeof(DWORD));
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Enable/disable device if needed */
|
|
if (PropChange->Scope == DICS_FLAG_GLOBAL
|
|
|| PropChange->HwProfile == 0
|
|
|| PropChange->HwProfile == SETUPAPI_GetCurrentHwProfile(DeviceInfoSet))
|
|
{
|
|
if (PropChange->StateChange == DICS_ENABLE)
|
|
ret = ResetDevice(DeviceInfoSet, DeviceInfoData);
|
|
else
|
|
ret = StopDevice(DeviceInfoSet, DeviceInfoData);
|
|
}
|
|
else
|
|
ret = TRUE;
|
|
break;
|
|
}
|
|
case DICS_PROPCHANGE:
|
|
{
|
|
ret = ResetDevice(DeviceInfoSet, DeviceInfoData);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ERR("Unknown StateChange 0x%lx\n", PropChange->StateChange);
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
if (hKey != INVALID_HANDLE_VALUE)
|
|
RegCloseKey(hKey);
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiSelectDevice (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiSelectDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
|
|
{
|
|
FIXME("%p %p\n", DeviceInfoSet, DeviceInfoData);
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetupDiRegisterCoDeviceInstallers (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiRegisterCoDeviceInstallers(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
BOOL ret = FALSE; /* Return value */
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (!DeviceInfoData)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
{
|
|
SP_DEVINSTALL_PARAMS_W InstallParams;
|
|
struct DriverInfoElement *SelectedDriver;
|
|
BOOL Result;
|
|
DWORD DoAction;
|
|
WCHAR SectionName[MAX_PATH];
|
|
DWORD SectionNameLength = 0;
|
|
HKEY hKey = INVALID_HANDLE_VALUE;
|
|
PVOID Context = NULL;
|
|
|
|
InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
|
|
Result = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
|
|
if (!Result)
|
|
goto cleanup;
|
|
|
|
SelectedDriver = (struct DriverInfoElement *)InstallParams.Reserved;
|
|
if (SelectedDriver == NULL)
|
|
{
|
|
SetLastError(ERROR_NO_DRIVER_SELECTED);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Get .CoInstallers section name */
|
|
Result = SetupDiGetActualSectionToInstallW(
|
|
SelectedDriver->InfFileDetails->hInf,
|
|
SelectedDriver->Details.SectionName,
|
|
SectionName, MAX_PATH, &SectionNameLength, NULL);
|
|
if (!Result || SectionNameLength > MAX_PATH - strlenW(DotCoInstallers) - 1)
|
|
goto cleanup;
|
|
lstrcatW(SectionName, DotCoInstallers);
|
|
|
|
/* Open/Create driver key information */
|
|
#if _WIN32_WINNT >= 0x502
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_WRITE);
|
|
#else
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS);
|
|
#endif
|
|
if (hKey == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
|
|
hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
|
|
/* Install .CoInstallers section */
|
|
DoAction = SPINST_REGISTRY;
|
|
if (!(InstallParams.Flags & DI_NOFILECOPY))
|
|
{
|
|
DoAction |= SPINST_FILES;
|
|
Context = SetupInitDefaultQueueCallback(InstallParams.hwndParent);
|
|
if (!Context)
|
|
goto cleanup;
|
|
}
|
|
Result = SetupInstallFromInfSectionW(InstallParams.hwndParent,
|
|
SelectedDriver->InfFileDetails->hInf, SectionName,
|
|
DoAction, hKey, SelectedDriver->InfFileDetails->DirectoryName, SP_COPY_NEWER,
|
|
SetupDefaultQueueCallbackW, Context,
|
|
DeviceInfoSet, DeviceInfoData);
|
|
if (!Result)
|
|
goto cleanup;
|
|
|
|
ret = TRUE;
|
|
|
|
cleanup:
|
|
if (Context)
|
|
SetupTermDefaultQueueCallback(Context);
|
|
if (hKey != INVALID_HANDLE_VALUE)
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL
|
|
InfIsFromOEMLocation(
|
|
IN PCWSTR FullName,
|
|
OUT LPBOOL IsOEMLocation)
|
|
{
|
|
PWCHAR last;
|
|
|
|
last = strrchrW(FullName, '\\');
|
|
if (!last)
|
|
{
|
|
/* No directory specified */
|
|
*IsOEMLocation = FALSE;
|
|
}
|
|
else
|
|
{
|
|
LPWSTR Windir;
|
|
UINT ret;
|
|
|
|
Windir = MyMalloc((MAX_PATH + 1 + strlenW(InfDirectory)) * sizeof(WCHAR));
|
|
if (!Windir)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
ret = GetSystemWindowsDirectoryW(Windir, MAX_PATH);
|
|
if (ret == 0 || ret > MAX_PATH)
|
|
{
|
|
MyFree(Windir);
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
return FALSE;
|
|
}
|
|
if (*Windir && Windir[strlenW(Windir) - 1] != '\\')
|
|
strcatW(Windir, BackSlash);
|
|
strcatW(Windir, InfDirectory);
|
|
|
|
if (strncmpiW(FullName, Windir, last - FullName) == 0)
|
|
{
|
|
/* The path is %SYSTEMROOT%\Inf */
|
|
*IsOEMLocation = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* The file is in another place */
|
|
*IsOEMLocation = TRUE;
|
|
}
|
|
MyFree(Windir);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiInstallDevice (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI
|
|
SetupDiInstallDevice(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN OUT PSP_DEVINFO_DATA DeviceInfoData)
|
|
{
|
|
SP_DEVINSTALL_PARAMS_W InstallParams;
|
|
struct DriverInfoElement *SelectedDriver;
|
|
SYSTEMTIME DriverDate;
|
|
WCHAR SectionName[MAX_PATH];
|
|
WCHAR Buffer[32];
|
|
DWORD SectionNameLength = 0;
|
|
BOOL Result = FALSE;
|
|
ULONG DoAction;
|
|
DWORD RequiredSize;
|
|
LPWSTR pSectionName = NULL;
|
|
WCHAR ClassName[MAX_CLASS_NAME_LEN];
|
|
GUID ClassGuid;
|
|
LPWSTR lpGuidString = NULL, lpFullGuidString = NULL;
|
|
BOOL RebootRequired = FALSE;
|
|
HKEY hKey = INVALID_HANDLE_VALUE;
|
|
BOOL NeedtoCopyFile;
|
|
LARGE_INTEGER fullVersion;
|
|
LONG rc;
|
|
PVOID Context = NULL;
|
|
BOOL ret = FALSE; /* Return value */
|
|
|
|
TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
|
|
|
|
if (!DeviceInfoSet)
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
|
|
SetLastError(ERROR_INVALID_USER_BUFFER);
|
|
else
|
|
Result = TRUE;
|
|
|
|
if (!Result)
|
|
{
|
|
/* One parameter is bad */
|
|
goto cleanup;
|
|
}
|
|
|
|
InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
|
|
Result = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
|
|
if (!Result)
|
|
goto cleanup;
|
|
|
|
if (InstallParams.FlagsEx & DI_FLAGSEX_SETFAILEDINSTALL)
|
|
{
|
|
/* Set FAILEDINSTALL in ConfigFlags registry value */
|
|
DWORD ConfigFlags, regType;
|
|
Result = SetupDiGetDeviceRegistryPropertyW(
|
|
DeviceInfoSet,
|
|
DeviceInfoData,
|
|
SPDRP_CONFIGFLAGS,
|
|
®Type,
|
|
(PBYTE)&ConfigFlags,
|
|
sizeof(ConfigFlags),
|
|
NULL);
|
|
if (!Result || regType != REG_DWORD)
|
|
{
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto cleanup;
|
|
}
|
|
ConfigFlags |= DNF_DISABLED;
|
|
Result = SetupDiSetDeviceRegistryPropertyW(
|
|
DeviceInfoSet,
|
|
DeviceInfoData,
|
|
SPDRP_CONFIGFLAGS,
|
|
(PBYTE)&ConfigFlags,
|
|
sizeof(ConfigFlags));
|
|
if (!Result)
|
|
{
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = TRUE;
|
|
goto cleanup;
|
|
}
|
|
|
|
SelectedDriver = (struct DriverInfoElement *)InstallParams.Reserved;
|
|
if (SelectedDriver == NULL)
|
|
{
|
|
SetLastError(ERROR_NO_DRIVER_SELECTED);
|
|
goto cleanup;
|
|
}
|
|
|
|
FileTimeToSystemTime(&SelectedDriver->Info.DriverDate, &DriverDate);
|
|
|
|
Result = SetupDiGetActualSectionToInstallW(
|
|
SelectedDriver->InfFileDetails->hInf,
|
|
SelectedDriver->Details.SectionName,
|
|
SectionName, MAX_PATH, &SectionNameLength, NULL);
|
|
if (!Result || SectionNameLength > MAX_PATH - strlenW(DotServices))
|
|
goto cleanup;
|
|
pSectionName = &SectionName[strlenW(SectionName)];
|
|
|
|
/* Get information from [Version] section */
|
|
if (!SetupDiGetINFClassW(SelectedDriver->Details.InfFileName, &ClassGuid, ClassName, MAX_CLASS_NAME_LEN, &RequiredSize))
|
|
goto cleanup;
|
|
/* Format ClassGuid to a string */
|
|
if (UuidToStringW((UUID*)&ClassGuid, &lpGuidString) != RPC_S_OK)
|
|
goto cleanup;
|
|
RequiredSize = lstrlenW(lpGuidString);
|
|
lpFullGuidString = HeapAlloc(GetProcessHeap(), 0, (RequiredSize + 3) * sizeof(WCHAR));
|
|
if (!lpFullGuidString)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
lpFullGuidString[0] = '{';
|
|
memcpy(&lpFullGuidString[1], lpGuidString, RequiredSize * sizeof(WCHAR));
|
|
lpFullGuidString[RequiredSize + 1] = '}';
|
|
lpFullGuidString[RequiredSize + 2] = '\0';
|
|
|
|
/* Copy .inf file to Inf\ directory (if needed) */
|
|
Result = InfIsFromOEMLocation(SelectedDriver->Details.InfFileName, &NeedtoCopyFile);
|
|
if (!Result)
|
|
goto cleanup;
|
|
if (NeedtoCopyFile)
|
|
{
|
|
WCHAR NewFileName[MAX_PATH];
|
|
struct InfFileDetails *newInfFileDetails;
|
|
Result = SetupCopyOEMInfW(
|
|
SelectedDriver->Details.InfFileName,
|
|
NULL,
|
|
SPOST_NONE,
|
|
SP_COPY_NOOVERWRITE,
|
|
NewFileName, MAX_PATH,
|
|
NULL,
|
|
NULL);
|
|
if (!Result && GetLastError() != ERROR_FILE_EXISTS)
|
|
goto cleanup;
|
|
/* Create a new struct InfFileDetails, and set it to
|
|
* SelectedDriver->InfFileDetails, to release use of
|
|
* current InfFile */
|
|
newInfFileDetails = CreateInfFileDetails(NewFileName);
|
|
if (!newInfFileDetails)
|
|
goto cleanup;
|
|
DereferenceInfFile(SelectedDriver->InfFileDetails);
|
|
SelectedDriver->InfFileDetails = newInfFileDetails;
|
|
strcpyW(SelectedDriver->Details.InfFileName, NewFileName);
|
|
}
|
|
|
|
/* Open/Create driver key information */
|
|
#if _WIN32_WINNT >= 0x502
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_WRITE);
|
|
#else
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS);
|
|
#endif
|
|
if (hKey == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
|
|
hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
|
|
/* Install main section */
|
|
DoAction = 0;
|
|
if (!(InstallParams.FlagsEx & DI_FLAGSEX_NO_DRVREG_MODIFY))
|
|
DoAction |= SPINST_REGISTRY;
|
|
if (!(InstallParams.Flags & DI_NOFILECOPY))
|
|
{
|
|
DoAction |= SPINST_FILES;
|
|
Context = SetupInitDefaultQueueCallback(InstallParams.hwndParent);
|
|
if (!Context)
|
|
goto cleanup;
|
|
}
|
|
*pSectionName = '\0';
|
|
Result = SetupInstallFromInfSectionW(InstallParams.hwndParent,
|
|
SelectedDriver->InfFileDetails->hInf, SectionName,
|
|
DoAction, hKey, SelectedDriver->InfFileDetails->DirectoryName, SP_COPY_NEWER,
|
|
SetupDefaultQueueCallbackW, Context,
|
|
DeviceInfoSet, DeviceInfoData);
|
|
if (!Result)
|
|
goto cleanup;
|
|
InstallParams.Flags |= DI_NOFILECOPY;
|
|
SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
|
|
|
|
/* Write information to driver key */
|
|
*pSectionName = UNICODE_NULL;
|
|
memcpy(&fullVersion, &SelectedDriver->Info.DriverVersion, sizeof(LARGE_INTEGER));
|
|
TRACE("Write information to driver key\n");
|
|
TRACE("DriverDate : '%u-%u-%u'\n", DriverDate.wMonth, DriverDate.wDay, DriverDate.wYear);
|
|
TRACE("DriverDesc : '%s'\n", debugstr_w(SelectedDriver->Info.Description));
|
|
TRACE("DriverVersion : '%ld.%ld.%lu.%ld'\n", fullVersion.HighPart >> 16, fullVersion.HighPart & 0xffff, fullVersion.LowPart >> 16, fullVersion.LowPart & 0xffff);
|
|
TRACE("InfPath : '%s'\n", debugstr_w(SelectedDriver->InfFileDetails->FileName));
|
|
TRACE("InfSection : '%s'\n", debugstr_w(SelectedDriver->Details.SectionName));
|
|
TRACE("InfSectionExt : '%s'\n", debugstr_w(&SectionName[strlenW(SelectedDriver->Details.SectionName)]));
|
|
TRACE("MatchingDeviceId: '%s'\n", debugstr_w(SelectedDriver->MatchingId));
|
|
TRACE("ProviderName : '%s'\n", debugstr_w(SelectedDriver->Info.ProviderName));
|
|
sprintfW(Buffer, DateFormat, DriverDate.wMonth, DriverDate.wDay, DriverDate.wYear);
|
|
rc = RegSetValueExW(hKey, REGSTR_DRIVER_DATE, 0, REG_SZ, (const BYTE *)Buffer, (strlenW(Buffer) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_DRIVER_DATE_DATA, 0, REG_BINARY, (const BYTE *)&SelectedDriver->Info.DriverDate, sizeof(FILETIME));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_DRVDESC, 0, REG_SZ, (const BYTE *)SelectedDriver->Info.Description, (strlenW(SelectedDriver->Info.Description) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
sprintfW(Buffer, VersionFormat, fullVersion.HighPart >> 16, fullVersion.HighPart & 0xffff, fullVersion.LowPart >> 16, fullVersion.LowPart & 0xffff);
|
|
rc = RegSetValueExW(hKey, REGSTR_DRIVER_VERSION, 0, REG_SZ, (const BYTE *)Buffer, (strlenW(Buffer) + 1) * sizeof(WCHAR));
|
|
}
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_INFPATH, 0, REG_SZ, (const BYTE *)SelectedDriver->InfFileDetails->FileName, (strlenW(SelectedDriver->InfFileDetails->FileName) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_INFSECTION, 0, REG_SZ, (const BYTE *)SelectedDriver->Details.SectionName, (strlenW(SelectedDriver->Details.SectionName) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_INFSECTIONEXT, 0, REG_SZ, (const BYTE *)&SectionName[strlenW(SelectedDriver->Details.SectionName)], (strlenW(SectionName) - strlenW(SelectedDriver->Details.SectionName) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_MATCHINGDEVID, 0, REG_SZ, (const BYTE *)SelectedDriver->MatchingId, (strlenW(SelectedDriver->MatchingId) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_PROVIDER_NAME, 0, REG_SZ, (const BYTE *)SelectedDriver->Info.ProviderName, (strlenW(SelectedDriver->Info.ProviderName) + 1) * sizeof(WCHAR));
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
RegCloseKey(hKey);
|
|
hKey = INVALID_HANDLE_VALUE;
|
|
|
|
/* FIXME: Process .LogConfigOverride section */
|
|
|
|
/* Install .Services section */
|
|
strcpyW(pSectionName, DotServices);
|
|
Result = SetupInstallServicesFromInfSectionExW(
|
|
SelectedDriver->InfFileDetails->hInf,
|
|
SectionName,
|
|
0,
|
|
DeviceInfoSet,
|
|
DeviceInfoData,
|
|
NULL,
|
|
NULL);
|
|
if (!Result)
|
|
goto cleanup;
|
|
if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED)
|
|
RebootRequired = TRUE;
|
|
|
|
/* Open device registry key */
|
|
hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_SET_VALUE);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
|
|
/* Install .HW section */
|
|
DoAction = 0;
|
|
if (!(InstallParams.FlagsEx & DI_FLAGSEX_NO_DRVREG_MODIFY))
|
|
DoAction |= SPINST_REGISTRY;
|
|
strcpyW(pSectionName, DotHW);
|
|
Result = SetupInstallFromInfSectionW(InstallParams.hwndParent,
|
|
SelectedDriver->InfFileDetails->hInf, SectionName,
|
|
DoAction, hKey, NULL, 0,
|
|
NULL, NULL,
|
|
DeviceInfoSet, DeviceInfoData);
|
|
if (!Result)
|
|
goto cleanup;
|
|
|
|
/* Write information to enum key */
|
|
TRACE("Write information to enum key\n");
|
|
TRACE("Class : '%s'\n", debugstr_w(ClassName));
|
|
TRACE("ClassGUID : '%s'\n", debugstr_w(lpFullGuidString));
|
|
TRACE("DeviceDesc : '%s'\n", debugstr_w(SelectedDriver->Info.Description));
|
|
TRACE("Mfg : '%s'\n", debugstr_w(SelectedDriver->Info.MfgName));
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_CLASS, 0, REG_SZ, (const BYTE *)ClassName, (strlenW(ClassName) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_CLASSGUID, 0, REG_SZ, (const BYTE *)lpFullGuidString, (strlenW(lpFullGuidString) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_DEVDESC, 0, REG_SZ, (const BYTE *)SelectedDriver->Info.Description, (strlenW(SelectedDriver->Info.Description) + 1) * sizeof(WCHAR));
|
|
if (rc == ERROR_SUCCESS)
|
|
rc = RegSetValueExW(hKey, REGSTR_VAL_MFG, 0, REG_SZ, (const BYTE *)SelectedDriver->Info.MfgName, (strlenW(SelectedDriver->Info.MfgName) + 1) * sizeof(WCHAR));
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Start the device */
|
|
if (!RebootRequired && !(InstallParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT | DI_DONOTCALLCONFIGMG)))
|
|
ret = ResetDevice(DeviceInfoSet, DeviceInfoData);
|
|
else
|
|
ret = TRUE;
|
|
|
|
cleanup:
|
|
/* End of installation */
|
|
if (hKey != INVALID_HANDLE_VALUE)
|
|
RegCloseKey(hKey);
|
|
if (lpGuidString)
|
|
RpcStringFreeW(&lpGuidString);
|
|
HeapFree(GetProcessHeap(), 0, lpFullGuidString);
|
|
if (Context)
|
|
SetupTermDefaultQueueCallback(Context);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static HKEY SETUPDI_OpenDevKey(HKEY RootKey, struct DeviceInfo *devInfo, REGSAM samDesired)
|
|
{
|
|
HKEY enumKey, key = INVALID_HANDLE_VALUE;
|
|
LONG l;
|
|
|
|
l = RegOpenKeyExW(RootKey, REGSTR_PATH_SYSTEMENUM, 0, READ_CONTROL, &enumKey);
|
|
if (!l)
|
|
{
|
|
l = RegOpenKeyExW(enumKey, devInfo->instanceId, 0, samDesired, &key);
|
|
RegCloseKey(enumKey);
|
|
}
|
|
if (l)
|
|
SetLastError(l);
|
|
return key;
|
|
}
|
|
|
|
static HKEY SETUPDI_OpenDrvKey(HKEY RootKey, struct DeviceInfo *devInfo, REGSAM samDesired)
|
|
{
|
|
LPWSTR DriverKey = NULL;
|
|
DWORD dwLength = 0;
|
|
DWORD dwRegType;
|
|
DWORD rc;
|
|
HKEY hEnumKey = NULL;
|
|
HKEY hKey = NULL;
|
|
HKEY key = INVALID_HANDLE_VALUE;
|
|
|
|
hKey = SETUPDI_OpenDevKey(RootKey, devInfo, KEY_QUERY_VALUE);
|
|
if (hKey == INVALID_HANDLE_VALUE)
|
|
goto cleanup;
|
|
/* Read the 'Driver' key */
|
|
rc = RegQueryValueExW(hKey, REGSTR_VAL_DRIVER, NULL, &dwRegType, NULL, &dwLength);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
else if (dwRegType != REG_SZ)
|
|
{
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto cleanup;
|
|
}
|
|
DriverKey = HeapAlloc(GetProcessHeap(), 0, dwLength);
|
|
if (!DriverKey)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
rc = RegQueryValueExW(hKey, REGSTR_VAL_DRIVER, NULL, &dwRegType, (LPBYTE)DriverKey, &dwLength);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
RegCloseKey(hKey);
|
|
hKey = NULL;
|
|
/* Need to open the driver key */
|
|
rc = RegOpenKeyExW(
|
|
RootKey,
|
|
REGSTR_PATH_CLASS_NT,
|
|
0, /* Options */
|
|
READ_CONTROL,
|
|
&hEnumKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
rc = RegOpenKeyExW(
|
|
hEnumKey,
|
|
DriverKey,
|
|
0, /* Options */
|
|
samDesired,
|
|
&hKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(rc);
|
|
goto cleanup;
|
|
}
|
|
key = hKey;
|
|
|
|
cleanup:
|
|
if (hEnumKey != NULL)
|
|
RegCloseKey(hEnumKey);
|
|
if (hKey != NULL && hKey != key)
|
|
RegCloseKey(hKey);
|
|
if (DriverKey)
|
|
HeapFree(GetProcessHeap(), 0, DriverKey);
|
|
return key;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiOpenDevRegKey (SETUPAPI.@)
|
|
*/
|
|
HKEY WINAPI SetupDiOpenDevRegKey(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Scope,
|
|
DWORD HwProfile,
|
|
DWORD KeyType,
|
|
REGSAM samDesired)
|
|
{
|
|
struct DeviceInfoSet *set = DeviceInfoSet;
|
|
struct DeviceInfo *devInfo;
|
|
HKEY key = INVALID_HANDLE_VALUE;
|
|
HKEY RootKey;
|
|
|
|
TRACE("%p %p %d %d %d %x\n", DeviceInfoSet, DeviceInfoData,
|
|
Scope, HwProfile, KeyType, samDesired);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
|
|
|| !DeviceInfoData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (Scope != DICS_FLAG_GLOBAL && Scope != DICS_FLAG_CONFIGSPECIFIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (KeyType != DIREG_DEV && KeyType != DIREG_DRV)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
|
|
if (devInfo->set != set)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
if (Scope != DICS_FLAG_GLOBAL)
|
|
{
|
|
RootKey = OpenHardwareProfileKey(set->HKLM, HwProfile, 0);
|
|
if (RootKey == INVALID_HANDLE_VALUE)
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
else
|
|
RootKey = set->HKLM;
|
|
switch (KeyType)
|
|
{
|
|
case DIREG_DEV:
|
|
key = SETUPDI_OpenDevKey(RootKey, devInfo, samDesired);
|
|
break;
|
|
case DIREG_DRV:
|
|
key = SETUPDI_OpenDrvKey(RootKey, devInfo, samDesired);
|
|
break;
|
|
default:
|
|
WARN("unknown KeyType %d\n", KeyType);
|
|
}
|
|
if (RootKey != set->HKLM)
|
|
RegCloseKey(RootKey);
|
|
return key;
|
|
}
|
|
|
|
static BOOL SETUPDI_DeleteDevKey(HKEY RootKey, struct DeviceInfo *devInfo)
|
|
{
|
|
FIXME("\n");
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL SETUPDI_DeleteDrvKey(HKEY RootKey, struct DeviceInfo *devInfo)
|
|
{
|
|
FIXME("\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SetupDiDeleteDevRegKey (SETUPAPI.@)
|
|
*/
|
|
BOOL WINAPI SetupDiDeleteDevRegKey(
|
|
HDEVINFO DeviceInfoSet,
|
|
PSP_DEVINFO_DATA DeviceInfoData,
|
|
DWORD Scope,
|
|
DWORD HwProfile,
|
|
DWORD KeyType)
|
|
{
|
|
struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
|
|
struct DeviceInfo *devInfo;
|
|
BOOL ret = FALSE;
|
|
HKEY RootKey;
|
|
|
|
TRACE("%p %p %d %d %d\n", DeviceInfoSet, DeviceInfoData, Scope, HwProfile,
|
|
KeyType);
|
|
|
|
if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
|
|
|| !DeviceInfoData->Reserved)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (Scope != DICS_FLAG_GLOBAL && Scope != DICS_FLAG_CONFIGSPECIFIC)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
if (KeyType != DIREG_DEV && KeyType != DIREG_DRV && KeyType != DIREG_BOTH)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
|
|
if (devInfo->set != set)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (Scope != DICS_FLAG_GLOBAL)
|
|
{
|
|
RootKey = OpenHardwareProfileKey(set->HKLM, HwProfile, 0);
|
|
if (RootKey == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
}
|
|
else
|
|
RootKey = set->HKLM;
|
|
switch (KeyType)
|
|
{
|
|
case DIREG_DEV:
|
|
ret = SETUPDI_DeleteDevKey(RootKey, devInfo);
|
|
break;
|
|
case DIREG_DRV:
|
|
ret = SETUPDI_DeleteDrvKey(RootKey, devInfo);
|
|
break;
|
|
case DIREG_BOTH:
|
|
ret = SETUPDI_DeleteDevKey(RootKey, devInfo);
|
|
if (ret)
|
|
ret = SETUPDI_DeleteDrvKey(RootKey, devInfo);
|
|
break;
|
|
default:
|
|
WARN("unknown KeyType %d\n", KeyType);
|
|
}
|
|
if (RootKey != set->HKLM)
|
|
RegCloseKey(RootKey);
|
|
return ret;
|
|
}
|