mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3445 lines
93 KiB
3445 lines
93 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
infinst.c
|
|
|
|
Abstract:
|
|
|
|
High-level INF install section processing API.
|
|
|
|
Author:
|
|
|
|
Ted Miller (tedm) 6-Mar-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "setupntp.h"
|
|
#pragma hdrstop
|
|
#include <shlobj.h>
|
|
|
|
//
|
|
// Flags for UpdateInis in INFs
|
|
//
|
|
#define FLG_MATCH_KEY_AND_VALUE 1
|
|
#ifdef INF_FLAGS
|
|
#define FLG_CONDITIONAL_ADD 2
|
|
#endif
|
|
|
|
//
|
|
// Flags for UpdateIniFields in INFs
|
|
//
|
|
#ifdef INF_FLAGS
|
|
#define FLG_INIFIELDS_WILDCARDS 1
|
|
#endif
|
|
#define FLG_INIFIELDS_USE_SEP2 2
|
|
|
|
typedef struct _INIFILESECTION {
|
|
PTSTR IniFileName;
|
|
PTSTR SectionName;
|
|
PTSTR SectionData;
|
|
int BufferSize;
|
|
int BufferUsed;
|
|
struct _INIFILESECTION *Next;
|
|
} INIFILESECTION, *PINIFILESECTION;
|
|
|
|
typedef struct _INISECTIONCACHE {
|
|
//
|
|
// Head of section list.
|
|
//
|
|
PINIFILESECTION Sections;
|
|
} INISECTIONCACHE, *PINISECTIONCACHE;
|
|
|
|
|
|
//
|
|
// Define context structure used in processing registry modification
|
|
// lines in an INF install section.
|
|
//
|
|
typedef struct _REGMOD_CONTEXT {
|
|
|
|
HKEY UserRootKey;
|
|
|
|
DEVINST DevInst;
|
|
|
|
} REGMOD_CONTEXT, *PREGMOD_CONTEXT;
|
|
|
|
|
|
CONST TCHAR pszUpdateInis[] = SZ_KEY_UPDATEINIS,
|
|
pszUpdateIniFields[] = SZ_KEY_UPDATEINIFIELDS,
|
|
pszIni2Reg[] = SZ_KEY_INI2REG,
|
|
pszAddReg[] = SZ_KEY_ADDREG,
|
|
pszDelReg[] = SZ_KEY_DELREG;
|
|
|
|
//
|
|
// Separator chars in an ini field
|
|
//
|
|
TCHAR pszIniFieldSeparators[] = TEXT(" ,\t");
|
|
|
|
//
|
|
// Mapping between registry key specs in an inf file
|
|
// and predefined registry handles.
|
|
//
|
|
STRING_TO_DATA InfRegSpecTohKey[] = { TEXT("HKEY_LOCAL_MACHINE"), (UINT)HKEY_LOCAL_MACHINE,
|
|
TEXT("HKLM") , (UINT)HKEY_LOCAL_MACHINE,
|
|
TEXT("HKEY_CLASSES_ROOT") , (UINT)HKEY_CLASSES_ROOT,
|
|
TEXT("HKCR") , (UINT)HKEY_CLASSES_ROOT,
|
|
TEXT("HKR") , (UINT)NULL,
|
|
TEXT("HKEY_CURRENT_USER") , (UINT)HKEY_CURRENT_USER,
|
|
TEXT("HKCU") , (UINT)HKEY_CURRENT_USER,
|
|
TEXT("HKEY_USERS") , (UINT)HKEY_USERS,
|
|
TEXT("HKU") , (UINT)HKEY_USERS,
|
|
NULL , (UINT)NULL
|
|
};
|
|
|
|
//
|
|
// Mapping between registry value names and CM device registry property (CM_DRP) codes.
|
|
//
|
|
// These values must be in the exact ordering of the SPDRP codes, as defined in setupapi.h.
|
|
// This allows us to easily map between SPDRP and CM_DRP property codes.
|
|
//
|
|
STRING_TO_DATA InfRegValToDevRegProp[] = { pszDeviceDesc, (UINT)CM_DRP_DEVICEDESC,
|
|
pszHardwareID, (UINT)CM_DRP_HARDWAREID,
|
|
pszCompatibleIDs, (UINT)CM_DRP_COMPATIBLEIDS,
|
|
pszNtDevicePaths, (UINT)CM_DRP_NTDEVICEPATHS,
|
|
pszService, (UINT)CM_DRP_SERVICE,
|
|
pszConfiguration, (UINT)CM_DRP_CONFIGURATION,
|
|
pszConfigurationVector, (UINT)CM_DRP_CONFIGURATIONVECTOR,
|
|
pszClass, (UINT)CM_DRP_CLASS,
|
|
pszClassGuid, (UINT)CM_DRP_CLASSGUID,
|
|
pszDriver, (UINT)CM_DRP_DRIVER,
|
|
pszConfigFlags, (UINT)CM_DRP_CONFIGFLAGS,
|
|
pszMfg, (UINT)CM_DRP_MFG,
|
|
pszFriendlyName, (UINT)CM_DRP_FRIENDLYNAME,
|
|
NULL, (UINT)0
|
|
};
|
|
|
|
|
|
HKEY
|
|
pSetupInfRegSpecToKeyHandle(
|
|
IN PCTSTR InfRegSpec,
|
|
IN HKEY UserRootKey
|
|
);
|
|
|
|
DWORD
|
|
pSetupValidateDevRegProp(
|
|
IN ULONG CmPropertyCode,
|
|
IN DWORD ValueType,
|
|
IN PCVOID Data,
|
|
IN DWORD DataSize,
|
|
OUT PVOID *ConvertedBuffer,
|
|
OUT PDWORD ConvertedBufferSize
|
|
);
|
|
|
|
//
|
|
// Internal ini file routines.
|
|
//
|
|
PINIFILESECTION
|
|
pSetupLoadIniFileSection(
|
|
IN PCTSTR FileName,
|
|
IN PCTSTR SectionName,
|
|
IN OUT PINISECTIONCACHE SectionList
|
|
);
|
|
|
|
DWORD
|
|
pSetupUnloadIniFileSections(
|
|
IN PINISECTIONCACHE SectionList,
|
|
IN BOOL WriteToFile
|
|
);
|
|
|
|
PTSTR
|
|
pSetupFindLineInSection(
|
|
IN PINIFILESECTION Section,
|
|
IN PCTSTR KeyName, OPTIONAL
|
|
IN PCTSTR RightHandSide OPTIONAL
|
|
);
|
|
|
|
BOOL
|
|
pSetupReplaceOrAddLineInSection(
|
|
IN PINIFILESECTION Section,
|
|
IN PCTSTR KeyName, OPTIONAL
|
|
IN PCTSTR RightHandSide, OPTIONAL
|
|
IN BOOL MatchRHS
|
|
);
|
|
|
|
BOOL
|
|
pSetupAppendLineToSection(
|
|
IN PINIFILESECTION Section,
|
|
IN PCTSTR KeyName, OPTIONAL
|
|
IN PCTSTR RightHandSide OPTIONAL
|
|
);
|
|
|
|
BOOL
|
|
pSetupDeleteLineFromSection(
|
|
IN PINIFILESECTION Section,
|
|
IN PCTSTR KeyName, OPTIONAL
|
|
IN PCTSTR RightHandSide OPTIONAL
|
|
);
|
|
|
|
|
|
DWORD
|
|
pSetupEnumInstallationSections(
|
|
IN PVOID Inf,
|
|
IN PCTSTR Section,
|
|
IN PCTSTR Key,
|
|
IN DWORD (*EnumCallbackFunc)(PINFCONTEXT,PVOID),
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Iterate all values on a line in a given section with a given key,
|
|
treating each as the name of a section, and then pass each of the lines
|
|
in the referenced sections to a callback function.
|
|
|
|
Arguments:
|
|
|
|
Inf - supplies a handle to an open inf file.
|
|
|
|
Section - supplies the name of the section in which the line whose
|
|
values are to be iterated resides.
|
|
|
|
Key - supplies the key of the line whose values are to be iterated.
|
|
|
|
EnumCallbackFunc - supplies a pointer to the callback function.
|
|
Each line in each referenced section is passed to this function.
|
|
|
|
Context - supplies a context value to be passes through to the
|
|
callback function.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b;
|
|
INFCONTEXT LineContext;
|
|
DWORD FieldCount;
|
|
DWORD Field;
|
|
DWORD d;
|
|
PCTSTR SectionName;
|
|
INFCONTEXT FirstLineContext;
|
|
|
|
//
|
|
// Find the relevent line in the given install section.
|
|
// If not present then we're done -- report success.
|
|
//
|
|
b = SetupFindFirstLine(Inf,Section,Key,&LineContext);
|
|
if(!b) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
do {
|
|
//
|
|
// Each value on the line in the given install section
|
|
// is the name of another section.
|
|
//
|
|
FieldCount = SetupGetFieldCount(&LineContext);
|
|
for(Field=1; Field<=FieldCount; Field++) {
|
|
|
|
if((SectionName = pSetupGetField(&LineContext,Field))
|
|
&& SetupFindFirstLine(Inf,SectionName,NULL,&FirstLineContext)) {
|
|
//
|
|
// Call the callback routine for every line in the section.
|
|
//
|
|
do {
|
|
d = EnumCallbackFunc(&FirstLineContext,Context);
|
|
if(d != NO_ERROR) {
|
|
return(d);
|
|
}
|
|
} while(SetupFindNextLine(&FirstLineContext,&FirstLineContext));
|
|
}
|
|
}
|
|
} while(SetupFindNextMatchLine(&LineContext,Key,&LineContext));
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupProcessUpdateInisLine(
|
|
IN PINFCONTEXT InfLineContext,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process a line containing update-inis directives.
|
|
|
|
The line is expected to be in the following format:
|
|
|
|
<filename>,<section>,<old-entry>,<new-entry>,<flags>
|
|
|
|
<filename> supplies the filename of the ini file.
|
|
|
|
<section> supplies the section in the ini file.
|
|
|
|
<old-entry> is optional and if specified supplies an entry to
|
|
be removed from the section, in the form "key=val".
|
|
|
|
<new-entry> is optional and if specified supplies an entry to
|
|
be added to the section, in the form "key=val".
|
|
|
|
<flags> are optional flags
|
|
FLG_MATCH_KEY_AND_VALUE (1)
|
|
FLG_CONDITIONAL_ADD (2)
|
|
|
|
Arguments:
|
|
|
|
InfLineContext - supplies context for current line in the section.
|
|
|
|
Context - Supplies pointer to structure describing loaded ini file sections.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR File;
|
|
PCTSTR Section;
|
|
PCTSTR OldLine;
|
|
PCTSTR NewLine;
|
|
BOOL b;
|
|
DWORD d;
|
|
PTCHAR Key,Value;
|
|
PTCHAR p;
|
|
UINT Flags;
|
|
PINIFILESECTION SectData;
|
|
PTSTR LineData;
|
|
PINISECTIONCACHE IniSectionCache;
|
|
|
|
IniSectionCache = Context;
|
|
|
|
//
|
|
// Get fields from the line.
|
|
//
|
|
File = pSetupGetField(InfLineContext,1);
|
|
Section = pSetupGetField(InfLineContext,2);
|
|
|
|
OldLine = pSetupGetField(InfLineContext,3);
|
|
if(OldLine && (*OldLine == 0)) {
|
|
OldLine = NULL;
|
|
}
|
|
|
|
NewLine = pSetupGetField(InfLineContext,4);
|
|
if(NewLine && (*NewLine == 0)) {
|
|
NewLine = NULL;
|
|
}
|
|
|
|
if(!SetupGetIntField(InfLineContext,5,&Flags)) {
|
|
Flags = 0;
|
|
}
|
|
|
|
//
|
|
// File and section must be specified.
|
|
//
|
|
if(!File || !Section) {
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// If oldline and newline are both not specified, we're done.
|
|
//
|
|
if(!OldLine && !NewLine) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//
|
|
// Open the file and section.
|
|
//
|
|
SectData = pSetupLoadIniFileSection(File,Section,IniSectionCache);
|
|
if(!SectData) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
//
|
|
// If there's an old entry specified, delete it.
|
|
//
|
|
if(OldLine) {
|
|
|
|
Key = DuplicateString(OldLine);
|
|
if(!Key) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
p = Key;
|
|
|
|
if(Value = _tcschr(Key,TEXT('='))) {
|
|
//
|
|
// Delete by key.
|
|
//
|
|
*Value = 0;
|
|
Value = NULL;
|
|
} else {
|
|
//
|
|
// Delete by value.
|
|
//
|
|
Value = Key;
|
|
Key = NULL;
|
|
}
|
|
|
|
pSetupDeleteLineFromSection(SectData,Key,Value);
|
|
|
|
MyFree(p);
|
|
}
|
|
|
|
//
|
|
// If there's a new entry specified, add it.
|
|
//
|
|
if(NewLine) {
|
|
|
|
Key = DuplicateString(NewLine);
|
|
if(!Key) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
if(Value = _tcschr(Key,TEXT('='))) {
|
|
//
|
|
// There is a key. Depending on flags, we want to match
|
|
// key only or key and value.
|
|
//
|
|
*Value++ = 0;
|
|
b = ((Flags & FLG_MATCH_KEY_AND_VALUE) != 0);
|
|
|
|
} else {
|
|
//
|
|
// No key. match whole line. This is the same as matching
|
|
// the RHS only, since no line with a key can match.
|
|
//
|
|
Value = Key;
|
|
Key = NULL;
|
|
b = TRUE;
|
|
}
|
|
|
|
if(!pSetupReplaceOrAddLineInSection(SectData,Key,Value,b)) {
|
|
d = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetupFieldPresentInIniFileLine(
|
|
IN PTCHAR Line,
|
|
IN PCTSTR Field,
|
|
OUT PTCHAR *Start,
|
|
OUT PTCHAR *End
|
|
)
|
|
{
|
|
TCHAR c;
|
|
PTCHAR p,q;
|
|
BOOL b;
|
|
|
|
//
|
|
// Skip the key if there is one (there should be one since we use
|
|
// GetPrivateProfileString to query the value!)
|
|
//
|
|
if(p = _tcschr(Line,TEXT('='))) {
|
|
Line = p+1;
|
|
}
|
|
|
|
//
|
|
// Skip ini field separators.
|
|
//
|
|
Line += _tcsspn(Line,pszIniFieldSeparators);
|
|
|
|
while(*Line) {
|
|
//
|
|
// Locate the end of the field.
|
|
//
|
|
p = Line;
|
|
while(*p && !_tcschr(pszIniFieldSeparators,*p)) {
|
|
if(*p == TEXT('\"')) {
|
|
//
|
|
// Find terminating quote. If none, ignore the quote.
|
|
//
|
|
if(q = _tcschr(p,TEXT('\"'))) {
|
|
p = q;
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
|
|
//
|
|
// p now points to first char past end of field.
|
|
// Make sure the field is 0-terminated and see if we have
|
|
// what we're looking for.
|
|
c = *p;
|
|
*p = 0;
|
|
b = (lstrcmpi(Line,Field) == 0);
|
|
*p = c;
|
|
//
|
|
// Skip separators so p points to first char in next field,
|
|
// or to the terminating 0.
|
|
//
|
|
p += _tcsspn(p,pszIniFieldSeparators);
|
|
|
|
if(b) {
|
|
*Start = Line;
|
|
*End = p;
|
|
return(TRUE);
|
|
}
|
|
|
|
Line = p;
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupProcessUpdateIniFieldsLine(
|
|
IN PINFCONTEXT InfLineContext,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process a line containing update-ini-fields directives. Such directives
|
|
allow individual values in ini files to be removed, added, or replaced.
|
|
|
|
The line is expected to be in the following format:
|
|
|
|
<filename>,<section>,<key>,<old-field>,<new-field>,<flags>
|
|
|
|
<filename> supplies the filename of the ini file.
|
|
|
|
<section> supplies the section in the ini file.
|
|
|
|
<key> supplies the keyname of the line in the section in the ini file.
|
|
|
|
<old-field> supplies the field to be deleted, if specified.
|
|
|
|
<new-field> supplies the field to be added to the line, if specified.
|
|
|
|
<flags> are optional flags
|
|
|
|
Arguments:
|
|
|
|
InfLineContext - supplies context for current line in the section.
|
|
|
|
Context - Supplies pointer to structure describing loaded ini file sections.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR File;
|
|
PCTSTR Section;
|
|
PCTSTR Key;
|
|
TCHAR Value[512];
|
|
#define BUF_SIZE (sizeof(Value)/sizeof(TCHAR))
|
|
TCHAR CONST *Old,*New;
|
|
PTCHAR Start,End;
|
|
BOOL b;
|
|
DWORD d;
|
|
DWORD Space;
|
|
PCTSTR Separator;
|
|
UINT Flags;
|
|
PINISECTIONCACHE IniSectionCache;
|
|
PINIFILESECTION SectData;
|
|
PTSTR Line;
|
|
|
|
IniSectionCache = Context;
|
|
|
|
//
|
|
// Get fields.
|
|
//
|
|
File = pSetupGetField(InfLineContext,1);
|
|
Section = pSetupGetField(InfLineContext,2);
|
|
Key = pSetupGetField(InfLineContext,3);
|
|
|
|
Old = pSetupGetField(InfLineContext,4);
|
|
if(Old && (*Old == 0)) {
|
|
Old = NULL;
|
|
}
|
|
|
|
New = pSetupGetField(InfLineContext,5);
|
|
if(New && (*New == 0)) {
|
|
New = NULL;
|
|
}
|
|
|
|
if(!SetupGetIntField(InfLineContext,6,&Flags)) {
|
|
Flags = 0;
|
|
}
|
|
|
|
//
|
|
// Filename, section name, and key name are mandatory.
|
|
//
|
|
if(!File || !Section || !Key) {
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// If oldline and newline are both not specified, we're done.
|
|
//
|
|
if(!Old && !New) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//
|
|
// Open the file and section.
|
|
//
|
|
SectData = pSetupLoadIniFileSection(File,Section,IniSectionCache);
|
|
if(!SectData) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
Separator = (Flags & FLG_INIFIELDS_USE_SEP2) ? TEXT(", ") : TEXT(" ");
|
|
|
|
if(Line = pSetupFindLineInSection(SectData,Key,NULL)) {
|
|
lstrcpyn(Value, Line, BUF_SIZE);
|
|
} else {
|
|
*Value = TEXT('\0');
|
|
}
|
|
|
|
//
|
|
// Look for the old field if specified and remove it.
|
|
//
|
|
if(Old) {
|
|
if(pSetupFieldPresentInIniFileLine(Value,Old,&Start,&End)) {
|
|
MoveMemory(Start,End,(lstrlen(End)+1)*sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a replacement/new field is specified, put it in there.
|
|
//
|
|
if(New) {
|
|
//
|
|
// Calculate the number of chars that can fit in the buffer.
|
|
//
|
|
Space = BUF_SIZE - (lstrlen(Value) + 1);
|
|
|
|
//
|
|
// If there's space, stick the new field at the end of the line.
|
|
//
|
|
if(Space >= (DWORD)lstrlen(Separator)) {
|
|
lstrcat(Value,Separator);
|
|
Space -= lstrlen(Separator);
|
|
}
|
|
|
|
if(Space >= (DWORD)lstrlen(New)) {
|
|
lstrcat(Value,New);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write the line back out.
|
|
//
|
|
b = pSetupReplaceOrAddLineInSection(SectData,Key,Value,FALSE);
|
|
d = b ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
return(d);
|
|
#undef BUF_SIZE
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupProcessDelRegLine(
|
|
IN PINFCONTEXT InfLineContext,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process a line in the registry that contains delete-registry instructions.
|
|
The line is expected to be in the following form:
|
|
|
|
<root-spec>,<subkey>,<value-name>
|
|
|
|
<Root-spec> is one of HKR, HKLM, etc.
|
|
|
|
<subkey> specifies the subkey relative to Root-spec.
|
|
|
|
<value-name> is optional. If present if specifies a value entry to be deleted
|
|
from the key. If not present the entire key is deleted. This routine
|
|
cannot handle deleting subtrees; the key to be deleted must not have any
|
|
subkeys or the routine will fail.
|
|
|
|
Arguments:
|
|
|
|
InfLineContext - supplies inf line context for the line containing
|
|
delete-registry instructions.
|
|
|
|
Context - supplies the address of a registry modification context
|
|
structure used in deleting the registry value. The structure is
|
|
defined as:
|
|
|
|
typedef struct _REGMOD_CONTEXT {
|
|
|
|
HKEY UserRootKey;
|
|
|
|
DEVINST DevInst;
|
|
|
|
} REGMOD_CONTEXT, *PREGMOD_CONTEXT;
|
|
|
|
where UserRootKey is a handle to the open inf key to be used as
|
|
the root when HKR is specified as the root for the operation, and
|
|
DevInst is the optional device instance handle that is supplied when
|
|
the DelReg section is for a hardware key (i.e., under the Enum branch).
|
|
If this handle is supplied, then the value is checked to see whether it
|
|
is the name of a Plug&Play device registry property, and if so, the
|
|
registry property is deleted via a CM API _as well as_ via a registry API
|
|
(the property is stored in a different location inaccessible to the registry
|
|
APIs under Windows NT).
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR RootKeySpec,SubKeyName,ValueName;
|
|
HKEY RootKey,Key;
|
|
DWORD d;
|
|
PREGMOD_CONTEXT RegModContext = (PREGMOD_CONTEXT)Context;
|
|
ULONG CmPropertyCode;
|
|
|
|
//
|
|
// Get root key spec, subkey name, and value name.
|
|
//
|
|
d = ERROR_INVALID_DATA;
|
|
if((RootKeySpec = pSetupGetField(InfLineContext,1))
|
|
&& (SubKeyName = pSetupGetField(InfLineContext,2))) {
|
|
|
|
ValueName = pSetupGetField(InfLineContext,3);
|
|
|
|
RootKey = pSetupInfRegSpecToKeyHandle(RootKeySpec, RegModContext->UserRootKey);
|
|
if(RootKey) {
|
|
if(ValueName && *ValueName) {
|
|
|
|
if(!SubKeyName || !(*SubKeyName)) {
|
|
//
|
|
// If the key being used is HKR with no subkey specified, and if we
|
|
// are doing the DelReg for a hardware key (i.e., DevInst is non-NULL,
|
|
// then we need to check to see whether the value entry is the name of
|
|
// a device registry property.
|
|
//
|
|
if(RegModContext->DevInst && (RegModContext->UserRootKey == RootKey) &&
|
|
LookUpStringInTable(InfRegValToDevRegProp, ValueName, &CmPropertyCode)) {
|
|
//
|
|
// This value is a device registry property--we must delete the property
|
|
// by calling a CM API.
|
|
//
|
|
CM_Set_DevInst_Registry_Property(RegModContext->DevInst,
|
|
CmPropertyCode,
|
|
NULL,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open subkey for delete.
|
|
//
|
|
d = RegOpenKeyEx(
|
|
RootKey,
|
|
SubKeyName,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&Key
|
|
);
|
|
|
|
if(d == NO_ERROR) {
|
|
//
|
|
// Do delete.
|
|
//
|
|
d = RegDeleteValue(Key,ValueName);
|
|
|
|
RegCloseKey(Key);
|
|
}
|
|
|
|
if(d == ERROR_FILE_NOT_FOUND) {
|
|
d = NO_ERROR;
|
|
}
|
|
|
|
} else {
|
|
d = RegistryDelnode(RootKey,SubKeyName);
|
|
}
|
|
} else {
|
|
d = ERROR_BADKEY;
|
|
}
|
|
}
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupProcessAddRegLine(
|
|
IN PINFCONTEXT InfLineContext,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process a line in the registry that contains add-registry instructions.
|
|
The line is expected to be in the following form:
|
|
|
|
<root-spec>,<subkey>,<value-name>,<flags>,<value>...
|
|
|
|
<Root-spec> is one of HKR, HKLM, etc.
|
|
|
|
<subkey> specifies the subkey relative to Root-spec.
|
|
|
|
<value-name> is optional. If not present the default value is set.
|
|
|
|
<flags> is optional and supplies flags, such as to indicate the data type.
|
|
These are the FLG_ADDREG_* flags defined in setupapi.h, and are a
|
|
superset of those defined for Win95 in setupx.h.
|
|
|
|
<value> is one or more values used as the data. The format depends
|
|
on the value type. This value is optional. For REG_DWORD, the
|
|
default is 0. For REG_SZ, REG_EXPAND_SZ, the default is the
|
|
empty string. For REG_BINARY the default is a 0-length entry.
|
|
For REG_MULTI_SZ the default is a single empty string.
|
|
|
|
Arguments:
|
|
|
|
InfLineContext - supplies inf line context for the line containing
|
|
add-registry instructions.
|
|
|
|
Context - supplies the address of a registry modification context
|
|
structure used in adding the registry value. The structure is
|
|
defined as:
|
|
|
|
typedef struct _REGMOD_CONTEXT {
|
|
|
|
HKEY UserRootKey;
|
|
|
|
DEVINST DevInst;
|
|
|
|
} REGMOD_CONTEXT, *PREGMOD_CONTEXT;
|
|
|
|
where UserRootKey is a handle to the open inf key to be used as
|
|
the root when HKR is specified as the root for the operation, and
|
|
DevInst is the optional device instance handle that is supplied when
|
|
the AddReg section is for a hardware key (i.e., under the Enum branch).
|
|
If this handle is supplied, then the value is checked to see whether it
|
|
is the name of a Plug&Play device registry property, and if so, the
|
|
registry property is set via a CM API instead of via the registry API
|
|
(which doesn't refer to the same location on Windows NT).
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR RootKeySpec,SubKeyName,ValueName;
|
|
PCTSTR ValueTypeSpec;
|
|
DWORD ValueType;
|
|
HKEY RootKey,Key;
|
|
DWORD d = NO_ERROR;
|
|
BOOL b;
|
|
INT IntVal;
|
|
DWORD Size;
|
|
PVOID Data;
|
|
DWORD Disposition;
|
|
UINT Flags;
|
|
PTSTR *Array;
|
|
PREGMOD_CONTEXT RegModContext = (PREGMOD_CONTEXT)Context;
|
|
ULONG CmPropertyCode;
|
|
PVOID ConvertedBuffer;
|
|
DWORD ConvertedBufferSize;
|
|
CONFIGRET cr;
|
|
|
|
//
|
|
// Get root key spec. If we can't get the root key spec, we don't do anything and
|
|
// return NO_ERROR.
|
|
//
|
|
if(RootKeySpec = pSetupGetField(InfLineContext,1)) {
|
|
|
|
RootKey = pSetupInfRegSpecToKeyHandle(RootKeySpec, RegModContext->UserRootKey);
|
|
if(!RootKey) {
|
|
return(ERROR_BADKEY);
|
|
}
|
|
|
|
//
|
|
// SubKeyName is optional.
|
|
//
|
|
SubKeyName = pSetupGetField(InfLineContext,2);
|
|
|
|
//
|
|
// ValueName is optional. Either NULL or "" are acceptable
|
|
// to pass to RegSetValueEx.
|
|
//
|
|
ValueName = pSetupGetField(InfLineContext,3);
|
|
|
|
//
|
|
// If we don't have a value name, the type is REG_SZ to force
|
|
// the right behavior in RegSetValueEx. Otherwise get the data type.
|
|
//
|
|
ValueType = REG_SZ;
|
|
if(ValueName) {
|
|
if(!SetupGetIntField(InfLineContext,4,&Flags)) {
|
|
Flags = 0;
|
|
}
|
|
switch(Flags & FLG_ADDREG_TYPE_MASK) {
|
|
|
|
case FLG_ADDREG_TYPE_SZ :
|
|
ValueType = REG_SZ;
|
|
break;
|
|
|
|
case FLG_ADDREG_TYPE_MULTI_SZ :
|
|
ValueType = REG_MULTI_SZ;
|
|
break;
|
|
|
|
case FLG_ADDREG_TYPE_EXPAND_SZ :
|
|
ValueType = REG_EXPAND_SZ;
|
|
break;
|
|
|
|
case FLG_ADDREG_TYPE_BINARY :
|
|
ValueType = REG_BINARY;
|
|
break;
|
|
|
|
case FLG_ADDREG_TYPE_DWORD :
|
|
ValueType = REG_DWORD;
|
|
break;
|
|
|
|
case FLG_ADDREG_TYPE_NONE :
|
|
ValueType = REG_NONE;
|
|
break;
|
|
|
|
default :
|
|
//
|
|
// If the FLG_ADDREG_BINVALUETYPE is set, then the highword
|
|
// can contain just about any random reg data type ordinal value.
|
|
//
|
|
if(Flags & FLG_ADDREG_BINVALUETYPE) {
|
|
//
|
|
// Disallow the following reg data types:
|
|
//
|
|
// REG_NONE, REG_SZ, REG_EXPAND_SZ, REG_MULTI_SZ
|
|
//
|
|
ValueType = (DWORD)HIWORD(Flags);
|
|
|
|
if((ValueType < REG_BINARY) || (ValueType == REG_MULTI_SZ)) {
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
} else {
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
//
|
|
// Presently, the append behavior flag is only supported for
|
|
// REG_MULTI_SZ values.
|
|
//
|
|
if((Flags & FLG_ADDREG_APPEND) && (ValueType != REG_MULTI_SZ)) {
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the data based on type.
|
|
//
|
|
switch(ValueType) {
|
|
|
|
case REG_MULTI_SZ:
|
|
if(Flags & FLG_ADDREG_APPEND) {
|
|
//
|
|
// This is MULTI_SZ_APPEND, which means to append the string value to
|
|
// an existing multi_sz if it's not already there.
|
|
//
|
|
if(SetupGetStringField(InfLineContext,5,NULL,0,&Size)) {
|
|
Data = MyMalloc(Size*sizeof(TCHAR));
|
|
if(!Data) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
if(SetupGetStringField(InfLineContext,5,Data,Size,NULL)) {
|
|
d = AppendStringToMultiSz(
|
|
RootKey,
|
|
SubKeyName,
|
|
((RegModContext->UserRootKey == RootKey) ? RegModContext->DevInst
|
|
: (DEVINST)NULL),
|
|
ValueName,
|
|
(PCTSTR)Data,
|
|
FALSE // don't allow duplicates.
|
|
);
|
|
} else {
|
|
d = GetLastError();
|
|
}
|
|
MyFree(Data);
|
|
} else {
|
|
d = ERROR_INVALID_DATA;
|
|
}
|
|
return(d);
|
|
|
|
} else {
|
|
|
|
if(SetupGetMultiSzField(InfLineContext, 5, NULL, 0, &Size)) {
|
|
Data = MyMalloc(Size*sizeof(TCHAR));
|
|
if(!Data) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
if(!SetupGetMultiSzField(InfLineContext, 5, Data, Size, NULL)) {
|
|
d = GetLastError();
|
|
MyFree(Data);
|
|
return(d);
|
|
}
|
|
Size *= sizeof(TCHAR);
|
|
} else {
|
|
Size = sizeof(TCHAR);
|
|
Data = MyMalloc(Size);
|
|
if(!Data) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
*((PTCHAR)Data) = TEXT('\0');
|
|
}
|
|
break;
|
|
}
|
|
|
|
case REG_DWORD:
|
|
//
|
|
// Since the old SetupX APIs only allowed REG_BINARY, INFs had to specify REG_DWORD
|
|
// by listing all 4 bytes separately. Support the old format here, by checking to
|
|
// see whether the line has 4 bytes, and if so, combine those to form the DWORD.
|
|
//
|
|
Size = sizeof(DWORD);
|
|
Data = MyMalloc(sizeof(DWORD));
|
|
if(!Data) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
if(SetupGetFieldCount(InfLineContext) == 8) {
|
|
//
|
|
// Then the DWORD is specified as a list of its constituent bytes.
|
|
//
|
|
if(!SetupGetBinaryField(InfLineContext,5,Data,Size,NULL)) {
|
|
d = GetLastError();
|
|
MyFree(Data);
|
|
return(d);
|
|
}
|
|
} else {
|
|
if(!SetupGetIntField(InfLineContext,5,(PINT)Data)) {
|
|
*(PINT)Data = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case REG_SZ:
|
|
case REG_EXPAND_SZ:
|
|
if(SetupGetStringField(InfLineContext,5,NULL,0,&Size)) {
|
|
Data = MyMalloc(Size*sizeof(TCHAR));
|
|
if(!Data) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
if(!SetupGetStringField(InfLineContext,5,Data,Size,NULL)) {
|
|
d = GetLastError();
|
|
MyFree(Data);
|
|
return(d);
|
|
}
|
|
Size *= sizeof(TCHAR);
|
|
} else {
|
|
Size = sizeof(TCHAR);
|
|
Data = DuplicateString(TEXT(""));
|
|
if(!Data) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case REG_BINARY:
|
|
default:
|
|
//
|
|
// All other values are specified in REG_BINARY form (i.e., one byte per field).
|
|
//
|
|
if(SetupGetBinaryField(InfLineContext, 5, NULL, 0, &Size)) {
|
|
Data = MyMalloc(Size);
|
|
if(!Data) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
if(!SetupGetBinaryField(InfLineContext, 5, Data, Size, NULL)) {
|
|
d = GetLastError();
|
|
MyFree(Data);
|
|
return d;
|
|
}
|
|
} else {
|
|
Data = MyMalloc(0);
|
|
Size = 0;
|
|
if(!Data) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set this variable to TRUE only if this value should not be set later on in a call to
|
|
// RegSetValueEx (e.g., if this value is a DevReg Property)
|
|
//
|
|
b = FALSE;
|
|
|
|
//
|
|
// Open/create the key.
|
|
//
|
|
if(SubKeyName && *SubKeyName) {
|
|
|
|
if((d = RegCreateKeyEx(RootKey,
|
|
SubKeyName,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&Key,
|
|
&Disposition)) == ERROR_SUCCESS) {
|
|
|
|
if(Disposition == REG_OPENED_EXISTING_KEY) {
|
|
|
|
if((Flags & FLG_ADDREG_NOCLOBBER) && (!ValueName || !(*ValueName))) {
|
|
//
|
|
// Added for compatibility with Setupx (lonnym):
|
|
// If NoClobber and ValueName is "", then if sub-key already present
|
|
// leave it alone (don't clobber its value even if it empty).
|
|
//
|
|
b = TRUE;
|
|
|
|
} else if(Flags & FLG_ADDREG_DELVAL) {
|
|
//
|
|
// Added for compatibility with Setupx (lonnym):
|
|
// If this flag is present, then the data for this value is ignored, and
|
|
// the value entry is deleted.
|
|
//
|
|
b = TRUE;
|
|
RegDeleteValue(Key, ValueName);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
d = NO_ERROR;
|
|
|
|
//
|
|
// If the key being used is HKR with no subkey specified, and if we are
|
|
// doing the AddReg for a hardware key (i.e., DevInst is non-NULL), then
|
|
// we need to check to see whether the value entry we have is the name of
|
|
// a device registry property.
|
|
//
|
|
if(RegModContext->DevInst && (RegModContext->UserRootKey == RootKey) && ValueName &&
|
|
(b = LookUpStringInTable(InfRegValToDevRegProp, ValueName, &CmPropertyCode))) {
|
|
|
|
ULONG ExistingPropDataSize = 0;
|
|
|
|
//
|
|
// This value is a device registry property--if noclobber flag is set, we must
|
|
// verify that the property doesn't currently exist.
|
|
//
|
|
if((!(Flags & FLG_ADDREG_NOCLOBBER)) ||
|
|
(CM_Get_DevInst_Registry_Property(RegModContext->DevInst,
|
|
CmPropertyCode,
|
|
NULL,
|
|
NULL,
|
|
&ExistingPropDataSize,
|
|
0) == CR_NO_SUCH_VALUE)) {
|
|
//
|
|
// Next, make sure the data is valid (doing conversion if necessary and possible).
|
|
//
|
|
if((d = pSetupValidateDevRegProp(CmPropertyCode,
|
|
ValueType,
|
|
Data,
|
|
Size,
|
|
&ConvertedBuffer,
|
|
&ConvertedBufferSize)) == NO_ERROR) {
|
|
|
|
if((cr = CM_Set_DevInst_Registry_Property(RegModContext->DevInst,
|
|
CmPropertyCode,
|
|
ConvertedBuffer ? ConvertedBuffer
|
|
: Data,
|
|
ConvertedBuffer ? ConvertedBufferSize
|
|
: Size,
|
|
0)) != CR_SUCCESS) {
|
|
|
|
d = (cr == CR_INVALID_DEVINST) ? ERROR_NO_SUCH_DEVINST
|
|
: ERROR_INVALID_DATA;
|
|
}
|
|
|
|
if(ConvertedBuffer) {
|
|
MyFree(ConvertedBuffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Regardless of whether this value is a devinst registry property, we need to set
|
|
// the Key equal to the RootKey (So we won't think it's a newly-opened key and
|
|
// try to close it later.
|
|
//
|
|
Key = RootKey;
|
|
}
|
|
|
|
if(d == NO_ERROR) {
|
|
|
|
if(!b) {
|
|
//
|
|
// If noclobber flag is set, then make sure that the value entry doesn't already exist.
|
|
//
|
|
if((!(Flags & FLG_ADDREG_NOCLOBBER)) ||
|
|
(RegQueryValueEx(Key, ValueName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)) {
|
|
//
|
|
// Set the value.
|
|
//
|
|
d = RegSetValueEx(Key, ValueName, 0, ValueType, Data, Size);
|
|
}
|
|
}
|
|
|
|
if(Key != RootKey) {
|
|
RegCloseKey(Key);
|
|
}
|
|
}
|
|
|
|
MyFree(Data);
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupProcessIni2RegLine(
|
|
IN PINFCONTEXT InfLineContext,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PCTSTR Filename,Section;
|
|
PCTSTR Key,RegRootSpec,SubkeyPath;
|
|
PTCHAR key,value;
|
|
HKEY UserRootKey,RootKey,hKey;
|
|
DWORD Disposition;
|
|
PTCHAR Line;
|
|
PTCHAR Buffer;
|
|
DWORD d;
|
|
TCHAR val[512];
|
|
#define BUF_SIZE (sizeof(val)/sizeof(TCHAR))
|
|
#ifdef INF_FLAGS
|
|
UINT Flags;
|
|
#endif
|
|
|
|
UserRootKey = (HKEY)Context;
|
|
|
|
//
|
|
// Get filename and section name of ini file.
|
|
//
|
|
Filename = pSetupGetField(InfLineContext,1);
|
|
Section = pSetupGetField(InfLineContext,2);
|
|
if(!Filename || !Section) {
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// Get the ini file key. If not specified,
|
|
// use the whole section.
|
|
//
|
|
Key = pSetupGetField(InfLineContext,3);
|
|
|
|
//
|
|
// Get the reg root spec and the subkey path.
|
|
//
|
|
RegRootSpec = pSetupGetField(InfLineContext,4);
|
|
SubkeyPath = pSetupGetField(InfLineContext,5);
|
|
if(SubkeyPath && (*SubkeyPath == 0)) {
|
|
SubkeyPath = NULL;
|
|
}
|
|
|
|
//
|
|
// Translate the root key spec into an hkey
|
|
//
|
|
RootKey = pSetupInfRegSpecToKeyHandle(RegRootSpec,UserRootKey);
|
|
if(!RootKey) {
|
|
return(ERROR_BADKEY);
|
|
}
|
|
|
|
#ifdef INF_FLAGS
|
|
//
|
|
// Get the flags value.
|
|
//
|
|
if(!SetupGetIntField(InfLineContext,6,&Flags)) {
|
|
Flags = 0;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Get the relevent line or section in the ini file.
|
|
//
|
|
if(Key = pSetupGetField(InfLineContext,3)) {
|
|
|
|
Buffer = MyMalloc(
|
|
( lstrlen(Key)
|
|
+ GetPrivateProfileString(Section,Key,TEXT(""),val,BUF_SIZE,Filename)
|
|
+ 3)
|
|
* sizeof(TCHAR)
|
|
);
|
|
|
|
if(!Buffer) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
Buffer[wsprintf((PTSTR)Buffer,TEXT("%s=%s"),Key,val)+1] = 0;
|
|
|
|
} else {
|
|
Buffer = MyMalloc(32768);
|
|
if(!Buffer) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
if(!GetPrivateProfileSection(Section,Buffer,32768,Filename)) {
|
|
*Buffer = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open/create the relevent key.
|
|
//
|
|
d = NO_ERROR;
|
|
if(SubkeyPath) {
|
|
d = RegCreateKeyEx(
|
|
RootKey,
|
|
SubkeyPath,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE,
|
|
NULL,
|
|
&hKey,
|
|
&Disposition
|
|
);
|
|
} else {
|
|
hKey = RootKey;
|
|
}
|
|
|
|
for(Line=Buffer; (d==NO_ERROR) && *Line; Line+=lstrlen(Line)+1) {
|
|
|
|
//
|
|
// Line points to the key=value pair.
|
|
//
|
|
key = Line;
|
|
if(value = _tcschr(key,TEXT('='))) {
|
|
*value++ = 0;
|
|
} else {
|
|
key = TEXT("");
|
|
value = Line;
|
|
}
|
|
|
|
//
|
|
// Now key points to the value name and value to the value.
|
|
//
|
|
d = RegSetValueEx(
|
|
hKey,
|
|
value,
|
|
0,
|
|
REG_SZ,
|
|
(CONST BYTE *)value,
|
|
(lstrlen(value)+1)*sizeof(TCHAR)
|
|
);
|
|
}
|
|
|
|
if(hKey != RootKey) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
MyFree(Buffer);
|
|
|
|
return(d);
|
|
#undef BUF_SIZE
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupInstallUpdateIniFiles(
|
|
IN HINF Inf,
|
|
IN PCTSTR SectionName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Locate the UpdateInis= and UpdateIniField= lines in an install section
|
|
and process each section listed therein.
|
|
|
|
Arguments:
|
|
|
|
Inf - supplies inf handle for inf containing the section indicated
|
|
by SectionName.
|
|
|
|
SectionName - supplies name of install section.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d,x;
|
|
INISECTIONCACHE IniSectionCache;
|
|
|
|
ZeroMemory(&IniSectionCache,sizeof(INISECTIONCACHE));
|
|
|
|
d = pSetupEnumInstallationSections(
|
|
Inf,
|
|
SectionName,
|
|
pszUpdateInis,
|
|
pSetupProcessUpdateInisLine,
|
|
&IniSectionCache
|
|
);
|
|
|
|
if(d == NO_ERROR) {
|
|
|
|
d = pSetupEnumInstallationSections(
|
|
Inf,
|
|
SectionName,
|
|
pszUpdateIniFields,
|
|
pSetupProcessUpdateIniFieldsLine,
|
|
&IniSectionCache
|
|
);
|
|
}
|
|
|
|
x = pSetupUnloadIniFileSections(&IniSectionCache,(d == NO_ERROR));
|
|
|
|
return((d == NO_ERROR) ? x : d);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupInstallRegistry(
|
|
IN HINF Inf,
|
|
IN PCTSTR SectionName,
|
|
IN HKEY UserRootKey,
|
|
IN DEVINST DevInst OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Look for AddReg= and DelReg= directives within an inf section
|
|
and parse them.
|
|
|
|
Arguments:
|
|
|
|
Inf - supplies inf handle for inf containing the section indicated
|
|
by SectionName.
|
|
|
|
SectionName - supplies name of install section.
|
|
|
|
UserRootKey - supplies root key for relative operations.
|
|
|
|
DevInst - Optionally, supplies the handle of the device instance for
|
|
which registry modifications are being performed. This handle is
|
|
used to migrate value entries that are really device registry
|
|
properties into the correct location into the registry (a location
|
|
that may not be directly accessible). For example, if the DevInst
|
|
handle is non-NULL, and the following registry line is encountered:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
REGMOD_CONTEXT RegModContext;
|
|
|
|
//
|
|
// Initialize the registry modification context structure that will be
|
|
// passed in to the AddReg and DelReg callback routines.
|
|
//
|
|
RegModContext.UserRootKey = UserRootKey;
|
|
RegModContext.DevInst = DevInst;
|
|
|
|
d = pSetupEnumInstallationSections(
|
|
Inf,
|
|
SectionName,
|
|
pszDelReg,
|
|
pSetupProcessDelRegLine,
|
|
&RegModContext
|
|
);
|
|
|
|
if(d == NO_ERROR) {
|
|
|
|
d = pSetupEnumInstallationSections(
|
|
Inf,
|
|
SectionName,
|
|
pszAddReg,
|
|
pSetupProcessAddRegLine,
|
|
&RegModContext
|
|
);
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupInstallIni2Reg(
|
|
IN HINF Inf,
|
|
IN PCTSTR SectionName,
|
|
IN HKEY UserRootKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Inf - supplies inf handle for inf containing the section indicated
|
|
by SectionName.
|
|
|
|
SectionName - supplies name of install section.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicatinh outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
|
|
d = pSetupEnumInstallationSections(
|
|
Inf,
|
|
SectionName,
|
|
pszIni2Reg,
|
|
pSetupProcessIni2RegLine,
|
|
(PVOID)UserRootKey
|
|
);
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupInstallFiles(
|
|
IN HINF Inf,
|
|
IN HINF LayoutInf, OPTIONAL
|
|
IN PCTSTR SectionName,
|
|
IN PCTSTR SourceRootPath, OPTIONAL
|
|
IN PSP_FILE_CALLBACK MsgHandler, OPTIONAL
|
|
IN PVOID Context, OPTIONAL
|
|
IN UINT CopyStyle,
|
|
IN HWND Owner, OPTIONAL
|
|
IN HSPFILEQ UserFileQ, OPTIONAL
|
|
IN BOOL IsMsgHandlerNativeCharWidth
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Look for file operation lines in an install section and process them.
|
|
|
|
Arguments:
|
|
|
|
Inf - supplies inf handle for inf containing the section indicated
|
|
by SectionName.
|
|
|
|
SectionName - supplies name of install section.
|
|
|
|
MsgHandler - supplies a callback to be used when the file queue is
|
|
committed. Not used if UserFileQ is specified.
|
|
|
|
Context - supplies context for callback function. Not used if UserFileQ
|
|
is specified.
|
|
|
|
Owner - supplies the window handle of a window to be the parent/owner
|
|
of any dialogs that are created. Not used if UserFileQ is specified.
|
|
|
|
UserFileQ - if specified, then this routine neither created nor commits the
|
|
file queue. File operations are queued on this queue and it is up to the
|
|
caller to flush the queue when it so desired. If this parameter is not
|
|
specified then this routine creates a file queue and commits it
|
|
before returning.
|
|
|
|
IsMsgHandlerNativeCharWidth - indicates whether any message handler callback
|
|
expects native char width args (or ansi ones, in the unicode build
|
|
of this dll).
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Field;
|
|
unsigned i;
|
|
PCTSTR Operations[3] = { TEXT("Copyfiles"),TEXT("Renfiles"),TEXT("Delfiles") };
|
|
BOOL b;
|
|
INFCONTEXT LineContext;
|
|
DWORD FieldCount;
|
|
PCTSTR SectionSpec;
|
|
INFCONTEXT SectionLineContext;
|
|
HSPFILEQ FileQueue;
|
|
DWORD rc;
|
|
BOOL FreeSourceRoot;
|
|
|
|
if(!LayoutInf) {
|
|
LayoutInf = Inf;
|
|
}
|
|
|
|
//
|
|
// Create a file queue.
|
|
//
|
|
if(UserFileQ) {
|
|
FileQueue = UserFileQ;
|
|
} else {
|
|
FileQueue = SetupOpenFileQueue();
|
|
if(FileQueue == INVALID_HANDLE_VALUE) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
FreeSourceRoot = FALSE;
|
|
if(!SourceRootPath) {
|
|
if(SourceRootPath = pSetupGetDefaultSourcePath(Inf)) {
|
|
FreeSourceRoot = TRUE;
|
|
} else {
|
|
//
|
|
// Use a fall-back just in case.
|
|
//
|
|
SourceRootPath = pszOemInfDefaultPath;
|
|
}
|
|
}
|
|
|
|
b = TRUE;
|
|
for(i=0; b && (i<3); i++) {
|
|
|
|
//
|
|
// Find the relevent line in the given install section.
|
|
// If not present then we're done with this operation.
|
|
//
|
|
if(!SetupFindFirstLine(Inf,SectionName,Operations[i],&LineContext)) {
|
|
continue;
|
|
}
|
|
|
|
do {
|
|
//
|
|
// Each value on the line in the given install section
|
|
// is the name of another section.
|
|
//
|
|
FieldCount = SetupGetFieldCount(&LineContext);
|
|
for(Field=1; b && (Field<=FieldCount); Field++) {
|
|
|
|
if(SectionSpec = pSetupGetField(&LineContext,Field)) {
|
|
|
|
//
|
|
// Handle single-file copy specially.
|
|
//
|
|
if((i == 0) && (*SectionSpec == TEXT('@'))) {
|
|
|
|
b = SetupQueueDefaultCopy(
|
|
FileQueue,
|
|
LayoutInf,
|
|
SourceRootPath,
|
|
SectionSpec + 1,
|
|
SectionSpec + 1,
|
|
CopyStyle
|
|
);
|
|
|
|
} else if(SetupGetLineCount(Inf,SectionSpec) > 0) {
|
|
//
|
|
// The section exists and is not empty.
|
|
// Add it to the copy/delete/rename queue.
|
|
//
|
|
switch(i) {
|
|
case 0:
|
|
b = SetupQueueCopySection(
|
|
FileQueue,
|
|
SourceRootPath,
|
|
LayoutInf,
|
|
Inf,
|
|
SectionSpec,
|
|
CopyStyle
|
|
);
|
|
break;
|
|
|
|
case 1:
|
|
b = SetupQueueRenameSection(FileQueue,Inf,NULL,SectionSpec);
|
|
break;
|
|
|
|
case 2:
|
|
b = SetupQueueDeleteSection(FileQueue,Inf,NULL,SectionSpec);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while(SetupFindNextMatchLine(&LineContext,Operations[i],&LineContext));
|
|
}
|
|
|
|
if(b && (FileQueue != UserFileQ)) {
|
|
//
|
|
// Perform the file operations.
|
|
//
|
|
b = _SetupCommitFileQueue(
|
|
Owner,
|
|
FileQueue,
|
|
MsgHandler,
|
|
Context,
|
|
IsMsgHandlerNativeCharWidth
|
|
);
|
|
}
|
|
|
|
rc = b ? NO_ERROR : GetLastError();
|
|
if(FileQueue != UserFileQ) {
|
|
SetupCloseFileQueue(FileQueue);
|
|
}
|
|
|
|
if(FreeSourceRoot) {
|
|
MyFree(SourceRootPath);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
BOOL
|
|
_SetupInstallFromInfSection(
|
|
IN HWND Owner, OPTIONAL
|
|
IN HINF InfHandle,
|
|
IN PCTSTR SectionName,
|
|
IN UINT Flags,
|
|
IN HKEY RelativeKeyRoot, OPTIONAL
|
|
IN PCTSTR SourceRootPath, OPTIONAL
|
|
IN UINT CopyFlags,
|
|
IN PVOID MsgHandler,
|
|
IN PVOID Context, OPTIONAL
|
|
IN HDEVINFO DeviceInfoSet, OPTIONAL
|
|
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
|
|
IN BOOL IsMsgHandlerNativeCharWidth
|
|
)
|
|
{
|
|
DWORD d = NO_ERROR;
|
|
BOOL CloseRelativeKeyRoot = FALSE;
|
|
DEVINST DevInst = (DEVINST)0;
|
|
|
|
if(Flags & (SPINST_REGISTRY | SPINST_INI2REG)) {
|
|
//
|
|
// If the caller supplied a device information set and element, then this is
|
|
// a device installation, and the registry modifications should be made to the
|
|
// device instance's hardware registry key.
|
|
//
|
|
if(DeviceInfoSet && (DeviceInfoSet != INVALID_HANDLE_VALUE)) {
|
|
|
|
if((RelativeKeyRoot = SetupDiCreateDevRegKey(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
DICS_FLAG_GLOBAL,
|
|
0,
|
|
DIREG_DEV,
|
|
NULL,
|
|
NULL)) == INVALID_HANDLE_VALUE) {
|
|
|
|
return FALSE; // last error already set.
|
|
}
|
|
|
|
CloseRelativeKeyRoot = TRUE;
|
|
|
|
//
|
|
// Retrieve the DevInst handle from the device information element. We know
|
|
// this element is valid, since SetupDiCreateDevRegKey succeeded. Even so,
|
|
// enclose this code in try/except, in case the devinfo element went south in
|
|
// the interim.
|
|
//
|
|
try {
|
|
DevInst = (DEVINST)DeviceInfoData->DevInst;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
d = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if(d != NO_ERROR) {
|
|
goto RegModsDone;
|
|
}
|
|
}
|
|
}
|
|
|
|
if((Flags & SPINST_LOGCONFIG) && DeviceInfoData) {
|
|
|
|
d = pSetupInstallLogConfig(InfHandle,SectionName,DeviceInfoData->DevInst);
|
|
if(d != NO_ERROR) {
|
|
goto RegModsDone;
|
|
}
|
|
}
|
|
|
|
if(Flags & SPINST_INIFILES) {
|
|
d = pSetupInstallUpdateIniFiles(InfHandle,SectionName);
|
|
if(d != NO_ERROR) {
|
|
goto RegModsDone;
|
|
}
|
|
}
|
|
|
|
if(Flags & SPINST_REGISTRY) {
|
|
d = pSetupInstallRegistry(InfHandle,SectionName,RelativeKeyRoot,DevInst);
|
|
if(d != NO_ERROR) {
|
|
goto RegModsDone;
|
|
}
|
|
}
|
|
|
|
if(Flags & SPINST_INI2REG) {
|
|
d = pSetupInstallIni2Reg(InfHandle,SectionName,RelativeKeyRoot);
|
|
}
|
|
|
|
RegModsDone:
|
|
|
|
if(CloseRelativeKeyRoot) {
|
|
RegCloseKey(RelativeKeyRoot);
|
|
}
|
|
|
|
if(d != NO_ERROR) {
|
|
SetLastError(d);
|
|
return FALSE;
|
|
}
|
|
|
|
if(Flags & SPINST_FILES) {
|
|
|
|
d = pSetupInstallFiles(
|
|
InfHandle,
|
|
NULL,
|
|
SectionName,
|
|
SourceRootPath,
|
|
MsgHandler,
|
|
Context,
|
|
CopyFlags,
|
|
Owner,
|
|
NULL,
|
|
IsMsgHandlerNativeCharWidth
|
|
);
|
|
|
|
if(d != NO_ERROR) {
|
|
SetLastError(d);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// ANSI version
|
|
//
|
|
BOOL
|
|
SetupInstallFromInfSectionA(
|
|
IN HWND Owner, OPTIONAL
|
|
IN HINF InfHandle,
|
|
IN PCSTR SectionName,
|
|
IN UINT Flags,
|
|
IN HKEY RelativeKeyRoot, OPTIONAL
|
|
IN PCSTR SourceRootPath, OPTIONAL
|
|
IN UINT CopyFlags,
|
|
IN PSP_FILE_CALLBACK_A MsgHandler,
|
|
IN PVOID Context, OPTIONAL
|
|
IN HDEVINFO DeviceInfoSet, OPTIONAL
|
|
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
|
|
)
|
|
{
|
|
PCWSTR sectionName;
|
|
PCWSTR sourceRootPath;
|
|
BOOL b;
|
|
DWORD d;
|
|
|
|
sectionName = NULL;
|
|
sourceRootPath = NULL;
|
|
d = NO_ERROR;
|
|
|
|
if(SectionName) {
|
|
d = CaptureAndConvertAnsiArg(SectionName,§ionName);
|
|
}
|
|
if((d == NO_ERROR) && SourceRootPath) {
|
|
d = CaptureAndConvertAnsiArg(SourceRootPath,&sourceRootPath);
|
|
}
|
|
|
|
if(d == NO_ERROR) {
|
|
|
|
b = _SetupInstallFromInfSection(
|
|
Owner,
|
|
InfHandle,
|
|
sectionName,
|
|
Flags,
|
|
RelativeKeyRoot,
|
|
sourceRootPath,
|
|
CopyFlags,
|
|
MsgHandler,
|
|
Context,
|
|
DeviceInfoSet,
|
|
DeviceInfoData,
|
|
FALSE
|
|
);
|
|
|
|
d = GetLastError();
|
|
} else {
|
|
b = FALSE;
|
|
}
|
|
|
|
if(sectionName) {
|
|
MyFree(sectionName);
|
|
}
|
|
if(sourceRootPath) {
|
|
MyFree(sourceRootPath);
|
|
}
|
|
|
|
SetLastError(d);
|
|
return(b);
|
|
}
|
|
#else
|
|
//
|
|
// Unicode stub
|
|
//
|
|
BOOL
|
|
SetupInstallFromInfSectionW(
|
|
IN HWND Owner, OPTIONAL
|
|
IN HINF InfHandle,
|
|
IN PCWSTR SectionName,
|
|
IN UINT Flags,
|
|
IN HKEY RelativeKeyRoot, OPTIONAL
|
|
IN PCWSTR SourceRootPath, OPTIONAL
|
|
IN UINT CopyFlags,
|
|
IN PSP_FILE_CALLBACK_W MsgHandler,
|
|
IN PVOID Context, OPTIONAL
|
|
IN HDEVINFO DeviceInfoSet, OPTIONAL
|
|
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(Owner);
|
|
UNREFERENCED_PARAMETER(InfHandle);
|
|
UNREFERENCED_PARAMETER(SectionName);
|
|
UNREFERENCED_PARAMETER(Flags);
|
|
UNREFERENCED_PARAMETER(RelativeKeyRoot);
|
|
UNREFERENCED_PARAMETER(SourceRootPath);
|
|
UNREFERENCED_PARAMETER(CopyFlags);
|
|
UNREFERENCED_PARAMETER(MsgHandler);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(DeviceInfoSet);
|
|
UNREFERENCED_PARAMETER(DeviceInfoData);
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return(FALSE);
|
|
}
|
|
#endif
|
|
|
|
BOOL
|
|
SetupInstallFromInfSection(
|
|
IN HWND Owner, OPTIONAL
|
|
IN HINF InfHandle,
|
|
IN PCTSTR SectionName,
|
|
IN UINT Flags,
|
|
IN HKEY RelativeKeyRoot, OPTIONAL
|
|
IN PCTSTR SourceRootPath, OPTIONAL
|
|
IN UINT CopyFlags,
|
|
IN PSP_FILE_CALLBACK MsgHandler,
|
|
IN PVOID Context, OPTIONAL
|
|
IN HDEVINFO DeviceInfoSet, OPTIONAL
|
|
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
|
|
)
|
|
{
|
|
BOOL b;
|
|
|
|
b = _SetupInstallFromInfSection(
|
|
Owner,
|
|
InfHandle,
|
|
SectionName,
|
|
Flags,
|
|
RelativeKeyRoot,
|
|
SourceRootPath,
|
|
CopyFlags,
|
|
MsgHandler,
|
|
Context,
|
|
DeviceInfoSet,
|
|
DeviceInfoData,
|
|
TRUE
|
|
);
|
|
|
|
return(b);
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// ANSI version
|
|
//
|
|
BOOL
|
|
SetupInstallFilesFromInfSectionA(
|
|
IN HINF InfHandle,
|
|
IN HINF LayoutInfHandle, OPTIONAL
|
|
IN HSPFILEQ FileQueue,
|
|
IN PCSTR SectionName,
|
|
IN PCSTR SourceRootPath, OPTIONAL
|
|
IN UINT CopyFlags
|
|
)
|
|
{
|
|
PCWSTR sectionName;
|
|
PCWSTR sourceRootPath;
|
|
BOOL b;
|
|
DWORD d;
|
|
|
|
|
|
d = CaptureAndConvertAnsiArg(SectionName,§ionName);
|
|
if((d == NO_ERROR) && SourceRootPath) {
|
|
d = CaptureAndConvertAnsiArg(SourceRootPath,&sourceRootPath);
|
|
} else {
|
|
sourceRootPath = NULL;
|
|
}
|
|
|
|
if(d == NO_ERROR) {
|
|
|
|
b = SetupInstallFilesFromInfSectionW(
|
|
InfHandle,
|
|
LayoutInfHandle,
|
|
FileQueue,
|
|
sectionName,
|
|
sourceRootPath,
|
|
CopyFlags
|
|
);
|
|
|
|
d = GetLastError();
|
|
|
|
} else {
|
|
b = FALSE;
|
|
}
|
|
|
|
if(sectionName) {
|
|
MyFree(sectionName);
|
|
}
|
|
if(sourceRootPath) {
|
|
MyFree(sourceRootPath);
|
|
}
|
|
|
|
SetLastError(d);
|
|
return(b);
|
|
}
|
|
#else
|
|
//
|
|
// Unicode stub
|
|
//
|
|
BOOL
|
|
SetupInstallFilesFromInfSectionW(
|
|
IN HINF InfHandle,
|
|
IN HINF LayoutInfHandle, OPTIONAL
|
|
IN HSPFILEQ FileQueue,
|
|
IN PCWSTR SectionName,
|
|
IN PCWSTR SourceRootPath, OPTIONAL
|
|
IN UINT CopyFlags
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(InfHandle);
|
|
UNREFERENCED_PARAMETER(LayoutInfHandle);
|
|
UNREFERENCED_PARAMETER(FileQueue);
|
|
UNREFERENCED_PARAMETER(SectionName);
|
|
UNREFERENCED_PARAMETER(SourceRootPath);
|
|
UNREFERENCED_PARAMETER(CopyFlags);
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return(FALSE);
|
|
}
|
|
#endif
|
|
|
|
BOOL
|
|
SetupInstallFilesFromInfSection(
|
|
IN HINF InfHandle,
|
|
IN HINF LayoutInfHandle, OPTIONAL
|
|
IN HSPFILEQ FileQueue,
|
|
IN PCTSTR SectionName,
|
|
IN PCTSTR SourceRootPath, OPTIONAL
|
|
IN UINT CopyFlags
|
|
)
|
|
{
|
|
DWORD d;
|
|
|
|
d = pSetupInstallFiles(
|
|
InfHandle,
|
|
LayoutInfHandle,
|
|
SectionName,
|
|
SourceRootPath,
|
|
NULL,
|
|
NULL,
|
|
CopyFlags,
|
|
NULL,
|
|
FileQueue,
|
|
TRUE // not used by pSetupInstallFiles with this combo of args
|
|
);
|
|
|
|
SetLastError(d);
|
|
return(d == NO_ERROR);
|
|
}
|
|
|
|
|
|
HKEY
|
|
pSetupInfRegSpecToKeyHandle(
|
|
IN PCTSTR InfRegSpec,
|
|
IN HKEY UserRootKey
|
|
)
|
|
{
|
|
BOOL b;
|
|
HKEY h;
|
|
|
|
return(LookUpStringInTable(InfRegSpecTohKey, InfRegSpec, (PUINT)&h)
|
|
? (h ? h : UserRootKey)
|
|
: NULL
|
|
);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Ini file support stuff.
|
|
//
|
|
// In Win95, the UpdateIni stuff is supported by a set of TpXXX routines.
|
|
// Those routines directly manipulate the ini file, which is bad news for us
|
|
// because inis can be mapped into the registry.
|
|
//
|
|
// Thus we want to use the profile APIs. However the profile APIs make it hard
|
|
// to manipulate lines without keys, so we have to manipulate whole sections
|
|
// at a time.
|
|
//
|
|
// [Section]
|
|
// a
|
|
//
|
|
// There is no way to get at the line "a" with the profile APIs. But the
|
|
// profile section APIs do let us get at it.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
PINIFILESECTION
|
|
pSetupLoadIniFileSection(
|
|
IN PCTSTR FileName,
|
|
IN PCTSTR SectionName,
|
|
IN OUT PINISECTIONCACHE SectionList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
PTSTR SectionData;
|
|
PVOID p;
|
|
DWORD BufferSize;
|
|
PINIFILESECTION Desc;
|
|
#define BUF_GROW 4096
|
|
|
|
//
|
|
// See if this section is already loaded.
|
|
//
|
|
for(Desc=SectionList->Sections; Desc; Desc=Desc->Next) {
|
|
if(!lstrcmpi(Desc->IniFileName,FileName) && !lstrcmpi(Desc->SectionName,SectionName)) {
|
|
return(Desc);
|
|
}
|
|
}
|
|
|
|
BufferSize = 0;
|
|
SectionData = NULL;
|
|
|
|
//
|
|
// Read the entire section. We don't know how big it is
|
|
// so keep growing the buffer until we succeed.
|
|
//
|
|
do {
|
|
BufferSize += BUF_GROW;
|
|
if(SectionData) {
|
|
p = MyRealloc(SectionData,BufferSize*sizeof(TCHAR));
|
|
} else {
|
|
p = MyMalloc(BufferSize*sizeof(TCHAR));
|
|
}
|
|
if(p) {
|
|
SectionData = p;
|
|
} else {
|
|
if(SectionData) {
|
|
MyFree(SectionData);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Attempt to get the entire section.
|
|
//
|
|
d = GetPrivateProfileSection(SectionName,SectionData,BufferSize,FileName);
|
|
|
|
} while(d == (BufferSize-2));
|
|
|
|
if(Desc = MyMalloc(sizeof(INIFILESECTION))) {
|
|
if(Desc->IniFileName = DuplicateString(FileName)) {
|
|
if(Desc->SectionName = DuplicateString(SectionName)) {
|
|
Desc->SectionData = SectionData;
|
|
Desc->BufferSize = BufferSize;
|
|
Desc->BufferUsed = d + 1;
|
|
|
|
Desc->Next = SectionList->Sections;
|
|
SectionList->Sections = Desc;
|
|
} else {
|
|
MyFree(SectionData);
|
|
MyFree(Desc->IniFileName);
|
|
MyFree(Desc);
|
|
Desc = NULL;
|
|
}
|
|
} else {
|
|
MyFree(SectionData);
|
|
MyFree(Desc);
|
|
Desc = NULL;
|
|
}
|
|
} else {
|
|
MyFree(SectionData);
|
|
}
|
|
|
|
return(Desc);
|
|
}
|
|
|
|
|
|
PTSTR
|
|
pSetupFindLineInSection(
|
|
IN PINIFILESECTION Section,
|
|
IN PCTSTR KeyName, OPTIONAL
|
|
IN PCTSTR RightHandSide OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR p,q,r;
|
|
BOOL b1,b2;
|
|
|
|
if(!KeyName && !RightHandSide) {
|
|
return(NULL);
|
|
}
|
|
|
|
for(p=Section->SectionData; *p; p+=lstrlen(p)+1) {
|
|
|
|
//
|
|
// Locate key separator if present.
|
|
//
|
|
q = _tcschr(p,TEXT('='));
|
|
|
|
//
|
|
// If we need to match by key, attempt that here.
|
|
// If the line has no key then it can't match.
|
|
//
|
|
if(KeyName) {
|
|
if(q) {
|
|
*q = 0;
|
|
b1 = (lstrcmpi(KeyName,p) == 0);
|
|
*q = TEXT('=');
|
|
} else {
|
|
b1 = FALSE;
|
|
}
|
|
} else {
|
|
b1 = TRUE;
|
|
}
|
|
|
|
//
|
|
// If we need to match by right hand side, attempt
|
|
// that here.
|
|
//
|
|
if(RightHandSide) {
|
|
//
|
|
// If we have a key, then the right hand side is everything
|
|
// after. If we have no key, then the right hand side is
|
|
// the entire line.
|
|
//
|
|
if(q) {
|
|
r = q + 1;
|
|
} else {
|
|
r = p;
|
|
}
|
|
b2 = (lstrcmpi(r,RightHandSide) == 0);
|
|
} else {
|
|
b2 = TRUE;
|
|
}
|
|
|
|
if(b1 && b2) {
|
|
//
|
|
// Return pointer to beginning of line.
|
|
//
|
|
return(p);
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetupReplaceOrAddLineInSection(
|
|
IN PINIFILESECTION Section,
|
|
IN PCTSTR KeyName, OPTIONAL
|
|
IN PCTSTR RightHandSide, OPTIONAL
|
|
IN BOOL MatchRHS
|
|
)
|
|
{
|
|
PTSTR LineInBuffer,NextLine;
|
|
int CurrentCharsInBuffer;
|
|
int ExistingLineLength,NewLineLength,BufferUsedDelta;
|
|
PVOID p;
|
|
|
|
//
|
|
// Locate the line.
|
|
//
|
|
LineInBuffer = pSetupFindLineInSection(
|
|
Section,
|
|
KeyName,
|
|
MatchRHS ? RightHandSide : NULL
|
|
);
|
|
|
|
if(LineInBuffer) {
|
|
|
|
//
|
|
// Line is in the section. Replace.
|
|
//
|
|
|
|
CurrentCharsInBuffer = Section->BufferUsed;
|
|
|
|
ExistingLineLength = lstrlen(LineInBuffer)+1;
|
|
|
|
NewLineLength = (KeyName ? (lstrlen(KeyName) + 1) : 0) // key=
|
|
+ (RightHandSide ? lstrlen(RightHandSide) : 0) // RHS
|
|
+ 1; // terminating nul
|
|
|
|
//
|
|
// Empty lines not allowed but not error either.
|
|
//
|
|
if(NewLineLength == 1) {
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Figure out whether we need to grow the buffer.
|
|
//
|
|
BufferUsedDelta = NewLineLength - ExistingLineLength;
|
|
if((BufferUsedDelta > 0) && ((Section->BufferSize - Section->BufferUsed) < BufferUsedDelta)) {
|
|
|
|
p = MyRealloc(
|
|
Section->SectionData,
|
|
(Section->BufferUsed + BufferUsedDelta)*sizeof(TCHAR)
|
|
);
|
|
|
|
if(p) {
|
|
(PUCHAR)LineInBuffer += (PUCHAR)p - (PUCHAR)Section->SectionData;
|
|
|
|
Section->SectionData = p;
|
|
Section->BufferSize = Section->BufferUsed + BufferUsedDelta;
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
NextLine = LineInBuffer + lstrlen(LineInBuffer) + 1;
|
|
Section->BufferUsed += BufferUsedDelta;
|
|
|
|
MoveMemory(
|
|
|
|
//
|
|
// Leave exactly enough space for the new line. Since the new line
|
|
// will start at the same place the existing line is at now, the
|
|
// target for the move is simply the first char past what will be
|
|
// copied in later as the new line.
|
|
//
|
|
LineInBuffer + NewLineLength,
|
|
|
|
//
|
|
// The rest of the buffer past the line as it exists now must be
|
|
// preserved. Thus the source for the move is the first char of
|
|
// the next line as it is now.
|
|
//
|
|
NextLine,
|
|
|
|
//
|
|
// Subtract out the chars in the line as it exists now, since we're
|
|
// going to overwrite it and are making room for the line in its
|
|
// new form. Also subtract out the chars in the buffer that are
|
|
// before the start of the line we're operating on.
|
|
//
|
|
((CurrentCharsInBuffer - ExistingLineLength) - (LineInBuffer - Section->SectionData))*sizeof(TCHAR)
|
|
|
|
);
|
|
|
|
if(KeyName) {
|
|
lstrcpy(LineInBuffer,KeyName);
|
|
lstrcat(LineInBuffer,TEXT("="));
|
|
}
|
|
if(RightHandSide) {
|
|
if(KeyName) {
|
|
lstrcat(LineInBuffer,RightHandSide);
|
|
} else {
|
|
lstrcpy(LineInBuffer,RightHandSide);
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
} else {
|
|
//
|
|
// Line is not already in the section. Add it to the end.
|
|
//
|
|
return(pSetupAppendLineToSection(Section,KeyName,RightHandSide));
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetupAppendLineToSection(
|
|
IN PINIFILESECTION Section,
|
|
IN PCTSTR KeyName, OPTIONAL
|
|
IN PCTSTR RightHandSide OPTIONAL
|
|
)
|
|
{
|
|
int LineLength;
|
|
PVOID p;
|
|
int EndOffset;
|
|
|
|
LineLength = (KeyName ? (lstrlen(KeyName) + 1) : 0) // Key=
|
|
+ (RightHandSide ? lstrlen(RightHandSide) : 0) // RHS
|
|
+ 1; // terminating nul
|
|
|
|
//
|
|
// Empty lines not allowed but not error either.
|
|
//
|
|
if(LineLength == 1) {
|
|
return(TRUE);
|
|
}
|
|
|
|
if((Section->BufferSize - Section->BufferUsed) < LineLength) {
|
|
|
|
p = MyRealloc(
|
|
Section->SectionData,
|
|
(Section->BufferUsed + LineLength) * sizeof(WCHAR)
|
|
);
|
|
|
|
if(p) {
|
|
Section->SectionData = p;
|
|
Section->BufferSize = Section->BufferUsed + LineLength;
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Put new text at end of section, remembering that the section
|
|
// is termianted with an extra nul character.
|
|
//
|
|
if(KeyName) {
|
|
lstrcpy(Section->SectionData + Section->BufferUsed - 1,KeyName);
|
|
lstrcat(Section->SectionData + Section->BufferUsed - 1,TEXT("="));
|
|
}
|
|
if(RightHandSide) {
|
|
if(KeyName) {
|
|
lstrcat(Section->SectionData + Section->BufferUsed - 1,RightHandSide);
|
|
} else {
|
|
lstrcpy(Section->SectionData + Section->BufferUsed - 1,RightHandSide);
|
|
}
|
|
}
|
|
|
|
Section->BufferUsed += LineLength;
|
|
Section->SectionData[Section->BufferUsed-1] = 0;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetupDeleteLineFromSection(
|
|
IN PINIFILESECTION Section,
|
|
IN PCTSTR KeyName, OPTIONAL
|
|
IN PCTSTR RightHandSide OPTIONAL
|
|
)
|
|
{
|
|
int LineLength;
|
|
PTSTR Line;
|
|
|
|
if(!KeyName && !RightHandSide) {
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Locate the line.
|
|
//
|
|
if(Line = pSetupFindLineInSection(Section,KeyName,RightHandSide)) {
|
|
|
|
LineLength = lstrlen(Line) + 1;
|
|
|
|
MoveMemory(
|
|
Line,
|
|
Line + LineLength,
|
|
((Section->SectionData + Section->BufferUsed) - (Line + LineLength))*sizeof(TCHAR)
|
|
);
|
|
|
|
Section->BufferUsed -= LineLength;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupUnloadIniFileSections(
|
|
IN PINISECTIONCACHE SectionList,
|
|
IN BOOL WriteToFile
|
|
)
|
|
{
|
|
DWORD d;
|
|
BOOL b;
|
|
PINIFILESECTION Section,Temp;
|
|
|
|
d = NO_ERROR;
|
|
for(Section=SectionList->Sections; Section; Section=Temp) {
|
|
|
|
Temp = Section->Next;
|
|
|
|
if(WriteToFile) {
|
|
|
|
//
|
|
// Delete the existing section first and then recreate it.
|
|
//
|
|
b = WritePrivateProfileString(
|
|
Section->SectionName,
|
|
NULL,
|
|
NULL,
|
|
Section->IniFileName
|
|
);
|
|
|
|
if(b) {
|
|
b = WritePrivateProfileSection(
|
|
Section->SectionName,
|
|
Section->SectionData,
|
|
Section->IniFileName
|
|
);
|
|
}
|
|
|
|
if(!b && (d == NO_ERROR)) {
|
|
d = GetLastError();
|
|
//
|
|
// Allow invalid param because sometime we have problems
|
|
// when ini files are mapped into the registry.
|
|
//
|
|
if(d == ERROR_INVALID_PARAMETER) {
|
|
d = NO_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
MyFree(Section->SectionData);
|
|
MyFree(Section->SectionName);
|
|
MyFree(Section->IniFileName);
|
|
MyFree(Section);
|
|
}
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupValidateDevRegProp(
|
|
IN ULONG CmPropertyCode,
|
|
IN DWORD ValueType,
|
|
IN PCVOID Data,
|
|
IN DWORD DataSize,
|
|
OUT PVOID *ConvertedBuffer,
|
|
OUT PDWORD ConvertedBufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates the data buffer passed in with respect to the specified
|
|
device registry property code. If the code is not of the correct form, but can
|
|
be converted (e.g., REG_EXPAND_SZ -> REG_SZ), then the conversion is done and placed
|
|
into a new buffer, that is returned to the caller.
|
|
|
|
Arguments:
|
|
|
|
CmPropertyCode - Specifies the CM_DRP code indentifying the device registry property
|
|
with which this data buffer is associated.
|
|
|
|
ValueType - Specifies the registry data type for the supplied buffer.
|
|
|
|
Data - Supplies the address of the data buffer.
|
|
|
|
DataSize - Supplies the size, in bytes, of the data buffer.
|
|
|
|
ConvertedBuffer - Supplies the address of a variable that receives a newly-allocated
|
|
buffer containing a converted form of the supplied data. If the data needs no
|
|
conversion, this parameter will be set to NULL on return.
|
|
|
|
ConvertedBufferSize - Supplies the address of a variable that receives the size, in
|
|
bytes, of the converted buffer, or 0 if no conversion was required.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is NO_ERROR, otherwise it is an ERROR_* code.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Initialize ConvertedBuffer output params to indicate that no conversion was necessary.
|
|
//
|
|
*ConvertedBuffer = NULL;
|
|
*ConvertedBufferSize = 0;
|
|
|
|
//
|
|
// Group all properties expecting the same data type together.
|
|
//
|
|
switch(CmPropertyCode) {
|
|
//
|
|
// REG_SZ properties. No other data type is supported.
|
|
//
|
|
case CM_DRP_DEVICEDESC :
|
|
case CM_DRP_SERVICE :
|
|
case CM_DRP_CLASS :
|
|
case CM_DRP_CLASSGUID :
|
|
case CM_DRP_DRIVER :
|
|
case CM_DRP_MFG :
|
|
case CM_DRP_FRIENDLYNAME :
|
|
|
|
if(ValueType != REG_SZ) {
|
|
return ERROR_INVALID_REG_PROPERTY;
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// REG_MULTI_SZ properties. Allow REG_SZ as well, by simply double-terminating
|
|
// the string (i.e., make it a REG_MULTI_SZ with only one string).
|
|
//
|
|
case CM_DRP_HARDWAREID :
|
|
case CM_DRP_COMPATIBLEIDS :
|
|
case CM_DRP_NTDEVICEPATHS :
|
|
|
|
if(ValueType == REG_SZ) {
|
|
|
|
if(*ConvertedBuffer = MyMalloc(*ConvertedBufferSize = DataSize + sizeof(TCHAR))) {
|
|
CopyMemory(*ConvertedBuffer, Data, DataSize);
|
|
*((PTSTR)((PBYTE)(*ConvertedBuffer) + DataSize)) = TEXT('\0');
|
|
} else {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
} else if(ValueType != REG_MULTI_SZ) {
|
|
return ERROR_INVALID_REG_PROPERTY;
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// REG_DWORD properties. Also allow REG_BINARY, as long as the size is right.
|
|
//
|
|
case CM_DRP_CONFIGFLAGS :
|
|
|
|
if(((ValueType != REG_DWORD) && (ValueType != REG_BINARY)) || (DataSize != sizeof(DWORD))) {
|
|
return ERROR_INVALID_REG_PROPERTY;
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// No other properties are supported. Save the trouble of calling a CM API and
|
|
// return failure now.
|
|
//
|
|
default :
|
|
|
|
return ERROR_INVALID_REG_PROPERTY;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// ANSI version
|
|
//
|
|
BOOL
|
|
WINAPI
|
|
SetupInstallServicesFromInfSectionA(
|
|
IN HINF InfHandle,
|
|
IN PCSTR SectionName,
|
|
IN DWORD Flags
|
|
)
|
|
{
|
|
PCWSTR UnicodeSectionName;
|
|
BOOL b;
|
|
DWORD d;
|
|
|
|
if((d = CaptureAndConvertAnsiArg(SectionName, &UnicodeSectionName)) == NO_ERROR) {
|
|
|
|
b = SetupInstallServicesFromInfSectionW(InfHandle, UnicodeSectionName, Flags);
|
|
|
|
//
|
|
// We're safe in always calling this, since we know that the widechar version
|
|
// will always set it, even in the NO_ERROR case.
|
|
//
|
|
d = GetLastError();
|
|
|
|
MyFree(UnicodeSectionName);
|
|
|
|
} else {
|
|
b = FALSE;
|
|
}
|
|
|
|
SetLastError(d);
|
|
return b;
|
|
}
|
|
#else
|
|
//
|
|
// Unicode stub
|
|
//
|
|
BOOL
|
|
WINAPI
|
|
SetupInstallServicesFromInfSectionW(
|
|
IN HINF InfHandle,
|
|
IN PCWSTR SectionName,
|
|
IN DWORD Flags
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(InfHandle);
|
|
UNREFERENCED_PARAMETER(SectionName);
|
|
UNREFERENCED_PARAMETER(Flags);
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetupInstallServicesFromInfSection(
|
|
IN HINF InfHandle,
|
|
IN PCTSTR SectionName,
|
|
IN DWORD Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API performs service installation/deletion operations specified in a service
|
|
install section. Refer to devinstd.c!InstallNtService() for details on the format
|
|
of this section.
|
|
|
|
Arguments:
|
|
|
|
InfHandle - Supplies the handle of the INF containing the service install section
|
|
|
|
SectionName - Supplies the name of the service install section to run.
|
|
|
|
Flags - Supplies flags controlling the installation. May be a combination of the
|
|
following values:
|
|
|
|
SPSVCINST_TAGTOFRONT - For every kernel or filesystem driver installed (that has
|
|
an associated LoadOrderGroup), always move this service's tag to the front
|
|
of the ordering list.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE.
|
|
|
|
If the function fails, the return value is FALSE. To get extended error
|
|
information, call GetLastError.
|
|
|
|
--*/
|
|
{
|
|
INFCONTEXT InfContext;
|
|
DWORD d;
|
|
|
|
//
|
|
// We don't do any validation that the section exists in the worker routine--make
|
|
// sure that it does exist.
|
|
//
|
|
if(SetupFindFirstLine(InfHandle, SectionName, NULL, &InfContext)) {
|
|
|
|
d = InstallNtService(NULL,
|
|
NULL,
|
|
InfHandle,
|
|
SectionName,
|
|
NULL,
|
|
Flags
|
|
);
|
|
} else {
|
|
d = ERROR_SECTION_NOT_FOUND;
|
|
}
|
|
|
|
SetLastError(d);
|
|
|
|
return (d == NO_ERROR);
|
|
}
|
|
|
|
|
|
//
|
|
// Taken from Win95 sxgen.c. These are flags used when
|
|
// we are installing an inf such as when a user right-clicks
|
|
// on one and selects the 'install' action.
|
|
//
|
|
#define HOW_NEVER_REBOOT 0
|
|
#define HOW_ALWAYS_SILENT_REBOOT 1
|
|
#define HOW_ALWAYS_PROMPT_REBOOT 2
|
|
#define HOW_SILENT_REBOOT 3
|
|
#define HOW_PROMPT_REBOOT 4
|
|
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// ANSI version
|
|
//
|
|
VOID
|
|
WINAPI
|
|
InstallHinfSectionA(
|
|
IN HWND Window,
|
|
IN HINSTANCE ModuleHandle,
|
|
IN PCSTR CommandLine,
|
|
IN INT ShowCommand
|
|
)
|
|
#else
|
|
//
|
|
// Unicode version
|
|
//
|
|
VOID
|
|
WINAPI
|
|
InstallHinfSectionW(
|
|
IN HWND Window,
|
|
IN HINSTANCE ModuleHandle,
|
|
IN PCWSTR CommandLine,
|
|
IN INT ShowCommand
|
|
)
|
|
#endif
|
|
{
|
|
UNREFERENCED_PARAMETER(Window);
|
|
UNREFERENCED_PARAMETER(ModuleHandle);
|
|
UNREFERENCED_PARAMETER(CommandLine);
|
|
UNREFERENCED_PARAMETER(ShowCommand);
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
InstallHinfSection(
|
|
IN HWND Window,
|
|
IN HINSTANCE ModuleHandle,
|
|
IN PCTSTR CommandLine,
|
|
IN INT ShowCommand
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the entry point that performs the INSTALL action when
|
|
a user right-clicks an inf file. It is called by the shell via rundll32.
|
|
|
|
The command line is expected to be of the following form:
|
|
|
|
<section name> <flags> <file name>
|
|
|
|
The section is expected to be a general format install section, and
|
|
may also have an include= line and a needs= line. Infs listed on the
|
|
include= line are append-loaded to the inf on the command line prior to
|
|
any installation. Sections on the needs= line are installed after the
|
|
section listed on the command line.
|
|
|
|
After the specified section has been installed, a section of the form:
|
|
|
|
[<section name>.Services]
|
|
|
|
is used in a call to SetupInstallServicesFromInfSection.
|
|
|
|
Arguments:
|
|
|
|
Flags - supplies flags for operation.
|
|
|
|
1 - reboot the machine in all cases
|
|
2 - ask the user if he wants to reboot
|
|
3 - reboot the machine without asking the user, if we think it is necessary
|
|
4 - if we think reboot is necessary, ask the user if he wants to reboot
|
|
|
|
0x80 - set the default file source path for file installation to
|
|
the path where the inf is located. (NOTE: this is hardly ever
|
|
necessary for the Setup APIs, since we intelligently determine what
|
|
the source path should be. The only case where this would still be
|
|
useful is if there were a directory that contained INFs that was in
|
|
our INF search path list, but that also contained the files to be
|
|
copied by this INF install action. In that case, this flag would
|
|
still need to be set, or we would look for the files in the location
|
|
from which the OS was installed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR SourcePathBuffer[MAX_PATH];
|
|
PTSTR SourcePath;
|
|
TCHAR szCmd[MAX_PATH];
|
|
PTCHAR p;
|
|
PTCHAR szHow;
|
|
PTSTR szInfFile, szSectionName;
|
|
INT iHow, NeedRebootFlags;
|
|
HINF InfHandle;
|
|
PTSTR List, CurListElem;
|
|
TCHAR InfSearchPath[MAX_PATH];
|
|
HSPFILEQ FileQueue;
|
|
PVOID QueueContext;
|
|
BOOL b, Error;
|
|
TCHAR ActualSection[MAX_SECT_NAME_LEN];
|
|
DWORD ActualSectionLength;
|
|
DWORD Win32ErrorCode;
|
|
INFCONTEXT InfContext;
|
|
|
|
UNREFERENCED_PARAMETER(ModuleHandle);
|
|
UNREFERENCED_PARAMETER(ShowCommand);
|
|
|
|
//
|
|
// Initialize variables that will later contain resource requiring clean-up.
|
|
//
|
|
InfHandle = INVALID_HANDLE_VALUE;
|
|
FileQueue = INVALID_HANDLE_VALUE;
|
|
QueueContext = NULL;
|
|
List = NULL;
|
|
|
|
Error = TRUE; // assume failure.
|
|
|
|
try {
|
|
//
|
|
// Take a copy of the command line then get pointers to the fields.
|
|
//
|
|
lstrcpyn(szCmd, CommandLine, SIZECHARS(szCmd));
|
|
|
|
szSectionName = szCmd;
|
|
szHow = _tcschr(szSectionName, TEXT(' '));
|
|
if(!szHow) {
|
|
goto c0;
|
|
}
|
|
*szHow++ = TEXT('\0');
|
|
szInfFile = _tcschr(szHow, TEXT(' '));
|
|
if(!szInfFile) {
|
|
goto c0;
|
|
}
|
|
*szInfFile++ = TEXT('\0');
|
|
|
|
iHow = _tcstol(szHow, NULL, 10);
|
|
|
|
//
|
|
// Get the full path to the INF, so that the path may be used as a first-pass attempt
|
|
// at locating any associated INFs.
|
|
//
|
|
if(!GetFullPathName(szInfFile, SIZECHARS(InfSearchPath), InfSearchPath, &p)) {
|
|
goto c0;
|
|
}
|
|
|
|
//
|
|
// If flag is set (and INF filename includes a path), set up so DIRID_SRCPATH is
|
|
// path where INF is located (i.e., override our default SourcePath determination).
|
|
//
|
|
if((iHow & 0x80) && (MyGetFileTitle(szInfFile) != szInfFile)) {
|
|
SourcePath = lstrcpyn(SourcePathBuffer, InfSearchPath, p - InfSearchPath + 1);
|
|
} else {
|
|
SourcePath = NULL;
|
|
}
|
|
|
|
iHow &= 0x7f;
|
|
|
|
//
|
|
// Load the inf file that was passed on the command line.
|
|
//
|
|
InfHandle = SetupOpenInfFile(szInfFile, NULL, INF_STYLE_WIN4, NULL);
|
|
if(InfHandle == INVALID_HANDLE_VALUE) {
|
|
goto c0;
|
|
}
|
|
|
|
//
|
|
// See if there is an nt-specific section
|
|
//
|
|
SetupDiGetActualSectionToInstall(InfHandle,
|
|
szSectionName,
|
|
ActualSection,
|
|
SIZECHARS(ActualSection),
|
|
&ActualSectionLength,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Check to see if the install section has a "Reboot" line. If so, then we always
|
|
// want to prompt for a reboot.
|
|
//
|
|
if(SetupFindFirstLine(InfHandle, ActualSection, pszReboot, &InfContext)) {
|
|
if(iHow == HOW_SILENT_REBOOT) {
|
|
//
|
|
// We were supposed to only do a silent reboot if necessary. Change this to
|
|
// _always_ do a silent reboot.
|
|
//
|
|
iHow = HOW_ALWAYS_SILENT_REBOOT;
|
|
|
|
} else if(iHow != HOW_ALWAYS_SILENT_REBOOT) {
|
|
//
|
|
// For any case other than silent rebooting, we want to force the (non-silent)
|
|
// reboot prompt.
|
|
//
|
|
iHow = HOW_ALWAYS_PROMPT_REBOOT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the include= line and append-load all infs listed there.
|
|
// Note that this line is optional.
|
|
//
|
|
if(List = GetMultiSzFromInf(InfHandle, ActualSection, TEXT("include"), &b)) {
|
|
|
|
for(CurListElem = List; *CurListElem; CurListElem += lstrlen(CurListElem) + 1) {
|
|
|
|
lstrcpy(p, CurListElem);
|
|
//
|
|
// Try full path and if that fails just use the inf name
|
|
// and let the open routine try to locate the inf.
|
|
// Ignore errors. We'll catch them later, during the install phases.
|
|
//
|
|
if(!SetupOpenAppendInfFile(InfSearchPath, InfHandle, NULL)) {
|
|
SetupOpenAppendInfFile(CurListElem, InfHandle, NULL);
|
|
}
|
|
}
|
|
|
|
MyFree(List);
|
|
List = NULL;
|
|
}
|
|
|
|
//
|
|
// Assume there is only one layout file and load it.
|
|
//
|
|
SetupOpenAppendInfFile(NULL, InfHandle, NULL);
|
|
|
|
//
|
|
// Create a setup file queue and initialize the default queue callback.
|
|
//
|
|
FileQueue = SetupOpenFileQueue();
|
|
if(FileQueue == INVALID_HANDLE_VALUE) {
|
|
goto c1;
|
|
}
|
|
|
|
QueueContext = SetupInitDefaultQueueCallback(Window);
|
|
if(!QueueContext) {
|
|
goto c2;
|
|
}
|
|
|
|
//
|
|
// Get needs= line
|
|
//
|
|
List = GetMultiSzFromInf(InfHandle, ActualSection, TEXT("needs"), &b);
|
|
|
|
//
|
|
// Enqueue file operations for the section passed on the cmd line.
|
|
//
|
|
b = SetupInstallFilesFromInfSection(
|
|
InfHandle,
|
|
NULL,
|
|
FileQueue,
|
|
ActualSection,
|
|
SourcePath,
|
|
SP_COPY_NEWER
|
|
);
|
|
|
|
if(!b) {
|
|
goto c3;
|
|
}
|
|
|
|
//
|
|
// Enqueue file operations for needs= sections.
|
|
//
|
|
if(List) {
|
|
|
|
for(CurListElem = List; *CurListElem; CurListElem += lstrlen(CurListElem) + 1) {
|
|
|
|
b = SetupInstallFilesFromInfSection(
|
|
InfHandle,
|
|
NULL,
|
|
FileQueue,
|
|
CurListElem,
|
|
SourcePath,
|
|
SP_COPY_NEWER
|
|
);
|
|
|
|
if(!b) {
|
|
goto c3;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Commit file queue.
|
|
//
|
|
if(!SetupCommitFileQueue(Window, FileQueue, SetupDefaultQueueCallback, QueueContext)) {
|
|
goto c3;
|
|
}
|
|
|
|
//
|
|
// Perform non-file operations for the section passed on the cmd line.
|
|
//
|
|
b = SetupInstallFromInfSection(
|
|
Window,
|
|
InfHandle,
|
|
ActualSection,
|
|
SPINST_ALL ^ SPINST_FILES,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if(!b) {
|
|
goto c3;
|
|
}
|
|
|
|
//
|
|
// Perform non-file operations for needs= sections.
|
|
//
|
|
if(List) {
|
|
|
|
for(CurListElem = List; *CurListElem; CurListElem += lstrlen(CurListElem) + 1) {
|
|
|
|
b = SetupInstallFromInfSection(
|
|
Window,
|
|
InfHandle,
|
|
CurListElem,
|
|
SPINST_ALL ^ SPINST_FILES,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if(!b) {
|
|
goto c3;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now run the corresponding ".Services" section (if there is one), and then
|
|
// finish up the install.
|
|
//
|
|
CopyMemory(&(ActualSection[ActualSectionLength - 1]),
|
|
pszServicesSectionSuffix,
|
|
sizeof(pszServicesSectionSuffix)
|
|
);
|
|
|
|
if(((Win32ErrorCode = InstallNtService(NULL,
|
|
NULL,
|
|
InfHandle,
|
|
ActualSection,
|
|
NULL,
|
|
0)) != NO_ERROR) ||
|
|
((Win32ErrorCode = InstallStop(TRUE)) != NO_ERROR))
|
|
{
|
|
SetLastError(Win32ErrorCode);
|
|
goto c3;
|
|
}
|
|
|
|
//
|
|
// Refresh the desktop.
|
|
//
|
|
SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_FLUSHNOWAIT,0,0);
|
|
|
|
switch(iHow) {
|
|
|
|
case HOW_NEVER_REBOOT:
|
|
break;
|
|
|
|
case HOW_ALWAYS_PROMPT_REBOOT:
|
|
RestartDialog(Window, NULL, EWX_REBOOT);
|
|
break;
|
|
|
|
case HOW_PROMPT_REBOOT:
|
|
SetupPromptReboot(FileQueue, Window, FALSE);
|
|
break;
|
|
|
|
case HOW_SILENT_REBOOT:
|
|
if(!(NeedRebootFlags = SetupPromptReboot(FileQueue, Window, TRUE))) {
|
|
break;
|
|
} else if(NeedRebootFlags == -1) {
|
|
//
|
|
// An error occurred--this should never happen.
|
|
//
|
|
goto c3;
|
|
}
|
|
//
|
|
// Let fall through to same code that handles 'always silent reboot' case.
|
|
//
|
|
|
|
case HOW_ALWAYS_SILENT_REBOOT:
|
|
//
|
|
// BUGBUG (lonnym): how should we handle the case where the user doesn't have
|
|
// reboot privilege?
|
|
//
|
|
if(EnablePrivilege(SE_SHUTDOWN_NAME, TRUE)) {
|
|
ExitWindowsEx(EWX_REBOOT, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we get to here, then this routine has been successful.
|
|
//
|
|
Error = FALSE;
|
|
|
|
c3:
|
|
if(Error && (GetLastError() == ERROR_CANCELLED)) {
|
|
//
|
|
// If the error was because the user cancelled, then we don't want to consider
|
|
// that as an error (i.e., we don't want to give an error popup later).
|
|
//
|
|
Error = FALSE;
|
|
}
|
|
|
|
SetupTermDefaultQueueCallback(QueueContext);
|
|
QueueContext = NULL;
|
|
c2:
|
|
SetupCloseFileQueue(FileQueue);
|
|
FileQueue = INVALID_HANDLE_VALUE;
|
|
c1:
|
|
SetupCloseInfFile(InfHandle);
|
|
InfHandle = INVALID_HANDLE_VALUE;
|
|
|
|
c0: ; // nothing to do.
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
if(QueueContext) {
|
|
SetupTermDefaultQueueCallback(QueueContext);
|
|
}
|
|
if(FileQueue != INVALID_HANDLE_VALUE) {
|
|
SetupCloseFileQueue(FileQueue);
|
|
}
|
|
if(InfHandle != INVALID_HANDLE_VALUE) {
|
|
SetupCloseInfFile(InfHandle);
|
|
}
|
|
//
|
|
// Reference the following variable so the compiler will respect our statement
|
|
// ordering for it.
|
|
//
|
|
List = List;
|
|
}
|
|
|
|
if(List) {
|
|
MyFree(List);
|
|
}
|
|
|
|
if(Error) {
|
|
//
|
|
// Re-use 'ActualSection' buffer to hold error dialog title.
|
|
//
|
|
if(!LoadString(MyDllModuleHandle,
|
|
IDS_ERROR,
|
|
ActualSection,
|
|
SIZECHARS(ActualSection))) {
|
|
*ActualSection = TEXT('\0');
|
|
}
|
|
|
|
FormatMessageBox(MyDllModuleHandle,
|
|
Window,
|
|
MSG_INF_FAILED,
|
|
ActualSection,
|
|
MB_OK | MB_ICONSTOP
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
PTSTR
|
|
GetMultiSzFromInf(
|
|
IN HINF InfHandle,
|
|
IN PCTSTR SectionName,
|
|
IN PCTSTR Key,
|
|
OUT PBOOL OutOfMemory
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a newly-allocated buffer filled with the multi-sz list contained
|
|
in the specified INF line. The caller must free this buffer via MyFree().
|
|
|
|
Arguments:
|
|
|
|
InfHandle - Supplies a handle to the INF containing the line
|
|
|
|
SectionName - Specifies which section within the INF contains the line
|
|
|
|
Key - Specifies the line whose fields are to be retrieved as a multi-sz list
|
|
|
|
OutOfMemory - Supplies the address of a boolean variable that is set upon return
|
|
to indicate whether or not a failure occurred because of an out-of-memory condition.
|
|
(Failure for any other reason is assumed to be OK.)
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is the address of a newly-allocated buffer containing
|
|
the multi-sz list, otherwise, it is NULL.
|
|
|
|
--*/
|
|
{
|
|
INFCONTEXT InfContext;
|
|
PTSTR MultiSz;
|
|
DWORD Size;
|
|
|
|
//
|
|
// Initialize out-of-memory indicator to FALSE.
|
|
//
|
|
*OutOfMemory = FALSE;
|
|
|
|
if(!SetupFindFirstLine(InfHandle, SectionName, Key, &InfContext) ||
|
|
!SetupGetMultiSzField(&InfContext, 1, NULL, 0, &Size) || (Size < 3)) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if(MultiSz = MyMalloc(Size * sizeof(TCHAR))) {
|
|
if(SetupGetMultiSzField(&InfContext, 1, MultiSz, Size, &Size)) {
|
|
return MultiSz;
|
|
}
|
|
MyFree(MultiSz);
|
|
} else {
|
|
*OutOfMemory = TRUE;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
DWORD
|
|
InstallStop(
|
|
IN BOOL DoRunOnce
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets up runonce/grpconv to run after a successful INF installation.
|
|
|
|
Arguments:
|
|
|
|
DoRunOnce - If TRUE, then invoke (via WinExec) the runonce utility to perform the
|
|
runonce actions. If this flag is FALSE, then this routine simply sets the
|
|
runonce registry values and returns.
|
|
|
|
NOTE: The return code from WinExec is not currently being checked, so the return
|
|
value of InstallStop only reflects whether the registry values were set up
|
|
successfully--_not_ whether 'runonce -r' was successfully run.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is NO_ERROR, otherwise it is the Win32 error code
|
|
indicating the error that was encountered.
|
|
|
|
--*/
|
|
{
|
|
HKEY hKey, hSetupKey;
|
|
DWORD Error;
|
|
LONG l;
|
|
|
|
//
|
|
// First, open the key "HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce"
|
|
//
|
|
if((l = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszPathRunOnce, 0, KEY_ALL_ACCESS, &hKey)) != ERROR_SUCCESS) {
|
|
return (DWORD)l;
|
|
}
|
|
|
|
//
|
|
// If we need to run the runonce exe for the setup key...
|
|
//
|
|
MYASSERT(*pszKeySetup == TEXT('\\'));
|
|
if(RegOpenKeyEx(hKey,
|
|
pszKeySetup + 1, // skip the preceding '\'
|
|
0,
|
|
KEY_READ,
|
|
&hSetupKey) == ERROR_SUCCESS) {
|
|
//
|
|
// We don't need the key--we just needed to check its existence.
|
|
//
|
|
RegCloseKey(hSetupKey);
|
|
|
|
//
|
|
// Add the runonce value.
|
|
//
|
|
Error = (DWORD)RegSetValueEx(hKey,
|
|
REGSTR_VAL_WRAPPER,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE)pszRunOnceExe,
|
|
sizeof(pszRunOnceExe)
|
|
);
|
|
} else {
|
|
//
|
|
// We're OK so far.
|
|
//
|
|
Error = NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// GroupConv is always run.
|
|
//
|
|
if((l = RegSetValueEx(hKey,
|
|
TEXT("GrpConv"),
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE)pszGrpConv,
|
|
sizeof(pszGrpConv))) != ERROR_SUCCESS) {
|
|
//
|
|
// Since GrpConv is always run, consider it a more serious error than any error
|
|
// encountered when setting 'runonce'. (This decision is rather arbitrary, but
|
|
// in practice, it should never make any difference. Once we get the registry key
|
|
// opened, there's no reason either of these calls to RegSetValueEx should fail.)
|
|
//
|
|
Error = (DWORD)l;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
if(DoRunOnce) {
|
|
WinExec("runonce -r", SW_SHOWNORMAL);
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|