Source code of Windows XP (NT5)
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.

654 lines
17 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. MoveIniToRegistry.cpp
  5. Abstract:
  6. This shim will move entries written directly into an INI file into the registry.
  7. Usage:
  8. IniFile [IniSection] IniKeyName RegBaseKey RegKeyPath RegValue RegValueType
  9. IniFile Full path to INI file (env variables like used for CorrectFilePaths may be used)
  10. [IniSection] INI section name, must include the brackets
  11. IniKeyName INI key name (the thing on the left of the =)
  12. RegBaseKey One of: HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE or HKEY_USERS
  13. RegKeyPath path of the registry key
  14. RegValue registry value name (it may be different from IniKeyName
  15. RegValueType One of: REG_SZ, REG_EXPAND_SZ, REG_DWORD
  16. Example:
  17. win.ini [Boot] SCRNSAVE.EXE HKEY_CURRENT_USER "Default\Control Panel\Desktop" SCRNSAVE.EXE REG_SZ
  18. Win.ini
  19. [Desktop]
  20. SCRNSAVE.EXE=goofy screen saver
  21. will be placed:
  22. RegSetValueEX("HKEY_USERS\Default\Control Panel\Desktop", "SCRNSAVE.EXE", 0, REG_SZ, "goofy screen saver", strlen("goofy screen saver"));
  23. Note:
  24. A section name of * implies that the data is not associated with any specific section,
  25. this allows this shim to work with (stupid) apps that put the data into random sections.
  26. If there are multiple entries, the first matching
  27. Created:
  28. 08/17/2000 robkenny
  29. Modified:
  30. --*/
  31. #include "precomp.h"
  32. #include <ClassCFP.h> // for EnvironmentValues
  33. IMPLEMENT_SHIM_BEGIN(MoveIniToRegistry)
  34. #include "ShimHookMacro.h"
  35. APIHOOK_ENUM_BEGIN
  36. APIHOOK_ENUM_ENTRY(CreateFileA)
  37. APIHOOK_ENUM_ENTRY(OpenFile)
  38. APIHOOK_ENUM_ENTRY(WriteFile)
  39. APIHOOK_ENUM_ENTRY(CloseHandle)
  40. APIHOOK_ENUM_END
  41. // Convert a string into a root HKEY
  42. HKEY ToHKEY(const CString & csKey)
  43. {
  44. if (csKey.CompareNoCase(L"HKEY_CLASSES_ROOT") == 0)
  45. {
  46. return HKEY_CLASSES_ROOT;
  47. }
  48. else if (csKey.CompareNoCase(L"HKEY_CURRENT_CONFIG") == 0)
  49. {
  50. return HKEY_CURRENT_CONFIG;
  51. }
  52. else if (csKey.CompareNoCase(L"HKEY_CURRENT_USER") == 0)
  53. {
  54. return HKEY_CURRENT_USER;
  55. }
  56. else if (csKey.CompareNoCase(L"HKEY_LOCAL_MACHINE") == 0)
  57. {
  58. return HKEY_LOCAL_MACHINE;
  59. }
  60. else if (csKey.CompareNoCase(L"HKEY_USERS") == 0)
  61. {
  62. return HKEY_USERS;
  63. }
  64. else
  65. {
  66. return NULL;
  67. }
  68. }
  69. DWORD ToRegType(const CString & csRegType)
  70. {
  71. if (csRegType.CompareNoCase(L"REG_SZ") == 0)
  72. {
  73. return REG_SZ;
  74. }
  75. else if (csRegType.CompareNoCase(L"REG_EXPAND_SZ") == 0)
  76. {
  77. return REG_EXPAND_SZ;
  78. }
  79. else if (csRegType.CompareNoCase(L"REG_DWORD") == 0)
  80. {
  81. return REG_DWORD;
  82. }
  83. else if (csRegType.CompareNoCase(L"REG_DWORD_LITTLE_ENDIAN") == 0)
  84. {
  85. // Same as REG_DWORD
  86. return REG_DWORD;
  87. }
  88. else
  89. {
  90. return REG_NONE;
  91. }
  92. }
  93. class IniEntry
  94. {
  95. protected:
  96. public:
  97. CString lpIniFileName;
  98. HANDLE hIniFileHandle;
  99. CString lpSectionName;
  100. CString lpKeyName;
  101. CString lpKeyPath;
  102. DWORD dwRegDataType;
  103. HKEY hkRootKey;
  104. BOOL bFileNameConverted;
  105. BOOL bDirty; // Has this file been modified
  106. BOOL Set(const char * iniFileName,
  107. const char * iniSectionName,
  108. const char * iniKeyName,
  109. const char * rootKeyName,
  110. const char * keyPath,
  111. const char * valueName,
  112. const char * valueType);
  113. void Clear();
  114. void Convert();
  115. VOID ReadINIEntry(CString & csEntry);
  116. void MoveToRegistry();
  117. inline void SetDirty(BOOL dirty)
  118. {
  119. bDirty = dirty;
  120. }
  121. inline void OpenFile(HANDLE hFile)
  122. {
  123. hIniFileHandle = hFile;
  124. bDirty = FALSE;
  125. }
  126. inline void CloseFile()
  127. {
  128. hIniFileHandle = INVALID_HANDLE_VALUE;
  129. bDirty = FALSE;
  130. }
  131. };
  132. void IniEntry::Clear()
  133. {
  134. if (hIniFileHandle != INVALID_HANDLE_VALUE)
  135. CloseHandle(hIniFileHandle);
  136. }
  137. BOOL IniEntry::Set(
  138. const char * iniFileName,
  139. const char * iniSectionName,
  140. const char * iniKeyName,
  141. const char * rootKeyName,
  142. const char * keyPath,
  143. const char * valueName,
  144. const char * valueType)
  145. {
  146. hIniFileHandle = INVALID_HANDLE_VALUE;
  147. dwRegDataType = REG_NONE;
  148. hkRootKey = NULL;
  149. bFileNameConverted = FALSE;
  150. bDirty = FALSE;
  151. CString csValue(valueType);
  152. CString csRootKey(rootKeyName);
  153. dwRegDataType = ToRegType(csValue);
  154. if (dwRegDataType == REG_NONE)
  155. return false;
  156. // Attempt to open the registry keys, if these fail, we need go no further
  157. hkRootKey = ToHKEY(csRootKey);
  158. if (hkRootKey == NULL)
  159. return false;
  160. // We cannot open the RegKey here; ADVAPI32.dll hasn't been initialzed, yet.
  161. lpKeyPath = keyPath;
  162. lpIniFileName = iniFileName;
  163. lpSectionName = iniSectionName;
  164. lpKeyName = iniKeyName;
  165. return TRUE;
  166. }
  167. // Read a single line of data from the file,
  168. // return TRUE if hit EOF
  169. BOOL GetLine(HANDLE hFile, char * line, DWORD lineSize, DWORD * charsRead)
  170. {
  171. BOOL retval = FALSE;
  172. *charsRead = 0;
  173. while (*charsRead < lineSize - 1)
  174. {
  175. DWORD bytesRead;
  176. char *nextChar = line + *charsRead;
  177. BOOL readOK = ReadFile(hFile, nextChar, 1, &bytesRead, NULL);
  178. if (!readOK || bytesRead != 1)
  179. {
  180. // Some sort of error
  181. retval = TRUE;
  182. break;
  183. }
  184. // Eat CR-LF
  185. if (!IsDBCSLeadByte(*nextChar) && *nextChar == '\n')
  186. break;
  187. if (!IsDBCSLeadByte(*nextChar) && *nextChar != '\r')
  188. *charsRead += 1;
  189. }
  190. line[*charsRead] = 0;
  191. return retval;
  192. }
  193. VOID FindLine(HANDLE hFile, const CString & findMe, CString & csLine, const WCHAR * stopLooking)
  194. {
  195. csLine.Empty();
  196. const size_t findMeLen = findMe.GetLength();
  197. // Search for findMe
  198. while (true)
  199. {
  200. char line[300];
  201. DWORD dataRead;
  202. BOOL eof = GetLine(hFile, line, sizeof(line), &dataRead);
  203. if (eof)
  204. break;
  205. CString csTemp(line);
  206. if (dataRead >= findMeLen)
  207. {
  208. csTemp.TrimLeft();
  209. if (csTemp.ComparePartNoCase(findMe, 0, findMeLen) == 0)
  210. {
  211. // Found the section
  212. csLine = csTemp;
  213. break;
  214. }
  215. // Check for termination
  216. if (stopLooking && csTemp.CompareNoCase(stopLooking) == 0)
  217. {
  218. csLine = csTemp;
  219. break;
  220. }
  221. }
  222. }
  223. }
  224. // Convert all %envVars% in the string to text.
  225. void IniEntry::Convert()
  226. {
  227. if (!bFileNameConverted)
  228. {
  229. EnvironmentValues env;
  230. WCHAR * fullIniFileName = env.ExpandEnvironmentValueW(lpIniFileName);
  231. if (fullIniFileName)
  232. {
  233. lpIniFileName = fullIniFileName;
  234. delete fullIniFileName;
  235. }
  236. bFileNameConverted = TRUE;
  237. }
  238. }
  239. // Read the data from the INI file
  240. // We *cannot* use GetPrivateProfileStringA since it might be re-routed to the registry
  241. // Return the number of chars read.
  242. VOID IniEntry::ReadINIEntry(CString & csEntry)
  243. {
  244. csEntry.Empty();
  245. CString csLine;
  246. HANDLE hFile = CreateFileW(lpIniFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  247. if (hFile != INVALID_HANDLE_VALUE)
  248. {
  249. // If the section name is *, we don't need to search
  250. if (lpSectionName.GetAt(0) != L'*')
  251. {
  252. FindLine(hFile, lpSectionName, csLine, NULL);
  253. }
  254. // Our early termination string.
  255. // If the section name is *, we look forever, otherwise
  256. // we stop looking if we find a line starting with a [
  257. const WCHAR * stopLooking = lpSectionName.GetAt(0) == L'*' ? NULL : L"[";
  258. // Search for lpKeyName
  259. FindLine(hFile, lpKeyName, csLine, stopLooking);
  260. if (!csLine.IsEmpty())
  261. {
  262. int nEqual = csLine.Find(L'=');
  263. if (nEqual >= 0)
  264. {
  265. csLine.Mid(nEqual + 1, csEntry);
  266. }
  267. }
  268. CloseHandle(hFile);
  269. }
  270. }
  271. // Move the INI file entry into the registry
  272. void IniEntry::MoveToRegistry()
  273. {
  274. // Don't bother with the work, if they never wrote any data into the file.
  275. if (!bDirty)
  276. return;
  277. HKEY regKey;
  278. LONG success = RegOpenKeyExW(
  279. hkRootKey,
  280. lpKeyPath,
  281. 0,
  282. KEY_ALL_ACCESS,
  283. &regKey);
  284. if (success != ERROR_SUCCESS)
  285. return;
  286. CString csIniEntry;
  287. ReadINIEntry(csIniEntry);
  288. if (!csIniEntry.IsEmpty())
  289. {
  290. switch (dwRegDataType)
  291. {
  292. case REG_SZ:
  293. case REG_EXPAND_SZ:
  294. {
  295. const WCHAR * lpIniEntry = csIniEntry.Get();
  296. DWORD dwValueSize = (csIniEntry.GetLength() + 1) * sizeof(WCHAR);
  297. success = RegSetValueExW(regKey, lpKeyName, 0, dwRegDataType, (CONST BYTE *)lpIniEntry, dwValueSize);
  298. if (success == ERROR_SUCCESS)
  299. {
  300. LOGN( eDbgLevelError, "IniEntry::MoveToRegistry, KeyPath(%S) Value(%S) set to (%S)\n",
  301. lpKeyPath, lpKeyName, lpIniEntry);
  302. }
  303. }
  304. break;
  305. case REG_DWORD:
  306. {
  307. WCHAR * unused;
  308. long iniValue = wcstol(csIniEntry, &unused, 10);
  309. RegSetValueExW(regKey, lpKeyName, 0, dwRegDataType, (CONST BYTE *)&iniValue, sizeof(iniValue));
  310. if (success == ERROR_SUCCESS)
  311. {
  312. LOGN( eDbgLevelError, "IniEntry::MoveToRegistry, KeyPath(%S) Value(%S) set to (%d)\n", lpKeyPath, lpKeyName, iniValue);
  313. }
  314. }
  315. break;
  316. }
  317. }
  318. RegCloseKey(regKey);
  319. }
  320. class IniEntryList : public VectorT<IniEntry>
  321. {
  322. public:
  323. void OpenFile(const char *fileName, HANDLE hFile);
  324. void CloseFile(HANDLE hFile);
  325. void WriteFile(HANDLE hFile);
  326. void Add(const char * iniFileName,
  327. const char * iniSectionName,
  328. const char * iniKeyName,
  329. const char * rootKeyName,
  330. const char * keyPath,
  331. const char * valueName,
  332. const char * valueType);
  333. };
  334. // A file is being opened.
  335. // If it is one that we are interested in, remember the handle
  336. void IniEntryList::OpenFile(const char *fileName, HANDLE handle)
  337. {
  338. CString csFileName(fileName);
  339. csFileName.GetFullPathNameW();
  340. const int nElem = Size();
  341. for (int i = 0; i < nElem; ++i)
  342. {
  343. IniEntry & elem = Get(i);
  344. elem.Convert();
  345. // Convert fileName to a full pathname for the compare.
  346. char fullPathName[MAX_PATH];
  347. char * filePart;
  348. if (csFileName.CompareNoCase(elem.lpIniFileName) == 0)
  349. {
  350. elem.OpenFile(handle);
  351. DPFN( eDbgLevelSpew, "IniEntryList::OpenFile(%S) Handle(%d) has been opened for write\n", elem.lpIniFileName.Get(), elem.hIniFileHandle);
  352. }
  353. }
  354. }
  355. // A file has been closed,
  356. // Check to see if this is a handle to a file that we are interested in.
  357. // If it is a match, then move the INI entries into the registry.
  358. void IniEntryList::CloseFile(HANDLE handle)
  359. {
  360. const int nElem = Size();
  361. for (int i = 0; i < nElem; ++i)
  362. {
  363. IniEntry & elem = Get(i);
  364. if (elem.hIniFileHandle == handle)
  365. {
  366. DPFN( eDbgLevelSpew, "IniEntryList::CloseFile(%S) Handle(%d) has been closed\n", elem.lpIniFileName.Get(), elem.hIniFileHandle);
  367. // Move the ini entry into the registry
  368. elem.MoveToRegistry();
  369. elem.CloseFile();
  370. }
  371. }
  372. }
  373. // A file has been closed,
  374. // Check to see if this is a handle to a file that we are interested in.
  375. // If it is a match, then move the INI entries into the registry.
  376. void IniEntryList::WriteFile(HANDLE handle)
  377. {
  378. const int nElem = Size();
  379. for (int i = 0; i < nElem; ++i)
  380. {
  381. IniEntry & elem = Get(i);
  382. if (elem.hIniFileHandle == handle && !elem.bDirty)
  383. {
  384. DPFN( eDbgLevelSpew, "IniEntryList::CloseFile(%S) Handle(%d) has been closed\n", elem.lpIniFileName.Get(), elem.hIniFileHandle);
  385. elem.SetDirty(TRUE);
  386. }
  387. }
  388. }
  389. // Attempt to add these values to the list.
  390. // Only if all values are valid, will a new entry be created.
  391. void IniEntryList::Add(const char * iniFileName,
  392. const char * iniSectionName,
  393. const char * iniKeyName,
  394. const char * rootKeyName,
  395. const char * keyPath,
  396. const char * valueName,
  397. const char * valueType)
  398. {
  399. // Make room for this
  400. int lastElem = Size();
  401. if (Resize(lastElem + 1))
  402. {
  403. IniEntry & iniEntry = Get(lastElem);
  404. // The VectorT does not call the constructors for new elements
  405. // Inplace new
  406. new (&iniEntry) IniEntry;
  407. if (iniEntry.Set(iniFileName, iniSectionName, iniKeyName, rootKeyName, keyPath, valueName, valueType))
  408. {
  409. // Keep the value
  410. nVectorList += 1;
  411. }
  412. }
  413. }
  414. IniEntryList * g_IniEntryList = NULL;
  415. /*++
  416. Create the appropriate g_PathCorrector
  417. --*/
  418. BOOL ParseCommandLine(const char * commandLine)
  419. {
  420. g_IniEntryList = new IniEntryList;
  421. if (!g_IniEntryList)
  422. return FALSE;
  423. int argc;
  424. char **argv = _CommandLineToArgvA(commandLine, &argc);
  425. // If there are no command line arguments, stop now
  426. if (argc == 0 || argv == NULL)
  427. return TRUE;
  428. #if DBG
  429. {
  430. for (int i = 0; i < argc; ++i)
  431. {
  432. const char * arg = argv[i];
  433. DPFN( eDbgLevelSpew, "Argv[%d] = (%s)\n", i, arg);
  434. }
  435. }
  436. #endif
  437. // Search the beginning of the command line for the switches
  438. for (int i = 0; i+6 < argc; i += 7)
  439. {
  440. g_IniEntryList->Add(
  441. argv[i + 0],
  442. argv[i + 1],
  443. argv[i + 2],
  444. argv[i + 3],
  445. argv[i + 4],
  446. argv[i + 5],
  447. argv[i + 6]);
  448. }
  449. return TRUE;
  450. }
  451. HANDLE
  452. APIHOOK(CreateFileA)(
  453. LPCSTR lpFileName, // file name
  454. DWORD dwDesiredAccess, // access mode
  455. DWORD dwShareMode, // share mode
  456. LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
  457. DWORD dwCreationDisposition, // how to create
  458. DWORD dwFlagsAndAttributes, // file attributes
  459. HANDLE hTemplateFile // handle to template file
  460. )
  461. {
  462. HANDLE returnValue = ORIGINAL_API(CreateFileA)(
  463. lpFileName,
  464. dwDesiredAccess,
  465. dwShareMode,
  466. lpSecurityAttributes,
  467. dwCreationDisposition,
  468. dwFlagsAndAttributes,
  469. hTemplateFile);
  470. if ( (dwDesiredAccess & GENERIC_WRITE) && (returnValue != INVALID_HANDLE_VALUE))
  471. g_IniEntryList->OpenFile(lpFileName, returnValue);
  472. return returnValue;
  473. }
  474. HFILE
  475. APIHOOK(OpenFile)(
  476. LPCSTR lpFileName, // file name
  477. LPOFSTRUCT lpReOpenBuff, // file information
  478. UINT uStyle // action and attributes
  479. )
  480. {
  481. HFILE returnValue = ORIGINAL_API(OpenFile)(lpFileName, lpReOpenBuff, uStyle);
  482. if ((uStyle & OF_WRITE) && (returnValue != HFILE_ERROR))
  483. g_IniEntryList->OpenFile(lpReOpenBuff->szPathName, (HANDLE)returnValue);
  484. return returnValue;
  485. }
  486. BOOL
  487. APIHOOK(CloseHandle)(
  488. HANDLE hObject // handle to object
  489. )
  490. {
  491. BOOL returnValue = ORIGINAL_API(CloseHandle)(hObject);
  492. if (hObject != INVALID_HANDLE_VALUE)
  493. g_IniEntryList->CloseFile(hObject);
  494. return returnValue;
  495. }
  496. BOOL
  497. APIHOOK(WriteFile)(
  498. HANDLE hFile, // handle to file
  499. LPCVOID lpBuffer, // data buffer
  500. DWORD nNumberOfBytesToWrite, // number of bytes to write
  501. LPDWORD lpNumberOfBytesWritten, // number of bytes written
  502. LPOVERLAPPED lpOverlapped // overlapped buffer
  503. )
  504. {
  505. BOOL returnValue = ORIGINAL_API(WriteFile)(
  506. hFile,
  507. lpBuffer,
  508. nNumberOfBytesToWrite,
  509. lpNumberOfBytesWritten,
  510. lpOverlapped
  511. );
  512. g_IniEntryList->WriteFile(hFile);
  513. return returnValue;
  514. }
  515. /*++
  516. Register hooked functions
  517. --*/
  518. BOOL
  519. NOTIFY_FUNCTION(
  520. DWORD fdwReason
  521. )
  522. {
  523. if (fdwReason == DLL_PROCESS_ATTACH)
  524. {
  525. return ParseCommandLine(COMMAND_LINE);
  526. }
  527. return TRUE;
  528. }
  529. HOOK_BEGIN
  530. APIHOOK_ENTRY(Kernel32.DLL, CreateFileA )
  531. APIHOOK_ENTRY(Kernel32.DLL, OpenFile )
  532. APIHOOK_ENTRY(Kernel32.DLL, WriteFile )
  533. APIHOOK_ENTRY(Kernel32.DLL, CloseHandle )
  534. CALL_NOTIFY_FUNCTION
  535. HOOK_END
  536. IMPLEMENT_SHIM_END