655 lines
17 KiB
C++
655 lines
17 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
MoveIniToRegistry.cpp
|
|
|
|
Abstract:
|
|
|
|
This shim will move entries written directly into an INI file into the registry.
|
|
|
|
Usage:
|
|
IniFile [IniSection] IniKeyName RegBaseKey RegKeyPath RegValue RegValueType
|
|
|
|
IniFile Full path to INI file (env variables like used for CorrectFilePaths may be used)
|
|
[IniSection] INI section name, must include the brackets
|
|
IniKeyName INI key name (the thing on the left of the =)
|
|
RegBaseKey One of: HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE or HKEY_USERS
|
|
RegKeyPath path of the registry key
|
|
RegValue registry value name (it may be different from IniKeyName
|
|
RegValueType One of: REG_SZ, REG_EXPAND_SZ, REG_DWORD
|
|
|
|
|
|
Example:
|
|
win.ini [Boot] SCRNSAVE.EXE HKEY_CURRENT_USER "Default\Control Panel\Desktop" SCRNSAVE.EXE REG_SZ
|
|
Win.ini
|
|
[Desktop]
|
|
SCRNSAVE.EXE=goofy screen saver
|
|
will be placed:
|
|
RegSetValueEX("HKEY_USERS\Default\Control Panel\Desktop", "SCRNSAVE.EXE", 0, REG_SZ, "goofy screen saver", strlen("goofy screen saver"));
|
|
|
|
|
|
Note:
|
|
A section name of * implies that the data is not associated with any specific section,
|
|
this allows this shim to work with (stupid) apps that put the data into random sections.
|
|
If there are multiple entries, the first matching
|
|
|
|
Created:
|
|
|
|
08/17/2000 robkenny
|
|
|
|
Modified:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include <ClassCFP.h> // for EnvironmentValues
|
|
|
|
IMPLEMENT_SHIM_BEGIN(MoveIniToRegistry)
|
|
#include "ShimHookMacro.h"
|
|
|
|
APIHOOK_ENUM_BEGIN
|
|
APIHOOK_ENUM_ENTRY(CreateFileA)
|
|
APIHOOK_ENUM_ENTRY(OpenFile)
|
|
APIHOOK_ENUM_ENTRY(WriteFile)
|
|
APIHOOK_ENUM_ENTRY(CloseHandle)
|
|
APIHOOK_ENUM_END
|
|
|
|
|
|
// Convert a string into a root HKEY
|
|
HKEY ToHKEY(const CString & csKey)
|
|
{
|
|
if (csKey.CompareNoCase(L"HKEY_CLASSES_ROOT") == 0)
|
|
{
|
|
return HKEY_CLASSES_ROOT;
|
|
}
|
|
else if (csKey.CompareNoCase(L"HKEY_CURRENT_CONFIG") == 0)
|
|
{
|
|
return HKEY_CURRENT_CONFIG;
|
|
}
|
|
else if (csKey.CompareNoCase(L"HKEY_CURRENT_USER") == 0)
|
|
{
|
|
return HKEY_CURRENT_USER;
|
|
}
|
|
else if (csKey.CompareNoCase(L"HKEY_LOCAL_MACHINE") == 0)
|
|
{
|
|
return HKEY_LOCAL_MACHINE;
|
|
}
|
|
else if (csKey.CompareNoCase(L"HKEY_USERS") == 0)
|
|
{
|
|
return HKEY_USERS;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
DWORD ToRegType(const CString & csRegType)
|
|
{
|
|
if (csRegType.CompareNoCase(L"REG_SZ") == 0)
|
|
{
|
|
return REG_SZ;
|
|
}
|
|
else if (csRegType.CompareNoCase(L"REG_EXPAND_SZ") == 0)
|
|
{
|
|
return REG_EXPAND_SZ;
|
|
}
|
|
else if (csRegType.CompareNoCase(L"REG_DWORD") == 0)
|
|
{
|
|
return REG_DWORD;
|
|
}
|
|
else if (csRegType.CompareNoCase(L"REG_DWORD_LITTLE_ENDIAN") == 0)
|
|
{
|
|
// Same as REG_DWORD
|
|
return REG_DWORD;
|
|
}
|
|
else
|
|
{
|
|
return REG_NONE;
|
|
}
|
|
}
|
|
|
|
class IniEntry
|
|
{
|
|
protected:
|
|
|
|
public:
|
|
CString lpIniFileName;
|
|
HANDLE hIniFileHandle;
|
|
CString lpSectionName;
|
|
CString lpKeyName;
|
|
CString lpKeyPath;
|
|
DWORD dwRegDataType;
|
|
HKEY hkRootKey;
|
|
|
|
BOOL bFileNameConverted;
|
|
BOOL bDirty; // Has this file been modified
|
|
|
|
BOOL Set(const char * iniFileName,
|
|
const char * iniSectionName,
|
|
const char * iniKeyName,
|
|
const char * rootKeyName,
|
|
const char * keyPath,
|
|
const char * valueName,
|
|
const char * valueType);
|
|
void Clear();
|
|
|
|
void Convert();
|
|
VOID ReadINIEntry(CString & csEntry);
|
|
void MoveToRegistry();
|
|
|
|
inline void SetDirty(BOOL dirty)
|
|
{
|
|
bDirty = dirty;
|
|
}
|
|
inline void OpenFile(HANDLE hFile)
|
|
{
|
|
hIniFileHandle = hFile;
|
|
bDirty = FALSE;
|
|
}
|
|
inline void CloseFile()
|
|
{
|
|
hIniFileHandle = INVALID_HANDLE_VALUE;
|
|
bDirty = FALSE;
|
|
}
|
|
};
|
|
|
|
|
|
void IniEntry::Clear()
|
|
{
|
|
if (hIniFileHandle != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hIniFileHandle);
|
|
}
|
|
|
|
BOOL IniEntry::Set(
|
|
const char * iniFileName,
|
|
const char * iniSectionName,
|
|
const char * iniKeyName,
|
|
const char * rootKeyName,
|
|
const char * keyPath,
|
|
const char * valueName,
|
|
const char * valueType)
|
|
{
|
|
hIniFileHandle = INVALID_HANDLE_VALUE;
|
|
dwRegDataType = REG_NONE;
|
|
hkRootKey = NULL;
|
|
bFileNameConverted = FALSE;
|
|
bDirty = FALSE;
|
|
|
|
CString csValue(valueType);
|
|
CString csRootKey(rootKeyName);
|
|
|
|
dwRegDataType = ToRegType(csValue);
|
|
if (dwRegDataType == REG_NONE)
|
|
return false;
|
|
|
|
// Attempt to open the registry keys, if these fail, we need go no further
|
|
hkRootKey = ToHKEY(csRootKey);
|
|
if (hkRootKey == NULL)
|
|
return false;
|
|
|
|
// We cannot open the RegKey here; ADVAPI32.dll hasn't been initialzed, yet.
|
|
|
|
lpKeyPath = keyPath;
|
|
lpIniFileName = iniFileName;
|
|
lpSectionName = iniSectionName;
|
|
lpKeyName = iniKeyName;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Read a single line of data from the file,
|
|
// return TRUE if hit EOF
|
|
BOOL GetLine(HANDLE hFile, char * line, DWORD lineSize, DWORD * charsRead)
|
|
{
|
|
BOOL retval = FALSE;
|
|
|
|
*charsRead = 0;
|
|
while (*charsRead < lineSize - 1)
|
|
{
|
|
DWORD bytesRead;
|
|
char *nextChar = line + *charsRead;
|
|
|
|
BOOL readOK = ReadFile(hFile, nextChar, 1, &bytesRead, NULL);
|
|
if (!readOK || bytesRead != 1)
|
|
{
|
|
// Some sort of error
|
|
retval = TRUE;
|
|
break;
|
|
}
|
|
// Eat CR-LF
|
|
if (!IsDBCSLeadByte(*nextChar) && *nextChar == '\n')
|
|
break;
|
|
if (!IsDBCSLeadByte(*nextChar) && *nextChar != '\r')
|
|
*charsRead += 1;
|
|
}
|
|
|
|
line[*charsRead] = 0;
|
|
|
|
return retval;
|
|
}
|
|
|
|
VOID FindLine(HANDLE hFile, const CString & findMe, CString & csLine, const WCHAR * stopLooking)
|
|
{
|
|
csLine.Empty();
|
|
|
|
const size_t findMeLen = findMe.GetLength();
|
|
|
|
// Search for findMe
|
|
while (true)
|
|
{
|
|
char line[300];
|
|
|
|
DWORD dataRead;
|
|
BOOL eof = GetLine(hFile, line, sizeof(line), &dataRead);
|
|
if (eof)
|
|
break;
|
|
|
|
CString csTemp(line);
|
|
if (dataRead >= findMeLen)
|
|
{
|
|
csTemp.TrimLeft();
|
|
if (csTemp.ComparePartNoCase(findMe, 0, findMeLen) == 0)
|
|
{
|
|
// Found the section
|
|
csLine = csTemp;
|
|
break;
|
|
}
|
|
|
|
// Check for termination
|
|
if (stopLooking && csTemp.CompareNoCase(stopLooking) == 0)
|
|
{
|
|
csLine = csTemp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert all %envVars% in the string to text.
|
|
void IniEntry::Convert()
|
|
{
|
|
if (!bFileNameConverted)
|
|
{
|
|
EnvironmentValues env;
|
|
WCHAR * fullIniFileName = env.ExpandEnvironmentValueW(lpIniFileName);
|
|
if (fullIniFileName)
|
|
{
|
|
lpIniFileName = fullIniFileName;
|
|
delete fullIniFileName;
|
|
}
|
|
|
|
bFileNameConverted = TRUE;
|
|
}
|
|
}
|
|
|
|
// Read the data from the INI file
|
|
// We *cannot* use GetPrivateProfileStringA since it might be re-routed to the registry
|
|
// Return the number of chars read.
|
|
VOID IniEntry::ReadINIEntry(CString & csEntry)
|
|
{
|
|
csEntry.Empty();
|
|
|
|
CString csLine;
|
|
|
|
HANDLE hFile = CreateFileW(lpIniFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
|
|
// If the section name is *, we don't need to search
|
|
if (lpSectionName.GetAt(0) != L'*')
|
|
{
|
|
FindLine(hFile, lpSectionName, csLine, NULL);
|
|
}
|
|
|
|
// Our early termination string.
|
|
// If the section name is *, we look forever, otherwise
|
|
// we stop looking if we find a line starting with a [
|
|
const WCHAR * stopLooking = lpSectionName.GetAt(0) == L'*' ? NULL : L"[";
|
|
|
|
// Search for lpKeyName
|
|
FindLine(hFile, lpKeyName, csLine, stopLooking);
|
|
if (!csLine.IsEmpty())
|
|
{
|
|
int nEqual = csLine.Find(L'=');
|
|
if (nEqual >= 0)
|
|
{
|
|
csLine.Mid(nEqual + 1, csEntry);
|
|
}
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
}
|
|
|
|
// Move the INI file entry into the registry
|
|
void IniEntry::MoveToRegistry()
|
|
{
|
|
// Don't bother with the work, if they never wrote any data into the file.
|
|
if (!bDirty)
|
|
return;
|
|
|
|
HKEY regKey;
|
|
LONG success = RegOpenKeyExW(
|
|
hkRootKey,
|
|
lpKeyPath,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
®Key);
|
|
if (success != ERROR_SUCCESS)
|
|
return;
|
|
|
|
CString csIniEntry;
|
|
|
|
ReadINIEntry(csIniEntry);
|
|
if (!csIniEntry.IsEmpty())
|
|
{
|
|
switch (dwRegDataType)
|
|
{
|
|
case REG_SZ:
|
|
case REG_EXPAND_SZ:
|
|
{
|
|
const WCHAR * lpIniEntry = csIniEntry.Get();
|
|
DWORD dwValueSize = (csIniEntry.GetLength() + 1) * sizeof(WCHAR);
|
|
success = RegSetValueExW(regKey, lpKeyName, 0, dwRegDataType, (CONST BYTE *)lpIniEntry, dwValueSize);
|
|
if (success == ERROR_SUCCESS)
|
|
{
|
|
LOGN( eDbgLevelError, "IniEntry::MoveToRegistry, KeyPath(%S) Value(%S) set to (%S)\n",
|
|
lpKeyPath, lpKeyName, lpIniEntry);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case REG_DWORD:
|
|
{
|
|
WCHAR * unused;
|
|
long iniValue = wcstol(csIniEntry, &unused, 10);
|
|
|
|
RegSetValueExW(regKey, lpKeyName, 0, dwRegDataType, (CONST BYTE *)&iniValue, sizeof(iniValue));
|
|
if (success == ERROR_SUCCESS)
|
|
{
|
|
LOGN( eDbgLevelError, "IniEntry::MoveToRegistry, KeyPath(%S) Value(%S) set to (%d)\n", lpKeyPath, lpKeyName, iniValue);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(regKey);
|
|
}
|
|
|
|
class IniEntryList : public VectorT<IniEntry>
|
|
{
|
|
public:
|
|
void OpenFile(const char *fileName, HANDLE hFile);
|
|
void CloseFile(HANDLE hFile);
|
|
void WriteFile(HANDLE hFile);
|
|
|
|
void Add(const char * iniFileName,
|
|
const char * iniSectionName,
|
|
const char * iniKeyName,
|
|
const char * rootKeyName,
|
|
const char * keyPath,
|
|
const char * valueName,
|
|
const char * valueType);
|
|
};
|
|
|
|
// A file is being opened.
|
|
// If it is one that we are interested in, remember the handle
|
|
void IniEntryList::OpenFile(const char *fileName, HANDLE handle)
|
|
{
|
|
CString csFileName(fileName);
|
|
csFileName.GetFullPathNameW();
|
|
|
|
const int nElem = Size();
|
|
for (int i = 0; i < nElem; ++i)
|
|
{
|
|
IniEntry & elem = Get(i);
|
|
|
|
elem.Convert();
|
|
|
|
// Convert fileName to a full pathname for the compare.
|
|
char fullPathName[MAX_PATH];
|
|
char * filePart;
|
|
|
|
if (csFileName.CompareNoCase(elem.lpIniFileName) == 0)
|
|
{
|
|
elem.OpenFile(handle);
|
|
|
|
DPFN( eDbgLevelSpew, "IniEntryList::OpenFile(%S) Handle(%d) has been opened for write\n", elem.lpIniFileName.Get(), elem.hIniFileHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
// A file has been closed,
|
|
// Check to see if this is a handle to a file that we are interested in.
|
|
// If it is a match, then move the INI entries into the registry.
|
|
void IniEntryList::CloseFile(HANDLE handle)
|
|
{
|
|
const int nElem = Size();
|
|
for (int i = 0; i < nElem; ++i)
|
|
{
|
|
IniEntry & elem = Get(i);
|
|
|
|
if (elem.hIniFileHandle == handle)
|
|
{
|
|
DPFN( eDbgLevelSpew, "IniEntryList::CloseFile(%S) Handle(%d) has been closed\n", elem.lpIniFileName.Get(), elem.hIniFileHandle);
|
|
|
|
// Move the ini entry into the registry
|
|
elem.MoveToRegistry();
|
|
|
|
elem.CloseFile();
|
|
}
|
|
}
|
|
}
|
|
|
|
// A file has been closed,
|
|
// Check to see if this is a handle to a file that we are interested in.
|
|
// If it is a match, then move the INI entries into the registry.
|
|
void IniEntryList::WriteFile(HANDLE handle)
|
|
{
|
|
const int nElem = Size();
|
|
for (int i = 0; i < nElem; ++i)
|
|
{
|
|
IniEntry & elem = Get(i);
|
|
|
|
if (elem.hIniFileHandle == handle && !elem.bDirty)
|
|
{
|
|
DPFN( eDbgLevelSpew, "IniEntryList::CloseFile(%S) Handle(%d) has been closed\n", elem.lpIniFileName.Get(), elem.hIniFileHandle);
|
|
|
|
elem.SetDirty(TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Attempt to add these values to the list.
|
|
// Only if all values are valid, will a new entry be created.
|
|
void IniEntryList::Add(const char * iniFileName,
|
|
const char * iniSectionName,
|
|
const char * iniKeyName,
|
|
const char * rootKeyName,
|
|
const char * keyPath,
|
|
const char * valueName,
|
|
const char * valueType)
|
|
{
|
|
// Make room for this
|
|
int lastElem = Size();
|
|
if (Resize(lastElem + 1))
|
|
{
|
|
IniEntry & iniEntry = Get(lastElem);
|
|
|
|
// The VectorT does not call the constructors for new elements
|
|
// Inplace new
|
|
new (&iniEntry) IniEntry;
|
|
|
|
if (iniEntry.Set(iniFileName, iniSectionName, iniKeyName, rootKeyName, keyPath, valueName, valueType))
|
|
{
|
|
// Keep the value
|
|
nVectorList += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
IniEntryList * g_IniEntryList = NULL;
|
|
|
|
/*++
|
|
|
|
Create the appropriate g_PathCorrector
|
|
|
|
--*/
|
|
BOOL ParseCommandLine(const char * commandLine)
|
|
{
|
|
g_IniEntryList = new IniEntryList;
|
|
if (!g_IniEntryList)
|
|
return FALSE;
|
|
|
|
int argc;
|
|
char **argv = _CommandLineToArgvA(commandLine, &argc);
|
|
|
|
// If there are no command line arguments, stop now
|
|
if (argc == 0 || argv == NULL)
|
|
return TRUE;
|
|
|
|
#if DBG
|
|
{
|
|
for (int i = 0; i < argc; ++i)
|
|
{
|
|
const char * arg = argv[i];
|
|
DPFN( eDbgLevelSpew, "Argv[%d] = (%s)\n", i, arg);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Search the beginning of the command line for the switches
|
|
for (int i = 0; i+6 < argc; i += 7)
|
|
{
|
|
g_IniEntryList->Add(
|
|
argv[i + 0],
|
|
argv[i + 1],
|
|
argv[i + 2],
|
|
argv[i + 3],
|
|
argv[i + 4],
|
|
argv[i + 5],
|
|
argv[i + 6]);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
APIHOOK(CreateFileA)(
|
|
LPCSTR lpFileName, // file name
|
|
DWORD dwDesiredAccess, // access mode
|
|
DWORD dwShareMode, // share mode
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
|
|
DWORD dwCreationDisposition, // how to create
|
|
DWORD dwFlagsAndAttributes, // file attributes
|
|
HANDLE hTemplateFile // handle to template file
|
|
)
|
|
{
|
|
HANDLE returnValue = ORIGINAL_API(CreateFileA)(
|
|
lpFileName,
|
|
dwDesiredAccess,
|
|
dwShareMode,
|
|
lpSecurityAttributes,
|
|
dwCreationDisposition,
|
|
dwFlagsAndAttributes,
|
|
hTemplateFile);
|
|
|
|
if ( (dwDesiredAccess & GENERIC_WRITE) && (returnValue != INVALID_HANDLE_VALUE))
|
|
g_IniEntryList->OpenFile(lpFileName, returnValue);
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
|
|
HFILE
|
|
APIHOOK(OpenFile)(
|
|
LPCSTR lpFileName, // file name
|
|
LPOFSTRUCT lpReOpenBuff, // file information
|
|
UINT uStyle // action and attributes
|
|
)
|
|
{
|
|
HFILE returnValue = ORIGINAL_API(OpenFile)(lpFileName, lpReOpenBuff, uStyle);
|
|
|
|
if ((uStyle & OF_WRITE) && (returnValue != HFILE_ERROR))
|
|
g_IniEntryList->OpenFile(lpReOpenBuff->szPathName, (HANDLE)returnValue);
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
BOOL
|
|
APIHOOK(CloseHandle)(
|
|
HANDLE hObject // handle to object
|
|
)
|
|
{
|
|
BOOL returnValue = ORIGINAL_API(CloseHandle)(hObject);
|
|
|
|
if (hObject != INVALID_HANDLE_VALUE)
|
|
g_IniEntryList->CloseFile(hObject);
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
BOOL
|
|
APIHOOK(WriteFile)(
|
|
HANDLE hFile, // handle to file
|
|
LPCVOID lpBuffer, // data buffer
|
|
DWORD nNumberOfBytesToWrite, // number of bytes to write
|
|
LPDWORD lpNumberOfBytesWritten, // number of bytes written
|
|
LPOVERLAPPED lpOverlapped // overlapped buffer
|
|
)
|
|
{
|
|
BOOL returnValue = ORIGINAL_API(WriteFile)(
|
|
hFile,
|
|
lpBuffer,
|
|
nNumberOfBytesToWrite,
|
|
lpNumberOfBytesWritten,
|
|
lpOverlapped
|
|
);
|
|
|
|
g_IniEntryList->WriteFile(hFile);
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/*++
|
|
|
|
Register hooked functions
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
NOTIFY_FUNCTION(
|
|
DWORD fdwReason
|
|
)
|
|
{
|
|
if (fdwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
return ParseCommandLine(COMMAND_LINE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HOOK_BEGIN
|
|
|
|
APIHOOK_ENTRY(Kernel32.DLL, CreateFileA )
|
|
APIHOOK_ENTRY(Kernel32.DLL, OpenFile )
|
|
APIHOOK_ENTRY(Kernel32.DLL, WriteFile )
|
|
APIHOOK_ENTRY(Kernel32.DLL, CloseHandle )
|
|
|
|
CALL_NOTIFY_FUNCTION
|
|
|
|
HOOK_END
|
|
|
|
IMPLEMENT_SHIM_END
|
|
|