windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upg/hwcomp/hwcomp.c

5993 lines
139 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
hwcomp.c
Abstract:
Win95 to NT hardware device comparison routines.
Author:
Jim Schmidt (jimschm) 8-Jul-1996
Revision History:
marcw 28-Jun-1999 Added HwComp_MakeLocalSourceDeviceExists
jimschm 04-Dec-1998 Fixed checksum problem caused by date rounding
jimschm 29-Sep-1998 Fixed incompatible hardware message to use proper roots
jimschm 28-Apr-1998 Support for description-less hardware
jimschm 01-Apr-1998 Added %1 to (not currently present) message
jimschm 27-Feb-1998 Added suppression of (not currently present) devices
marcw 11-Nov-1997 Minor change to ensure that we can find the dial-up adapter.
jimschm 03-Nov-1997 Revised to use GROWBUFs and project's reg api
jimschm 08-Oct-1997 Added legacy keyboard support
marcw 18-Sep-1997 Added some fields to netcard enumerator
jimschm 24-Jun-1997 Added net card enumerator
marcw 05-May-1997 Fixed problem with looking for usable hdd. Need to
look for "diskdrive" class instead of "hdc" class.
marcw 18-Apr-1997 Added ability to determine if a CdRom and Hdd
compatible with Windows Nt is online.
marcw 14-Apr-1997 Retrofitted new progress bar handling code in.
jimschm 2-Jan-1997 Added INF verification to hwcomp.dat
to automatically detect when an OEM
changes one or more INFs
--*/
#include "pch.h"
#include "hwcompp.h"
#include <cfgmgr32.h>
#define DBG_HWCOMP "HwComp"
#ifdef UNICODE
#error "hwcomp.c cannot be compiled as UNICODE"
#endif
#define S_HWCOMP_DAT TEXT("hwcomp.dat")
#define S_HKLM_ENUM TEXT("HKLM\\Enum")
#define S_HARDWAREID TEXT("HardwareID")
#define S_COMPATIBLEIDS TEXT("CompatibleIDs")
#define S_CLASS TEXT("Class")
#define S_MANUFACTURER TEXT("Manufacturer")
#define S_IGNORE_THIS_FILE TEXT("*")
#define PNPID_FIELD 2
#define DECLARE(varname,text) text,
PCTSTR g_DeviceFields[] = {
DEVICE_FIELDS /* , */
NULL
};
#undef DECLARE
GROWBUFFER g_FileNames = GROWBUF_INIT;
GROWBUFFER g_DecompFileNames = GROWBUF_INIT;
HASHTABLE g_PnpIdTable;
HASHTABLE g_UnsupPnpIdTable;
HASHTABLE g_ForceBadIdTable;
HASHTABLE g_InfFileTable;
HASHTABLE g_NeededHardwareIds;
HASHTABLE g_UiSuppliedIds;
BOOL g_ValidWinDir;
BOOL g_ValidSysDrive;
BOOL g_IncompatibleScsiDevice = FALSE;
BOOL g_ValidCdRom;
BOOL g_FoundPnp8387;
PPARSEDPATTERN g_PatternCompatibleIDsTable;
typedef enum {
HW_INCOMPATIBLE,
HW_REINSTALL,
HW_UNSUPPORTED
} HWTYPES;
static PCTSTR g_ExcludeTable[] = {
TEXT("wkstamig.inf"),
TEXT("desktop.inf"),
TEXT("usermig.inf"),
TEXT("dosnet.inf"),
TEXT("pad.inf"),
TEXT("msmail.inf"),
TEXT("wordpad.inf"),
TEXT("syssetup.inf"),
TEXT("pinball.inf"),
TEXT("perms.inf"),
TEXT("optional.inf"),
TEXT("multimed.inf"),
TEXT("mmopt.inf"),
TEXT("layout.inf"),
TEXT("kbd.inf"),
TEXT("iexplore.inf"),
TEXT("intl.inf"),
TEXT("imagevue.inf"),
TEXT("games.inf"),
TEXT("font.inf"),
TEXT("communic.inf"),
TEXT("apps.inf"),
TEXT("accessor.inf"),
TEXT("mailnews.inf"),
TEXT("cchat.inf"),
TEXT("iermv2.inf"),
TEXT("default.inf"),
TEXT("setup16.inf"),
TEXT("")
};
BOOL
GetFileNames (
IN PCTSTR *InfDirs,
IN UINT InfDirCount,
IN BOOL QueryFlag,
IN OUT PGROWBUFFER FileNames,
IN OUT PGROWBUFFER DecompFileNames
);
VOID
FreeFileNames (
IN PGROWBUFFER FileNames,
IN PGROWBUFFER DecompFileNames,
IN BOOL QueryFlag
);
VOID
pFreeHwCompDatName (
PCTSTR Name
);
BOOL
pIsInfFileExcluded (
PCTSTR FileNamePtr
);
BOOL
pGetFileNamesWorker (
IN OUT PGROWBUFFER FileNames,
IN OUT PGROWBUFFER DecompFileNames,
IN PCTSTR InfDir,
IN BOOL QueryFlag
);
BOOL
pIsDeviceConsideredCompatible (
PCTSTR DevIds
);
BOOL
pFindForcedBadHardwareId (
IN PCTSTR PnpIdList,
OUT PTSTR InfFileName OPTIONAL
);
//
// Implementation
//
BOOL
WINAPI
HwComp_Entry (
IN HINSTANCE hinstDLL,
IN DWORD dwReason,
IN LPVOID lpv
)
/*++
Routine Description:
HwComp_Entry initializes the hwcomp library. It does what would normally
happen if this were a standalone dll, as opposed to a library.
At process detach, the device buffer is freed if necessary.
Arguments:
hinstDLL - (OS-supplied) instance handle for the DLL
dwReason - (OS-supplied) indicates attach or detatch from process or
thread
lpv - unused
Return Value:
Return value is always TRUE (indicating successful init).
--*/
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
g_UiSuppliedIds = HtAlloc();
if (!g_UiSuppliedIds) {
DEBUGMSG ((DBG_ERROR, "HwComp_Entry: Can't create g_UiSuppliedIds"));
return FALSE;
}
break;
case DLL_PROCESS_DETACH:
DEBUGMSG_IF ((
g_EnumsActive,
DBG_ERROR,
"%u hardware enumerations still active",
g_EnumsActive
));
DEBUGMSG_IF ((
g_NetEnumsActive,
DBG_ERROR,
"%u network hardware enumerations still active",
g_NetEnumsActive
));
FreeNtHardwareList();
if (g_NeededHardwareIds) {
HtFree (g_NeededHardwareIds);
g_NeededHardwareIds = NULL;
}
if (g_UiSuppliedIds) {
HtFree (g_UiSuppliedIds);
g_UiSuppliedIds = NULL;
}
if (g_PatternCompatibleIDsTable) {
DestroyParsedPattern (g_PatternCompatibleIDsTable);
g_PatternCompatibleIDsTable = NULL;
}
break;
}
return TRUE;
}
PCTSTR
pGetHwCompDat (
IN PCTSTR SourceDir,
IN BOOL MustExist
)
/*++
Routine Description:
GetHwCompDat builds e:\i386\hwcomp.dat, where e:\i386 is specified in
SourceDir. The caller must call pFreeHwCompDatName to clean up the
memory allocation.
Arguments:
SourceDir - The directory holding hwcomp.dat
MustExist - Specifies TRUE if hwcomp.dat must exist as specified,
or FALSE if hwcomp.dat does not necessarily exist.
Return Value:
MustExist = TRUE:
A pointer to a string, or NULL hwcomp.dat does not exist as
specified, or if allocation failed.
MustExist = FALSE:
A pointer to a string, or NULL if a memory allocation failed.
Caller must free the string via pFreeHwCompDatName.
--*/
{
PTSTR FileName;
if (SourceDir) {
FileName = JoinPaths (SourceDir, S_HWCOMP_DAT);
} else {
FileName = DuplicatePathString (S_HWCOMP_DAT, 0);
}
if (MustExist && GetFileAttributes (FileName) == 0xffffffff) {
pFreeHwCompDatName (FileName);
return NULL;
}
return FileName;
}
VOID
pFreeHwCompDatName (
PCTSTR Name
)
{
if (Name) {
FreePathString (Name);
}
}
//
// Routines for accessing the registry
//
PVOID
pPrivateRegValAllocator (
DWORD Size
)
{
return AllocText (Size);
}
VOID
pPrivateRegValDeallocator (
PCVOID Mem
)
{
FreeText (Mem);
}
PCTSTR
pGetAltDeviceDesc (
PCTSTR DriverSubKey
)
{
TCHAR DriverKey[MAX_REGISTRY_KEY];
HKEY Key;
PCTSTR Data;
PTSTR ReturnText = NULL;
if (!DriverSubKey) {
return NULL;
}
//
// Get driver key
//
wsprintf (DriverKey, TEXT("HKLM\\System\\CurrentControlSet\\Services\\Class\\%s"), DriverSubKey);
Key = OpenRegKeyStr (DriverKey);
if (!Key) {
return NULL;
}
Data = GetRegValueString (Key, TEXT("DriverDesc"));
CloseRegKey (Key);
if (Data) {
ReturnText = pPrivateRegValAllocator (SizeOfString (Data));
if (ReturnText) {
StringCopy (ReturnText, Data);
}
MemFree (g_hHeap, 0, Data);
}
return ReturnText;
}
VOID
pGetRegValText (
HKEY Key,
PCTSTR VarText,
PCTSTR *RetPtr
)
{
MYASSERT (!(*RetPtr));
*RetPtr = (PCTSTR) GetRegValueDataOfType2 (
Key,
VarText,
REG_SZ,
pPrivateRegValAllocator,
pPrivateRegValDeallocator
);
}
VOID
pFreeRegValText (
PHARDWARE_ENUM EnumPtr
)
{
//
// Free all device field text
//
#define DECLARE(varname,text) pPrivateRegValDeallocator((PVOID) EnumPtr->varname); EnumPtr->varname = NULL;
DEVICE_FIELDS
#undef DECLARE
}
VOID
pGetAllRegVals (
PHARDWARE_ENUM EnumPtr
)
{
PCTSTR AltDesc;
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
PTSTR BetterDesc = NULL;
TCHAR PnpId[MAX_PNP_ID];
PCTSTR PnpIdList;
PCTSTR OldDesc;
PCTSTR p;
PCTSTR end;
PCTSTR start;
PTSTR newBuf;
PTSTR ptr;
CHARTYPE ch;
#define DECLARE(varname,text) if(!EnumPtr->varname) { \
pGetRegValText ( \
EnumPtr->ek.CurrentKey->KeyHandle, \
text, \
&EnumPtr->varname \
); \
} \
DEVICE_FIELDS
#undef DECLARE
//
// If there is a better name for this device, use it
//
PnpIdList = EnumPtr->HardwareID;
while (PnpIdList && *PnpIdList) {
PnpIdList = ExtractPnpId (PnpIdList, PnpId);
if (*PnpId) {
if (InfFindFirstLine (g_Win95UpgInf, S_PNP_DESCRIPTIONS, PnpId, &is)) {
AltDesc = InfGetStringField (&is, 1);
if (AltDesc) {
OldDesc = EnumPtr->DeviceDesc;
BetterDesc = (PTSTR) pPrivateRegValAllocator (SizeOfString (AltDesc));
StringCopy (BetterDesc, AltDesc);
EnumPtr->DeviceDesc = BetterDesc;
DEBUGMSG ((
DBG_HWCOMP,
"Using %s for description (instead of %s)",
BetterDesc,
OldDesc
));
pPrivateRegValDeallocator ((PVOID) OldDesc);
break;
}
ELSE_DEBUGMSG ((DBG_WHOOPS, "Better description could not be retrieved"));
}
}
}
//
// Workaround: if the device description is bad, use the driver
// description if it is available
//
if (!BetterDesc) {
if (!EnumPtr->DeviceDesc || CharCount (EnumPtr->DeviceDesc) < 5) {
AltDesc = pGetAltDeviceDesc (EnumPtr->Driver);
if (AltDesc) {
pPrivateRegValDeallocator ((PVOID) EnumPtr->DeviceDesc);
EnumPtr->DeviceDesc = AltDesc;
}
}
//
// Fix leading/trail space problems
//
if (EnumPtr->DeviceDesc) {
start = SkipSpace (EnumPtr->DeviceDesc);
end = GetEndOfString (start);
p = SkipSpaceR (start, end);
if (p && (p != end || start != EnumPtr->DeviceDesc)) {
p = _tcsinc (p);
newBuf = pPrivateRegValAllocator (
(PBYTE) (p + 1) - (PBYTE) start
);
StringCopyAB (newBuf, start, p);
pPrivateRegValDeallocator ((PVOID) EnumPtr->DeviceDesc);
EnumPtr->DeviceDesc = newBuf;
}
}
//
// Eliminate newline chars inside description; replace \r or \n with a space
// the replacement occurs inplace, so chars will be replaced inplace
//
if (EnumPtr->DeviceDesc) {
for (ptr = (PTSTR)EnumPtr->DeviceDesc; *ptr; ptr = _tcsinc (ptr)) {
ch = _tcsnextc (ptr);
if (!_istprint (ch)) {
//
// in this case it's a single TCHAR, just replace it inplace
//
MYASSERT (*ptr == (TCHAR)ch);
*ptr = TEXT(' ');
}
}
}
}
InfCleanUpInfStruct (&is);
}
BOOL
pGetPnpIdList (
IN PINFSTRUCT is,
OUT PTSTR Buffer,
IN UINT Size
)
/*++
Routine Description:
pGetPnpIdList is similar to SetupGetMultiSzField, except it supports
skipping of blank fields.
Arguments:
is - Specifies the INFSTRUCT that indicates the line being processed.
Buffer - Receives the multi-sz ID list.
Size - Specifies the size of Buffer.
Return Value:
TRUE if one or more PNP ID fields exist, or FALSE if none exist.
--*/
{
UINT FieldCount;
UINT Field;
PTSTR p;
PTSTR End;
PCTSTR FieldStr;
p = Buffer;
End = (PTSTR) ((PBYTE) Buffer + Size);
End--;
FieldCount = InfGetFieldCount (is);
if (FieldCount < PNPID_FIELD) {
return FALSE;
}
for (Field = PNPID_FIELD ; Field <= FieldCount ; Field++) {
FieldStr = InfGetStringField (is, Field);
if (FieldStr && *FieldStr) {
if (SizeOfString (FieldStr) > (UINT) (End - p)) {
DEBUGMSG ((DBG_WHOOPS, "PNP ID list is bigger than %u bytes", Size));
break;
}
StringCopy (p, FieldStr);
p = GetEndOfString (p) + 1;
}
}
*p = 0;
return TRUE;
}
/*++
Routine Description:
BeginHardwareEnum initializes a structure for enumeration of all hardware
configuration registry values. Call BeginHardwareEnum, followed by
either NextHardwareEnum or AbortHardwareEnum.
Arguments:
EnumPtr - Receives the next enumerated item
Return Value:
TRUE if the supplied enumeration structure was filled, or FALSE if
there are no hardware items (unlikely) or an error occurred.
GetLastError() will provide the failure reason or ERROR_SUCCESS.
--*/
BOOL
RealEnumFirstHardware (
OUT PHARDWARE_ENUM EnumPtr,
IN TYPE_OF_ENUM TypeOfEnum,
IN DWORD EnumFlags
)
{
//
// If string tables have not been created, create them
// before enumerating.
//
if (TypeOfEnum != ENUM_ALL_DEVICES) {
if (!g_PnpIdTable || !g_UnsupPnpIdTable || !g_InfFileTable || !g_ForceBadIdTable) {
if (!CreateNtHardwareList (
SOURCEDIRECTORYARRAY(),
SOURCEDIRECTORYCOUNT(),
NULL,
REGULAR_OUTPUT
)) {
LOG ((
LOG_ERROR,
"Unable to create NT hardware list "
"(required for hardware enumeration)"
));
return FALSE;
}
}
}
START_ENUM;
//
// Init enum struct
//
ZeroMemory (EnumPtr, sizeof (HARDWARE_ENUM));
EnumPtr->State = STATE_ENUM_FIRST_KEY;
EnumPtr->TypeOfEnum = TypeOfEnum;
EnumPtr->EnumFlags = EnumFlags;
//
// Call NextHardwareEnum to fill in rest of struct
//
return RealEnumNextHardware (EnumPtr);
}
VOID
pGenerateTapeIds (
IN OUT PGROWBUFFER HackBuf,
IN PCTSTR PnpIdList
)
/*++
Routine Description:
pGenerateTapeIds creates two IDs based on the IDs given by the caller. The
first created ID is the caller's ID prefixed with Sequential. The second
created ID is the caller's ID prefixed with Sequential and without the
revision character. These new IDs match the tape IDs that NT supports.
Arguments:
HackBuf - Specifies a buffer that holds the new IDs, in a multi-sz. It
may have some initial IDs in it. Receives additional IDs.
PnpIdList - Specifies the ID list (either a hardware ID list or compatible
ID list).
Return Value:
None.
--*/
{
TCHAR PnpId[MAX_PNP_ID];
TCHAR HackedPnpIdBuf[MAX_PNP_ID + 32];
PTSTR HackedPnpId;
PTSTR p;
if (PnpIdList) {
while (*PnpIdList) {
PnpIdList = ExtractPnpId (PnpIdList, PnpId);
//
// Ignore PNP IDs that specify the root enumerator, or that
// begin with Gen and don't have an underscore (such as GenDisk)
//
*HackedPnpIdBuf = 0;
if (StringIMatchCharCount (TEXT("SCSI\\"), PnpId, 5)) {
MoveMemory (PnpId, PnpId + 5, SizeOfString (PnpId + 5));
HackedPnpId = _tcsappend (HackedPnpIdBuf, TEXT("SCSI\\"));
} else {
HackedPnpId = HackedPnpIdBuf;
}
if (_tcschr (PnpId, TEXT('\\'))) {
continue;
}
if (StringIMatchCharCount (PnpId, TEXT("Gen"), 3) &&
!_tcschr (PnpId, TEXT('_'))
) {
continue;
}
//
// Add another ID with Sequential
//
wsprintf (HackedPnpId, TEXT("Sequential%s"), PnpId);
MultiSzAppend (HackBuf, HackedPnpIdBuf);
//
// Add another ID with Sequential and without the single
// character revision
//
p = GetEndOfString (HackedPnpId);
p = _tcsdec (HackedPnpId, p);
*p = 0;
MultiSzAppend (HackBuf, HackedPnpIdBuf);
}
}
}
BOOL
pIsMultiFunctionDevice (
IN PCTSTR PnpIdList OPTIONAL
)
/*++
Routine Description:
pIsMultiFunctionDevice scans the caller-supplied list of PNP IDs for one
that starts with MF\. This prefix indicates the multi-function enumerator
root.
Arguments:
PnpIdList - Specifies the comma-separated list of PNP IDs
Return Value:
TRUE if a multi-function ID is in the list, FALSE otherwise.
--*/
{
TCHAR PnpId[MAX_PNP_ID];
BOOL b = FALSE;
if (PnpIdList) {
while (*PnpIdList) {
PnpIdList = ExtractPnpId (PnpIdList, PnpId);
if (StringIMatchCharCount (TEXT("MF\\"), PnpId, 3)) {
b = TRUE;
break;
}
}
}
return b;
}
BOOL
pGenerateMultiFunctionIDs (
IN OUT PGROWBUFFER IdList,
IN PCTSTR EncodedDevicePath
)
/*++
Routine Description:
pGenerateMultiFunctionIDs locates the device node for the device related to
the multi-function node. If a device node is found, all of its IDs (both
hardware IDs and compatible IDs) are added to the multi-function device as
compatible IDs.
Arguments:
IdList - Specifies an initialized grow buffer that has zero or
more multi-sz strings. Receives additional multi-sz
entries.
EncodedDevicePath - Specifies a device path in the form of
Root&Device&Instance, as obtained from the
multi-function dev node key name.
Return Value:
TRUE if the multi-function device has a master device, FALSE otherwise.
--*/
{
HKEY Parent;
HKEY Key;
BOOL b = FALSE;
TCHAR DevicePathCopy[MAX_REGISTRY_KEY];
PCTSTR Start;
PTSTR End;
TCHAR c;
PCTSTR Data;
PTSTR q;
//
// Multifunction devices have IDs in the form of:
//
// MF\CHILDxxxx\Root&Device&Instance
//
// Find the original device by parsing this string.
//
Parent = OpenRegKeyStr (S_HKLM_ENUM);
if (!Parent) {
return FALSE;
}
StringCopy (DevicePathCopy, EncodedDevicePath);
Start = DevicePathCopy;
End = _tcschr (Start, TEXT('&'));
for (;;) {
if (!End) {
c = 0;
} else {
c = *End;
*End = 0;
}
Key = OpenRegKey (Parent, Start);
if (Key) {
//
// Key exists. Close the parent, and begin
// using the current key as the parent. Then
// continue parsing if necessary.
//
CloseRegKey (Parent);
Parent = Key;
if (!c) {
b = TRUE;
break;
}
*End = TEXT('\\'); // turns DevicePathCopy into a subkey path
Start = End + 1;
End = _tcschr (Start, TEXT('&'));
} else if (c) {
//
// Key does not exist, try breaking at the
// next ampersand.
//
*End = c;
End = _tcschr (End + 1, TEXT('&'));
} else {
//
// Nothing left, key was not found
//
MYASSERT (!End);
break;
}
}
if (b) {
DEBUGMSG ((DBG_HWCOMP, "Parsed MF device node is %s", DevicePathCopy));
//
// Now get all the IDs for this device
//
q = (PTSTR) IdList->Buf;
Data = GetRegValueString (Parent, S_HARDWAREID);
if (Data) {
MultiSzAppend (IdList, Data);
MemFree (g_hHeap, 0, Data);
}
Data = GetRegValueString (Parent, S_COMPATIBLEIDS);
if (Data) {
MultiSzAppend (IdList, Data);
MemFree (g_hHeap, 0, Data);
}
//
// Convert commas into nuls, because we are passing
// back a multi-sz.
//
if (!q) {
q = (PTSTR) IdList->Buf;
}
End = (PTSTR) (IdList->Buf + IdList->End);
while (q < End) {
if (_tcsnextc (q) == TEXT(',')) {
*q = 0;
}
q = _tcsinc (q);
}
//
// Do not double-terminate the multi-sz yet. The caller
// may want to append more IDs to the list.
//
}
CloseRegKey (Parent);
return b;
}
/*++
Routine Description:
NextHardwareEnum returns the next registry value related to hardware
configuration.
Arguments:
EnumPtr - Specifies the current enumeration structure
Return Value:
TRUE if the supplied enumeration structure was filled, or FALSE if
there are no hardware items (unlikely) or an error occurred.
GetLastError() will provide the failure reason or ERROR_SUCCESS.
--*/
BOOL
RealEnumNextHardware (
IN OUT PHARDWARE_ENUM EnumPtr
)
{
TCHAR InstanceBuf[MAX_REGISTRY_KEY];
PTSTR p;
GROWBUFFER HackBuf = GROWBUF_INIT;
MULTISZ_ENUM e;
PTSTR NewBuf;
BOOL TapeDevice;
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
PCTSTR pattern;
for (;;) {
switch (EnumPtr->State) {
case STATE_ENUM_FIRST_KEY:
EnumPtr->State = STATE_ENUM_CHECK_KEY;
if (!EnumFirstRegKeyInTree (&EnumPtr->ek, S_HKLM_ENUM)) {
END_ENUM;
return FALSE;
}
break;
case STATE_ENUM_NEXT_KEY:
EnumPtr->State = STATE_ENUM_CHECK_KEY;
if (!EnumNextRegKeyInTree (&EnumPtr->ek)) {
END_ENUM;
return FALSE;
}
break;
case STATE_ENUM_CHECK_KEY:
EnumPtr->State = STATE_ENUM_FIRST_VALUE;
if (InfFindFirstLine (g_Win95UpgInf, S_IGNORE_REG_KEY, NULL, &is)) {
do {
pattern = InfGetStringField (&is, 1);
if (pattern && IsPatternMatch (pattern, EnumPtr->ek.FullKeyName)) {
DEBUGMSG ((DBG_WARNING, "Hardware key %s is excluded", EnumPtr->ek.FullKeyName));
EnumPtr->State = STATE_ENUM_NEXT_KEY;
break;
}
} while (InfFindNextLine (&is));
InfCleanUpInfStruct (&is);
}
break;
case STATE_ENUM_FIRST_VALUE:
if (!EnumFirstRegValue (
&EnumPtr->ev,
EnumPtr->ek.CurrentKey->KeyHandle
)) {
EnumPtr->State = STATE_ENUM_NEXT_KEY;
} else {
EnumPtr->State = STATE_EVALUATE_VALUE;
}
break;
case STATE_ENUM_NEXT_VALUE:
if (!EnumNextRegValue (&EnumPtr->ev)) {
EnumPtr->State = STATE_ENUM_NEXT_KEY;
} else {
EnumPtr->State = STATE_EVALUATE_VALUE;
}
break;
case STATE_EVALUATE_VALUE:
if (StringIMatch (EnumPtr->ev.ValueName, S_CLASS)) {
//
// Match found: fill struct with data
//
EnumPtr->State = STATE_VALUE_CLEANUP;
//
// Get HardwareID and CompatibleIDs
//
pGetRegValText (
EnumPtr->ek.CurrentKey->KeyHandle,
S_HARDWAREID,
&EnumPtr->HardwareID
);
pGetRegValText (
EnumPtr->ek.CurrentKey->KeyHandle,
S_COMPATIBLEIDS,
&EnumPtr->CompatibleIDs
);
//
// Special case: flip hardware ID and compatible IDs
// if we don't have a hardware ID but we do have a
// compatible ID
//
if (!EnumPtr->HardwareID && EnumPtr->CompatibleIDs) {
DEBUGMSG ((
DBG_WARNING,
"Reversing hardware and compatible IDs for %s",
EnumPtr->CompatibleIDs
));
EnumPtr->HardwareID = EnumPtr->CompatibleIDs;
EnumPtr->CompatibleIDs = NULL;
}
//
// Multifunction device special case
//
if (pIsMultiFunctionDevice (EnumPtr->HardwareID)) {
//
// Multifunction devices have IDs in the form of:
//
// MF\CHILDxxxx\Root&Device&Instance
//
// Find the original device by parsing this string.
//
pGenerateMultiFunctionIDs (
&HackBuf,
EnumPtr->ek.CurrentKey->KeyName
);
}
//
// Tape device special case
//
else if (_tcsistr (EnumPtr->ek.FullKeyName, TEXT("SCSI"))) {
pGetRegValText (
EnumPtr->ek.CurrentKey->KeyHandle,
S_CLASS,
&EnumPtr->Class
);
TapeDevice = FALSE;
if (!EnumPtr->Class) {
TapeDevice = TRUE;
} else if (_tcsistr (EnumPtr->Class, TEXT("tape"))) {
TapeDevice = TRUE;
}
ELSE_DEBUGMSG ((
DBG_VERBOSE,
"SCSI device class %s is not a tape device",
EnumPtr->Class
));
if (TapeDevice) {
//
// For tape devices in the SCSI enumerator, we must create
// extra compatible IDs. For each ID, we add two more,
// both prefixed with Sequential, and one with its revision
// number stripped off.
//
pGenerateTapeIds (&HackBuf, EnumPtr->HardwareID);
pGenerateTapeIds (&HackBuf, EnumPtr->CompatibleIDs);
DEBUGMSG_IF ((
HackBuf.End,
DBG_HWCOMP,
"Tape Device detected, fixing PNP IDs."
));
DEBUGMSG_IF ((
!HackBuf.End,
DBG_VERBOSE,
"Tape Device detected, but no IDs to fix.\n"
"Hardware ID: %s\n"
"Compatible IDs: %s",
EnumPtr->HardwareID,
EnumPtr->CompatibleIDs
));
}
}
//
// Add all IDs in HackBuf (a multi-sz) to the compatible
// ID list.
//
if (HackBuf.End) {
MultiSzAppend (&HackBuf, S_EMPTY);
if (EnumPtr->CompatibleIDs) {
NewBuf = pPrivateRegValAllocator (
ByteCount (EnumPtr->CompatibleIDs) +
HackBuf.End
);
StringCopy (NewBuf, EnumPtr->CompatibleIDs);
} else {
NewBuf = pPrivateRegValAllocator (HackBuf.End);
*NewBuf = 0;
}
p = GetEndOfString (NewBuf);
if (EnumFirstMultiSz (&e, (PCTSTR) HackBuf.Buf)) {
do {
if (p != NewBuf) {
p = _tcsappend (p, TEXT(","));
}
p = _tcsappend (p, e.CurrentString);
} while (EnumNextMultiSz (&e));
}
DEBUGMSG ((
DBG_HWCOMP,
"Hardware ID: %s\n"
"Old compatible ID list: %s\n"
"New compatible ID list: %s",
EnumPtr->HardwareID,
EnumPtr->CompatibleIDs,
NewBuf
));
if (EnumPtr->CompatibleIDs) {
pPrivateRegValDeallocator ((PVOID) EnumPtr->CompatibleIDs);
}
EnumPtr->CompatibleIDs = NewBuf;
FreeGrowBuffer (&HackBuf);
}
//
// Unless the user specified that the hardware ID was not required, break if it does not
// exist.
//
if (!EnumPtr->HardwareID && !(EnumPtr->EnumFlags & ENUM_DONT_REQUIRE_HARDWAREID)) {
break;
}
//
// Process enumeration filter
//
if ((EnumPtr->EnumFlags & ENUM_WANT_COMPATIBLE_FLAG) ||
(EnumPtr->TypeOfEnum != ENUM_ALL_DEVICES)
) {
EnumPtr->HardwareIdCompatible = FindHardwareId (EnumPtr->HardwareID, NULL);
EnumPtr->CompatibleIdCompatible = FindHardwareId (EnumPtr->CompatibleIDs, NULL);
EnumPtr->HardwareIdUnsupported = FindUnsupportedHardwareId (EnumPtr->HardwareID, NULL);
EnumPtr->CompatibleIdUnsupported = FindUnsupportedHardwareId (EnumPtr->CompatibleIDs, NULL);
//
// Process UI-based IDs and unsupported IDs
//
if (EnumPtr->EnumFlags & ENUM_USER_SUPPLIED_FLAG_NEEDED) {
EnumPtr->SuppliedByUi = FindUserSuppliedDriver (EnumPtr->HardwareID, EnumPtr->CompatibleIDs);
}
if (EnumPtr->EnumFlags & ENUM_DONT_WANT_USER_SUPPLIED) {
if (EnumPtr->SuppliedByUi) {
break;
}
}
if (EnumPtr->EnumFlags & ENUM_WANT_USER_SUPPLIED_ONLY) {
if (!EnumPtr->SuppliedByUi) {
break;
}
}
EnumPtr->Compatible = EnumPtr->HardwareIdCompatible ||
EnumPtr->CompatibleIdCompatible;
EnumPtr->Unsupported = EnumPtr->HardwareIdUnsupported ||
EnumPtr->CompatibleIdUnsupported;
//
// This logic is broken for a USB device that has both
// unsupported and compatible IDs in its hardware ID list.
//
// Removing this if statement causes that device to be
// reported as unsupported.
//
//if (EnumPtr->HardwareIdCompatible) {
// EnumPtr->Unsupported = FALSE;
//}
if (EnumPtr->Unsupported) {
EnumPtr->Compatible = FALSE;
}
//
// Special case: force incompatible? If so, we indicate
// this only by modifying the abstract Compatible flag.
//
if (pFindForcedBadHardwareId (EnumPtr->HardwareID, NULL) ||
pFindForcedBadHardwareId (EnumPtr->CompatibleIDs, NULL)
) {
EnumPtr->Compatible = FALSE;
}
//
// Continue enumerating if this device does not fit the
// caller's request.
//
if (EnumPtr->TypeOfEnum == ENUM_COMPATIBLE_DEVICES) {
if (!EnumPtr->Compatible) {
break;
}
} else if (EnumPtr->TypeOfEnum == ENUM_INCOMPATIBLE_DEVICES) {
if (EnumPtr->Compatible || EnumPtr->Unsupported) {
break;
}
} else if (EnumPtr->TypeOfEnum == ENUM_UNSUPPORTED_DEVICES) {
if (!EnumPtr->Unsupported) {
break;
}
} else if (EnumPtr->TypeOfEnum == ENUM_NON_FUNCTIONAL_DEVICES) {
if (EnumPtr->Compatible) {
break;
}
}
}
//
// Copy reg key to struct
//
StringCopy (InstanceBuf, EnumPtr->ek.FullKeyName);
p = _tcschr (InstanceBuf, TEXT('\\'));
MYASSERT(p);
if (p) {
p = _tcschr (_tcsinc (p), TEXT('\\'));
MYASSERT(p);
}
if (p) {
p = _tcschr (_tcsinc (p), TEXT('\\'));
MYASSERT(p);
}
if (p) {
p = _tcsinc (p);
}
EnumPtr->InstanceId = DuplicateText (p);
EnumPtr->FullKey = EnumPtr->ek.FullKeyName;
EnumPtr->KeyHandle = EnumPtr->ek.CurrentKey->KeyHandle;
//
// Get all fields; require Class field
//
if (!(EnumPtr->EnumFlags & ENUM_DONT_WANT_DEV_FIELDS)) {
pGetAllRegVals (EnumPtr);
if (!EnumPtr->Class) {
DEBUGMSG ((
DBG_HWCOMP,
"Device %s does not have a Class field",
EnumPtr->InstanceId
));
break;
}
}
//
// Determine if device is online
//
if (EnumPtr->EnumFlags & ENUM_WANT_ONLINE_FLAG) {
EnumPtr->Online = IsPnpIdOnline (EnumPtr->InstanceId, EnumPtr->Class);
}
return TRUE;
} else {
EnumPtr->State = STATE_ENUM_NEXT_VALUE;
}
break;
case STATE_VALUE_CLEANUP:
//
// Free all device field text
//
pFreeRegValText (EnumPtr);
FreeText (EnumPtr->InstanceId);
EnumPtr->InstanceId = NULL;
EnumPtr->State = STATE_ENUM_NEXT_VALUE;
break;
default:
MYASSERT(FALSE);
END_ENUM;
return FALSE;
}
}
}
/*++
Routine Description:
AbortHardwareEnum cleans up all resources in use by an enumeration. Call
this function with the EnumPtr value of BeginHardwareEnum or NextHardwareEnum.
Arguments:
EnumPtr - Specifies the enumeration to abort.
Return Value:
none
--*/
VOID
AbortHardwareEnum (
IN OUT PHARDWARE_ENUM EnumPtr
)
{
PushError();
END_ENUM;
if (EnumPtr->State == STATE_VALUE_CLEANUP) {
pFreeRegValText (EnumPtr);
FreeText (EnumPtr->InstanceId);
}
AbortRegKeyTreeEnum (&EnumPtr->ek);
ZeroMemory (EnumPtr, sizeof (HARDWARE_ENUM));
PopError();
}
//
// NT5 INF database
//
BOOL
FindHardwareId (
IN PCTSTR PnpIdList,
OUT PTSTR InfFileName OPTIONAL
)
/*++
Routine Description:
FindHardwareId parses an ID string that may contain zero or more
plug and play device IDs, separated by commas. The function then
searches for each ID in the device ID table, copying the INF file
name to a supplied buffer when a match is found.
Arguments:
PnpIdList - An ID string that contains zero or more plug and play
device IDs, separated by commas.
InfFileName - A buffer (big enough to hold MAX_PATH characters)
that receives the INF file name upon successful
match. If a match is not found, InfFileName is
set to an empty string.
Return Value:
TRUE if a match was found, or FALSE if a match was not found.
--*/
{
return FindHardwareIdInHashTable (PnpIdList, InfFileName, g_PnpIdTable, TRUE);
}
BOOL
FindUnsupportedHardwareId (
IN PCTSTR PnpIdList,
OUT PTSTR InfFileName OPTIONAL
)
/*++
Routine Description:
FindUnsupportedHardwareId parses an ID string that may contain zero
or more plug and play device IDs, separated by commas. The function
then searches for each ID in the device ID table, copying the INF file
name to a supplied buffer when a match is found.
Arguments:
PnpIdList - An ID string that contains zero or more plug and play
device IDs, separated by commas.
InfFileName - A buffer (big enough to hold MAX_PATH characters)
that receives the INF file name upon successful
match. If a match is not found, InfFileName is
set to an empty string.
Return Value:
TRUE if a match was found, or FALSE if a match was not found.
--*/
{
return FindHardwareIdInHashTable (PnpIdList, InfFileName, g_UnsupPnpIdTable, FALSE);
}
BOOL
pFindForcedBadHardwareId (
IN PCTSTR PnpIdList,
OUT PTSTR InfFileName OPTIONAL
)
/*++
Routine Description:
pFindForcedBadHardwareId parses an ID string that may contain zero or more
plug and play device IDs, separated by commas. The function then searches
for each ID in the force bad device ID table, copying the INF file name to a
supplied buffer when a match is found.
Arguments:
PnpIdList - An ID string that contains zero or more plug and play
device IDs, separated by commas.
InfFileName - A buffer (big enough to hold MAX_PATH characters)
that receives the INF file name upon successful
match. If a match is not found, InfFileName is
set to an empty string.
Return Value:
TRUE if a match was found, or FALSE if a match was not found.
--*/
{
return FindHardwareIdInHashTable (PnpIdList, InfFileName, g_ForceBadIdTable, FALSE);
}
BOOL
FindUserSuppliedDriver (
IN PCTSTR HardwareIdList, OPTIONAL
IN PCTSTR CompatibleIdList OPTIONAL
)
/*++
Routine Description:
FindUserSuppliedDriver parses hardware and compatible hardware ID
strings that may contain zero or more plug and play device IDs,
separated by commas. The function then searches for each ID in
g_UiSuppliedIds table.
Arguments:
HardwareIdList - An ID string that contains zero or more plug and play
device IDs, separated by commas.
CompatibleIdList - An ID string that contains zero or more plug and play
device IDs, separated by commas.
Return Value:
TRUE if a match was found, or FALSE if a match was not found.
--*/
{
BOOL b = FALSE;
if (HardwareIdList) {
b = FindHardwareIdInHashTable (HardwareIdList, NULL, g_UiSuppliedIds, FALSE);
}
if (!b && CompatibleIdList) {
b = FindHardwareIdInHashTable (CompatibleIdList, NULL, g_UiSuppliedIds, FALSE);
}
return b;
}
BOOL
FindHardwareIdInHashTable (
IN PCTSTR PnpIdList,
OUT PTSTR InfFileName, OPTIONAL
IN HASHTABLE StrTable,
IN BOOL UseOverrideList
)
/*++
Routine Description:
FindHardwareIdInHashTable queries a string table for each PNP ID in
the specified list. If one is found, the routine optionally copies the
INF file it was found in. The caller can also choose to scan the win95upg.inf
override list.
Arguments:
PnpIdList - Specifies zero or more PNP IDs, separated by commas.
InfFileName - Receives the file name of the INF containing the PNP IDs.
StrTable - Specifies the string table to query. If InfFileName is not NULL,
the string table must have an extra data value of the offset in
g_InfFileTable.
UseOverrideList - Specifies TRUE if the win95upg.inf file is to be queried for
the PNP ID. This query is performed after it has been
determined that all IDs in PnpIdList are not in StrTable.
Return Value:
TRUE if at least one PNP ID in PnpIdList was found, or FALSE if none of the
IDs were found.
--*/
{
HASHITEM InfName;
TCHAR PnpId[MAX_PNP_ID];
PCSTR p;
TCHAR FixedEisaId[MAX_PNP_ID];
//
// Extract a PNP ID from PnpIdList, then look for it in string table
//
if (!PnpIdList) {
return FALSE;
}
MYASSERT (StrTable);
if (!StrTable) {
return FALSE;
}
p = PnpIdList;
while (*p) {
p = ExtractPnpId (p, PnpId);
if (*PnpId == 0) {
continue;
}
//
// Locate ID in PNP ID table
//
if (HtFindStringAndData (StrTable, PnpId, (PVOID) &InfName)) {
//
// Found PNP ID. Get INF file and return.
//
if (InfFileName) {
if (StrTable != g_PnpIdTable && StrTable != g_UnsupPnpIdTable && StrTable != g_ForceBadIdTable) {
DEBUGMSG ((DBG_WHOOPS, "Caller wants InfFileName from private string table"));
} else {
_tcssafecpy (
InfFileName,
HtGetStringFromItem (InfName),
MAX_TCHAR_PATH
);
}
}
return TRUE;
}
//
// This is a fix for the EISA roots. On Win9x, we have an EISA
// enumerator, but on NT, the ISA enumerator handles EISA too.
//
if (StringIMatchCharCount (TEXT("EISA\\"), PnpId, 5)) {
StringCopy (FixedEisaId, TEXT("EISA&"));
StringCat (FixedEisaId, PnpId + 5);
if (HtFindStringAndData (StrTable, FixedEisaId, (PVOID) &InfName)) {
//
// Found PNP ID. Get INF file and return.
//
if (InfFileName) {
if (StrTable != g_PnpIdTable && StrTable != g_UnsupPnpIdTable && StrTable != g_ForceBadIdTable) {
DEBUGMSG ((DBG_WHOOPS, "Caller wants InfFileName from private string table (2)"));
} else {
_tcssafecpy (
InfFileName,
HtGetStringFromItem (InfName),
MAX_TCHAR_PATH
);
}
}
return TRUE;
}
}
}
//
// Locate ID in override table
//
if (UseOverrideList) {
if (pIsDeviceConsideredCompatible (PnpIdList)) {
DEBUGMSG ((
DBG_WARNING,
"%s is considered compatible but actually does not have PNP support in NT.",
PnpIdList
));
return TRUE;
}
}
return FALSE;
}
BOOL
pProcessNtInfFile (
IN PCTSTR InfFile,
IN INT UiMode,
IN OUT HASHTABLE InfFileTable,
IN OUT HASHTABLE PnpIdTable,
IN OUT HASHTABLE UnsupPnpIdTable
)
/*++
Routine Description:
pProcessNtInfFile scans an NT INF and places all hardware device
IDs in the PNP string table. All entries of the string table
have extra data that points to the INF file (added to the INF
file name string table).
Arguments:
InfFile - The path to an INF file to be examined
UiMode - Specifies VERBOSE_OUTPUT or PNPREPT_OUTPUT when the PNP
IDs are to be dumped tvia the progress bar output
routines. If REGULAR_OUTPUT, no output is generated.
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
Call GetLastError for additional failure information.
--*/
{
HINF hInf;
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
INFSTRUCT isMfg = INITINFSTRUCT_GROWBUFFER;
INFSTRUCT isDev = INITINFSTRUCT_GROWBUFFER;
BOOL UnsupportedDevice;
PCTSTR DevSection;
PCTSTR Manufacturer;
TCHAR PnpId[MAX_PNPID_LENGTH * 4];
PTSTR CurrentDev;
TCHAR TrimmedId[MAX_PNP_ID];
PCTSTR FileName;
PCTSTR p;
CHARTYPE ch;
HASHITEM InfOffset = NULL;
LONG rc;
LONG DontCare = 0;
BOOL Result = FALSE;
PCTSTR RealDevSection = NULL;
BOOL b;
PCTSTR TempStr;
HASHITEM hashItem;
//
// Get a pointer to the inf file excluding the path
//
FileName = NULL;
for (p = InfFile ; *p ; p = _tcsinc (p)) {
ch = _tcsnextc (p);
if (ch == TEXT('\\')) {
FileName = _tcsinc (p);
} else if (!FileName && ch == TEXT(':')) {
FileName = _tcsinc (p);
}
}
MYASSERT (*FileName);
//
// Open INF file with Setup APIs
//
hInf = InfOpenInfFile (InfFile);
if (hInf == INVALID_HANDLE_VALUE) {
LOG ((LOG_ERROR, "Failed to open %s while processing hardware INFs.", InfFile));
return FALSE;
}
__try {
//
// Enumerate [Manufacturer] section
//
if (!InfFindFirstLine (hInf, S_MANUFACTURER, NULL, &is)) {
rc = GetLastError();
// If section not found, return success
if (rc == ERROR_SECTION_NOT_FOUND || rc == ERROR_LINE_NOT_FOUND) {
SetLastError (ERROR_SUCCESS);
Result = TRUE;
__leave;
}
SetLastError (rc);
LOG ((LOG_ERROR, "Error trying to find %s in %s", S_MANUFACTURER, InfFile));
__leave;
}
do {
//
// Get the manufacturer name
//
Manufacturer = InfGetLineText (&is);
if (!Manufacturer) {
LOG ((LOG_ERROR, "Error getting line text of enumerated line"));
__leave;
}
//
// Enumerate the devices listed in the manufacturer's section,
// looking for PnpId
//
if (!InfFindFirstLine (hInf, Manufacturer, NULL, &isMfg)) {
rc = GetLastError();
// if section not found, move on to next manufacturer
if (rc == ERROR_SECTION_NOT_FOUND || rc == ERROR_LINE_NOT_FOUND) {
DEBUGMSG ((
DBG_HWCOMP,
"Manufacturer %s section does not exist in %s",
Manufacturer,
InfFile
));
continue;
}
LOG((LOG_ERROR, "Error while searching for %s in %s.", Manufacturer, InfFile));
__leave;
}
do {
//
// Is this an unsupported device?
//
DevSection = InfGetStringField (&isMfg, 1);
if (!DevSection) {
// There is no field 1
continue;
}
UnsupportedDevice = FALSE;
//
// Try section.NTx86 first, then section.NT, then section
//
RealDevSection = JoinText (DevSection, TEXT(".NTx86"));
b = InfFindFirstLine (hInf, RealDevSection, NULL, &isDev);
if (!b) {
FreeText (RealDevSection);
RealDevSection = JoinText (DevSection, TEXT(".NT"));
b = InfFindFirstLine (hInf, RealDevSection, NULL, &isDev);
}
if (!b) {
FreeText (RealDevSection);
RealDevSection = DuplicateText (DevSection);
b = InfFindFirstLine (hInf, RealDevSection, NULL, &isDev);
}
if (!b) {
DEBUGMSG ((
DBG_HWCOMP,
"Device section for %s does not exist in %s of %s",
RealDevSection,
Manufacturer,
InfFile
));
} else {
if (InfFindFirstLine (hInf, RealDevSection, TEXT("DeviceUpgradeUnsupported"), &isDev)) {
TempStr = InfGetStringField (&isDev, 1);
if (TempStr && _ttoi (TempStr)) {
UnsupportedDevice = TRUE;
}
}
}
FreeText (RealDevSection);
//
// Get the device id
//
if (!pGetPnpIdList (&isMfg, PnpId, sizeof (PnpId))) {
// There is no field 2
continue;
}
//
// Add each device id to the id tree
//
CurrentDev = PnpId;
while (*CurrentDev) {
//
// First time through add the INF file name to string table
//
if (!InfOffset) {
if (InfFileTable) {
InfOffset = HtAddString (InfFileTable, FileName);
if (!InfOffset) {
LOG ((LOG_ERROR, "Cannot add %s to table of INFs.", FileName));
__leave;
}
}
}
//
// Add PNP ID to string table
//
StringCopy (TrimmedId, SkipSpace (CurrentDev));
TruncateTrailingSpace (TrimmedId);
if (UnsupportedDevice) {
hashItem = HtAddStringAndData (UnsupPnpIdTable, TrimmedId, &InfOffset);
} else {
hashItem = HtAddStringAndData (PnpIdTable, TrimmedId, &InfOffset);
}
if (!hashItem) {
LOG ((LOG_ERROR, "Cannot add %s to table of PNP IDs.", CurrentDev));
__leave;
}
MYASSERT (
UnsupportedDevice ?
hashItem == HtFindString (UnsupPnpIdTable, TrimmedId) :
hashItem == HtFindString (PnpIdTable, TrimmedId)
);
//
// UI options
//
if (UiMode == VERBOSE_OUTPUT || UiMode == PNPREPT_OUTPUT) {
TCHAR Msg[MAX_ENCODED_PNPID_LENGTH + MAX_INF_DESCRIPTION + 16];
TCHAR Desc[MAX_INF_DESCRIPTION];
TCHAR EncPnpId[MAX_ENCODED_PNPID_LENGTH * 4];
TCHAR EncDesc[MAX_INF_DESCRIPTION * 2];
if (SetupGetStringField (
&isMfg.Context,
0,
Desc,
MAX_INF_DESCRIPTION,
NULL
)) {
if (UiMode == VERBOSE_OUTPUT) {
wsprintf (Msg, TEXT(" PNP ID: %s, Desc: %s"), PnpId, Desc);
} else {
StringCopy (EncPnpId, PnpId);
StringCopy (EncDesc, Desc);
EncodePnpId (EncPnpId);
EncodePnpId (EncDesc);
wsprintf (Msg, TEXT("%s\\%s\\%s"), EncPnpId, EncDesc, FileName);
}
ProgressBar_SetSubComponent (Msg);
}
}
CurrentDev = GetEndOfString (CurrentDev) + 1;
}
} while (InfFindNextLine (&isMfg));
} while (InfFindNextLine (&is));
InfCloseInfFile (hInf);
SetLastError (ERROR_SUCCESS);
Result = TRUE;
}
__finally {
PushError();
InfCleanUpInfStruct (&is);
InfCleanUpInfStruct (&isMfg);
InfCleanUpInfStruct (&isDev);
InfCloseInfFile (hInf);
PopError();
}
return Result;
}
PCTSTR
ExtractPnpId (
IN PCTSTR PnpIdList,
OUT PTSTR PnpIdBuf
)
/*++
Routine Description:
ExtractPnpId removes the next PNP ID from a list of zero or more
PNP IDs (separated by commas). Upon return, PnpIdBuf contains the
PNP ID (or empty string if none found), and the return value points
to the next PNP ID in the list.
This routine is designed to be called in a loop until the return
value points to the nul terminated of PnpIdList.
Arguments:
PnpIdList - Specifies a pointer to the next string in the PNP ID list.
PnpIdBuf - Receives the PNP ID with spaces trimmed on both sides of the
ID.
Return Value:
A pointer to the next item in the list, or a pointer to the nul at the
end of the list. If the pointer points to a non-nul character, call
ExtractPnpId again, using the return value for the PnpIdList param.
--*/
{
PCTSTR p, q;
PnpIdList = SkipSpace (PnpIdList);
q = _tcschr (PnpIdList, TEXT(','));
if (!q) {
q = GetEndOfString (PnpIdList);
}
p = q;
if (p > (PnpIdList + MAX_PNP_ID - 1)) {
p = PnpIdList + MAX_PNP_ID - 1;
}
StringCopyAB (PnpIdBuf, PnpIdList, p);
TruncateTrailingSpace (PnpIdBuf);
if (*q) {
q = _tcsinc (q);
}
return q;
}
BOOL
AddPnpIdsToHashTable (
IN OUT HASHTABLE Table,
IN PCTSTR PnpIdList
)
/*++
Routine Description:
AddPnpIdsToHashTable extracts all PNP IDs from a comma-separated
list of PNP IDs and places each one in the specified string table.
PNP IDs are added to the string table as case-insensitive.
Arguments:
Table - Specifies the table to add each PNP ID to
PnpIdList - Specifies a comma-separated list of zero or more PNP
IDs to add to Table.
Return Value:
TRUE if all IDs were processed successfully, or FALSE if an error
occurred adding to the string table.
--*/
{
TCHAR PnpId[MAX_PNP_ID];
PCTSTR p;
p = PnpIdList;
if (!p) {
return TRUE;
}
while (*p) {
p = ExtractPnpId (p, PnpId);
if (*PnpId) {
if (!HtAddString (Table, PnpId)) {
LOG ((LOG_ERROR, "Can't add %s to table of PNP ids.", PnpId));
return FALSE;
}
}
}
return TRUE;
}
BOOL
AddPnpIdsToGrowList (
IN OUT PGROWLIST GrowList,
IN PCTSTR PnpIdList
)
/*++
Routine Description:
AddPnpIdsToHashTable extracts all PNP IDs from a comma-separated
list of PNP IDs and places each one in the specified grow list.
Arguments:
GrowList - Specifies the list to add each PNP ID to
PnpIdList - Specifies a comma-separated list of zero or more PNP
IDs to add to GrowList.
Return Value:
TRUE if all IDs were processed successfully, or FALSE if an error
occurred adding to the grow list.
--*/
{
TCHAR PnpId[MAX_PNP_ID];
PCTSTR p;
p = PnpIdList;
while (*p) {
p = ExtractPnpId (p, PnpId);
if (*PnpId) {
if (!GrowListAppendString (GrowList, PnpId)) {
DEBUGMSG ((DBG_ERROR, "AddPnpIdsToGrowList: Can't add %s", PnpId));
return FALSE;
}
}
}
return TRUE;
}
PCTSTR
AddPnpIdsToGrowBuf (
IN OUT PGROWBUFFER GrowBuffer,
IN PCTSTR PnpIdList
)
/*++
Routine Description:
AddPnpIdsToGrowBuf extracts all PNP IDs from a comma-separated
list of PNP IDs and places each one in the specified grow buffer.
Arguments:
GrowBuffer - Specifies the buffer to add each PNP ID to
PnpIdList - Specifies a comma-separated list of zero or more PNP
IDs to add to GrowBuffer.
Return Value:
A pointer to the beginning of the multisz buffer
--*/
{
TCHAR PnpId[MAX_PNP_ID];
PCTSTR p;
p = PnpIdList;
while (*p) {
p = ExtractPnpId (p, PnpId);
if (*PnpId) {
if (!MultiSzAppend (GrowBuffer, PnpId)) {
DEBUGMSG ((DBG_ERROR, "AddPnpIdsToGrowBuf: Can't add %s", PnpId));
return FALSE;
}
}
}
return GrowBuffer->Buf;
}
BOOL
pIsFileOnCD (
PCTSTR File
)
/*++
Routine Description:
pIsFileOnCd checks the drive letter at the head of File to see if
it is a CD-ROM.
This function also emulates the CD-ROM behavior for the report tool.
Arguments:
File - Specifies the full path of the file to compare
Return Value:
TRUE if the file is on a CD-ROM, or FALSE if it is not.
--*/
{
TCHAR RootDir[4];
//
// If report tool, or private stress option, always return TRUE.
//
if (REPORTONLY()) {
return TRUE;
}
#ifdef PRERELEASE
if (g_Stress) {
return TRUE;
}
#endif
//
// A CD drive cannot be a UNC path
//
if (File[0] && File[1] != TEXT(':')) {
return FALSE;
}
RootDir[0] = File[0];
RootDir[1] = File[1];
RootDir[2] = TEXT('\\');
RootDir[3] = 0;
return DRIVE_CDROM == GetDriveType (RootDir);
}
DWORD
pComputeInfChecksum (
IN PCTSTR HwCompDat, OPTIONAL
OUT PBOOL Rebuild OPTIONAL
)
/*++
Routine Description:
pComputeInfChecksum calculates a checksum for all INFs in the
source directories. This routine scans all directories in the
SOURCEDIRECTORYARRAY() global string array.
Arguments:
HwCompDat - Specifies path to hwcomp.dat, required if Rebuild is
specified.
Rebuild - Receives TRUE if an INF file was found with a greater
date than hwcomp.dat.
Return Value:
The checksum.
--*/
{
HANDLE hFind;
WIN32_FIND_DATA fd;
DWORD Checksum = 0;
PTSTR p;
TCHAR InfPattern[MAX_TCHAR_PATH];
UINT u, v;
FILETIME HwCompDatTime;
MYASSERT ((!HwCompDat && !Rebuild) || (HwCompDat && Rebuild));
if (Rebuild) {
if (DoesFileExistEx (HwCompDat, &fd)) {
*Rebuild = FALSE;
HwCompDatTime = fd.ftLastWriteTime;
} else {
*Rebuild = TRUE;
}
}
//
// NTRAID#NTBUG9-379084-2001/04/27-jimschm disable this until a better solution is found
//
#if 0
for (u = 0 ; u < SOURCEDIRECTORYCOUNT() ; u++) {
//
// Have we already processed this source dir?
//
for (v = 0 ; v < u ; v++) {
if (StringIMatch (SOURCEDIRECTORY(u),SOURCEDIRECTORY(v))) {
break;
}
}
if (v != u) {
continue;
}
//
// Process this directory
//
StringCopy (InfPattern, SOURCEDIRECTORY(u));
AppendWack (InfPattern);
StringCat (InfPattern, TEXT("*.in?"));
hFind = FindFirstFile (InfPattern, &fd);
if (hFind != INVALID_HANDLE_VALUE) {
do {
//
// Make sure file name ends in underscore or f
//
// We cheat... because we know that if the file was DBCS,
// it couldn't end in .INF
//
p = GetEndOfString (fd.cFileName);
MYASSERT (p != fd.cFileName);
p = _tcsdec2 (fd.cFileName, p);
if (*p == TEXT('_')) {
if (_istlower (*(p -1)))
*p = TEXT('f');
else
*p = TEXT('F');
} else if (tolower (*p) != TEXT('f')) {
continue;
}
// Make sure the file is not excluded
if (pIsInfFileExcluded (fd.cFileName)) {
continue;
}
if (Rebuild) {
// Check file times
if (CompareFileTime (&fd.ftLastWriteTime, &HwCompDatTime) > 0) {
*Rebuild = TRUE;
// abandon computation
break;
}
}
// Add the file size to the checksum
Checksum = _rotl (Checksum, 1) ^ fd.nFileSizeLow;
// Add file name
for (p = fd.cFileName ; *p ; p++) {
// preserve character and order
Checksum += (DWORD) (*p) * (DWORD) (1 + fd.cFileName - p);
}
} while (FindNextFile (hFind, &fd));
FindClose (hFind);
}
}
#endif
return Checksum;
}
BOOL
LoadDeviceList (
IN LOADOP Operation,
IN PCTSTR HwCompDatPath
)
/*++
Routine Description:
LoadDeviceList attempts to load hwcomp.dat from the path specified
in the HwCompDat parameter. If it is able to load this file, all
PNP IDs for all INFs are valid. If it is not able to load this file,
the file does not exist, or the file does not match the INFs.
Arguments:
Operation - QUERY: the validity of hwcomp.dat is to be checked
LOAD: load the data into memory.
DUMP: dump the file to stdout
HwCompDatPath - The path of hwcomp.dat, the data file holding a
pre-compiled compatible PNP ID list.
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
Call GetLastError for additional failure information.
--*/
{
DWORD StoredChecksum;
BOOL b = FALSE;
HASHTABLE InfFileTable = NULL;
HASHTABLE PnpIdTable = NULL;
BOOL Rebuild;
DWORD CurrentChecksum;
DWORD HwCompDatId = 0;
//
// !!! IMPORTANT !!!
//
// hwcomp.dat is used by other parts of NT. *DO NOT* change it without first e-mailing
// the NT group. Also, be sure to keep code in lib.c in sync with changes.
//
if (Operation == DUMP) {
DumpHwCompDat (HwCompDatPath, TRUE);
return TRUE;
}
__try {
//
// Open the hardware compatibility database
//
HwCompDatId = OpenHwCompDat (HwCompDatPath);
if (!HwCompDatId) {
__leave;
}
#if 0
//
// Get the checksum
//
StoredChecksum = GetHwCompDatChecksum (HwCompDatId);
//
// Verify the checksum
//
CurrentChecksum = pComputeInfChecksum (HwCompDatPath, &Rebuild);
if (CurrentChecksum != StoredChecksum || Rebuild) {
if (!pIsFileOnCD (HwCompDatPath)) {
DEBUGMSG ((DBG_WARNING, "PNP dat file's internal checksum does not match"));
__leave;
}
DEBUGMSG ((
DBG_WARNING,
"PNP dat file's internal checksum does not match. Error "
"ignored because %s is on a CD.",
HwCompDatPath
));
}
#endif
//
// Load the rest of hwcomp.dat
//
if (!LoadHwCompDat (HwCompDatId)) {
DEBUGMSG ((DBG_ERROR, "Can't load hwcomp.dat"));
__leave;
}
//
// If a load operation, put the hash tables into globals for use by
// the rest of hwcomp.c.
//
if (Operation == LOAD) {
//
// Take ownership of the hash tables
//
if (g_InfFileTable) {
HtFree (g_InfFileTable);
}
if (g_PnpIdTable) {
HtFree (g_PnpIdTable);
}
if (g_UnsupPnpIdTable) {
HtFree (g_UnsupPnpIdTable);
}
TakeHwCompHashTables (
HwCompDatId,
(PVOID *) (&g_PnpIdTable),
(PVOID *) (&g_UnsupPnpIdTable),
(PVOID *) (&g_InfFileTable)
);
}
b = TRUE;
}
__finally {
CloseHwCompDat (HwCompDatId);
}
return b;
}
BOOL
pWriteDword (
IN HANDLE File,
IN DWORD Val
)
/*++
Routine Description:
pWriteDword writes the specified DWORD value to File.
Arguments:
File - Specifies file to write to
Val - Specifies value to write
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
Call GetLastError for additional failure information.
--*/
{
DWORD BytesWritten;
return WriteFile (File, &Val, sizeof (Val), &BytesWritten, NULL);
}
BOOL
pWriteWord (
IN HANDLE File,
IN WORD Val
)
/*++
Routine Description:
pWriteWord writes the specified WORD vlue to File.
Arguments:
File - Specifies file to write to
Val - Specifies value to write
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
Call GetLastError for additional failure information.
--*/
{
DWORD BytesWritten;
return WriteFile (File, &Val, sizeof (Val), &BytesWritten, NULL);
}
BOOL
pWriteStringWithLength (
IN HANDLE File,
IN PCTSTR String
)
/*++
Routine Description:
pWriteStringWithLength writes the length of String as a WORD,
and then writes String (excluding the nul terminator).
Arguments:
File - Specifies file to write to
String - Specifies string to write
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
Call GetLastError for additional failure information.
--*/
{
DWORD BytesWritten;
WORD Length;
Length = (WORD) ByteCount (String);
if (!pWriteWord (File, Length)) {
DEBUGMSG ((DBG_ERROR, "pWriteStringWithLength: Can't write word"));
return FALSE;
}
if (Length) {
if (!WriteFile (File, String, Length, &BytesWritten, NULL)) {
DEBUGMSG ((DBG_ERROR, "pWriteStringWithLength: Can't write %s", String));
return FALSE;
}
}
return TRUE;
}
BOOL
pPnpIdEnum (
IN HASHTABLE Table,
IN HASHITEM StringId,
IN PCTSTR String,
IN PVOID ExtraData,
IN UINT ExtraDataSize,
IN LPARAM lParam
)
/*++
Routine Description:
pPnpIdEnum is a string table callback function that writes a PNP
ID to the file indicated in the Params struct (the lParam member).
This function only writes PNP IDs for a specific INF file (indicated
by the ExtraData arg).
Arguments:
Table - Specifies table being enumerated
StringId - Specifies offset of string in Table
String - Specifies string being enumerated
ExtraData - Specifies a pointer to a LONG that holds the INF ID
to enumerate. The PNP ID's INF ID must match this
parameter.
lParam - Specifies a pointer to a SAVE_ENUM_PARAMS struct
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
--*/
{
PSAVE_ENUM_PARAMS Params;
PCSTR BangString;
BOOL b = TRUE;
Params = (PSAVE_ENUM_PARAMS) lParam;
if (*((HASHITEM *) ExtraData) == Params->InfFileOffset) {
//
// Write this PNP ID to the file
//
if (Params->UnsupportedDevice) {
BangString = JoinTextExA (NULL, "!", String, NULL, 0, NULL);
b = pWriteStringWithLength (Params->File, BangString);
FreeTextA (BangString);
} else {
b = pWriteStringWithLength (Params->File, String);
}
}
return b;
}
BOOL
pInfFileEnum (
IN HASHTABLE Table,
IN HASHITEM StringId,
IN PCTSTR String,
IN HASHTABLE ExtraData,
IN UINT ExtraDataSize,
IN LPARAM lParam
)
/*++
Routine Description:
pInfFileEnum is a string table callback function and is called for
each INF in g_InfFileTable.
This routine writes the name of the INF to disk, and then enumerates
the PNP IDs for the INF, writing them to disk.
The PNP ID list is terminated with an empty string.
Arguments:
Table - Specifies g_InfFileTable
StringId - Specifies offset of String in g_InfFileTable
String - Specifies current INF file being enumerated
ExtraData - unused
ExtraDataSize - unused
lParam - Specifies a pointer to SAVE_ENUM_PARAMS struct.
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
--*/
{
PSAVE_ENUM_PARAMS Params;
Params = (PSAVE_ENUM_PARAMS) lParam;
Params->InfFileOffset = StringId;
//
// Save the file name
//
if (!pWriteStringWithLength (Params->File, String)) {
return FALSE;
}
//
// Enumerate all PNP IDs
//
Params->UnsupportedDevice = FALSE;
if (!EnumHashTableWithCallback (g_PnpIdTable, pPnpIdEnum, lParam)) {
LOG ((LOG_ERROR, "Error while saving device list."));
return FALSE;
}
Params->UnsupportedDevice = TRUE;
if (!EnumHashTableWithCallback (g_UnsupPnpIdTable, pPnpIdEnum, lParam)) {
LOG ((LOG_ERROR, "Error while saving device list. (2)"));
return FALSE;
}
//
// Terminate the PNP ID list
//
if (!pWriteStringWithLength (Params->File, S_EMPTY)) {
return FALSE;
}
return TRUE;
}
BOOL
SaveDeviceList (
PCTSTR HwCompDatPath
)
/*++
Routine Description:
SaveDeviceList writes all data stored in g_InfFileTable and g_PnpIdTable
to the file specified by HwCompDat. This file will therefore contain
all PNP IDs in all INFs of Windows NT.
Arguments:
HwCompDatPath - Specifies path of file to write
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
Call GetLastError for additional failure information.
--*/
{
HANDLE File;
DWORD BytesWritten;
BOOL b = FALSE;
SAVE_ENUM_PARAMS Params;
DWORD ChecksumToStore;
//
// !!! IMPORTANT !!!
//
// hwcomp.dat is used by other parts of NT. *DO NOT* change it without first e-mailing
// the NT group. Also, be sure to keep code in lib.c in sync with changes.
//
ChecksumToStore = pComputeInfChecksum (NULL, NULL);
File = CreateFile (
HwCompDatPath,
GENERIC_WRITE,
0, // open for exclusive access
NULL, // no security attribs
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL // no template
);
if (File == INVALID_HANDLE_VALUE) {
LOG ((LOG_ERROR, "Cannot open %s for writing", HwCompDatPath));
return FALSE;
}
__try {
//
// Write version stamp
//
if (!WriteFile (File, HWCOMPDAT_SIGNATURE, ByteCount (HWCOMPDAT_SIGNATURE), &BytesWritten, NULL)) {
LOG ((LOG_ERROR, "Can't write signature file."));
__leave;
}
//
// Write checksum
//
if (!pWriteDword (File, ChecksumToStore)) {
LOG ((LOG_ERROR, "Can't write checksum"));
__leave;
}
//
// Enumerate the INF table, writing the INF file name and all PNP IDs
//
Params.File = File;
if (!EnumHashTableWithCallback (
g_InfFileTable,
pInfFileEnum,
(LPARAM) (&Params)
)) {
DEBUGMSG ((DBG_WARNING, "SaveDeviceList: EnumHashTableWithCallback returned FALSE"));
__leave;
}
//
// Terminate the INF file list
//
if (!pWriteStringWithLength (File, S_EMPTY)) {
DEBUGMSG ((DBG_WARNING, "SaveDeviceList: Can't write INF terminator"));
__leave;
}
b = TRUE;
}
__finally {
CloseHandle (File);
if (!b) {
DeleteFile (HwCompDatPath);
}
}
return b;
}
BOOL
pIsInfFileExcluded (
PCTSTR FileNamePtr
)
/*++
Routine Description:
IsInfFileExcluded returns TRUE when the specified file name does
not contain PNP IDs.
Arguments:
FileNamePtr - The name of the uncompressed INF file, without any path info.
Return Value:
TRUE if the file should be ignored by the PNP parser, or FALSE if
the file may contain PNP IDs.
--*/
{
PCTSTR *p;
// Check for OEMN (old network INFs)
if (StringIMatchCharCount (FileNamePtr, TEXT("OEMN"), 4)) {
return TRUE;
}
// Make sure extension has INF
if (!StringIMatch (FileNamePtr + TcharCount (FileNamePtr) - 3 * sizeof (TCHAR), TEXT("INF"))) {
return TRUE;
}
// Check list of excluded files
for (p = g_ExcludeTable ; **p ; p++) {
if (StringIMatch (FileNamePtr, *p)) {
return TRUE;
}
}
return FALSE;
}
VOID
pGetNonExistingFile (
IN PCTSTR Path,
OUT PTSTR EndOfPath,
IN PCTSTR DefaultName
)
/*++
Routine Description:
pGetNonExistingFile generates a file name of a file that does
not exist. It creates an empty file with that name, to reserve it.
Arguments:
Path - Specifies the path where the file will exist. Path must
end in a backslash.
EndOfPath - Points to the nul at the end of Path and is used to
write the new file name.
DefaultName - Specifies the default file name to try to use. If such
a file already exists, numbers are appended to
DefaultName until a unique name is found.
Return Value:
none
--*/
{
UINT Count = 0;
StringCopy (EndOfPath, DefaultName);
while (GetFileAttributes (Path) != 0xffffffff) {
Count++;
wsprintf (EndOfPath, TEXT("%s.%03u"), DefaultName, Count);
}
}
BOOL
GetFileNames (
IN PCTSTR *InfDirs,
IN UINT InfDirCount,
IN BOOL QueryFlag,
IN OUT PGROWBUFFER FileNames,
IN OUT PGROWBUFFER DecompFileNames
)
/*++
Routine Description:
GetFileNames searches InfDirs for any file that ends with .INF or .IN_.
It builds a MULTI_SZ list of file names that may contain PNP IDs. All
compressed INFs are decompressed into a temporary directory.
If the QueryFlag is set, the file name list is prepared but no files
are decompressed.
Arguments:
InfDirs - A list of paths to the directory containing INFs, either
compressed or non-compressed.
InfDirCount - Specifies the number of dirs in the InfDirs array.
QueryFlag - TRUE if the function should build the file list but
should not decompress; FALSE if the function
should build the file list and decompress as needed.
FileNames - Specifies an empty GROWBUFFER struct that is used to build
a multi-sz list of full paths to the INF files.
Return Value:
A pointer to the MUTLI_SZ list. The caller is responsible for freeing
this buffer via FreeFileNames.
The return value is NULL if an error occurred. Call GetLastError for
an error code.
--*/
{
UINT u;
//
// Add list of files for each directory
//
for (u = 0 ; u < InfDirCount ; u++) {
if (!pGetFileNamesWorker (FileNames, DecompFileNames, InfDirs[u], QueryFlag)) {
FreeFileNames (FileNames, DecompFileNames, QueryFlag);
return FALSE;
}
}
MultiSzAppend (FileNames, S_EMPTY);
MultiSzAppend (DecompFileNames, S_EMPTY);
return TRUE;
}
BOOL
pGetFileNamesWorker (
IN OUT PGROWBUFFER FileNames,
IN OUT PGROWBUFFER DecompFileNames,
IN PCTSTR InfDir,
IN BOOL QueryFlag
)
/*++
Routine Description:
pGetFileNamesWorker gets the file names for a single directory.
See GetFileNames for more details.
Arguments:
FileNames - Specifies GROWBUFFER of file names. This routine
appends file names using MultiSzAppend but does not
append the final empty string.
InfDir - Specifies directory holding zero or more INFs (either
compressed or non-compressed).
QueryFlag - Specifies TRUE if INF list is to be queried, or
FALSE if the list is to be fully processed. When
QueryFlag is TRUE, files are not decompressed or
opened.
Return Value:
TRUE if the function completes successfully, or FALSE if it fails.
Call GetLastError for additional failure information.
--*/
{
PTSTR p;
TCHAR ActualFile[MAX_TCHAR_PATH];
CHAR AnsiFileName[MAX_MBCHAR_PATH];
PTSTR FileNameOnDisk;
HANDLE hFile;
DWORD BytesRead;
HANDLE hFind;
WIN32_FIND_DATA fd;
TCHAR Pattern[MAX_TCHAR_PATH];
TCHAR UncompressedFile[MAX_TCHAR_PATH];
TCHAR CompressedFile[MAX_TCHAR_PATH];
PTSTR FileNamePtr;
BOOL DecompressFlag;
DWORD rc;
BYTE BufForSp[2048];
PSP_INF_INFORMATION psp;
psp = (PSP_INF_INFORMATION) BufForSp;
//
// Get file names
//
StringCopy (Pattern, InfDir);
StringCopy (AppendWack (Pattern), TEXT("*.in?"));
hFind = FindFirstFile (Pattern, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_NO_MORE_FILES) {
return TRUE;
}
LOG ((LOG_ERROR, "FindFirstFile failed for %s", Pattern));
return FALSE;
}
//
// Determine if each matching file is actually an INF, and if so
// add it to the FileNames growbuf.
//
rc = ERROR_SUCCESS;
do {
if (*g_CancelFlagPtr) {
rc = ERROR_CANCELLED;
break;
}
//
// Make sure file has _ or f at the end.
//
p = GetEndOfString (fd.cFileName);
MYASSERT (p != fd.cFileName);
p = _tcsdec2 (fd.cFileName, p);
MYASSERT (p);
if (!p) {
continue;
}
if (*p != TEXT('_') && _totlower (*p) != TEXT('f')) {
continue;
}
//
// Default actual file to uncompressed name
//
StringCopy (ActualFile, fd.cFileName);
//
// Build source file (CompressedFile)
//
StringCopy (CompressedFile, InfDir);
StringCopy (AppendWack (CompressedFile), ActualFile);
//
// Build destination file (UncompressedFile) and detect collisions
//
/*
StringCopy (UncompressedFile, g_TempDir);
FileNamePtr = AppendWack (UncompressedFile);
pGetNonExistingFile (UncompressedFile, FileNamePtr, ActualFile);
*/
DecompressFlag = FALSE;
if (!GetTempFileName (g_TempDir, TEXT("inf"), 0, UncompressedFile)) {
rc = GetLastError ();
break;
}
//
// Create uncompressed file path
//
if (*p == TEXT('_')) {
//
// Extract real name from INF file at offset 0x3c
//
ActualFile[0] = 0;
hFile = CreateFile (
CompressedFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile != INVALID_HANDLE_VALUE) {
if (0xffffffff != SetFilePointer (hFile, 0x3c, NULL, FILE_BEGIN)) {
if (ReadFile (
hFile,
AnsiFileName,
sizeof (AnsiFileName),
&BytesRead,
NULL
)) {
if (BytesRead >= SizeOfString (fd.cFileName)) {
FileNameOnDisk = ConvertAtoT (AnsiFileName);
if (StringIMatchCharCount (
fd.cFileName,
FileNameOnDisk,
CharCount (fd.cFileName) - 1
)) {
//
// Real name found -- use it as ActualFile
//
StringCopy (ActualFile, FileNameOnDisk);
//
// Also use real file name for decompression, but
// append numbers if collision.
//
/*
pGetNonExistingFile (
UncompressedFile,
FileNamePtr,
FileNameOnDisk
);
*/
}
FreeAtoT (FileNameOnDisk);
}
}
}
CloseHandle (hFile);
}
//
// If file name could not be found, discard this file
//
if (!ActualFile[0]) {
DEBUGMSG ((DBG_HWCOMP, "%s is not an INF file", fd.cFileName));
continue;
}
DecompressFlag = TRUE;
} else {
StringCopy (UncompressedFile, CompressedFile);
}
//
// Skip excluded files
//
if (pIsInfFileExcluded (ActualFile)) {
continue;
}
if (!QueryFlag) {
//
// Uncompress file if necessary
//
if (DecompressFlag) {
/*
SetFileAttributes (UncompressedFile, FILE_ATTRIBUTE_NORMAL);
DeleteFile (UncompressedFile);
*/
rc = SetupDecompressOrCopyFile (CompressedFile, UncompressedFile, 0);
if (rc != ERROR_SUCCESS) {
LOG ((LOG_ERROR, "Could not decompress %s to %s", CompressedFile, UncompressedFile));
break;
}
}
//
// Determine if this is an NT 4 INF
//
if (!SetupGetInfInformation (
UncompressedFile,
INFINFO_INF_NAME_IS_ABSOLUTE,
psp,
sizeof (BufForSp),
NULL) ||
psp->InfStyle != INF_STYLE_WIN4
) {
DEBUGMSG ((DBG_HWCOMP, "%s is not a WIN4 INF file", UncompressedFile));
/*
if (DecompressFlag && !QueryFlag) {
DeleteFile (UncompressedFile);
}
*/
StringCopy (UncompressedFile, S_IGNORE_THIS_FILE);
}
TickProgressBar();
}
//
// Add file to grow buffer
//
MultiSzAppend (DecompressFlag ? DecompFileNames : FileNames, UncompressedFile);
} while (rc == ERROR_SUCCESS && FindNextFile (hFind, &fd));
FindClose (hFind);
if (rc != ERROR_SUCCESS) {
SetLastError (rc);
DEBUGMSG ((DBG_ERROR, "pGetFileNamesWorker: Error encountered in loop"));
return FALSE;
}
return TRUE;
}
VOID
FreeFileNames (
IN PGROWBUFFER FileNames,
IN OUT PGROWBUFFER DecompFileNames,
IN BOOL QueryFlag
)
/*++
Routine Description:
FreeFileNames cleans up the list generated by GetFileNames. If
QueryFlag is set to FALSE, all temporary decompressed
files are deleted.
Arguments:
FileNames - The same grow buffer passed to GetFileNames
QueryFlag - The same flag passed to GetFileNames
Return Value:
none
--*/
{
PTSTR p;
p = (PTSTR) DecompFileNames->Buf;
if (!p) {
return;
}
//
// Remove all files in temp dir (we created them when performing decompression)
//
if (!QueryFlag) {
while (*p) {
if (StringIMatchCharCount (p, g_TempDirWack, g_TempDirWackChars)) {
SetFileAttributes (p, FILE_ATTRIBUTE_NORMAL);
DeleteFile (p);
}
p = GetEndOfString (p) + 1;
}
}
//
// Deallocate FileNames
//
FreeGrowBuffer (DecompFileNames);
FreeGrowBuffer (FileNames);
}
VOID
pBuildPatternCompatibleIDsTable (
VOID
)
{
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
PCTSTR p;
GROWBUFFER joinedPattern = GROWBUF_INIT;
PPARSEDPATTERN test;
MYASSERT (g_Win95UpgInf != INVALID_HANDLE_VALUE);
if (InfFindFirstLine (g_Win95UpgInf, S_COMPATIBLE_PNP_IDS, NULL, &is)) {
do {
p = InfGetStringField (&is, 0);
if (*p) {
//
// first check if the pattern is correct
// if it isn't, we skip it
//
test = CreateParsedPattern (p);
if (test) {
DestroyParsedPattern (test);
GrowBufAppendString (&joinedPattern, TEXT("<"));
GrowBufAppendString (&joinedPattern, p);
GrowBufAppendString (&joinedPattern, TEXT(">"));
}
ELSE_DEBUGMSG ((DBG_WHOOPS, "Unable to parse pattern %s in [%s]", p, S_COMPATIBLE_PNP_IDS));
}
} while (InfFindNextLine (&is));
}
InfCleanUpInfStruct (&is);
if (joinedPattern.Buf) {
g_PatternCompatibleIDsTable = CreateParsedPattern (joinedPattern.Buf);
FreeGrowBuffer (&joinedPattern);
}
}
BOOL
CreateNtHardwareList (
IN PCTSTR * NtInfPaths,
IN UINT NtInfPathCount,
IN PCTSTR HwCompDatPath, OPTIONAL
IN INT UiMode
)
/*++
Routine Description:
CreateNtHardwareList gets a list of all INF files and calls pProcessNtInfFile
to build the NT device list. This routine is called at initialization.
The resulting list is saved to disk as hwcomp.dat. If hwcomp.dat already
exists, the device list is read from disk.
Arguments:
NtInfPaths - Specifies an array of full paths to the NT INF files.
NtInfPathCount - Specifies the number of elements in NtInfPaths. Cannot
be zero.
HwCompDatPath - Specifies a full path spec where the new HWCOMP.DAT file
should be loaded from. This is used by the hwdatgen tool.
UiMode - Specifies the type of output (if any) to produce while building
the device lists. Values are zero, PNPREPT_OUTPUT, or
VERBOSE_OUTPUT.
Return Value:
Returns TRUE if successful, or FALSE if not. Call GetLastError for
failure code.
--*/
{
PCTSTR SourceFile;
PCTSTR DestFile;
BOOL FreeSourceAndDest;
UINT u;
PTSTR File;
DWORD rc;
BOOL bSaved = FALSE;
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
PCTSTR p;
MYASSERT (NtInfPathCount > 0);
//
// If string tables already exist, then we do not have to build this list
// a second time.
//
if (!g_PatternCompatibleIDsTable) {
//
// table not set up; build it now
//
pBuildPatternCompatibleIDsTable ();
}
if (g_PnpIdTable && g_UnsupPnpIdTable && g_InfFileTable && g_ForceBadIdTable) {
return TRUE;
}
DEBUGMSG ((DBG_VERBOSE, "CreateNtHardwareList: building hardware list"));
MYASSERT (!g_PnpIdTable);
MYASSERT (!g_UnsupPnpIdTable);
MYASSERT (!g_InfFileTable);
MYASSERT (!g_ForceBadIdTable);
//
// Prepare file names. If HwCompDatPath is provided, use it only.
//
if (HwCompDatPath) {
//
// Use caller-supplied path; the caller is not necessarily NT Setup.
//
SourceFile = HwCompDatPath;
DestFile = HwCompDatPath;
FreeSourceAndDest = FALSE;
} else {
//
// Locate the source hwcomp.dat. If one does not exist,
// use the first source directory as Source.
//
SourceFile = NULL;
for (u = 0 ; !SourceFile && u < NtInfPathCount ; u++) {
SourceFile = pGetHwCompDat (NtInfPaths[u], TRUE);
}
if (!SourceFile) {
SourceFile = pGetHwCompDat (NtInfPaths[0], FALSE);
}
DestFile = pGetHwCompDat (g_TempDir, FALSE);
FreeSourceAndDest = TRUE;
}
//
// Build force table
//
if (g_ForceBadIdTable) {
HtFree (g_ForceBadIdTable);
}
g_ForceBadIdTable = HtAlloc();
if (InfFindFirstLine (g_Win95UpgInf, TEXT("Forced Incompatible IDs"), NULL, &is)) {
do {
p = InfGetStringField (&is, 0);
if (*p) {
HtAddString (g_ForceBadIdTable, p);
}
} while (InfFindNextLine (&is));
}
InfCleanUpInfStruct (&is);
__try {
//
// Try loading state from CD
//
if (UiMode != PNPREPT_OUTPUT) {
if (!LoadDeviceList (LOAD, SourceFile)) {
//
// Could not load from CD -- try loading from temporary storage
// location
//
if (!HwCompDatPath && LoadDeviceList (LOAD, DestFile)) {
return TRUE;
}
DEBUGMSG ((DBG_HWCOMP, "%s does not exist or needs to be rebuilt", SourceFile));
} else {
return TRUE;
}
}
//
// Load the INF file names
//
ProgressBar_SetComponentById (MSG_DECOMPRESSING);
// Get file names
if (!g_FileNames.Buf && !g_DecompFileNames.Buf) {
if (!GetFileNames (NtInfPaths, NtInfPathCount, FALSE, &g_FileNames, &g_DecompFileNames)) {
DEBUGMSG ((DBG_WARNING, "HWCOMP: Can't get INF file names"));
return FALSE;
}
}
__try {
ProgressBar_SetComponentById (MSG_HWCOMP);
//
// Initialize string tables
//
g_PnpIdTable = HtAllocWithData (sizeof (HASHITEM));
g_UnsupPnpIdTable = HtAllocWithData (sizeof (HASHITEM));
g_InfFileTable = HtAlloc();
if (!g_PnpIdTable || !g_UnsupPnpIdTable || !g_InfFileTable) {
LOG ((LOG_ERROR, "HWCOMP: Can't allocate string tables"));
return FALSE;
}
//
// Walk through list of INF files, and locate device names inside
// manufacturer sections. Add each name to the string table.
//
File = (PTSTR) g_FileNames.Buf;
while (*File) {
//
// Skip non-WIN4 INF files
//
if (StringMatch (File, S_IGNORE_THIS_FILE)) {
File = GetEndOfString (File) + 1;
if (!TickProgressBar()) {
break;
}
continue;
}
//
// Process all WIN4 INF files
//
if (UiMode != PNPREPT_OUTPUT) {
ProgressBar_SetSubComponent (File);
}
if (!pProcessNtInfFile (File, UiMode, g_InfFileTable, g_PnpIdTable, g_UnsupPnpIdTable)) {
if ((GetLastError() & 0xe0000000) == 0xe0000000) {
DEBUGMSG ((DBG_WARNING, "pProcessNtInfFile failed to parse %s.", File));
} else {
break;
}
}
if (!TickProgressBar()) {
break;
}
File = GetEndOfString (File) + 1;
}
rc = GetLastError();
if (rc == ERROR_SUCCESS) {
File = (PTSTR) g_DecompFileNames.Buf;
while (*File) {
//
// Skip non-WIN4 INF files
//
if (StringMatch (File, S_IGNORE_THIS_FILE)) {
File = GetEndOfString (File) + 1;
if (!TickProgressBar()) {
break;
}
continue;
}
//
// Process all WIN4 INF files
//
if (UiMode != PNPREPT_OUTPUT) {
ProgressBar_SetSubComponent (File);
}
if (!pProcessNtInfFile (File, UiMode, g_InfFileTable, g_PnpIdTable, g_UnsupPnpIdTable)) {
if ((GetLastError() & 0xe0000000) == 0xe0000000) {
DEBUGMSG ((DBG_WARNING, "pProcessNtInfFile failed to parse %s.", File));
} else {
break;
}
}
if (!TickProgressBar()) {
break;
}
File = GetEndOfString (File) + 1;
}
rc = GetLastError();
}
//
// Clean up UI
//
ProgressBar_SetComponent (NULL);
ProgressBar_SetSubComponent (NULL);
//
// Save string tables to hwcomp.dat
//
if (UiMode == PNPREPT_OUTPUT) {
bSaved = TRUE;
} else if (rc == ERROR_SUCCESS) {
bSaved = SaveDeviceList (DestFile);
//
// Try copying this file to the right place for future installs
//
if (bSaved && !HwCompDatPath) {
if (!StringIMatch (DestFile, SourceFile)) {
CopyFile (DestFile, SourceFile, FALSE);
}
}
if (!bSaved) {
rc = GetLastError();
}
}
}
__finally {
FreeFileNames (&g_FileNames, &g_DecompFileNames, FALSE);
}
}
__finally {
if (FreeSourceAndDest) {
pFreeHwCompDatName (SourceFile);
pFreeHwCompDatName (DestFile);
}
}
return bSaved;
}
VOID
FreeNtHardwareList (
VOID
)
/*++
Routine Description:
FreeNtHardwareList cleans up the string tables. This function is called by
DllMain when a process detaches.
Arguments:
none
Return Value:
none
--*/
{
if (g_InfFileTable) {
HtFree (g_InfFileTable);
g_InfFileTable = NULL;
}
if (g_PnpIdTable) {
HtFree (g_PnpIdTable);
g_PnpIdTable = NULL;
}
if (g_UnsupPnpIdTable) {
HtFree (g_UnsupPnpIdTable);
g_UnsupPnpIdTable = NULL;
}
if (g_ForceBadIdTable) {
HtFree (g_ForceBadIdTable);
g_ForceBadIdTable = NULL;
}
}
//
// Routines that use the enumerators
//
BOOL
HwComp_ScanForCriticalDevices (
VOID
)
/*++
Routine Description:
HwComp_ScanForCriticalDevices is one of the first functions called in
the upgrade module. It enumerates the hardware and determines if
certain required devices are compatible.
Arguments:
none
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
Call GetLastError() for failure code.
--*/
{
HARDWARE_ENUM e;
//
// Reset flags for reentrancy
//
g_ValidWinDir = FALSE;
g_ValidSysDrive = FALSE;
g_ValidCdRom = FALSE;
g_FoundPnp8387 = FALSE;
if (g_NeededHardwareIds) {
HtFree (g_NeededHardwareIds);
g_NeededHardwareIds = NULL;
}
g_NeededHardwareIds = HtAlloc();
MYASSERT (g_NeededHardwareIds);
//
// Make sure hardware list is valid
//
if (!CreateNtHardwareList (
SOURCEDIRECTORYARRAY(),
SOURCEDIRECTORYCOUNT(),
NULL,
REGULAR_OUTPUT
)) {
DEBUGMSG_IF ((
GetLastError() != ERROR_CANCELLED,
DBG_ERROR,
"HwComp_ScanForCriticalDevices: CreateNtHardwareList failed!"
));
return FALSE;
}
//
// Scan all hardware
//
if (EnumFirstHardware (&e, ENUM_ALL_DEVICES, ENUM_WANT_COMPATIBLE_FLAG | ENUM_DONT_REQUIRE_HARDWAREID)) {
do {
//
// Fill g_NeededHardwareIDs with all PNP IDs of incompatible devices.
// Skip the devices that are deliberately unsupported.
//
if (!e.Compatible && !e.Unsupported) {
if (e.HardwareID) {
AddPnpIdsToHashTable (g_NeededHardwareIds, e.HardwareID);
}
if (e.CompatibleIDs) {
AddPnpIdsToHashTable (g_NeededHardwareIds, e.CompatibleIDs);
}
}
//
// Test 1: Check to see if (A) g_WinDir is on supported device, and
// (B) g_BootDriveLetter is on a supported device.
//
if (e.Compatible && e.CurrentDriveLetter) {
if (_tcschr (e.CurrentDriveLetter, _tcsnextc (g_WinDir))) {
g_ValidWinDir = TRUE;
}
if (_tcschr (e.CurrentDriveLetter, g_BootDriveLetter)) {
g_ValidSysDrive = TRUE;
}
}
//
// Test 2: Check to see if the class is CDROM
//
if (e.Compatible && e.Class) {
if (StringIMatch (e.Class, TEXT("CDROM"))) {
g_ValidCdRom = TRUE;
}
}
//
// Test 3: Check to see if HardwareID or CompatibleIDs contains
// *PNP8387 (Dial-Up Adapter)
//
if (e.CompatibleIDs && _tcsistr (e.CompatibleIDs, TEXT("*PNP8387"))) {
g_FoundPnp8387 = TRUE;
}
if (e.HardwareID && _tcsistr (e.HardwareID, TEXT("*PNP8387"))) {
g_FoundPnp8387 = TRUE;
}
//
// Test 4: Test for an incompatible SCSI adapter
//
if (e.HardwareID && !e.Compatible && _tcsistr (e.Class, TEXT("SCSI"))) {
g_IncompatibleScsiDevice = TRUE;
}
} while (EnumNextHardware (&e));
}
return TRUE;
}
BOOL
HwComp_DialUpAdapterFound (
VOID
)
/*++
Routine Description:
HwComp_DialUpAdapterFound returns TRUE if *PNP8387 was found
during the HwComp_ScanForCriticalDevices routine.
Arguments:
none
Return Value:
TRUE if the Microsoft Dial-Up Adapter exists, or FALSE if it
does not.
--*/
{
return g_FoundPnp8387;
}
BOOL
HwComp_NtUsableHardDriveExists (
VOID
)
/*++
Routine Description:
HwComp_NtUsableHardDriveExists returns TRUE if a compatible
hard disk exists for the Windows directory and the boot
drive.
Arguments:
none
Return Value:
TRUE if a compatible hard disk exists, or FALSE if one does
not exist.
--*/
{
return g_ValidSysDrive && g_ValidWinDir;
}
BOOL
HwComp_ReportIncompatibleController (
VOID
)
/*++
Routine Description:
HwComp_ReportIncompatibleController adds a message when an incompatible
hard disk controller is found. If the boot drive or windir drive is
incompatible, then a strong warning is given. Otherwise, the message
is informational.
Arguments:
none
Return Value:
TRUE if an incompatible controller message was added, FALSE otherwise.
--*/
{
HARDWARE_ENUM e;
BOOL MsgAdded = FALSE;
PCTSTR Group;
PCTSTR Message;
BOOL BadMainDev;
//
// Do this only if a bad controller exists
//
BadMainDev = HwComp_NtUsableHardDriveExists();
if (!BadMainDev && !g_IncompatibleScsiDevice) {
return FALSE;
}
//
// Scan incompatible hardware
//
if (EnumFirstHardware (
&e,
ENUM_NON_FUNCTIONAL_DEVICES,
ENUM_WANT_COMPATIBLE_FLAG | ENUM_DONT_REQUIRE_HARDWAREID
)) {
do {
//
// this test is not reliable
// there are CDROMs that will falll into this category but they are not HD controllers
// and there are also real SCSI controllers that have an "Unknown" class because
// Win9x doesn't have (or need) a driver for them
//
#if 0
if (_tcsistr (e.Class, TEXT("SCSI"))) {
if (!MsgAdded) {
MsgAdded = TRUE;
Group = BuildMessageGroup (MSG_INCOMPATIBLE_HARDWARE_ROOT, MSG_INCOMPATIBLE_HARD_DISK_SUBGROUP, NULL);
Message = GetStringResource (
BadMainDev ? MSG_INCOMPATIBLE_HARD_DISK_WARNING :
MSG_INCOMPATIBLE_HARD_DISK_NOTIFICATION
);
MsgMgr_ContextMsg_Add (TEXT("*BadController"), Group, Message);
FreeText (Group);
FreeStringResource (Message);
}
MsgMgr_LinkObjectWithContext (TEXT("*BadController"), e.FullKey);
DEBUGMSG ((DBG_HWCOMP, "Bad controller key: %s", e.FullKey));
}
#endif
} while (EnumNextHardware (&e));
}
return MsgAdded;
}
BOOL
HwComp_NtUsableCdRomDriveExists (
VOID
)
/*++
Routine Description:
HwComp_NtUsableCdRomDriveExists returns TRUE if a compatible
CD-ROM drive exists.
Arguments:
none
Return Value:
TRUE if a compatible CD-ROM drive exists, or FALSE if it does not.
--*/
{
return g_ValidCdRom;
}
BOOL
HwComp_MakeLocalSourceDeviceExists (
VOID
)
/*++
Routine Description:
MakeLocalSourceDevice scans the IDs found on the system for a match in a
special section in win95upg.inf. If one of these devices is found, the
function returns TRUE. This is used to catch cases where the user's CDrom
drive may be attached to a device that will not be available during
textmode.
Arguments:
None.
Return Value:
TRUE if such a device exists, FALSE otherwise.
--*/
{
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
HASHTABLE table;
HARDWARE_ENUM e;
BOOL rDeviceExists = FALSE;
PTSTR p = NULL;
__try {
if (InfFindFirstLine (g_Win95UpgInf, S_MAKELOCALSOURCEDEVICES, NULL, &is)) {
table = HtAlloc ();
//
// Add all of the "bad" pnpids listed in win95upg.inf.
//
do {
p = InfGetStringField (&is, 0);
HtAddString (table, p);
} while (InfFindNextLine (&is));
//
// Now, enumerate the devices on the system and see if we have any matches.
//
if (EnumFirstHardware (&e, ENUM_ALL_DEVICES, ENUM_WANT_ONLINE_FLAG)) {
do {
if (HtFindString (table, e.HardwareID)) {
rDeviceExists = TRUE;
AbortHardwareEnum (&e);
DEBUGMSG ((DBG_WARNING, "Device %s requires us to turn on the local source flag.", e.HardwareID));
break;
}
} while (EnumNextHardware (&e));
}
HtFree (table);
}
}
__except (1) {
return FALSE;
}
return rDeviceExists;
}
BOOL
HwComp_DoesDatFileNeedRebuilding (
VOID
)
/*++
Routine Description:
HwComp_DoesDatFileNeedRebuilding locates hwcomp.dat in the source
directories and determines if it needs to be rebuilt by obtaining
the checksum in the file and comparing it against the one from
the current INF files.
Arguments:
none
Return Value:
TRUE if the hwcomp.dat file needs to be rebuilt, or FALSE if it
does not.
--*/
{
PCTSTR SourceFile = NULL;
UINT u;
BOOL b = FALSE;
for (u = 0 ; u < SOURCEDIRECTORYCOUNT() ; u++) {
SourceFile = pGetHwCompDat(SOURCEDIRECTORY(u), TRUE);
if (SourceFile) {
break;
}
}
if (SourceFile) {
TurnOnWaitCursor();
b = LoadDeviceList (QUERY, SourceFile);
TurnOffWaitCursor();
pFreeHwCompDatName (SourceFile);
}
// b is TRUE when hwcomp.dat is valid, so return opposite
return !b;
}
INT
HwComp_GetProgressMax (
VOID
)
/*++
Routine Description:
HwComp_GetProgressMax calculates the number of INF files that would
need to be scanned if hwcomp.dat needs to be rebuilt. If it is
determined that hwcomp.dat does not need to be rebuilt, the function
returns zero.
Arguments:
none
Return Value:
The number of INF files that need to be processed, times two. (One
pass for decompression, another pass for parsing the INFs.)
--*/
{
INT FileCount = 0;
PCTSTR File;
PCTSTR SourceFile = NULL;
BOOL b = FALSE;
//
// Query validity of hwcomp.dat
//
if (!HwComp_DoesDatFileNeedRebuilding()) {
return 0;
}
//
// hwcomp.dat needs to be rebuilt, so return the number of files
// that need to be scanned times two.
//
// Count files
if (!g_FileNames.Buf && !g_DecompFileNames.Buf) {
if (!GetFileNames (
SOURCEDIRECTORYARRAY(),
SOURCEDIRECTORYCOUNT(),
TRUE,
&g_FileNames,
&g_DecompFileNames
)) {
LOG ((LOG_ERROR, "HWCOMP: Can't estimate number of INF files"));
return 850; // estimation, so there is some progress bar activity
}
}
for (File = (PCTSTR) g_FileNames.Buf ; *File ; File = GetEndOfString (File) + 1) {
FileCount++;
}
for (File = (PCTSTR) g_DecompFileNames.Buf ; *File ; File = GetEndOfString (File) + 1) {
FileCount++;
}
FreeFileNames (&g_FileNames, &g_DecompFileNames, TRUE);
MYASSERT (!g_FileNames.Buf);
MYASSERT (!g_DecompFileNames.Buf);
return FileCount*2;
}
BOOL
pIsDeviceConsideredCompatible (
PCTSTR DevIds
)
/*++
Routine Description:
pIsDeviceConsideredCompatible scans a list of comma-separated
PNP IDs against the list in win95upg.inf. If at least one
ID matches, TRUE is returned.
This function also implements a hack for the VIRTUAL root.
Arguments:
DevIds - Specifies a list of zero or more PNP IDs, separated
by commas.
Return Value:
TRUE if a PNP ID was found to be overridden as compatible, or
FALSE if none of the IDs are in win95upg.inf.
--*/
{
TCHAR Id[MAX_PNP_ID];
INFCONTEXT ic;
while (*DevIds) {
//
// Create Id string from comma-separated PNP ID list
//
DevIds = ExtractPnpId (DevIds, Id);
if (*Id == 0) {
continue;
}
//
// Search win95upg.inf for the PNP ID
//
if (SetupFindFirstLine (g_Win95UpgInf, S_STANDARD_PNP_IDS, Id, &ic)) {
DEBUGMSG ((DBG_HWCOMP, "%s is incompatible, but suppressed in win95upg.inf", Id));
return TRUE;
}
//
// This is a hack for the VIRTUAL enumerator, used by Turtle Beach.
//
if (StringIMatchCharCount (TEXT("VIRTUAL\\"), Id, 8)) {
return TRUE;
}
//
// Test for pattern PNPIDs
//
if (g_PatternCompatibleIDsTable && TestParsedPattern (g_PatternCompatibleIDsTable, Id)) {
DEBUGMSG ((DBG_HWCOMP, "%s is incompatible, but suppressed in win95upg.inf", Id));
return TRUE;
}
}
return FALSE;
}
BOOL
pIsDeviceInstalled (
IN PCTSTR DeviceDesc
)
/*++
Routine Description:
pIsDeviceInstalled scans the registry looking for a device that has the
specified description and is online.
Arguments:
DeviceDesc - Specifies the description of the duplicate device to find
(i.e., Dial-Up Adapter)
Return Value:
TRUE if an identical device was found and is online, or FALSE if not.
--*/
{
HARDWARE_ENUM e;
if (EnumFirstHardware (
&e,
ENUM_NON_FUNCTIONAL_DEVICES,
ENUM_WANT_ONLINE_FLAG|ENUM_DONT_WANT_USER_SUPPLIED
)) {
do {
if (e.Online) {
if (e.DeviceDesc) {
if (StringIMatch (e.DeviceDesc, DeviceDesc)) {
AbortHardwareEnum (&e);
return TRUE;
}
}
}
} while (EnumNextHardware (&e));
}
return FALSE;
}
VOID
pGetFriendlyClassName (
IN HKEY ClassKey,
IN PCTSTR Class,
OUT PTSTR Buffer
)
{
PCTSTR Data = NULL;
HKEY SubKey;
SubKey = OpenRegKey (ClassKey, Class);
if (SubKey) {
Data = GetRegValueString (SubKey, S_EMPTY);
CloseRegKey (SubKey);
if (!Data || !*Data) {
SubKey = NULL;
}
}
if (!SubKey) {
Data = GetStringResource (MSG_UNKNOWN_DEVICE_CLASS);
if (Data) {
_tcssafecpy (Buffer, Data, MAX_TCHAR_PATH);
FreeStringResource (Data);
}
ELSE_DEBUGMSG ((DBG_ERROR, "Unable to load string resource MSG_UNKNOWN_DEVICE_CLASS. Check localization."));
return;
}
_tcssafecpy (Buffer, Data, MAX_TCHAR_PATH);
MemFree (g_hHeap, 0, Data);
return;
}
BOOL
pStuffDeviceInReport (
PHARDWARE_ENUM e,
HKEY Key,
HWTYPES SupportedType
)
/*++
Routine Description:
pStuffDeviceInReport adds a device to the upgrade report. The device is
either incompatible or unsupported.
Arguments:
e - Specifies the current hardware enumerator struct.
Key - Specifies a handle to the Class key in HKLM\System\Services
SupportedType - Specifies one of the HWTYPES constants -- HW_INCOMPATIBLE,
HW_REINSTALL or HW_UNINSTALL, to indicate which category
to stuff the message into.
Return Value:
None.
--*/
{
PCTSTR ModifiedDescription = NULL;
PCTSTR Group = NULL;
PCTSTR Message = NULL;
PCTSTR DeviceDesc = NULL;
PCTSTR Array[6];
BOOL UnknownClass = FALSE;
PCTSTR Mfg;
PCTSTR Class;
PCTSTR HardwareID;
PCTSTR CompatibleID;
PCTSTR ClassAndName;
TCHAR FriendlyClass[MAX_TCHAR_PATH];
UINT SubGroup;
PCTSTR SubGroupText; // for log output only, hard-coded text
BOOL b = FALSE;
//
// Determine which group this message belongs in. The order is determined
// by the alphanumeric order of SubGroup.
//
if (SupportedType == HW_INCOMPATIBLE) {
SubGroup = MSG_INCOMPATIBLE_HARDWARE_PNP_SUBGROUP;
SubGroupText = TEXT("Incompatible");
} else if (SupportedType == HW_REINSTALL) {
SubGroup = MSG_REINSTALL_HARDWARE_PNP_SUBGROUP;
SubGroupText = TEXT("Reinstall");
} else {
SubGroup = MSG_UNSUPPORTED_HARDWARE_PNP_SUBGROUP;
SubGroupText = TEXT("Unsupported");
}
//
// Is device suppressed?
//
if (IsReportObjectHandled (e->FullKey)) {
return FALSE;
}
//
// Sometimes blank entries are found!!
//
__try {
DeviceDesc = e->DeviceDesc;
if (!DeviceDesc || (DeviceDesc && *DeviceDesc == 0)) {
LOG ((
LOG_ERROR,
"Skipping device because it lacks DriverDesc (%s,%s,%s)",
e->Mfg,
e->Class,
e->HardwareID
));
__leave;
}
Mfg = e->Mfg;
if (!Mfg) {
DEBUGMSG ((
DBG_WARNING,
"Device lacking manufacturer (%s,%s,%s)",
e->DeviceDesc,
e->Class,
e->HardwareID
));
Mfg = S_EMPTY;
}
Class = e->Class;
if (!Class) {
DEBUGMSG ((
DBG_WARNING,
"Device lacking class (%s,%s,%s)",
e->DeviceDesc,
e->Mfg,
e->HardwareID
));
Class = GetStringResource (MSG_UNKNOWN_DEVICE_CLASS);
MYASSERT (Class);
UnknownClass = TRUE;
}
HardwareID = e->HardwareID;
if (!HardwareID) {
DEBUGMSG ((
DBG_WARNING,
"Device lacking hardware ID (%s,%s,%s)",
e->DeviceDesc,
e->Mfg,
e->Class
));
HardwareID = S_EMPTY;
}
CompatibleID = e->CompatibleIDs;
if (!CompatibleID) {
CompatibleID = S_EMPTY;
}
//
// Add "(not currently present)" to offline devices
//
if (!e->Online) {
//
// Verify identical online device doesn't exist
//
if (pIsDeviceInstalled (DeviceDesc)) {
__leave;
}
Array[0] = DeviceDesc;
ModifiedDescription = ParseMessageID (MSG_OFFLINE_DEVICE, Array);
}
//
// Add hardware message to the incompatibility table
//
if (UnknownClass) {
StringCopy (FriendlyClass, Class);
} else {
pGetFriendlyClassName (Key, Class, FriendlyClass);
}
DEBUGMSG ((
DBG_HWCOMP,
"%s Device:\n"
" %s (%s)\n"
" %s\n"
" %s (%s)\n"
" %s\n",
SubGroupText,
HardwareID,
CompatibleID,
ModifiedDescription ? ModifiedDescription : DeviceDesc,
Class,
FriendlyClass,
Mfg
));
//
// Add the message via message manager
//
Array[0] = ModifiedDescription ? ModifiedDescription : DeviceDesc;
Array[1] = S_EMPTY; // formerly Enumerator Text
Array[2] = Class;
Array[3] = Mfg;
Array[4] = HardwareID;
Array[5] = FriendlyClass;
ClassAndName = JoinPaths (Array[5], Array[0]);
Group = BuildMessageGroup (
MSG_INCOMPATIBLE_HARDWARE_ROOT,
SubGroup,
ClassAndName
);
MYASSERT (Group);
FreePathString (ClassAndName);
Message = ParseMessageID (MSG_HARDWARE_MESSAGE, Array);
MYASSERT (Message);
MsgMgr_ObjectMsg_Add (e->FullKey, Group, Message);
LOG ((
LOG_INFORMATION,
"%s Device:\n"
" %s (%s)\n"
" %s\n"
" %s\n"
" %s\n",
SubGroupText,
HardwareID,
CompatibleID,
ModifiedDescription ? ModifiedDescription : DeviceDesc,
Class,
Mfg
));
b = TRUE;
}
__finally {
//
// Cleanup
//
FreeStringResource (ModifiedDescription);
FreeText (Group);
FreeStringResource (Message);
if (UnknownClass) {
FreeStringResource (Class);
}
}
return b;
}
LONG
HwComp_PrepareReport (
VOID
)
/*++
Routine Description:
HwComp_PrepareReport is called after the progress bar on the
Win9x side of the upgrade. It enumerates the hardware and adds
incompatibility messages for all incompatible hardware.
Arguments:
None.
Return Value:
A Win32 status code.
--*/
{
LONG rc;
HARDWARE_ENUM e;
HKEY Key;
HWTYPES msgType;
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
PTSTR pnpIdList;
PTSTR p, q;
TCHAR ch;
Key = OpenRegKeyStr (TEXT("HKLM\\System\\CurrentControlSet\\Services\\Class"));
__try {
if (!CreateNtHardwareList (
SOURCEDIRECTORYARRAY(),
SOURCEDIRECTORYCOUNT(),
NULL,
REGULAR_OUTPUT
)) {
rc = GetLastError();
if (rc != ERROR_CANCELLED) {
LOG ((LOG_ERROR, "Could not create list of NT hardware."));
}
__leave;
}
if (EnumFirstHardware (
&e,
ENUM_INCOMPATIBLE_DEVICES,
ENUM_WANT_ONLINE_FLAG|ENUM_DONT_WANT_USER_SUPPLIED
)) {
do {
msgType = HW_INCOMPATIBLE;
if (e.HardwareID) {
pnpIdList = DuplicateText (e.HardwareID);
p = pnpIdList;
do {
q = _tcschr (p, TEXT(','));
if (!q) {
q = GetEndOfString (p);
}
ch = *q;
*q = 0;
if (InfFindFirstLine (g_Win95UpgInf, S_REINSTALL_PNP_IDS, p, &is)) {
msgType = HW_REINSTALL;
DEBUGMSG ((DBG_HWCOMP, "Found reinstall hardware ID %s", p));
break;
}
*q = ch;
p = q + 1;
} while (ch);
FreeText (pnpIdList);
}
if (msgType == HW_INCOMPATIBLE && e.CompatibleIDs) {
pnpIdList = DuplicateText (e.CompatibleIDs);
p = pnpIdList;
do {
q = _tcschr (p, TEXT(','));
if (!q) {
q = GetEndOfString (p);
}
ch = *q;
*q = 0;
if (InfFindFirstLine (g_Win95UpgInf, S_REINSTALL_PNP_IDS, p, &is)) {
msgType = HW_REINSTALL;
DEBUGMSG ((DBG_HWCOMP, "Found reinstall compatible ID %s", p));
break;
}
*q = ch;
p = q + 1;
} while (ch);
FreeText (pnpIdList);
}
if (pStuffDeviceInReport (&e, Key, msgType)) {
DEBUGMSG ((DBG_HWCOMP, "Found incompatible hardware %s", e.DeviceDesc));
}
} while (EnumNextHardware (&e));
}
if (EnumFirstHardware (
&e,
ENUM_UNSUPPORTED_DEVICES,
ENUM_WANT_ONLINE_FLAG|ENUM_DONT_WANT_USER_SUPPLIED
)) {
do {
if (pStuffDeviceInReport (&e, Key, HW_UNSUPPORTED)) {
DEBUGMSG ((DBG_HWCOMP, "Found incompatible hardware %s", e.DeviceDesc));
}
} while (EnumNextHardware (&e));
}
rc = ERROR_SUCCESS;
}
__finally {
if (Key) {
CloseRegKey (Key);
}
InfCleanUpInfStruct (&is);
}
return rc;
}
BUSTYPE
pGetBusType (
IN PHARDWARE_ENUM EnumPtr
)
/*++
Routine Description:
pGetBusType returns the type of bus that the current hardware
devices belongs to.
Arguments:
EnumPtr - Specifies hardware device to process as returned from
the hardware enum functions.
Return Value:
The enumerated bus type.
--*/
{
PCTSTR p, q;
TCHAR Bus[MAX_HARDWARE_STRING];
if (EnumPtr->BusType) {
StringCopy (Bus, EnumPtr->BusType);
} else {
p = EnumPtr->FullKey;
p = _tcschr (p, TEXT('\\'));
if (p) {
p = _tcschr (_tcsinc (p), TEXT('\\'));
}
if (!p) {
return BUSTYPE_UNKNOWN;
}
p++;
q = _tcschr (p, TEXT('\\'));
if (!q) {
q = GetEndOfString (p);
}
StringCopyAB (Bus, p, q);
}
if (StringIMatch (Bus, S_ISA)) {
return BUSTYPE_ISA;
}
if (StringIMatch (Bus, S_EISA)) {
return BUSTYPE_EISA;
}
if (StringIMatch (Bus, S_MCA)) {
return BUSTYPE_MCA;
}
if (StringIMatch (Bus, S_PCI)) {
return BUSTYPE_PCI;
}
if (StringIMatch (Bus, S_PNPISA)) {
return BUSTYPE_PNPISA;
}
if (StringIMatch (Bus, S_PCMCIA)) {
return BUSTYPE_PCMCIA;
}
if (StringIMatch (Bus, S_ROOT)) {
return BUSTYPE_ROOT;
}
if (ISPC98()) {
if (StringIMatch (Bus, S_C98PNP)) {
return BUSTYPE_PNPISA;
}
}
return BUSTYPE_UNKNOWN;
}
VOID
pGetIoAddrs (
IN PHARDWARE_ENUM EnumPtr,
OUT PTSTR Buf
)
/*++
Routine Description:
pGetIoAddrs returns a list of comma-separated IO address ranges.
For example:
0x310-0x31F,0x388-0x38F
Arguments:
EnumPtr - Specifies device to process
Buf - Receives zero or more comma-separated address ranges.
Return Value:
none
--*/
{
DEVNODERESOURCE_ENUM e;
PIO_RESOURCE_9X IoRes;
PTSTR p;
*Buf = 0;
p = Buf;
if (EnumFirstDevNodeResource (&e, EnumPtr->FullKey)) {
do {
if (e.Type == ResType_IO) {
if (p > Buf) {
p = _tcsappend (p, TEXT(","));
}
IoRes = (PIO_RESOURCE_9X) e.ResourceData;
wsprintf (
p,
TEXT("0x%X-0x%X"),
IoRes->IO_Header.IOD_Alloc_Base,
IoRes->IO_Header.IOD_Alloc_End
);
p = GetEndOfString (p);
}
} while (EnumNextDevNodeResource (&e));
}
}
VOID
pGetIrqs (
IN PHARDWARE_ENUM EnumPtr,
OUT PTSTR Buf
)
/*++
Routine Description:
pGetIrqs returns a list of comma-separated IRQs used by the device.
For example:
0x07,0x0F
Arguments:
EnumPtr - Specifies the device to process
Buf - Receives zero or more comma-separated IRQs
Return Value:
none
--*/
{
DEVNODERESOURCE_ENUM e;
PIRQ_RESOURCE_9X IrqRes;
PTSTR p;
*Buf = 0;
p = Buf;
if (EnumFirstDevNodeResource (&e, EnumPtr->FullKey)) {
do {
if (e.Type == ResType_IRQ) {
if (p > Buf) {
p = _tcsappend (p, TEXT(","));
}
IrqRes = (PIRQ_RESOURCE_9X) e.ResourceData;
wsprintf (
p,
TEXT("0x%02X"),
IrqRes->AllocNum
);
p = GetEndOfString (p);
}
} while (EnumNextDevNodeResource (&e));
}
}
VOID
pGetDma (
IN PHARDWARE_ENUM EnumPtr,
OUT PTSTR Buf
)
/*++
Routine Description:
pGetDma returns a list of comma-separated DMA channels used
by the device. For example:
1,4
An empty string means "auto"
Arguments:
EnumPtr - Specifies hardware device to process
Buf - Receives zero or more comma-separated DMA channel numbers
Return Value:
none
--*/
{
DEVNODERESOURCE_ENUM e;
PDMA_RESOURCE_9X DmaRes;
DWORD Bit, Channel;
PTSTR p;
*Buf = 0;
p = Buf;
if (EnumFirstDevNodeResource (&e, EnumPtr->FullKey)) {
do {
if (e.Type == ResType_DMA) {
if (p > Buf) {
p = _tcsappend (p, TEXT(","));
}
DmaRes = (PDMA_RESOURCE_9X) e.ResourceData;
Channel = 0;
for (Bit = 1 ; Bit ; Bit <<= 1) {
if (DmaRes->DMA_Bits & Bit) {
wsprintf (p, TEXT("%u"), Channel);
p = GetEndOfString (p);
}
Channel++;
}
p = GetEndOfString (p);
}
} while (EnumNextDevNodeResource (&e));
}
}
VOID
pGetMemRanges (
IN PHARDWARE_ENUM EnumPtr,
OUT PTSTR Buf
)
/*++
Routine Description:
pGetMemRanges returns a list of comma-separated memory addresses
used by the device. For example:
0x0000D800-0x0000D9FF,0x0000F000-0x0000FFFF
Arguments:
EnumPtr - Specifies hardware to process
Buf - Receives zero or more comma-separated address ranges
Return Value:
none
--*/
{
DEVNODERESOURCE_ENUM e;
PMEM_RESOURCE_9X MemRes;
PTSTR p;
*Buf = 0;
p = Buf;
if (EnumFirstDevNodeResource (&e, EnumPtr->FullKey)) {
do {
if (e.Type == ResType_Mem) {
if (p > Buf) {
p = _tcsappend (p, TEXT(","));
}
MemRes = (PMEM_RESOURCE_9X) e.ResourceData;
wsprintf (
p,
TEXT("0x%08X-0x%08X"),
MemRes->MEM_Header.MD_Alloc_Base,
MemRes->MEM_Header.MD_Alloc_End
);
p = GetEndOfString (p);
}
} while (EnumNextDevNodeResource (&e));
}
}
TRANSCIEVERTYPE
pGetTranscieverType (
IN PHARDWARE_ENUM EnumPtr
)
/*++
Routine Description:
pGetTranscieverType returns the transciever type for the specified
device (i.e. net card).
Arguments:
EnumPtr - Specifies hardware device to process
Return Value:
The devices's transciever type
--*/
{
return TRANSCIEVERTYPE_AUTO;
}
IOCHANNELREADY
pGetIoChannelReady (
IN PHARDWARE_ENUM EnumPtr
)
/*++
Routine Description:
pGetIoChannelReady returns the setting of the IoChannelReady
mode for a device.
Arguments:
EnumPtr - Specifies hardware device to process
Return Value:
The device's IO Channel Ready mode
--*/
{
return IOCHANNELREADY_AUTODETECT;
}
/*++
Routine Description:
EnumFirstNetCard/EnumNextNetCard enumerate all installed network adapters
on a machine.
Arguments:
EnumPtr - An uninitiailzed structure that receives the state of enumeration.
Return Value:
TRUE if a net card was enumerated, or FALSE if no more net cards exist.
--*/
BOOL
EnumFirstNetCard (
OUT PNETCARD_ENUM EnumPtr
)
{
START_NET_ENUM;
ZeroMemory (EnumPtr, sizeof (NETCARD_ENUM));
EnumPtr->State = STATE_ENUM_FIRST_HARDWARE;
return EnumNextNetCard (EnumPtr);
}
BOOL
EnumNextNetCard (
IN OUT PNETCARD_ENUM EnumPtr
)
{
PHARDWARE_ENUM ep;
for (;;) {
switch (EnumPtr->State) {
case STATE_ENUM_FIRST_HARDWARE:
if (!EnumFirstHardware (&EnumPtr->HardwareEnum, ENUM_ALL_DEVICES,ENUM_DONT_REQUIRE_HARDWAREID)) {
END_NET_ENUM;
return FALSE;
}
EnumPtr->State = STATE_EVALUATE_HARDWARE;
break;
case STATE_ENUM_NEXT_HARDWARE:
if (!EnumNextHardware (&EnumPtr->HardwareEnum)) {
END_NET_ENUM;
return FALSE;
}
EnumPtr->State = STATE_EVALUATE_HARDWARE;
break;
case STATE_EVALUATE_HARDWARE:
ep = &EnumPtr->HardwareEnum;
EnumPtr->State = STATE_ENUM_NEXT_HARDWARE;
if (!StringIMatch (ep->Class, TEXT("Net"))) {
break;
}
if (ep -> HardwareID) {
_tcssafecpy (EnumPtr->HardwareId, ep->HardwareID, MAX_HARDWARE_STRING);
}
if (ep -> CompatibleIDs) {
_tcssafecpy (EnumPtr -> CompatibleIDs, ep -> CompatibleIDs, MAX_HARDWARE_STRING);
}
if (ep->DeviceDesc) {
_tcssafecpy (EnumPtr->Description, ep->DeviceDesc, MAX_HARDWARE_STRING);
} else {
EnumPtr->Description[0] = 0;
}
_tcssafecpy (EnumPtr->CurrentKey, ep->ek.FullKeyName, MAX_HARDWARE_STRING);
EnumPtr->BusType = pGetBusType (ep);
EnumPtr->TranscieverType = pGetTranscieverType (ep);
EnumPtr->IoChannelReady = pGetIoChannelReady (ep);
pGetIoAddrs (ep, EnumPtr->IoAddrs);
pGetIrqs (ep, EnumPtr->Irqs);
pGetDma (ep, EnumPtr->Dma);
pGetMemRanges (ep, EnumPtr->MemRanges);
return TRUE;
}
}
}
VOID
EnumNetCardAbort (
IN PNETCARD_ENUM EnumPtr
)
{
PushError();
END_NET_ENUM;
AbortHardwareEnum (&EnumPtr->HardwareEnum);
PopError();
}
VOID
EncodePnpId (
IN OUT LPSTR Id
)
/*++
Routine Description:
This routine is used for the pnprept tool to encode a PNP ID so it does not
have any backslashes.
Arguments:
Id - Specifies ID that may have backslashes. The buffer must be big enough
to hold strlen(Id) * 2 characters.
Return Value:
none
--*/
{
CHAR TempBuf[MAX_MBCHAR_PATH];
LPSTR s, d;
s = Id;
d = TempBuf;
while (*s) {
if (*s == '\\') {
*d = '~';
d++;
*d = '1';
} else if (*s == '*') {
*d = '~';
d++;
*d = '2';
} else if (*s == '~') {
*d = '~';
d++;
*d = '~';
}
else {
*d = *s;
}
d++;
s++;
}
*d = 0;
lstrcpy (Id, TempBuf);
}
VOID
DecodePnpId (
IN OUT LPSTR Id
)
/*++
Routine Description:
This routine is used by pnprept to decode a PNP ID that was encoded by
EncodePnpId.
Arguments:
Id - Specifies the encoded string to process. The string is truncated if
any encoded characters are found.
Return Value:
none
--*/
{
LPSTR s, d;
s = Id;
d = Id;
while (*s) {
if (*s == '~') {
s++;
if (*s == '1') {
*d = '\\';
} else if (*s == '2') {
*d = '*';
} else {
*d = *s;
}
} else if (d < s) {
*d = *s;
}
d++;
s++;
}
*d = 0;
}
BOOL
pFindFileInAnySourceDir (
IN PCTSTR File,
OUT PTSTR MatchPath
)
{
UINT Count;
UINT i;
PCTSTR Path;
Count = SOURCEDIRECTORYCOUNT();
for (i = 0 ; i < Count ; i++) {
Path = JoinPaths (SOURCEDIRECTORY(i), File);
__try {
if (DoesFileExist (Path)) {
_tcssafecpy (MatchPath, Path, MAX_TCHAR_PATH);
return TRUE;
}
}
__finally {
FreePathString (Path);
}
}
return FALSE;
}
BOOL
GetLegacyKeyboardId (
OUT PTSTR Buffer,
IN UINT BufferSize
)
/*++
Routine Description:
GetLegacyKeyboardId looks in NT's keyboard.inf for a legacy mapping.
If one is found, the legacy ID is returned to the caller so they
can write it to the answer file.
Arguments:
Buffer - Receives the legacy text corresponding to the installed
keyboard device.
BufferSize - Specifies the size of Buffer in bytes
Return Value:
TRUE if the legacy ID was found and copied, or FALSE if an error
occurred. If FALSE is returned, the caller should add an incompatibility
message and let NT decide which keyboard to use.
--*/
{
HINF Inf;
TCHAR KeyboardInfPath[MAX_TCHAR_PATH];
TCHAR TempDir[MAX_TCHAR_PATH];
PCTSTR TempKeyboardInfPath;
INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
PCTSTR PnpId, LegacyId;
LONG rc;
UINT keyboardType;
UINT keyboardSubtype;
PTSTR id = NULL;
HARDWARE_ENUM e;
TCHAR curId[MAX_PNP_ID];
PCTSTR devIds = NULL;
keyboardType = GetKeyboardType (0);
keyboardSubtype = GetKeyboardType (1);
//
// keyboard type : 7 - Japanese keyboard
//
if (keyboardType == 7) {
//
// sub type : 2 - 106 keyboard
//
if (keyboardSubtype == 2) {
id = TEXT("PCAT_106KEY");
//
// Ok, we have a japanese keyboard. Still, it may be USB which
// requires a different entry. Lets check.
//
if (EnumFirstHardware (&e, ENUM_COMPATIBLE_DEVICES, ENUM_WANT_DEV_FIELDS)) {
do {
devIds = e.HardwareID;
while (*devIds) {
devIds = ExtractPnpId (devIds, curId);
if (*curId == 0) {
continue;
}
if (InfFindFirstLine (g_Win95UpgInf, S_JPN_USB_KEYBOARDS, curId, &is)) {
id = TEXT("kbdhid");
AbortHardwareEnum (&e);
break;
}
}
} while (EnumNextHardware (&e) && !StringIMatch (id, TEXT("kbdhid")));
}
_tcssafecpy (Buffer, id, BufferSize / sizeof (TCHAR));
return TRUE;
}
}
//
// keyboard type : 8 - Korean keyboard
//
if (keyboardType == 8) {
switch (keyboardSubtype) {
case 3:
//
// 101a keyboard
//
id = TEXT("STANDARD");
break;
case 4:
//
// 101b keyboard
//
id = TEXT("101B TYPE");
break;
case 5:
//
// 101c keyboard
//
id = TEXT("101C TYPE");
break;
case 6:
//
// 103 keyboard
//
id = TEXT("103 TYPE");
break;
}
if (id) {
_tcssafecpy (Buffer, id, BufferSize);
return TRUE;
}
}
//
// Move keyboard.in_ (or keyboard.inf) from NT sources into
// temp dir.
//
if (!pFindFileInAnySourceDir (S_KEYBOARD_IN_, KeyboardInfPath)) {
if (!pFindFileInAnySourceDir (S_KEYBOARD_INF, KeyboardInfPath)) {
LOG ((LOG_ERROR,"GetLegacyKeyboardId: keyboard.inf not found in sources"));
return FALSE;
}
}
GetTempPath (sizeof (TempDir), TempDir);
TempKeyboardInfPath = JoinPaths (TempDir, S_KEYBOARD_INF);
__try {
rc = SetupDecompressOrCopyFile (
KeyboardInfPath,
TempKeyboardInfPath,
0
);
if (rc != ERROR_SUCCESS) {
LOG ((LOG_ERROR,"GetLegacyKeyboardId: keyboard.inf could not be copied to temp dir"));
return FALSE;
}
__try {
Inf = InfOpenInfFile (TempKeyboardInfPath);
if (Inf == INVALID_HANDLE_VALUE) {
LOG ((LOG_ERROR,"GetLegacyKeyboardId: %s could not be opened",TempKeyboardInfPath));
return FALSE;
}
__try {
//
// We now have keyboard.inf open. Let's enumerate each PNP
// ID in the [LegacyXlate.DevId] section and check if the
// hardware is available.
//
if (InfFindFirstLine (Inf, S_WIN9XUPGRADE, NULL, &is)) {
do {
PnpId = InfGetStringField (&is, 0);
if (PnpId) {
//
// Is PnpId online?
//
DEBUGMSG ((DBG_HWCOMP, "Searching for keyboard ID %s", PnpId));
if (IsPnpIdOnline (PnpId, S_KEYBOARD_CLASS)) {
//
// Yes - obtain PNP, copy to caller's buffer
// and get out
//
LegacyId = InfGetStringField (&is, 1);
if (LegacyId) {
DEBUGMSG ((DBG_HWCOMP, "Found match: %s = %s", LegacyId, PnpId));
_tcssafecpy (Buffer, LegacyId, BufferSize / sizeof (TCHAR));
return TRUE;
}
}
}
InfResetInfStruct (&is);
} while (InfFindNextLine (&is));
}
}
__finally {
InfCleanUpInfStruct (&is);
InfCloseInfFile (Inf);
}
}
__finally {
DeleteFile (TempKeyboardInfPath);
}
}
__finally {
FreePathString (TempKeyboardInfPath);
}
return FALSE;
}
BOOL
IsComputerOffline (
VOID
)
/*++
Routine Description:
IsComputerOffline checks if a network card exists on the machine and is
currently present.
Arguments:
None.
Return Value:
TRUE if the machine is known to be offline, or FALSE if the online state is
not known.
--*/
{
HARDWARE_ENUM e;
BOOL possiblyOnline = FALSE;
if (EnumFirstHardware (&e, ENUM_ALL_DEVICES, ENUM_WANT_ONLINE_FLAG|ENUM_WANT_COMPATIBLE_FLAG)) {
do {
//
// Enumerate all PNP devices of class Net
// or Modem
//
if (e.Class) {
if (StringIMatch (e.Class, TEXT("net")) ||
StringIMatch (e.Class, TEXT("modem"))
) {
//
// Determine if this is not a RAS adapter
//
if (e.CompatibleIDs && !_tcsistr (e.CompatibleIDs, TEXT("*PNP8387"))) {
possiblyOnline = TRUE;
}
if (e.HardwareID && !_tcsistr (e.HardwareID, TEXT("*PNP8387"))) {
possiblyOnline = TRUE;
}
//
// If this is not a RAS adapter, we assume it may be a LAN
// adapter. If the LAN adapter is present, then we assume
// that it might be online.
//
if (possiblyOnline) {
possiblyOnline = e.Online;
}
}
}
//
// Other tests for online go here
//
if (possiblyOnline) {
AbortHardwareEnum (&e);
return FALSE;
}
} while (EnumNextHardware (&e));
}
//
// We have one of the following cases:
//
// - No device with the class of "Net"
// - Only the RAS adapter is installed
// - All "Net" class devices are not present
//
// That makes us say "this computer is offline"
//
return TRUE;
}
BOOL
HwComp_AnyNeededDrivers (
VOID
)
{
return !HtIsEmpty (g_NeededHardwareIds);
}
BOOL
AppendDynamicSuppliedDrivers (
IN PCTSTR DriversPath
)
/*++
Routine Description:
AppendDynamicSuppliedDrivers scans a path and its subdirs for new drivers and places
all hardware device IDs in the global PNP string tables.
Arguments:
DriversPath - The root path to new drivers
Return Value:
TRUE if the function completes successfully, or FALSE if it fails for at least one driver.
--*/
{
TREE_ENUM te;
DWORD HwCompDatId;
BOOL b = TRUE;
if (EnumFirstFileInTree (&te, DriversPath, TEXT("hwcomp.dat"), TRUE)) {
do {
if (te.Directory) {
continue;
}
//
// Open the hardware compatibility database
//
HwCompDatId = OpenAndLoadHwCompDatEx (
te.FullPath,
(PVOID)g_PnpIdTable,
(PVOID)g_UnsupPnpIdTable,
(PVOID)g_InfFileTable
);
if (HwCompDatId) {
SetWorkingTables (HwCompDatId, NULL, NULL, NULL);
CloseHwCompDat (HwCompDatId);
} else {
LOG ((
LOG_WARNING,
"AppendDynamicSuppliedDrivers: OpenAndLoadHwCompDat failed for %s (rc=0x%x)",
te.FullPath,
GetLastError ()
));
//
// report failure, but keep going
//
b = FALSE;
}
} while (EnumNextFileInTree (&te));
}
return b;
}