778 lines
21 KiB
C
778 lines
21 KiB
C
#define MAX_DRIVES 64
|
|
|
|
|
|
BOOL
|
|
EnumerateAllDrivesT (
|
|
IN FILEENUMPROCT fnEnumCallback,
|
|
IN FILEENUMFAILPROCT fnFailCallback,
|
|
IN DWORD EnumID,
|
|
IN LPVOID pParam,
|
|
IN PEXCLUDEINFT ExcludeInfStruct,
|
|
IN DWORD AttributeFilter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumerateAllDrives first builds an exclusion list if an exclusion INF path
|
|
is provided, and then enumerates every file on every drive that is not
|
|
excluded. The callback function is called once per file. The pParam
|
|
parameter is passed to the callback.
|
|
|
|
Arguments:
|
|
|
|
fnEnumCallback - A pointer to your callback function
|
|
EnumID - A caller-defined value used to identify the exclusion list
|
|
pParam - LPVOID passed to callback function
|
|
ExcludeInfStruct - Struct containing INF file information for excluding dirs or files
|
|
AttributeFilter - FILTER_xxx constants
|
|
|
|
Return Value:
|
|
|
|
TRUE if function succeeds. Call GetLastError for error code if return
|
|
value is FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR LogicalDrives[MAX_DRIVES];
|
|
DWORD rc;
|
|
PCTSTR p;
|
|
UINT driveType;
|
|
|
|
rc = GetLogicalDriveStrings (
|
|
MAX_DRIVES,
|
|
LogicalDrives
|
|
);
|
|
|
|
if (!rc || rc > MAX_DRIVES) {
|
|
if (rc)
|
|
SetLastError (ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
for (p = LogicalDrives ; *p ; p = GetEndOfString (p) + 1) {
|
|
|
|
driveType = GetDriveType(p);
|
|
if (driveType == DRIVE_REMOTE || driveType == DRIVE_CDROM) {
|
|
continue;
|
|
}
|
|
|
|
|
|
if (!EnumerateTreeT (p,
|
|
fnEnumCallback,
|
|
fnFailCallback,
|
|
EnumID,
|
|
pParam,
|
|
ENUM_ALL_LEVELS,
|
|
ExcludeInfStruct,
|
|
AttributeFilter
|
|
))
|
|
break;
|
|
}
|
|
|
|
return (*p == 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
EnumerateTreeT (
|
|
IN PCTSTR EnumRoot,
|
|
IN FILEENUMPROCT fnEnumCallback,
|
|
IN FILEENUMFAILPROCT fnFailCallback, OPTIONAL
|
|
IN DWORD EnumID,
|
|
IN LPVOID pParam,
|
|
IN DWORD Levels,
|
|
IN PEXCLUDEINFT ExcludeInfStruct, OPTIONAL
|
|
IN DWORD AttributeFilter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumerateTree is similar to EnumerateAllDrives, except it allows you to
|
|
enumerate a specific drive, or a specific subdir on a drive. Supply the
|
|
drive letter and optional subdirectory in EnumRoot. Before enumerating,
|
|
EnumerateTree will first build an exclusion list if an exclusion INF path
|
|
is provided. Then every file below EnumRoot is enumerated, and the
|
|
callback is called once per file, passing pParam unchanged.
|
|
|
|
Arguments:
|
|
|
|
EnumRoot - Drive and optional path to enumerate
|
|
fnEnumCallback - A pointer to your callback function
|
|
fnFailCallback - A pointer to optional fn that logs enumeration errors
|
|
EnumID - A caller-defined value used to identify the exclusion list
|
|
pParam - LPVOID passed to callback function
|
|
ExcludeInfStruct - Struct containing INF file information for excluding dirs or files
|
|
AttributeFilter - FILTER_xxx constants
|
|
|
|
Return Value:
|
|
|
|
TRUE if function succeeds. Call GetLastError for error code if return
|
|
value is FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENUMSTRUCTT es;
|
|
BOOL b;
|
|
|
|
if (ExcludeInfStruct)
|
|
if (!BuildExclusionsFromInfT (
|
|
EnumID,
|
|
ExcludeInfStruct
|
|
)) {
|
|
DEBUGMSG ((DBG_ERROR, "Error in exclusion file"));
|
|
return FALSE;
|
|
}
|
|
|
|
es.fnEnumCallback = fnEnumCallback;
|
|
es.fnFailCallback = fnFailCallback;
|
|
es.EnumID = EnumID;
|
|
es.pParam = pParam;
|
|
es.Levels = Levels;
|
|
es.CurrentLevel = 1;
|
|
es.AttributeFilter = AttributeFilter;
|
|
|
|
if (!IsPathLengthOk(EnumRoot))
|
|
{
|
|
if (NULL != fnFailCallback)
|
|
{
|
|
fnFailCallback(EnumRoot);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (IsPathExcludedT (EnumID, EnumRoot))
|
|
return TRUE;
|
|
|
|
b = EnumTreeEngineT (EnumRoot, &es);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumTreeEngineT (
|
|
PCTSTR CurrentPath,
|
|
PENUMSTRUCTT pes
|
|
)
|
|
{
|
|
WIN32_FIND_DATA fd; // A find struct for this subdir
|
|
HANDLE hFind; // A find handle for this subdir
|
|
PTSTR FullFilePath; // Buffer used to build file path
|
|
static TCHAR FindPattern[MAX_TCHAR_PATH * 2]; // Temp buffer used to build pattern
|
|
BYTE byBitmask[MAX_PATH]; // Bitmask is used to speed exclusion lookup
|
|
static DWORD Attrib; // Temp attribute storage for filter processing
|
|
static INT rc; // Callback return value
|
|
DWORD PrevLevelCt; // Storage for parent's max depth setting
|
|
BOOL RecurseStatus;
|
|
DWORD CurrentDirData = 0;
|
|
|
|
//
|
|
// Do nothing when CurrentPath is at the size limit.
|
|
//
|
|
if (!IsPathLengthOk(CurrentPath))
|
|
{
|
|
if (NULL != pes->fnFailCallback)
|
|
{
|
|
pes->fnFailCallback(CurrentPath);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
PrevLevelCt = pes->Levels;
|
|
|
|
|
|
StringCopy (FindPattern, CurrentPath);
|
|
|
|
//
|
|
// Create a bitmask that tells us when subdirectories match partial
|
|
// file patterns.
|
|
//
|
|
|
|
ZeroMemory (byBitmask, sizeof (byBitmask));
|
|
CreateBitmaskT (pes->EnumID, FindPattern, byBitmask);
|
|
|
|
AppendPathWack (FindPattern);
|
|
StringCat (FindPattern, TEXT("*"));
|
|
hFind = FindFirstFile (FindPattern, &fd);
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE) {
|
|
|
|
do {
|
|
|
|
FullFilePath = JoinPaths (CurrentPath, fd.cFileName);
|
|
|
|
__try {
|
|
//
|
|
// Ignore this path if FullFilePath is too long
|
|
// this way fd.cFileName will surely be within limits (since it's shorter)
|
|
//
|
|
if (!IsPathLengthOk (FullFilePath)) {
|
|
if (NULL != pes->fnFailCallback) {
|
|
pes->fnFailCallback(FullFilePath);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Filter directories named ".", "..". Set Attrib symbol.
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
|
|
if (!StringCompare (fd.cFileName, TEXT(".")) ||
|
|
!StringCompare (fd.cFileName, TEXT("..")))
|
|
continue;
|
|
|
|
Attrib = FILTER_DIRECTORIES;
|
|
} else {
|
|
Attrib = FILTER_FILES;
|
|
}
|
|
|
|
// Call the callback
|
|
if (Attrib & pes->AttributeFilter) {
|
|
rc = CALLBACK_CONTINUE;
|
|
|
|
switch (Attrib) {
|
|
case FILTER_DIRECTORIES:
|
|
// Ignore excluded paths
|
|
if (IsPathExcludedT (pes->EnumID, FullFilePath)) {
|
|
break;
|
|
}
|
|
|
|
// Callback for 'directory first'
|
|
if (!(pes->AttributeFilter & FILTER_DIRS_LAST)) {
|
|
rc = pes->fnEnumCallback (
|
|
FullFilePath,
|
|
NULL,
|
|
&fd,
|
|
pes->EnumID,
|
|
pes->pParam,
|
|
&CurrentDirData
|
|
);
|
|
}
|
|
|
|
if (rc >= CALLBACK_CONTINUE && pes->CurrentLevel != pes -> Levels) {
|
|
// Recurse on directory
|
|
pes->CurrentLevel++;
|
|
RecurseStatus = EnumTreeEngineT (FullFilePath, pes);
|
|
pes->CurrentLevel--;
|
|
if (!RecurseStatus) {
|
|
PushError();
|
|
FindClose(hFind);
|
|
PopError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Callback for 'directory last'
|
|
if (pes->AttributeFilter & FILTER_DIRS_LAST) {
|
|
rc = pes->fnEnumCallback (
|
|
FullFilePath,
|
|
NULL,
|
|
&fd,
|
|
pes->EnumID,
|
|
pes->pParam,
|
|
&CurrentDirData
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case FILTER_FILES:
|
|
if (!IsFileExcludedT (pes->EnumID, FullFilePath, byBitmask)) {
|
|
rc = pes->fnEnumCallback (FullFilePath,
|
|
NULL,
|
|
&fd,
|
|
pes->EnumID,
|
|
pes->pParam,
|
|
&CurrentDirData
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (rc == CALLBACK_FAILED) {
|
|
PushError();
|
|
FindClose (hFind);
|
|
PopError();
|
|
return FALSE;
|
|
}
|
|
else if (rc == CALLBACK_SUBDIR_DONE) {
|
|
break;
|
|
}
|
|
else if (rc > 0) {
|
|
pes->Levels = pes->CurrentLevel + rc;
|
|
}
|
|
}
|
|
else if (Attrib == FILTER_DIRECTORIES && !IsPathExcludedT (pes->EnumID, FullFilePath)) {
|
|
// Recurse on directory.
|
|
if (pes->CurrentLevel != pes -> Levels) {
|
|
|
|
pes->CurrentLevel++;
|
|
RecurseStatus = EnumTreeEngineT (FullFilePath, pes);
|
|
pes->CurrentLevel--;
|
|
if (!RecurseStatus) {
|
|
PushError();
|
|
FindClose(hFind);
|
|
PopError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
FreePathString (FullFilePath);
|
|
}
|
|
} while (FindNextFile (hFind, &fd));
|
|
|
|
FindClose (hFind);
|
|
|
|
//
|
|
// Test error code returned from FindNextFile
|
|
//
|
|
if (GetLastError() != ERROR_NO_MORE_FILES && GetLastError() != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Caller to handle not-ready message
|
|
//
|
|
if (GetLastError() != ERROR_NOT_READY)
|
|
{
|
|
DEBUGMSG((DBG_ERROR,
|
|
"EnumTreeEngineT: Error from FindNextFile.\n"
|
|
" FindPattern: %s\n"
|
|
" Error: %u (%x)",
|
|
FindPattern,
|
|
GetLastError(),GetLastError()));
|
|
}
|
|
return FALSE;
|
|
}
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Test return codes from FindFirstFile
|
|
//
|
|
if (GetLastError () != ERROR_NO_MORE_FILES)
|
|
{
|
|
//
|
|
// Caller to handle not-ready message
|
|
//
|
|
if (GetLastError() != ERROR_NOT_READY)
|
|
{
|
|
DEBUGMSG((DBG_WARNING,
|
|
"EnumTreeEngineT: Warning from FindFirstFile.\n"
|
|
" FindPattern: %s\n",
|
|
FindPattern));
|
|
}
|
|
// return FALSE;
|
|
}
|
|
SetLastError (ERROR_SUCCESS);
|
|
}
|
|
|
|
// If a callback returned a positive, non-zero number, the depth
|
|
// of the subdirectory search was limited for this level. Now that
|
|
// this level is done, we must restore the depth value of our parent.
|
|
pes->Levels = PrevLevelCt;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
A bitmask is used in IsFileExcluded for faster relative directory searches.
|
|
Instead of looking in the MemDb for each part of the path, IsFileExcluded
|
|
skips segments that are known not to match. We create the bitmask here
|
|
by looking up each portion of FindPattern. Bit 1 is set if the last
|
|
subdirectory exists in the file exclusion list, Bit 2 is set if the last
|
|
two subdirectories exist in the file exclusion list, and so on.
|
|
|
|
For example, assume FindPattern is set to C:\DEV\FOO\BAR. CreateBitmask
|
|
first looks in the memory database for BAR\*, and if it is found bit 1 is set.
|
|
Then CreateBitmask looks in the memory database for FOO\BAR\*, and sets bit
|
|
2. Again the function looks up DEV\FOO\BAR\* for bit 3 and finally
|
|
C:\DEV\FOO\BAR\* for bit 4.
|
|
|
|
Bit 0 is always set (empty paths always match).
|
|
|
|
Once this bitmask is set up, IsFileExcluded can test only the patterns that
|
|
are known to exist.
|
|
|
|
--*/
|
|
|
|
void
|
|
CreateBitmaskT (
|
|
DWORD EnumID,
|
|
PCTSTR FindPattern,
|
|
BYTE byBitmask[]
|
|
)
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
TCHAR ShortPath[MAX_TCHAR_PATH * 2];
|
|
PCTSTR p;
|
|
PTSTR End;
|
|
int nByte;
|
|
int nBitVal;
|
|
|
|
// Always set bit 0
|
|
byBitmask[0] |= 1;
|
|
|
|
// Build full file spec
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES
|
|
);
|
|
|
|
End = GetEndOfString (EnumPath);
|
|
StringCopy (End, FindPattern);
|
|
AppendPathWack (End);
|
|
StringCat (End, TEXT("*"));
|
|
|
|
// Start with last subdirectory, and build mask in reverse
|
|
p = _tcsrchr (EnumPath, TEXT('\\'));
|
|
nByte = 0;
|
|
nBitVal = 2;
|
|
do {
|
|
// Move back to previous backslash
|
|
for (p = _tcsdec (EnumPath, p) ;
|
|
p >= End && *p != TEXT('\\') ;
|
|
p = _tcsdec (EnumPath, p))
|
|
{
|
|
}
|
|
|
|
// Check if partial file is in the tree
|
|
wsprintf (
|
|
ShortPath,
|
|
TEXT("%s\\%X\\%s%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES,
|
|
p
|
|
);
|
|
|
|
if (MemDbGetPatternValueWithPattern (ShortPath, NULL))
|
|
byBitmask[nByte] |= nBitVal;
|
|
|
|
// Inc bit pos
|
|
nBitVal *= 2;
|
|
if (nBitVal == 256) {
|
|
nBitVal = 1;
|
|
nByte++;
|
|
}
|
|
} while (p > End);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsPathExcludedT (DWORD EnumID, PCTSTR Path)
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
TCHAR ShortPath[MAX_TCHAR_PATH * 2];
|
|
PCTSTR p;
|
|
PTSTR End;
|
|
|
|
// Try full paths
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_PATHS
|
|
);
|
|
|
|
End = GetEndOfString (EnumPath);
|
|
p = _tcsappend (End, Path);
|
|
|
|
if (MemDbGetPatternValue (EnumPath, NULL)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Try partial paths
|
|
do {
|
|
// Move back to previous backslash
|
|
for (p = _tcsdec (EnumPath, p) ;
|
|
p > End && (*p != TEXT('\\')) ;
|
|
p = _tcsdec (EnumPath, p))
|
|
{
|
|
}
|
|
|
|
// Check if partial path is in the tree
|
|
if (p > End && p[1]) {
|
|
wsprintf (
|
|
ShortPath,
|
|
TEXT("%s\\%X\\%s%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_PATHS,
|
|
p
|
|
);
|
|
|
|
if (MemDbGetPatternValue (ShortPath, NULL)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
} while (p > End);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsFileExcludedT (DWORD EnumID, PCTSTR File, BYTE byBitmask[])
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
TCHAR ShortPath[MAX_TCHAR_PATH * 2];
|
|
PCTSTR p;
|
|
PTSTR End;
|
|
int nByte;
|
|
int nBit;
|
|
|
|
// Build full file spec
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES
|
|
);
|
|
|
|
End = GetEndOfString (EnumPath);
|
|
p = _tcsappend (End, File);
|
|
|
|
//
|
|
// Try partial file specs until full spec is reached
|
|
//
|
|
|
|
nByte = 0;
|
|
nBit = 1;
|
|
do {
|
|
//
|
|
// Move back to previous backslash in path
|
|
// (p starts at NULL of EnumPath, End is in the middle of EnumPath)
|
|
//
|
|
|
|
for (p = _tcsdec (EnumPath, p) ;
|
|
p >= End && (*p != TEXT('\\')) ;
|
|
p = _tcsdec (EnumPath, p))
|
|
{
|
|
}
|
|
|
|
// Bitmask is used to make sure slightly expensive query is necessary
|
|
if (byBitmask[nByte] & nBit) {
|
|
|
|
//
|
|
// Check if partial file is in the tree
|
|
//
|
|
|
|
wsprintf (
|
|
ShortPath,
|
|
TEXT("%s\\%X\\%s%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES,
|
|
p
|
|
);
|
|
|
|
if (MemDbGetPatternValue (ShortPath, NULL)) {
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
nBit *= 2;
|
|
if (nBit == 256) {
|
|
nBit = 1;
|
|
nByte++;
|
|
}
|
|
} while (p > End);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// ClearExclusions removes all enumaration exclusions. It is called
|
|
// automatically at the end of enumeration when an exclusion INF file is
|
|
// used. Use it when you need to programmatically build an exclusion list
|
|
// with ExcludeDrive, ExcludePath, and ExcludeFile.
|
|
//
|
|
// You can combine programmatic exclusions with an exclusion INF file, but
|
|
// beware that the programmatic exclusions will be cleared after
|
|
// EnumarteAllDrives or EnumerateTree completes.
|
|
//
|
|
// If you do not use an INF, the programmatic exclusions will not
|
|
// automatically be cleared.
|
|
//
|
|
// EnumID - Caller-defined value to identify enumeration exclusion list
|
|
//
|
|
|
|
VOID
|
|
ClearExclusionsT (
|
|
DWORD EnumID
|
|
)
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
|
|
wsprintf (EnumPath, TEXT("%s\\%X"), MEMDB_CATEGORY_FILEENUM, EnumID);
|
|
|
|
MemDbDeleteTree (EnumPath);
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ExcludePath adds a path name to the exclusion list. There are two
|
|
cases:
|
|
|
|
1. A full path spec is supplied, including the drive letter or
|
|
UNC double-backslash.
|
|
2. The path does not start with a drive letter and is a portion of
|
|
a full path.
|
|
|
|
The dot and double-dot directories are not supported. Any part of
|
|
the path may contain wildcard characters, but a wildcard can not
|
|
be used in place of a backslash.
|
|
|
|
Arguments:
|
|
|
|
EnumID - A caller-defined value that identifies the exclusion list
|
|
Path - The path specification as described above
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
VOID
|
|
ExcludePathT (
|
|
IN DWORD EnumID,
|
|
IN PCTSTR Path
|
|
)
|
|
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_PATHS,
|
|
Path
|
|
);
|
|
|
|
MemDbSetValue (EnumPath, 0);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ExcludeFile adds a file spec to the exclusion list. There are two
|
|
cases:
|
|
|
|
1. A full path spec is supplied, including the drive letter or
|
|
UNC double-backslash.
|
|
2. The path does not start with a drive letter and is a portion of
|
|
a full path.
|
|
|
|
The dot and double-dot directories are not supported. Any part of
|
|
the path may contain wildcard characters, but a wildcard can not
|
|
be used in place of a backslash.
|
|
|
|
Arguments:
|
|
|
|
EnumID - A caller-defined value that identifies the exclusion list
|
|
File - The file specification as described above
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
VOID
|
|
ExcludeFileT (
|
|
IN DWORD EnumID,
|
|
IN PCTSTR File
|
|
)
|
|
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES,
|
|
File
|
|
);
|
|
|
|
MemDbSetValue (EnumPath, 0);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BuildExclusionsFromInfT (DWORD EnumID,
|
|
PEXCLUDEINFT ExcludeInfStruct)
|
|
{
|
|
HINF hInf;
|
|
INFCONTEXT ic;
|
|
TCHAR Exclude[MAX_TCHAR_PATH * 2];
|
|
|
|
// Attempt to open
|
|
hInf = SetupOpenInfFile (ExcludeInfStruct->ExclusionInfPath, NULL, INF_STYLE_WIN4, NULL);
|
|
if (hInf == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
// Read in path exclusions
|
|
if (ExcludeInfStruct->PathSection) {
|
|
if (SetupFindFirstLine (hInf, ExcludeInfStruct->PathSection, NULL, &ic)) {
|
|
do {
|
|
if (SetupGetStringField (&ic, 1, Exclude, MAX_TCHAR_PATH, NULL)) {
|
|
ExcludePathT (EnumID, Exclude);
|
|
}
|
|
} while (SetupFindNextLine (&ic, &ic));
|
|
}
|
|
}
|
|
|
|
// Read in file exclusions
|
|
if (ExcludeInfStruct->FileSection) {
|
|
if (SetupFindFirstLine (hInf, ExcludeInfStruct->FileSection, NULL, &ic)) {
|
|
do {
|
|
if (SetupGetStringField (&ic, 1, Exclude, MAX_TCHAR_PATH, NULL)) {
|
|
ExcludeFileT (EnumID, Exclude);
|
|
}
|
|
} while (SetupFindNextLine (&ic, &ic));
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
SetupCloseInfFile (hInf);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|