Leaked source code of windows server 2003
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.

2813 lines
73 KiB

  1. /*******************************************************************************
  2. *
  3. * (C) COPYRIGHT MICROSOFT CORP., 1993-2000
  4. *
  5. * TITLE: REGPORTE.C
  6. *
  7. * VERSION: 5.00
  8. *
  9. * AUTHOR: Tracy Sharpe
  10. *
  11. * DATE: 06 Apr 1994
  12. *
  13. * File import and export engine routines for the Registry Editor.
  14. *
  15. *******************************************************************************/
  16. #include "pch.h"
  17. #include "regresid.h"
  18. #include "reg1632.h"
  19. #include "regedit.h"
  20. #include "regkey.h"
  21. #include "regfile.h"
  22. #include "regedit.h"
  23. #include <malloc.h>
  24. #include "regdebug.h"
  25. // Association between the ASCII name and the handle of the registry key.
  26. const REGISTRY_ROOT g_RegistryRoots[] = {
  27. TEXT("HKEY_CLASSES_ROOT"), HKEY_CLASSES_ROOT, PREDEFINE_KEY_CLASSES_ROOT,
  28. TEXT("HKEY_CURRENT_USER"), HKEY_CURRENT_USER, PREDEFINE_KEY_CURRENT_USER,
  29. TEXT("HKEY_LOCAL_MACHINE"), HKEY_LOCAL_MACHINE, PREDEFINE_KEY_LOCAL_MACHINE,
  30. TEXT("HKEY_USERS"), HKEY_USERS, PREDEFINE_KEY_USERS,
  31. TEXT("HKEY_CURRENT_CONFIG"), HKEY_CURRENT_CONFIG, PREDEFINE_KEY_CURRENT_CONFIG,
  32. TEXT("HKEY_DYN_DATA"), HKEY_DYN_DATA, 0
  33. };
  34. const TCHAR s_RegistryHeader[] = TEXT("REGEDIT");
  35. const TCHAR s_OldWin31RegFileRoot[] = TEXT(".classes");
  36. const TCHAR s_Win40RegFileHeader[] = TEXT("REGEDIT4\n\n");
  37. #ifdef UNICODE
  38. //
  39. // New header is required for version 5.0 because the version detection code
  40. // in Win 4.0 regedit wasn't very good (See comments in ImportRegFile for
  41. // details)
  42. //
  43. const WORD s_UnicodeByteOrderMark = 0xFEFF;
  44. const TCHAR s_WinNT50RegFileHeader[] = TEXT("Windows Registry Editor Version");
  45. const TCHAR s_WinNT50RegFileVersion[] = TEXT("5.00");
  46. #endif
  47. const TCHAR s_HexPrefix[] = TEXT("hex");
  48. const TCHAR s_DwordPrefix[] = TEXT("dword:");
  49. const TCHAR g_HexConversion[16] = {TEXT('0'), TEXT('1'), TEXT('2'), TEXT('3'), TEXT('4'),
  50. TEXT('5'), TEXT('6'), TEXT('7'), TEXT('8'), TEXT('9'),
  51. TEXT('a'), TEXT('b'), TEXT('c'), TEXT('d'), TEXT('e'), TEXT('f')};
  52. const TCHAR s_FileLineBreak[] = TEXT(",\\\n ");
  53. extern BOOL g_fMultiLineStrings;
  54. //REARCHITECT - we upped the size of this buffer from 512 to 64K to reduce the chance of hitting the bug
  55. //where a DBCS character is split across two buffers. The true fix was too risky at the time.
  56. //Changed for NT5 RC2
  57. #define SIZE_FILE_IO_BUFFER 0x10000 //64K
  58. typedef struct _FILE_IO{
  59. #ifdef UNICODE
  60. //
  61. // Space for unicode/ansi conversions, assumes worst case
  62. // where every unicode char is a double-byte char
  63. //
  64. CHAR ConversionBuffer[SIZE_FILE_IO_BUFFER*2];
  65. #endif
  66. TCHAR Buffer[SIZE_FILE_IO_BUFFER];
  67. FILE_HANDLE hFile;
  68. int BufferOffset;
  69. int CurrentColumn;
  70. int CharsAvailable;
  71. DWORD FileSizeDiv100;
  72. DWORD FileOffset;
  73. UINT LastPercentage;
  74. #ifdef DEBUG
  75. BOOL fValidateUngetChar;
  76. #endif
  77. } FILE_IO;
  78. FILE_IO s_FileIo;
  79. UINT g_FileErrorStringID;
  80. UINT g_ImportFileVersion;
  81. BOOL s_fTreatFileAsUnicode = TRUE;
  82. VOID ImportWin31RegFile(HTREEITEM hComputerItem);
  83. VOID
  84. NEAR PASCAL
  85. ImportNewerRegFile(HTREEITEM hComputerItem);
  86. VOID ParseHeader(LPHKEY lphKey, HTREEITEM hComputerItem);
  87. VOID
  88. NEAR PASCAL
  89. ParseValue(
  90. HKEY hKey,
  91. LPCTSTR lpszValueName
  92. );
  93. VOID
  94. NEAR PASCAL
  95. ParseValuename(
  96. HKEY hKey
  97. );
  98. BOOL
  99. NEAR PASCAL
  100. ParseString(LPPORTVALUEPARAM pPortValueParam);
  101. BOOL
  102. NEAR PASCAL
  103. ParseHexSequence(LPPORTVALUEPARAM pPortValueParam);
  104. BOOL
  105. NEAR PASCAL
  106. ParseHexDword(
  107. LPDWORD lpDword
  108. );
  109. BOOL
  110. NEAR PASCAL
  111. ParseHexByte(
  112. LPBYTE lpByte
  113. );
  114. BOOL
  115. NEAR PASCAL
  116. ParseHexDigit(
  117. LPBYTE lpDigit
  118. );
  119. BOOL
  120. NEAR PASCAL
  121. ParseEndOfLine(
  122. VOID
  123. );
  124. VOID
  125. NEAR PASCAL
  126. SkipWhitespace(
  127. VOID
  128. );
  129. VOID
  130. NEAR PASCAL
  131. SkipPastEndOfLine(
  132. VOID
  133. );
  134. BOOL
  135. NEAR PASCAL
  136. GetChar(
  137. PTCHAR lpChar
  138. );
  139. VOID
  140. NEAR PASCAL
  141. UngetChar(
  142. VOID
  143. );
  144. BOOL
  145. NEAR PASCAL
  146. MatchChar(
  147. TCHAR CharToMatch
  148. );
  149. BOOL
  150. NEAR PASCAL
  151. IsWhitespace(
  152. TCHAR Char
  153. );
  154. BOOL
  155. NEAR PASCAL
  156. IsNewLine(
  157. TCHAR Char
  158. );
  159. VOID
  160. NEAR PASCAL
  161. PutBranch(
  162. HKEY hKey,
  163. LPTSTR lpKeyName
  164. );
  165. VOID
  166. NEAR PASCAL
  167. PutLiteral(
  168. LPCTSTR lpString
  169. );
  170. VOID
  171. NEAR PASCAL
  172. PutString(
  173. LPCTSTR lpString
  174. );
  175. VOID
  176. NEAR PASCAL
  177. PutBinary(
  178. CONST BYTE FAR* lpBuffer,
  179. DWORD Type,
  180. DWORD cbBytes
  181. );
  182. VOID
  183. NEAR PASCAL
  184. PutDword(
  185. DWORD Dword,
  186. BOOL fLeadingZeroes
  187. );
  188. VOID
  189. NEAR PASCAL
  190. PutChar(
  191. TCHAR Char
  192. );
  193. VOID
  194. NEAR PASCAL
  195. FlushIoBuffer(
  196. VOID
  197. );
  198. //------------------------------------------------------------------------------
  199. // Regedit_GetRootKeyFromComputer
  200. //
  201. // DESCRIPTION: Invokes the security editor for the currently selected item.
  202. //
  203. // PARAMETERS: hWnd - handle to the current window
  204. //------------------------------------------------------------------------------
  205. HKEY Regedit_GetRootKeyFromComputer(HTREEITEM hComputerItem, PTSTR pszFullKeyName)
  206. {
  207. HKEY hRootKey = NULL;
  208. if (hComputerItem == NULL)
  209. {
  210. int i;
  211. for (i = 0; i < NUMBER_REGISTRY_ROOTS; i++)
  212. {
  213. if (lstrcmp(g_RegistryRoots[i].lpKeyName, pszFullKeyName) == 0)
  214. {
  215. hRootKey = g_RegistryRoots[i].hKey;
  216. break;
  217. }
  218. }
  219. }
  220. else
  221. {
  222. TCHAR acComputerName[MAXKEYNAME];
  223. TV_ITEM TVItem;
  224. // walk through each of the registry roots to see if this key
  225. // belongs to it
  226. HTREEITEM hRegistryRoot =
  227. TreeView_GetChild(g_RegEditData.hKeyTreeWnd, hComputerItem);
  228. TVItem.mask = TVIF_TEXT | TVIF_PARAM;
  229. TVItem.hItem = hRegistryRoot;
  230. TVItem.pszText = acComputerName;
  231. TVItem.cchTextMax = ARRAYSIZE(acComputerName);
  232. while (hRegistryRoot != NULL)
  233. {
  234. TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
  235. if (lstrcmp(acComputerName, pszFullKeyName) == 0)
  236. {
  237. hRootKey = (HKEY)TVItem.lParam;
  238. break;
  239. }
  240. hRegistryRoot =
  241. TreeView_GetNextSibling(g_RegEditData.hKeyTreeWnd, hRegistryRoot);
  242. TVItem.hItem = hRegistryRoot;
  243. }
  244. }
  245. return hRootKey;
  246. }
  247. /*******************************************************************************
  248. *
  249. * EditRegistryKey
  250. *
  251. * DESCRIPTION:
  252. * Parses the pFullKeyName string and creates a handle to the registry key.
  253. *
  254. * PARAMETERS:
  255. * lphKey, location to store handle to registry key.
  256. * lpFullKeyName, string of form "HKEY_LOCAL_MACHINE\Subkey1\Subkey2".
  257. * fCreate, TRUE if key should be created, else FALSE for open only.
  258. * (returns), ERROR_SUCCESS, no errors occurred, phKey is valid,
  259. * ERROR_CANTOPEN, registry access error of some form,
  260. * ERROR_BADKEY, incorrectly formed pFullKeyName.
  261. *
  262. *******************************************************************************/
  263. DWORD EditRegistryKey(HTREEITEM hComputerItem, LPHKEY lphKey, LPTSTR lpFullKeyName, UINT uOperation)
  264. {
  265. LPTSTR lpSubKeyName;
  266. TCHAR PrevChar;
  267. UINT Counter;
  268. DWORD Result;
  269. HKEY hRootKey = NULL;
  270. if ((lpSubKeyName = (LPTSTR) StrChr(lpFullKeyName, TEXT('\\'))) != NULL) {
  271. PrevChar = *lpSubKeyName;
  272. *lpSubKeyName++ = 0;
  273. }
  274. CharUpper(lpFullKeyName);
  275. hRootKey = Regedit_GetRootKeyFromComputer(hComputerItem, lpFullKeyName);
  276. if (hRootKey)
  277. {
  278. Result = ERROR_CANTOPEN;
  279. switch (uOperation)
  280. {
  281. case ERK_CREATE:
  282. //
  283. // If trying to open one of these keys just return OK
  284. // When lpSubKeyName is NULL, you recreate the parent key
  285. // Since these keys are usually in use this will fail
  286. // This code path only occurs when restoring a whole root key
  287. // from a .reg file.
  288. //
  289. if (((hRootKey == HKEY_LOCAL_MACHINE) || (hRootKey == HKEY_USERS))
  290. && lpSubKeyName == NULL) {
  291. Result = ERROR_SUCCESS;
  292. }
  293. else if (RegCreateKey(hRootKey, lpSubKeyName, lphKey) == ERROR_SUCCESS)
  294. Result = ERROR_SUCCESS;
  295. break;
  296. case ERK_OPEN:
  297. //
  298. // Used when exporting.
  299. //
  300. if(RegOpenKeyEx(hRootKey,lpSubKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,lphKey) == ERROR_SUCCESS)
  301. Result = ERROR_SUCCESS;
  302. break;
  303. case ERK_DELETE:
  304. RegDeleteKeyRecursive(hRootKey, lpSubKeyName);
  305. // asssume success... don't care if this fails
  306. Result = ERROR_SUCCESS;
  307. *lphKey = NULL;
  308. break;
  309. }
  310. }
  311. else
  312. {
  313. Result = ERROR_BADKEY;
  314. }
  315. if (lpSubKeyName != NULL) {
  316. lpSubKeyName--;
  317. *lpSubKeyName = PrevChar;
  318. }
  319. return Result;
  320. }
  321. /*******************************************************************************
  322. *
  323. * ImportRegFile
  324. *
  325. * DESCRIPTION:
  326. *
  327. * PARAMETERS:
  328. * lpFileName, address of name of file to be imported.
  329. *
  330. *******************************************************************************/
  331. VOID
  332. PASCAL
  333. ImportRegFile(HWND hWnd, LPTSTR lpFileName, HTREEITEM hComputerItem)
  334. {
  335. TCHAR Char;
  336. LPCTSTR lpHeader;
  337. BOOL fNewRegistryFile;
  338. #ifdef UNICODE
  339. UINT Temp, i;
  340. TCHAR StrToIntBuf[2];
  341. LPCTSTR lp50Header;
  342. #endif // UNICODE
  343. DWORD cch;
  344. TCHAR tchBuffer[MAX_PATH] = {0};
  345. LPTSTR lpFilePart;
  346. g_FileErrorStringID = IDS_IMPFILEERRSUCCESS;
  347. // OPENREADFILE used to be OpenFile(), but there isn't any Unicode version
  348. // of that API, so now it's CreateFile(). But OpenFile searched the path
  349. // automatically, whereas CreateFile does not. Corel's 'Perfect Office v6'
  350. // install app depends on the path being searched, so do it manually.
  351. cch = SearchPath(NULL, // pointer to search path
  352. lpFileName, // pointer to filename
  353. NULL, // pointer to extension
  354. ARRAYSIZE(tchBuffer), // size, in characters, of buffer
  355. (TCHAR*)tchBuffer, // pointer to buffer for found filename
  356. &lpFilePart); // pointer to pointer to file component);
  357. if ((cch != 0) && (cch <= MAX_PATH) && OPENREADFILE((TCHAR*)tchBuffer, s_FileIo.hFile))
  358. {
  359. WORD wBOM;
  360. DWORD NumberOfBytesRead;
  361. s_FileIo.FileSizeDiv100 = GetFileSize(s_FileIo.hFile, NULL) / 100;
  362. s_FileIo.FileOffset = 0;
  363. s_FileIo.CharsAvailable = 0;
  364. s_FileIo.LastPercentage = 0;
  365. //
  366. // Read the first two bytes. If it's the Unicode byte order mark,
  367. // set a flag so all the rest of the file will be interpreted
  368. // as ANSI or Unicode text properly.
  369. //
  370. if (!READFILE(s_FileIo.hFile, &wBOM,
  371. sizeof(wBOM), &NumberOfBytesRead)) {
  372. g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
  373. goto exit_gracefully;
  374. }
  375. if (wBOM == s_UnicodeByteOrderMark)
  376. s_fTreatFileAsUnicode = TRUE;
  377. else {
  378. s_fTreatFileAsUnicode = FALSE;
  379. // We probably just read "RE" from "REGEDIT4". Back up the file
  380. // position so the ANSI import routines get what they expect
  381. SetFilePointer(s_FileIo.hFile, -2, NULL, FILE_CURRENT);
  382. }
  383. //
  384. // The following will force GetChar to read in the first block of data.
  385. //
  386. s_FileIo.BufferOffset = 0;
  387. SkipWhitespace();
  388. lpHeader = s_RegistryHeader;
  389. g_ImportFileVersion = 0;
  390. # if 0
  391. Sit back, and I will tell ye a tale of woe.
  392. Win95 and NT 4 shipped with regedit compiled ANSI. There are a couple
  393. of registry types on NT (namely REG_EXPAND_SZ and REG_MULTI_SZ) that
  394. weren't on Win95, and which regedit doesn't really understand. regedit
  395. treats these registry types as hex binary streams.
  396. You can probably see where this is going.
  397. If you exported, say your user TEMP environment variable on NT 4
  398. using regedit, you'd get something that looked like this:
  399. REGEDIT4
  400. [HKEY_CURRENT_USER\Environment]
  401. "TEMP"=hex(2):25,53,59,53,54,45,4d,44,52,49,56,45,25,5c,53,48,54,65,6d,70,00
  402. ...a nice, null-terminated ANSI string. Nice, that is, until we decided
  403. to compile regedit UNICODE for NT 5. A unicode regedit exports your
  404. user TEMP variable like this:
  405. REGEDIT4
  406. [HKEY_CURRENT_USER\Environment]
  407. "TEMP"=hex(2):25,00,53,00,59,00,53,00,54,00,45,00,4d,00,44,00,52,00,49,00,56,\
  408. 00,45,00,25,00,5c,00,53,00,48,00,54,00,65,00,6d,00,70,00,00,00
  409. ...mmmm. Unicode. Of course, a unicode regedit also expects anything
  410. it imports to have all those interspersed zeroes, too. Otherwise,
  411. it dumps garbage into your registry. All it takes is a -DUNICODE, and
  412. regedit is suddenly incompatible with the thousdands of existing .reg
  413. files out there.
  414. So just bump the version in the header to REGEDIT5 and be done with
  415. it, right? Wrong. The regedit on Win95 and NT 4 looks at the first
  416. character after the string "REGEDIT" and compares it to the digit "4".
  417. If that character is anything other than the digit "4", the parser
  418. assumes it is looking at a Windows 3.1 file. Yep. There will only
  419. ever be two formats, right? Just Win95 and Win3.1. That's all the
  420. world needs.
  421. So a completely new .reg file header had to be invented, so that the
  422. older, regedits of the world would simply regect the new,
  423. unicodized .reg files outright. An NT 5 .reg file, exporting your user
  424. TEMP variable, looks like this:
  425. Windows Registry Editor Version 5.00
  426. [HKEY_CURRENT_USER\Environment]
  427. "TEMP"=hex(2):25,00,53,00,59,00,53,00,54,00,45,00,4d,00,44,00,52,00,49,00,56,\
  428. 00,45,00,25,00,5c,00,53,00,48,00,54,00,65,00,6d,00,70,00,00,00
  429. The parser is still not very good, but it does bother to convert that 5.00
  430. into a version number, so that future generations can bump it to 5.50 or
  431. 6.00, and the regedit 5.00 that shipped with NT 5.00 will properly reject
  432. the files.
  433. #endif // 0
  434. #ifdef UNICODE
  435. //
  436. // Compare to the new .reg file header
  437. //
  438. lp50Header = s_WinNT50RegFileHeader;
  439. while (*lp50Header != 0) {
  440. if (MatchChar(*lp50Header))
  441. lp50Header = CharNext(lp50Header);
  442. else
  443. break;
  444. }
  445. //
  446. // If the above loop pushed lp50Header to its terminating null
  447. // character, then the header matches.
  448. //
  449. if (0 == *lp50Header) {
  450. SkipWhitespace();
  451. //
  452. // Now, decode the version number into a hex, _WIN32_WINNT
  453. // style version number.
  454. //
  455. StrToIntBuf[1] = 0;
  456. //
  457. // Any number of digits can come before the decimal point
  458. //
  459. while (!MatchChar(TEXT('.'))) {
  460. if (!GetChar(StrToIntBuf) || !IsCharAlphaNumeric(*StrToIntBuf)) {
  461. g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
  462. goto exit_gracefully;
  463. }
  464. Temp = StrToInt(StrToIntBuf);
  465. // Hex version number, so move left four bits.
  466. g_ImportFileVersion <<= 4;
  467. g_ImportFileVersion += Temp;
  468. }
  469. //
  470. // Fixed at two digits after the decimal point
  471. //
  472. for (i = 0; i < 2; i++) {
  473. if (!GetChar(StrToIntBuf) || !IsCharAlphaNumeric(*StrToIntBuf)) {
  474. g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
  475. goto exit_gracefully;
  476. }
  477. Temp = StrToInt(StrToIntBuf);
  478. // Hex version number, so move left four bits.
  479. g_ImportFileVersion <<= 4;
  480. g_ImportFileVersion += Temp;
  481. }
  482. //
  483. // For NT 5, reject any version number that isn't
  484. // 5. This can be expanded into a switch statement
  485. // when the version number is bumped later.
  486. //
  487. if (0x0500 != g_ImportFileVersion) {
  488. g_FileErrorStringID = IDS_IMPFILEERRVERBAD;
  489. goto exit_gracefully;
  490. }
  491. else {
  492. SkipWhitespace();
  493. ImportNewerRegFile(hComputerItem);
  494. }
  495. } // if (0 == *lp50Header)
  496. //
  497. // It doesn't use the new .reg file header, so
  498. // it's not an NT 5.0+ registry file, so use the
  499. // older algorithm to see if it's a valid older registry file
  500. //
  501. else {
  502. #endif // UNICODE
  503. while (*lpHeader != 0) {
  504. if (MatchChar(*lpHeader))
  505. lpHeader = CharNext(lpHeader);
  506. else
  507. break;
  508. }
  509. if (*lpHeader == 0) {
  510. //
  511. // Win95's and NT 4's regedit shipped with this line
  512. // of code. It is the cause of all of the suffering above.
  513. // Notice the incorrect assumption: "If the very next
  514. // character isn't a '4', then we must be reading
  515. // a Windows 3.1 registry file!" Of course there won't
  516. // be a version 5 of regedit. Version 4 was perfect!
  517. //
  518. fNewRegistryFile = MatchChar(TEXT('4'));
  519. SkipWhitespace();
  520. if (GetChar(&Char) && IsNewLine(Char)) {
  521. if (fNewRegistryFile) {
  522. g_ImportFileVersion = 0x0400;
  523. ImportNewerRegFile(hComputerItem);
  524. }
  525. else {
  526. g_ImportFileVersion = 0x0310;
  527. ImportWin31RegFile(hComputerItem);
  528. }
  529. }
  530. }
  531. else if (!hWnd)
  532. {
  533. // only registry script files can be imported without the
  534. // regedit window open
  535. g_FileErrorStringID = IDS_IMPFILEERRNOTASCRPT;
  536. }
  537. else if (!hComputerItem || (hComputerItem ==
  538. RegEdit_GetComputerItem(TreeView_GetSelection(g_RegEditData.hKeyTreeWnd))))
  539. {
  540. // It's not a registry file, but since the selected key is on the computer
  541. // they choose, try to import it as a binary registy key (regedt32 type)
  542. // remove the progress dialog
  543. if (g_hRegProgressWnd != NULL)
  544. {
  545. EnableWindow(hWnd, TRUE);
  546. DestroyWindow(g_hRegProgressWnd);
  547. g_hRegProgressWnd = NULL;
  548. }
  549. RestoreBinaryKeyFile(hWnd, lpFileName);
  550. }
  551. else
  552. {
  553. g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
  554. }
  555. #ifdef UNICODE
  556. }
  557. #endif // UNICODE
  558. } // if (OPENREADFILE...
  559. else
  560. {
  561. {
  562. TCHAR buff[250];
  563. StringCchPrintf(buff, ARRAYSIZE(buff), L"REGEDIT: CreateFile failed, GetLastError() = %d\n",
  564. GetLastError());
  565. OutputDebugString(buff);
  566. }
  567. s_FileIo.hFile = NULL;
  568. g_FileErrorStringID = IDS_IMPFILEERRFILEOPEN;
  569. }
  570. #ifdef UNICODE // Urefd labels generate warnings
  571. exit_gracefully:
  572. #endif // UNICODE
  573. if (s_FileIo.hFile) {
  574. CloseHandle(s_FileIo.hFile);
  575. }
  576. }
  577. //------------------------------------------------------------------------------
  578. // ImportBinaryKeyFile
  579. //
  580. // DESCRIPTION: Loads a binary key file at the currently selected key
  581. // This is the format of regedt32.exe saved keys
  582. //
  583. // PARAMETERS: LPTSTR lpFileName - file with the binary key
  584. //------------------------------------------------------------------------------
  585. void RestoreBinaryKeyFile(HWND hWnd, LPTSTR lpFileName)
  586. {
  587. TCHAR achKeyName[MAXKEYNAME];
  588. HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);
  589. KeyTree_GetKeyName(hSelectedTreeItem, achKeyName, ARRAYSIZE(achKeyName));
  590. // confirm that they want to replace the registry key
  591. if (InternalMessageBox(g_hInstance, hWnd,
  592. MAKEINTRESOURCE(IDS_CONFIRMRESTOREKEY), MAKEINTRESOURCE(IDS_CONFIRMRESKEYTITLE),
  593. MB_ICONQUESTION | MB_YESNO , achKeyName) == IDYES)
  594. {
  595. HRESULT hr;
  596. RegEdit_SetPrivilege(SE_RESTORE_NAME, TRUE);
  597. // attempt to load the file as a registry key
  598. if ((hr = RegRestoreKey(g_RegEditData.hCurrentSelectionKey, lpFileName,
  599. REG_FORCE_RESTORE)) != ERROR_SUCCESS)
  600. {
  601. switch (hr)
  602. {
  603. case ERROR_PRIVILEGE_NOT_HELD:
  604. g_FileErrorStringID = IDS_IMPFILEERRNOPRIV;
  605. break;
  606. case ERROR_FILE_NOT_FOUND:
  607. g_FileErrorStringID = IDS_IMPFILEERRNOFILE;
  608. break;
  609. case ERROR_INVALID_HANDLE:
  610. g_FileErrorStringID = IDS_IMPFILEERRINVALID;
  611. break;
  612. default:
  613. g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
  614. break;
  615. }
  616. }
  617. RegEdit_SetPrivilege(SE_RESTORE_NAME, FALSE);
  618. }
  619. else
  620. {
  621. g_FileErrorStringID = IDS_IMPFILEERRORCANCEL;
  622. }
  623. }
  624. /*******************************************************************************
  625. *
  626. * ImportWin31RegFile
  627. *
  628. * DESCRIPTION:
  629. * Imports the contents of a Windows 3.1 style registry file into the
  630. * registry.
  631. *
  632. * We scan over the file looking for lines of the following type:
  633. * HKEY_CLASSES_ROOT\keyname = value_data
  634. * HKEY_CLASSES_ROOT\keyname =value_data
  635. * HKEY_CLASSES_ROOT\keyname value_data
  636. * HKEY_CLASSES_ROOT\keyname (null value data)
  637. *
  638. * In all cases, any number of spaces may follow 'keyname'. Although we
  639. * only document the first syntax, the Windows 3.1 Regedit handled all of
  640. * these formats as valid, so this version will as well (fortunately, it
  641. * doesn't make the parsing any more complex!).
  642. *
  643. * Note, we also support replacing HKEY_CLASSES_ROOT with \.classes above
  644. * which must come from some early releases of Windows.
  645. *
  646. * PARAMETERS: HTREEITEM hComputerItem - computer item to receive imported key
  647. *
  648. *******************************************************************************/
  649. VOID ImportWin31RegFile(HTREEITEM hComputerItem)
  650. {
  651. HKEY hKey;
  652. TCHAR Char;
  653. BOOL fSuccess;
  654. LPCTSTR lpClassesRoot;
  655. TCHAR KeyName[MAXKEYNAME];
  656. UINT Index;
  657. //
  658. // Keep an open handle to the classes root. We may prevent some
  659. // unneccessary flushing.
  660. //
  661. hKey = Regedit_GetRootKeyFromComputer(hComputerItem,
  662. g_RegistryRoots[INDEX_HKEY_CLASSES_ROOT].lpKeyName);
  663. if (RegOpenKeyEx(hKey, NULL, 0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS)
  664. {
  665. g_FileErrorStringID = IDS_IMPFILEERRREGOPEN;
  666. return;
  667. }
  668. while (TRUE) {
  669. //
  670. // Check for the end of file condition.
  671. //
  672. if (!GetChar(&Char))
  673. break;
  674. UngetChar(); // Not efficient, but works for now.
  675. //
  676. // Match the beginning of the line against one of the two aliases for
  677. // HKEY_CLASSES_ROOT.
  678. //
  679. if (MatchChar(TEXT('\\')))
  680. lpClassesRoot = s_OldWin31RegFileRoot;
  681. else
  682. lpClassesRoot = g_RegistryRoots[INDEX_HKEY_CLASSES_ROOT].lpKeyName;
  683. fSuccess = TRUE;
  684. while (*lpClassesRoot != 0) {
  685. if (!MatchChar(*lpClassesRoot++)) {
  686. fSuccess = FALSE;
  687. break;
  688. }
  689. }
  690. //
  691. // Make sure that we have a backslash seperating one of the aliases
  692. // from the keyname.
  693. //
  694. if (fSuccess)
  695. fSuccess = MatchChar(TEXT('\\'));
  696. if (fSuccess) {
  697. //
  698. // We've found one of the valid aliases, so read in the keyname.
  699. //
  700. // fSuccess = TRUE; // Must be TRUE if we're in this block
  701. Index = 0;
  702. while (GetChar(&Char)) {
  703. if (Char == TEXT(' ') || IsNewLine(Char))
  704. break;
  705. //
  706. // Make sure that the keyname buffer doesn't overflow. We must
  707. // leave room for a terminating null.
  708. //
  709. if (Index >= (ARRAYSIZE(KeyName)) - 1)
  710. {
  711. fSuccess = FALSE;
  712. break;
  713. }
  714. KeyName[Index++] = Char;
  715. }
  716. if (fSuccess)
  717. {
  718. UINT cMaxDataLength = ALLOCATION_INCR;
  719. PBYTE pbValueDataBuffer;
  720. KeyName[Index] = 0;
  721. //
  722. // Now see if we have a value to assign to this keyname.
  723. //
  724. SkipWhitespace();
  725. if (MatchChar(TEXT('=')))
  726. MatchChar(TEXT(' '));
  727. // fSuccess = TRUE; // Must be TRUE if we're in this block
  728. Index = 0;
  729. pbValueDataBuffer = LocalAlloc(LPTR, cMaxDataLength);
  730. fSuccess = (pbValueDataBuffer != NULL);
  731. while (GetChar(&Char) && fSuccess)
  732. {
  733. if (IsNewLine(Char))
  734. break;
  735. //
  736. // Make sure that the value data buffer doesn't overflow.
  737. // Because this is always string data, we must leave room
  738. // for a terminating null.
  739. //
  740. if (Index >= cMaxDataLength - 1)
  741. {
  742. PBYTE pbValueData =
  743. LocalReAlloc(pbValueDataBuffer, cMaxDataLength + ALLOCATION_INCR, LMEM_MOVEABLE);
  744. fSuccess = (pbValueData != NULL);
  745. if (!fSuccess)
  746. {
  747. break;
  748. }
  749. else
  750. {
  751. pbValueDataBuffer = pbValueData;
  752. cMaxDataLength += ALLOCATION_INCR;
  753. }
  754. }
  755. ((PTSTR)pbValueDataBuffer)[Index++] = Char;
  756. }
  757. if (fSuccess)
  758. {
  759. ((PTSTR)pbValueDataBuffer)[Index] = 0;
  760. if (RegSetValue(hKey, KeyName, REG_SZ, (LPCTSTR)pbValueDataBuffer,
  761. Index*sizeof(TCHAR)) != ERROR_SUCCESS)
  762. g_FileErrorStringID = IDS_IMPFILEERRREGSET;
  763. }
  764. else
  765. {
  766. g_FileErrorStringID = IDS_NOMEMORY;
  767. }
  768. if (pbValueDataBuffer)
  769. {
  770. LocalFree(pbValueDataBuffer);
  771. }
  772. }
  773. }
  774. //
  775. // Somewhere along the line, we had a parsing error, so resynchronize
  776. // on the next line.
  777. //
  778. if (!fSuccess)
  779. SkipPastEndOfLine();
  780. }
  781. RegFlushKey(hKey);
  782. RegCloseKey(hKey);
  783. }
  784. /*******************************************************************************
  785. *
  786. * ImportNewerRegFile
  787. *
  788. * DESCRIPTION:
  789. *
  790. * PARAMETERS:
  791. *
  792. *******************************************************************************/
  793. VOID
  794. NEAR PASCAL
  795. ImportNewerRegFile(HTREEITEM hComputerItem)
  796. {
  797. TCHAR Char;
  798. HKEY hKey;
  799. hKey = NULL;
  800. while (TRUE) {
  801. SkipWhitespace();
  802. //
  803. // Check for the end of file condition.
  804. //
  805. if (!GetChar(&Char))
  806. break;
  807. switch (Char) {
  808. case TEXT('['):
  809. //
  810. // If a registry key is currently open, we must close it first.
  811. // If ParseHeader happens to fail (for example, no closing
  812. // bracket), then hKey will be NULL and any values that we
  813. // parse must be ignored.
  814. //
  815. if (hKey != NULL) {
  816. RegCloseKey(hKey);
  817. hKey = NULL;
  818. }
  819. ParseHeader(&hKey, hComputerItem);
  820. break;
  821. case TEXT('"'):
  822. //
  823. // As noted above, if we don't have an open registry key, then
  824. // just skip the line.
  825. //
  826. if (hKey != NULL)
  827. ParseValuename(hKey);
  828. else
  829. SkipPastEndOfLine();
  830. break;
  831. case TEXT('@'):
  832. //
  833. //
  834. //
  835. if (hKey != NULL)
  836. ParseValue(hKey, NULL);
  837. else
  838. SkipPastEndOfLine();
  839. break;
  840. case TEXT(';'):
  841. //
  842. // This line is a comment so just dump the rest of it.
  843. //
  844. SkipPastEndOfLine();
  845. break;
  846. default:
  847. if (IsNewLine(Char))
  848. break;
  849. SkipPastEndOfLine();
  850. break;
  851. }
  852. }
  853. if (hKey != NULL)
  854. RegCloseKey(hKey);
  855. }
  856. /*******************************************************************************
  857. *
  858. * ParseHeader
  859. *
  860. * DESCRIPTION:
  861. *
  862. * PARAMETERS:
  863. *
  864. *******************************************************************************/
  865. // REARCHITECT - each subkeyname can be MAXKEYNAME in size
  866. // ideally - we should handle unlimited size names
  867. // let's at least handle bigger names for now
  868. // at least a depth of 10 with maximum length subkey names
  869. #define SIZE_FULL_KEYNAME ((MAXKEYNAME + 40)*10)
  870. VOID ParseHeader(LPHKEY lphKey, HTREEITEM hComputerItem)
  871. {
  872. TCHAR FullKeyName[SIZE_FULL_KEYNAME];
  873. int CurrentIndex;
  874. int LastRightBracketIndex;
  875. TCHAR Char;
  876. UINT uOperation = ERK_CREATE;
  877. CurrentIndex = 0;
  878. LastRightBracketIndex = -1;
  879. if (!GetChar(&Char))
  880. return;
  881. if (Char == TEXT('-')) {
  882. if (!GetChar(&Char))
  883. return;
  884. uOperation = ERK_DELETE;
  885. }
  886. do {
  887. if (IsNewLine(Char))
  888. break;
  889. if (Char == TEXT(']'))
  890. LastRightBracketIndex = CurrentIndex;
  891. FullKeyName[CurrentIndex++] = Char;
  892. if (CurrentIndex == SIZE_FULL_KEYNAME) {
  893. do {
  894. if (Char == TEXT(']'))
  895. LastRightBracketIndex = -1;
  896. if (IsNewLine(Char))
  897. break;
  898. } while (GetChar(&Char));
  899. break;
  900. }
  901. } while (GetChar(&Char));
  902. if (LastRightBracketIndex != -1)
  903. {
  904. FullKeyName[LastRightBracketIndex] = 0;
  905. switch (EditRegistryKey(hComputerItem, lphKey, FullKeyName, uOperation))
  906. {
  907. //
  908. // Be afraid of adding code to handle more error cases here.
  909. //
  910. // We broke Picture Publisher 8.0 by adding an ERROR_BADKEY
  911. // case. As part of their setup, they run regedit on a v4
  912. // reg file that has a bad section, which EditRegistryKey
  913. // will fail to parse with ERROR_BADKEY. We need to keep
  914. // chugging along in that case like Win2K did, or else we
  915. // break their setup.
  916. //
  917. case ERROR_CANTOPEN:
  918. g_FileErrorStringID = IDS_IMPFILEERRREGOPEN;
  919. break;
  920. }
  921. }
  922. }
  923. /*******************************************************************************
  924. *
  925. * ParseValuename
  926. *
  927. * DESCRIPTION:
  928. *
  929. * PARAMETERS:
  930. *
  931. *******************************************************************************/
  932. VOID
  933. NEAR PASCAL
  934. ParseValuename(
  935. HKEY hKey
  936. )
  937. {
  938. PORTVALUEPARAM PortValueParam;
  939. PortValueParam.cbData = MAXVALUENAME_LENGTH * sizeof(TCHAR);
  940. PortValueParam.pbData = LocalAlloc(LPTR, PortValueParam.cbData);
  941. if (PortValueParam.pbData)
  942. {
  943. if (ParseString(&PortValueParam))
  944. {
  945. ParseValue(hKey, (PTSTR)PortValueParam.pbData);
  946. }
  947. else
  948. {
  949. SkipPastEndOfLine();
  950. }
  951. LocalFree(PortValueParam.pbData);
  952. }
  953. }
  954. VOID
  955. NEAR PASCAL
  956. ParseValue(
  957. HKEY hKey,
  958. LPCTSTR lpszValueName
  959. )
  960. {
  961. BOOL fSuccess = TRUE;
  962. BOOL fSkipPastLine = FALSE;
  963. DWORD Type;
  964. DWORD cbData = 0;
  965. DWORD cbMaxData = ALLOCATION_INCR;
  966. LPCTSTR lpPrefix;
  967. PBYTE pbValueDataBuffer;
  968. SkipWhitespace();
  969. if (!MatchChar(TEXT('=')))
  970. {
  971. fSuccess = FALSE;
  972. fSkipPastLine = TRUE;
  973. }
  974. else
  975. {
  976. SkipWhitespace();
  977. pbValueDataBuffer = LocalAlloc(LPTR, cbMaxData);
  978. if (!pbValueDataBuffer)
  979. {
  980. g_FileErrorStringID = IDS_IMPFILEERRREGSET;
  981. fSuccess = FALSE;
  982. }
  983. else
  984. {
  985. //
  986. // REG_SZ.
  987. //
  988. // "ValueName" = "string of text"
  989. //
  990. if (MatchChar(TEXT('"')))
  991. {
  992. // FEATURE: Line continuations for strings?
  993. PORTVALUEPARAM PortValueParam;
  994. PortValueParam.pbData = pbValueDataBuffer;
  995. PortValueParam.cbData = cbMaxData;
  996. if (!ParseString(&PortValueParam) || !ParseEndOfLine())
  997. {
  998. fSuccess = FALSE;
  999. fSkipPastLine = TRUE;
  1000. }
  1001. // pointer might have been swapped for one with more memory
  1002. pbValueDataBuffer = PortValueParam.pbData;
  1003. cbData = PortValueParam.cbData;
  1004. Type = REG_SZ;
  1005. }
  1006. //
  1007. // REG_DWORD.
  1008. //
  1009. // "ValueName" = dword: 12345678
  1010. //
  1011. else if (MatchChar(s_DwordPrefix[0])) {
  1012. lpPrefix = &s_DwordPrefix[1];
  1013. while (*lpPrefix != 0)
  1014. {
  1015. if (!MatchChar(*lpPrefix++))
  1016. {
  1017. fSuccess = FALSE;
  1018. fSkipPastLine = TRUE;
  1019. }
  1020. }
  1021. if (fSuccess)
  1022. {
  1023. SkipWhitespace();
  1024. if (!ParseHexDword((LPDWORD) pbValueDataBuffer) || !ParseEndOfLine())
  1025. {
  1026. fSuccess = FALSE;
  1027. fSkipPastLine = TRUE;
  1028. }
  1029. Type = REG_DWORD;
  1030. cbData = sizeof(DWORD);
  1031. }
  1032. }
  1033. else if (MatchChar('-'))
  1034. {
  1035. if (!ParseEndOfLine())
  1036. {
  1037. fSuccess = FALSE;
  1038. fSkipPastLine = TRUE;
  1039. }
  1040. else
  1041. {
  1042. RegDeleteValue(hKey, lpszValueName);
  1043. fSuccess = FALSE;
  1044. }
  1045. }
  1046. //
  1047. // REG_BINARY and other.
  1048. //
  1049. // "ValueName" = hex: 00 , 11 , 22
  1050. // "ValueName" = hex(12345678): 00, 11, 22
  1051. //
  1052. else {
  1053. lpPrefix = s_HexPrefix;
  1054. while (*lpPrefix != 0)
  1055. {
  1056. if (!MatchChar(*lpPrefix++))
  1057. {
  1058. fSuccess = FALSE;
  1059. fSkipPastLine = TRUE;
  1060. }
  1061. }
  1062. if (fSuccess)
  1063. {
  1064. //
  1065. // Check if this is a type of registry data that we don't directly
  1066. // support. If so, then it's just a dump of hex data of the specified
  1067. // type.
  1068. //
  1069. if (MatchChar(TEXT('(')))
  1070. {
  1071. if (!ParseHexDword(&Type) || !MatchChar(TEXT(')')))
  1072. {
  1073. fSuccess = FALSE;
  1074. fSkipPastLine = TRUE;
  1075. }
  1076. }
  1077. else
  1078. Type = REG_BINARY;
  1079. if (fSuccess)
  1080. {
  1081. PORTVALUEPARAM PortValueParam;
  1082. PortValueParam.pbData = pbValueDataBuffer;
  1083. PortValueParam.cbData = cbMaxData;
  1084. if (!MatchChar(TEXT(':')) || !ParseHexSequence(&PortValueParam) ||
  1085. !ParseEndOfLine())
  1086. {
  1087. fSuccess = FALSE;
  1088. fSkipPastLine = TRUE;
  1089. }
  1090. // pointer might have been swapped for one with more memory
  1091. pbValueDataBuffer = PortValueParam.pbData;
  1092. cbData = PortValueParam.cbData;
  1093. }
  1094. }
  1095. }
  1096. if (fSuccess)
  1097. {
  1098. #ifdef UNICODE
  1099. //
  1100. // If we're compiled UNICODE and we're reading an older, ANSI .reg
  1101. // file, we have to write all of the data to the registry using
  1102. // RegSetValueExA, because it was read from the registry using
  1103. // RegQueryValueExA.
  1104. //
  1105. if ((g_ImportFileVersion < 0x0500) && ((REG_EXPAND_SZ == Type) || (REG_MULTI_SZ == Type)))
  1106. {
  1107. CHAR AnsiValueName[MAXVALUENAME_LENGTH];
  1108. AnsiValueName[0] = 0;
  1109. //
  1110. // It's much easier to convert the value name to ANSI
  1111. // and call RegSetValueExA than to try to convert
  1112. // a REG_MULTI_SZ to Unicode before calling RegSetValueExW.
  1113. // We don't lose anything because this is coming from a
  1114. // downlevel .reg file that could only contain ANSI characters
  1115. // to begin with.
  1116. //
  1117. WideCharToMultiByte(
  1118. CP_THREAD_ACP,
  1119. 0,
  1120. lpszValueName,
  1121. -1,
  1122. AnsiValueName,
  1123. ARRAYSIZE(AnsiValueName),
  1124. NULL,
  1125. NULL
  1126. );
  1127. if (RegSetValueExA(
  1128. hKey,
  1129. AnsiValueName,
  1130. 0,
  1131. Type,
  1132. pbValueDataBuffer,
  1133. cbData)
  1134. != ERROR_SUCCESS)
  1135. g_FileErrorStringID = IDS_IMPFILEERRREGSET;
  1136. }
  1137. else
  1138. {
  1139. #endif // UNICODE
  1140. if (RegSetValueEx(hKey, lpszValueName, 0, Type, pbValueDataBuffer, cbData) != ERROR_SUCCESS)
  1141. g_FileErrorStringID = IDS_IMPFILEERRREGSET;
  1142. #ifdef UNICODE
  1143. }
  1144. #endif // UNICODE
  1145. }
  1146. LocalFree(pbValueDataBuffer);
  1147. }
  1148. }
  1149. if (fSkipPastLine)
  1150. {
  1151. SkipPastEndOfLine();
  1152. }
  1153. }
  1154. /*******************************************************************************
  1155. *
  1156. * ParseString
  1157. *
  1158. * DESCRIPTION:
  1159. *
  1160. * PARAMETERS:
  1161. *
  1162. *******************************************************************************/
  1163. BOOL
  1164. NEAR PASCAL
  1165. ParseString(LPPORTVALUEPARAM pPortValueParam)
  1166. {
  1167. TCHAR Char;
  1168. DWORD cbMaxStringData;
  1169. DWORD cbStringData;
  1170. LPTSTR psz = (LPTSTR)pPortValueParam->pbData; // this one is incremented
  1171. cbMaxStringData = pPortValueParam->cbData;
  1172. cbStringData = sizeof(TCHAR); // Account for the null terminator
  1173. while (GetChar(&Char))
  1174. {
  1175. if (cbStringData >= cbMaxStringData)
  1176. {
  1177. // allocate a bigger buffer
  1178. PBYTE pbValueData =
  1179. LocalReAlloc(pPortValueParam->pbData, cbMaxStringData + ALLOCATION_INCR, LMEM_MOVEABLE);
  1180. if (pbValueData)
  1181. {
  1182. pPortValueParam->pbData = pbValueData;
  1183. // incr psz to next char in new buffer
  1184. psz = (LPTSTR)(pPortValueParam->pbData + (cbMaxStringData - sizeof(TCHAR)));
  1185. cbMaxStringData += ALLOCATION_INCR;
  1186. }
  1187. else
  1188. {
  1189. break;
  1190. }
  1191. }
  1192. switch (Char)
  1193. {
  1194. case TEXT('\\'):
  1195. if (!GetChar(&Char))
  1196. return FALSE;
  1197. switch (Char)
  1198. {
  1199. case TEXT('\\'):
  1200. *psz++ = TEXT('\\');
  1201. break;
  1202. case TEXT('"'):
  1203. *psz++ = TEXT('"');
  1204. break;
  1205. case TEXT('r'):
  1206. if(g_fMultiLineStrings)
  1207. {
  1208. *psz++ = TEXT('\r');
  1209. break;
  1210. }
  1211. else
  1212. {
  1213. DebugPrintf(("ParseString: Invalid escape sequence"));
  1214. return FALSE;
  1215. }
  1216. case TEXT('n'):
  1217. if(g_fMultiLineStrings)
  1218. {
  1219. *psz++ = TEXT('\n');
  1220. break;
  1221. }
  1222. else
  1223. {
  1224. DebugPrintf(("ParseString: Invalid escape sequence"));
  1225. return FALSE;
  1226. }
  1227. default:
  1228. DebugPrintf(("ParseString: Invalid escape sequence"));
  1229. return FALSE;
  1230. }
  1231. break;
  1232. case TEXT('"'):
  1233. *psz = 0;
  1234. pPortValueParam->cbData = cbStringData;
  1235. return TRUE;
  1236. default:
  1237. if (IsNewLine(Char))
  1238. return FALSE;
  1239. *psz++ = Char;
  1240. break;
  1241. }
  1242. cbStringData += sizeof(TCHAR);
  1243. }
  1244. return FALSE;
  1245. }
  1246. /*******************************************************************************
  1247. *
  1248. * ParseHexSequence
  1249. *
  1250. * DESCRIPTION:
  1251. *
  1252. * PARAMETERS:
  1253. *
  1254. *******************************************************************************/
  1255. BOOL
  1256. NEAR PASCAL
  1257. ParseHexSequence(LPPORTVALUEPARAM pPortValueParam)
  1258. {
  1259. BOOL fSuccess = TRUE;
  1260. DWORD cbHexData = 0;
  1261. DWORD cbMaxStringData = pPortValueParam->cbData;
  1262. LPBYTE lpHexData = pPortValueParam->pbData;
  1263. do
  1264. {
  1265. if (cbHexData >= cbMaxStringData)
  1266. {
  1267. // allocate a bigger buffer
  1268. PBYTE pbValueData = LocalReAlloc(pPortValueParam->pbData,
  1269. cbMaxStringData + ALLOCATION_INCR, LMEM_MOVEABLE);
  1270. if (pbValueData)
  1271. {
  1272. pPortValueParam->pbData = pbValueData;
  1273. // incr psz to next char in new buffer
  1274. lpHexData = pPortValueParam->pbData + cbMaxStringData;
  1275. cbMaxStringData += ALLOCATION_INCR;
  1276. }
  1277. else
  1278. {
  1279. fSuccess = FALSE;
  1280. break;
  1281. }
  1282. }
  1283. SkipWhitespace();
  1284. if (MatchChar(TEXT('\\')) && !ParseEndOfLine())
  1285. {
  1286. fSuccess = FALSE;
  1287. break;
  1288. }
  1289. SkipWhitespace();
  1290. if (!ParseHexByte(lpHexData++))
  1291. break;
  1292. cbHexData++;
  1293. SkipWhitespace();
  1294. } while (MatchChar(TEXT(',')));
  1295. pPortValueParam->cbData = cbHexData;
  1296. return fSuccess;
  1297. }
  1298. /*******************************************************************************
  1299. *
  1300. * ParseHexDword
  1301. *
  1302. * DESCRIPTION:
  1303. * Parses a one dword hexadecimal string from the registry file stream and
  1304. * converts it to a binary number. A maximum of eight hex digits will be
  1305. * parsed from the stream.
  1306. *
  1307. * PARAMETERS:
  1308. * lpByte, location to store binary number.
  1309. * (returns), TRUE if a hexadecimal dword was parsed, else FALSE.
  1310. *
  1311. *******************************************************************************/
  1312. BOOL
  1313. NEAR PASCAL
  1314. ParseHexDword(
  1315. LPDWORD lpDword
  1316. )
  1317. {
  1318. UINT CountDigits;
  1319. DWORD Dword;
  1320. BYTE Byte;
  1321. Dword = 0;
  1322. CountDigits = 0;
  1323. while (TRUE) {
  1324. if (!ParseHexDigit(&Byte))
  1325. break;
  1326. Dword = (Dword << 4) + (DWORD) Byte;
  1327. if (++CountDigits == 8)
  1328. break;
  1329. }
  1330. *lpDword = Dword;
  1331. return CountDigits != 0;
  1332. }
  1333. /*******************************************************************************
  1334. *
  1335. * ParseHexByte
  1336. *
  1337. * DESCRIPTION:
  1338. * Parses a one byte hexadecimal string from the registry file stream and
  1339. * converts it to a binary number.
  1340. *
  1341. * PARAMETERS:
  1342. * lpByte, location to store binary number.
  1343. * (returns), TRUE if a hexadecimal byte was parsed, else FALSE.
  1344. *
  1345. *******************************************************************************/
  1346. BOOL
  1347. NEAR PASCAL
  1348. ParseHexByte(
  1349. LPBYTE lpByte
  1350. )
  1351. {
  1352. BYTE SecondDigit;
  1353. if (ParseHexDigit(lpByte)) {
  1354. if (ParseHexDigit(&SecondDigit))
  1355. *lpByte = (BYTE) ((*lpByte << 4) | SecondDigit);
  1356. return TRUE;
  1357. }
  1358. else
  1359. return FALSE;
  1360. }
  1361. /*******************************************************************************
  1362. *
  1363. * ParseHexDigit
  1364. *
  1365. * DESCRIPTION:
  1366. * Parses a hexadecimal character from the registry file stream and converts
  1367. * it to a binary number.
  1368. *
  1369. * PARAMETERS:
  1370. * lpDigit, location to store binary number.
  1371. * (returns), TRUE if a hexadecimal digit was parsed, else FALSE.
  1372. *
  1373. *******************************************************************************/
  1374. BOOL
  1375. NEAR PASCAL
  1376. ParseHexDigit(
  1377. LPBYTE lpDigit
  1378. )
  1379. {
  1380. TCHAR Char;
  1381. BYTE Digit;
  1382. if (GetChar(&Char)) {
  1383. if (Char >= TEXT('0') && Char <= TEXT('9'))
  1384. Digit = (BYTE) (Char - TEXT('0'));
  1385. else if (Char >= TEXT('a') && Char <= TEXT('f'))
  1386. Digit = (BYTE) (Char - TEXT('a') + 10);
  1387. else if (Char >= TEXT('A') && Char <= TEXT('F'))
  1388. Digit = (BYTE) (Char - TEXT('A') + 10);
  1389. else {
  1390. UngetChar();
  1391. return FALSE;
  1392. }
  1393. *lpDigit = Digit;
  1394. return TRUE;
  1395. }
  1396. return FALSE;
  1397. }
  1398. /*******************************************************************************
  1399. *
  1400. * ParseEndOfLine
  1401. *
  1402. * DESCRIPTION:
  1403. *
  1404. * PARAMETERS:
  1405. *
  1406. *******************************************************************************/
  1407. BOOL
  1408. NEAR PASCAL
  1409. ParseEndOfLine(
  1410. VOID
  1411. )
  1412. {
  1413. TCHAR Char;
  1414. BOOL fComment;
  1415. BOOL fFoundOneEndOfLine;
  1416. BOOL fEOF;
  1417. fComment = FALSE;
  1418. fFoundOneEndOfLine = FALSE;
  1419. fEOF = TRUE;
  1420. while (GetChar(&Char)) {
  1421. if (IsWhitespace(Char))
  1422. continue;
  1423. if (IsNewLine(Char)) {
  1424. fComment = FALSE;
  1425. fFoundOneEndOfLine = TRUE;
  1426. }
  1427. //
  1428. // Like .INIs and .INFs, comments begin with a semicolon character.
  1429. //
  1430. else if (Char == TEXT(';'))
  1431. fComment = TRUE;
  1432. else if (!fComment) {
  1433. UngetChar();
  1434. fEOF = FALSE;
  1435. break;
  1436. }
  1437. }
  1438. return fFoundOneEndOfLine || fEOF;
  1439. }
  1440. /*******************************************************************************
  1441. *
  1442. * SkipWhitespace
  1443. *
  1444. * DESCRIPTION:
  1445. * Advances the registry file pointer to the first character past any
  1446. * detected whitespace.
  1447. *
  1448. * PARAMETERS:
  1449. * (none).
  1450. *
  1451. *******************************************************************************/
  1452. VOID
  1453. NEAR PASCAL
  1454. SkipWhitespace(
  1455. VOID
  1456. )
  1457. {
  1458. TCHAR Char;
  1459. while (GetChar(&Char)) {
  1460. if (!IsWhitespace(Char)) {
  1461. UngetChar();
  1462. break;
  1463. }
  1464. }
  1465. }
  1466. /*******************************************************************************
  1467. *
  1468. * SkipPastEndOfLine
  1469. *
  1470. * DESCRIPTION:
  1471. * Advances the registry file pointer to the first character past the first
  1472. * detected new line character.
  1473. *
  1474. * PARAMETERS:
  1475. * (none).
  1476. *
  1477. *******************************************************************************/
  1478. VOID
  1479. NEAR PASCAL
  1480. SkipPastEndOfLine(
  1481. VOID
  1482. )
  1483. {
  1484. TCHAR Char;
  1485. while (GetChar(&Char)) {
  1486. if (IsNewLine(Char))
  1487. break;
  1488. }
  1489. while (GetChar(&Char)) {
  1490. if (!IsNewLine(Char)) {
  1491. UngetChar();
  1492. break;
  1493. }
  1494. }
  1495. }
  1496. /*******************************************************************************
  1497. *
  1498. * GetChar
  1499. *
  1500. * DESCRIPTION:
  1501. *
  1502. * PARAMETERS:
  1503. *
  1504. *******************************************************************************/
  1505. BOOL
  1506. NEAR PASCAL
  1507. GetChar(
  1508. PTCHAR lpChar
  1509. )
  1510. {
  1511. DWORD NumberOfBytesRead;
  1512. UINT NewPercentage;
  1513. // If we're at the end of the buffer, read some more.
  1514. // Initially BufferOffset and CharsAvailable will be 0
  1515. if (s_FileIo.BufferOffset == s_FileIo.CharsAvailable) {
  1516. if (TRUE == s_fTreatFileAsUnicode)
  1517. {
  1518. if (!READFILE(s_FileIo.hFile, s_FileIo.Buffer, SIZE_FILE_IO_BUFFER, &NumberOfBytesRead))
  1519. {
  1520. g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
  1521. return FALSE;
  1522. }
  1523. s_FileIo.CharsAvailable = ((int) NumberOfBytesRead / 2);
  1524. }
  1525. else
  1526. {
  1527. if (!READFILE(s_FileIo.hFile, s_FileIo.ConversionBuffer, SIZE_FILE_IO_BUFFER, &NumberOfBytesRead))
  1528. {
  1529. g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
  1530. return FALSE;
  1531. }
  1532. {
  1533. int i;
  1534. i = MultiByteToWideChar(
  1535. CP_THREAD_ACP,
  1536. MB_PRECOMPOSED,
  1537. s_FileIo.ConversionBuffer,
  1538. NumberOfBytesRead,
  1539. s_FileIo.Buffer,
  1540. ARRAYSIZE(s_FileIo.Buffer)
  1541. );
  1542. s_FileIo.CharsAvailable = i;
  1543. }
  1544. }
  1545. s_FileIo.BufferOffset = 0;
  1546. s_FileIo.FileOffset += NumberOfBytesRead;
  1547. if (s_FileIo.FileSizeDiv100 != 0) {
  1548. NewPercentage = ((UINT) (s_FileIo.FileOffset /
  1549. s_FileIo.FileSizeDiv100));
  1550. if (NewPercentage > 100)
  1551. NewPercentage = 100;
  1552. }
  1553. else
  1554. NewPercentage = 100;
  1555. if (s_FileIo.LastPercentage != NewPercentage) {
  1556. s_FileIo.LastPercentage = NewPercentage;
  1557. ImportRegFileUICallback(NewPercentage);
  1558. }
  1559. }
  1560. if (s_FileIo.BufferOffset >= s_FileIo.CharsAvailable)
  1561. return FALSE;
  1562. *lpChar = s_FileIo.Buffer[s_FileIo.BufferOffset++];
  1563. return TRUE;
  1564. }
  1565. /*******************************************************************************
  1566. *
  1567. * UngetChar
  1568. *
  1569. * DESCRIPTION:
  1570. *
  1571. * PARAMETERS:
  1572. *
  1573. *******************************************************************************/
  1574. VOID
  1575. NEAR PASCAL
  1576. UngetChar(
  1577. VOID
  1578. )
  1579. {
  1580. #ifdef DEBUG
  1581. if (s_FileIo.fValidateUngetChar)
  1582. DebugPrintf(("REGEDIT ERROR: Too many UngetChar's called!\n\r"));
  1583. #endif
  1584. s_FileIo.BufferOffset--;
  1585. }
  1586. /*******************************************************************************
  1587. *
  1588. * MatchChar
  1589. *
  1590. * DESCRIPTION:
  1591. *
  1592. * PARAMETERS:
  1593. *
  1594. *******************************************************************************/
  1595. BOOL
  1596. NEAR PASCAL
  1597. MatchChar(
  1598. TCHAR CharToMatch
  1599. )
  1600. {
  1601. BOOL fMatch;
  1602. TCHAR NextChar;
  1603. fMatch = FALSE;
  1604. if (GetChar(&NextChar)) {
  1605. if (CharToMatch == NextChar)
  1606. fMatch = TRUE;
  1607. else
  1608. UngetChar();
  1609. }
  1610. return fMatch;
  1611. }
  1612. /*******************************************************************************
  1613. *
  1614. * IsWhitespace
  1615. *
  1616. * DESCRIPTION:
  1617. * Checks if the given character is whitespace.
  1618. *
  1619. * PARAMETERS:
  1620. * Char, character to check.
  1621. * (returns), TRUE if character is whitespace, else FALSE.
  1622. *
  1623. *******************************************************************************/
  1624. BOOL
  1625. NEAR PASCAL
  1626. IsWhitespace(
  1627. TCHAR Char
  1628. )
  1629. {
  1630. return Char == TEXT(' ') || Char == TEXT('\t');
  1631. }
  1632. /*******************************************************************************
  1633. *
  1634. * IsNewLine
  1635. *
  1636. * DESCRIPTION:
  1637. * Checks if the given character is a new line character.
  1638. *
  1639. * PARAMETERS:
  1640. * Char, character to check.
  1641. * (returns), TRUE if character is a new line, else FALSE.
  1642. *
  1643. *******************************************************************************/
  1644. BOOL
  1645. NEAR PASCAL
  1646. IsNewLine(
  1647. TCHAR Char
  1648. )
  1649. {
  1650. return Char == TEXT('\n') || Char == TEXT('\r');
  1651. }
  1652. /*******************************************************************************
  1653. *
  1654. * ExportWinNT50RegFile
  1655. *
  1656. * DESCRIPTION:
  1657. * Exports an NT 5.0, unicode registry file. Use this export function
  1658. * for all future .reg file writing.
  1659. *
  1660. * PARAMETERS:
  1661. *
  1662. *******************************************************************************/
  1663. VOID ExportWinNT50RegFile(LPTSTR lpFileName, LPTSTR lpSelectedPath)
  1664. {
  1665. HKEY hKey;
  1666. TCHAR SelectedPath[SIZE_SELECTED_PATH];
  1667. HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);
  1668. g_FileErrorStringID = IDS_EXPFILEERRSUCCESS;
  1669. if (lpSelectedPath != NULL &&
  1670. EditRegistryKey(RegEdit_GetComputerItem(hSelectedTreeItem), &hKey, lpSelectedPath, ERK_OPEN)
  1671. != ERROR_SUCCESS)
  1672. {
  1673. g_FileErrorStringID = IDS_EXPFILEERRBADREGPATH;
  1674. return;
  1675. }
  1676. if (OPENWRITEFILE(lpFileName, s_FileIo.hFile))
  1677. {
  1678. DWORD dwNumberOfBytesWritten;
  1679. s_FileIo.BufferOffset = 0;
  1680. s_FileIo.CurrentColumn = 0;
  1681. WRITEFILE(s_FileIo.hFile, &s_UnicodeByteOrderMark, sizeof(s_UnicodeByteOrderMark), &dwNumberOfBytesWritten);
  1682. PutLiteral(s_WinNT50RegFileHeader);
  1683. PutLiteral(TEXT(" "));
  1684. PutLiteral(s_WinNT50RegFileVersion);
  1685. PutLiteral(TEXT("\n\n"));
  1686. if (lpSelectedPath != NULL)
  1687. {
  1688. StringCchCopy(SelectedPath, ARRAYSIZE(SelectedPath), lpSelectedPath);
  1689. PutBranch(hKey, SelectedPath);
  1690. }
  1691. else
  1692. {
  1693. HTREEITEM hComputerItem = RegEdit_GetComputerItem(hSelectedTreeItem);
  1694. StringCchCopy(SelectedPath, ARRAYSIZE(SelectedPath), g_RegistryRoots[INDEX_HKEY_LOCAL_MACHINE].lpKeyName);
  1695. PutBranch(Regedit_GetRootKeyFromComputer(hComputerItem, SelectedPath), SelectedPath);
  1696. StringCchCopy(SelectedPath, ARRAYSIZE(SelectedPath), g_RegistryRoots[INDEX_HKEY_USERS].lpKeyName);
  1697. PutBranch(Regedit_GetRootKeyFromComputer(hComputerItem, SelectedPath), SelectedPath);
  1698. }
  1699. FlushIoBuffer();
  1700. CloseHandle(s_FileIo.hFile);
  1701. }
  1702. else
  1703. g_FileErrorStringID = IDS_EXPFILEERRFILEOPEN;
  1704. if (lpSelectedPath != NULL)
  1705. RegCloseKey(hKey);
  1706. }
  1707. //------------------------------------------------------------------------------
  1708. // ExportRegedt32File
  1709. //
  1710. // DESCRIPTION: This function exports file in the format of Regedt32.
  1711. // The binary is key is stored to a file without any key path info
  1712. //
  1713. // PARAMETERS: LPTSTR lpFileName
  1714. // LPTSTR lpSelectedPath
  1715. //------------------------------------------------------------------------------
  1716. void ExportRegedt32File(LPTSTR lpFileName, LPTSTR lpSelectedPath)
  1717. {
  1718. HKEY hKey = NULL;
  1719. HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);
  1720. g_FileErrorStringID = IDS_EXPFILEERRSUCCESS;
  1721. if (lpSelectedPath == NULL || EditRegistryKey(RegEdit_GetComputerItem(hSelectedTreeItem),
  1722. &hKey, lpSelectedPath, ERK_OPEN) == ERROR_SUCCESS)
  1723. {
  1724. HRESULT hr;
  1725. RegEdit_SetPrivilege(SE_BACKUP_NAME, TRUE);
  1726. //
  1727. // If a file with the same name already exists, we need to delete
  1728. // it ourselves before calling RegSaveKey or that call will fail.
  1729. //
  1730. DeleteFile(lpFileName);
  1731. if ((hr = RegSaveKey(hKey, lpFileName, NULL)) != ERROR_SUCCESS)
  1732. {
  1733. switch (hr)
  1734. {
  1735. case ERROR_PRIVILEGE_NOT_HELD:
  1736. g_FileErrorStringID = IDS_EXPFILEERRNOPRIV;
  1737. break;
  1738. case ERROR_INVALID_HANDLE:
  1739. g_FileErrorStringID = IDS_EXPFILEERRINVALID;
  1740. break;
  1741. default:
  1742. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  1743. break;
  1744. }
  1745. // RegSaveKey created an empty file
  1746. DeleteFile(lpFileName);
  1747. }
  1748. RegEdit_SetPrivilege(SE_BACKUP_NAME, FALSE);
  1749. }
  1750. else
  1751. {
  1752. g_FileErrorStringID = IDS_EXPFILEERRBADREGPATH;
  1753. }
  1754. }
  1755. /*******************************************************************************
  1756. *
  1757. * ExportWin40RegFile
  1758. *
  1759. * DESCRIPTION:
  1760. * This function is only kept around to export old, ANSI, regedit 4 .reg
  1761. * files. Don't touch it except to fix bugs. Meddling with this code
  1762. * path will result in .reg files that can't be read by older verions
  1763. * of regedit, which is the whole reason this code path is here. Meddle
  1764. * with ExportWinNT50RegFile if you want to break backwards compatibility.
  1765. *
  1766. * PARAMETERS:
  1767. *
  1768. *******************************************************************************/
  1769. VOID ExportWin40RegFile(LPTSTR lpFileName, LPTSTR lpSelectedPath)
  1770. {
  1771. HKEY hKey;
  1772. TCHAR SelectedPath[SIZE_SELECTED_PATH];
  1773. HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);
  1774. g_FileErrorStringID = IDS_EXPFILEERRSUCCESS;
  1775. if (lpSelectedPath != NULL && EditRegistryKey(RegEdit_GetComputerItem(hSelectedTreeItem),
  1776. &hKey, lpSelectedPath, ERK_OPEN) != ERROR_SUCCESS)
  1777. {
  1778. g_FileErrorStringID = IDS_EXPFILEERRBADREGPATH;
  1779. return;
  1780. }
  1781. if (OPENWRITEFILE(lpFileName, s_FileIo.hFile))
  1782. {
  1783. s_FileIo.BufferOffset = 0;
  1784. s_FileIo.CurrentColumn = 0;
  1785. PutLiteral(s_Win40RegFileHeader);
  1786. if (lpSelectedPath != NULL)
  1787. {
  1788. StringCchCopy(SelectedPath, ARRAYSIZE(SelectedPath), lpSelectedPath);
  1789. PutBranch(hKey, SelectedPath);
  1790. }
  1791. else
  1792. {
  1793. StringCchCopy(SelectedPath, ARRAYSIZE(SelectedPath), g_RegistryRoots[INDEX_HKEY_LOCAL_MACHINE].lpKeyName);
  1794. PutBranch(HKEY_LOCAL_MACHINE, SelectedPath);
  1795. StringCchCopy(SelectedPath, ARRAYSIZE(SelectedPath), g_RegistryRoots[INDEX_HKEY_USERS].lpKeyName);
  1796. PutBranch(HKEY_USERS, SelectedPath);
  1797. }
  1798. FlushIoBuffer();
  1799. CloseHandle(s_FileIo.hFile);
  1800. }
  1801. else
  1802. g_FileErrorStringID = IDS_EXPFILEERRFILEOPEN;
  1803. if (lpSelectedPath != NULL)
  1804. RegCloseKey(hKey);
  1805. }
  1806. /*******************************************************************************
  1807. *
  1808. * PutBranch
  1809. *
  1810. * DESCRIPTION:
  1811. * Writes out all of the value names and their data and recursively calls
  1812. * this routine for all of the key's subkeys to the registry file stream.
  1813. *
  1814. * PARAMETERS:
  1815. * hKey, registry key to write to file.
  1816. * lpFullKeyName, string that gives the full path, including the root key
  1817. * name, of the hKey.
  1818. *
  1819. *******************************************************************************/
  1820. VOID
  1821. NEAR PASCAL
  1822. PutBranch(
  1823. HKEY hKey,
  1824. LPTSTR lpFullKeyName
  1825. )
  1826. {
  1827. HKEY hSubKey;
  1828. LONG RegError;
  1829. DWORD EnumIndex;
  1830. DWORD cchValueName;
  1831. DWORD cbValueData;
  1832. DWORD Type;
  1833. LPTSTR lpSubKeyName;
  1834. LPTSTR lpTempFullKeyName;
  1835. int nLenTempFullKey;
  1836. int nLenFullKey;
  1837. //
  1838. // Write out the section header.
  1839. //
  1840. PutChar(TEXT('['));
  1841. PutLiteral(lpFullKeyName);
  1842. PutLiteral(TEXT("]\n"));
  1843. //
  1844. // Write out all of the value names and their data.
  1845. //
  1846. EnumIndex = 0;
  1847. while (TRUE)
  1848. {
  1849. PBYTE pbValueData;
  1850. cchValueName = ARRAYSIZE(g_ValueNameBuffer);
  1851. // VALUE DATA
  1852. // Query for data size
  1853. RegError = RegEnumValue(hKey, EnumIndex++, g_ValueNameBuffer,
  1854. &cchValueName, NULL, &Type, NULL, &cbValueData);
  1855. if (RegError != ERROR_SUCCESS)
  1856. {
  1857. break;
  1858. }
  1859. // allocate memory for data
  1860. pbValueData = LocalAlloc(LPTR, cbValueData+ExtraAllocLen(Type));
  1861. if (pbValueData)
  1862. {
  1863. if (RegEdit_QueryValueEx(hKey, g_ValueNameBuffer,
  1864. NULL, &Type, pbValueData, &cbValueData) !=
  1865. ERROR_SUCCESS)
  1866. {
  1867. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  1868. }
  1869. else
  1870. {
  1871. //
  1872. // If cbValueName is zero, then this is the default value of
  1873. // the key, or the Windows 3.1 compatible key value.
  1874. //
  1875. if (cchValueName)
  1876. PutString(g_ValueNameBuffer);
  1877. else
  1878. PutChar(TEXT('@'));
  1879. PutChar(TEXT('='));
  1880. switch (Type)
  1881. {
  1882. case REG_SZ:
  1883. PutString((LPTSTR) pbValueData);
  1884. break;
  1885. case REG_DWORD:
  1886. if (cbValueData == sizeof(DWORD))
  1887. {
  1888. PutLiteral(s_DwordPrefix);
  1889. PutDword(*((LPDWORD) pbValueData), TRUE);
  1890. break;
  1891. }
  1892. // FALL THROUGH
  1893. case REG_BINARY:
  1894. default:
  1895. PutBinary((LPBYTE) pbValueData, Type, cbValueData);
  1896. break;
  1897. }
  1898. PutChar(TEXT('\n'));
  1899. }
  1900. LocalFree(pbValueData);
  1901. }
  1902. else
  1903. {
  1904. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  1905. }
  1906. if (g_FileErrorStringID == IDS_EXPFILEERRFILEWRITE)
  1907. return;
  1908. }
  1909. PutChar(TEXT('\n'));
  1910. if (RegError != ERROR_NO_MORE_ITEMS)
  1911. g_FileErrorStringID = IDS_EXPFILEERRREGENUM;
  1912. //
  1913. // Write out all of the subkeys and recurse into them.
  1914. //
  1915. //copy the existing key into a new buffer with enough room for the next key
  1916. nLenFullKey = lstrlen(lpFullKeyName);
  1917. nLenTempFullKey = nLenFullKey + MAXKEYNAME;
  1918. __try
  1919. {
  1920. lpTempFullKeyName = (LPTSTR) alloca(nLenTempFullKey * sizeof(TCHAR));
  1921. }
  1922. __except(GetExceptionCode() == STATUS_STACK_OVERFLOW)
  1923. {
  1924. _resetstkoflw();
  1925. return;
  1926. }
  1927. StringCchCopy(lpTempFullKeyName, nLenTempFullKey, lpFullKeyName);
  1928. lpSubKeyName = lpTempFullKeyName + nLenFullKey;
  1929. *lpSubKeyName++ = TEXT('\\');
  1930. *lpSubKeyName = 0;
  1931. EnumIndex = 0;
  1932. while (TRUE)
  1933. {
  1934. if ((RegError = RegEnumKey(hKey, EnumIndex++, lpSubKeyName, MAXKEYNAME-1)) != ERROR_SUCCESS)
  1935. break;
  1936. if(RegOpenKeyEx(hKey,lpSubKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,&hSubKey) == ERROR_SUCCESS)
  1937. {
  1938. PutBranch(hSubKey, lpTempFullKeyName);
  1939. RegCloseKey(hSubKey);
  1940. if (g_FileErrorStringID == IDS_EXPFILEERRFILEWRITE)
  1941. return;
  1942. }
  1943. else
  1944. g_FileErrorStringID = IDS_EXPFILEERRREGOPEN;
  1945. }
  1946. if (RegError != ERROR_NO_MORE_ITEMS)
  1947. g_FileErrorStringID = IDS_EXPFILEERRREGENUM;
  1948. }
  1949. /*******************************************************************************
  1950. *
  1951. * PutLiteral
  1952. *
  1953. * DESCRIPTION:
  1954. * Writes a literal string to the registry file stream. No special handling
  1955. * is done for the string-- it is written out as is.
  1956. *
  1957. * PARAMETERS:
  1958. * lpLiteral, null-terminated literal to write to file.
  1959. *
  1960. *******************************************************************************/
  1961. VOID
  1962. NEAR PASCAL
  1963. PutLiteral(
  1964. LPCTSTR lpLiteral
  1965. )
  1966. {
  1967. while (*lpLiteral != 0)
  1968. PutChar(*lpLiteral++);
  1969. }
  1970. /*******************************************************************************
  1971. *
  1972. * PutString
  1973. *
  1974. * DESCRIPTION:
  1975. * Writes a string to the registry file stream. A string is surrounded by
  1976. * double quotes and some characters may be translated to escape sequences
  1977. * to enable a parser to read the string back in.
  1978. *
  1979. * PARAMETERS:
  1980. * lpString, null-terminated string to write to file.
  1981. *
  1982. *******************************************************************************/
  1983. VOID
  1984. NEAR PASCAL
  1985. PutString(
  1986. LPCTSTR lpString
  1987. )
  1988. {
  1989. TCHAR Char;
  1990. PutChar(TEXT('"'));
  1991. while ((Char = *lpString++) != 0) {
  1992. switch (Char)
  1993. {
  1994. case TEXT('\r'):
  1995. if(g_fMultiLineStrings)
  1996. {
  1997. PutChar('\\');
  1998. PutChar('r');
  1999. }
  2000. else
  2001. {
  2002. PutChar(Char);
  2003. }
  2004. break;
  2005. case TEXT('\n'):
  2006. if(g_fMultiLineStrings)
  2007. {
  2008. PutChar('\\');
  2009. PutChar('n');
  2010. }
  2011. else
  2012. {
  2013. PutChar(Char);
  2014. }
  2015. break;
  2016. case TEXT('\\'):
  2017. case TEXT('"'):
  2018. PutChar(TEXT('\\'));
  2019. // FALL THROUGH
  2020. default:
  2021. PutChar(Char);
  2022. break;
  2023. }
  2024. }
  2025. PutChar(TEXT('"'));
  2026. }
  2027. /*******************************************************************************
  2028. *
  2029. * PutBinary
  2030. *
  2031. * DESCRIPTION:
  2032. * Writes a sequence of hexadecimal bytes to the registry file stream. The
  2033. * output is formatted such that it doesn't exceed a defined line length.
  2034. *
  2035. * PARAMETERS:
  2036. * lpBuffer, bytes to write to file.
  2037. * Type, value data type.
  2038. * cbBytes, number of bytes to write.
  2039. *
  2040. *******************************************************************************/
  2041. VOID
  2042. NEAR PASCAL
  2043. PutBinary(
  2044. CONST BYTE FAR* lpBuffer,
  2045. DWORD Type,
  2046. DWORD cbBytes
  2047. )
  2048. {
  2049. BOOL fFirstByteOnLine;
  2050. BYTE Byte;
  2051. // If we're writing one of the string formats that regedit doesn't write
  2052. // natively (but rather converts to a string of hex digits for streaming
  2053. // out), AND we're writing in downlevel/ANSI/REGEDIT4 format, we aren't
  2054. // going to write out the high byte of each (internally Unicode) character.
  2055. // So we will be writing half as many characters as the buffer byte size.
  2056. if ((g_RegEditData.uExportFormat == FILE_TYPE_REGEDIT4) &&
  2057. ((Type == REG_EXPAND_SZ) || (Type == REG_MULTI_SZ))) {
  2058. cbBytes = cbBytes / 2;
  2059. }
  2060. PutLiteral(s_HexPrefix);
  2061. if (Type != REG_BINARY) {
  2062. PutChar(TEXT('('));
  2063. PutDword(Type, FALSE);
  2064. PutChar(TEXT(')'));
  2065. }
  2066. PutChar(TEXT(':'));
  2067. fFirstByteOnLine = TRUE;
  2068. while (cbBytes--) {
  2069. if (s_FileIo.CurrentColumn > 75 && !fFirstByteOnLine) {
  2070. PutLiteral(s_FileLineBreak);
  2071. fFirstByteOnLine = TRUE;
  2072. }
  2073. if (!fFirstByteOnLine)
  2074. PutChar(TEXT(','));
  2075. Byte = *lpBuffer++;
  2076. // If we're writing one of the string formats that regedit doesn't
  2077. // write natively (REG_EXPAND_SZ and REG_MULTI_SZ values get converted
  2078. // to a string of hex digits for streaming out), AND we're writing in
  2079. // downlevel/ANSI/REGEDIT4 format, we don't want to write out the high
  2080. // byte of each (internally Unicode) character. So in those cases, we
  2081. // advance another byte to get to the next ANSI character. Yes, this
  2082. // will lose data on non-SBCS characters, but that's what you get for
  2083. // saving in the downlevel format.
  2084. if ((g_RegEditData.uExportFormat == FILE_TYPE_REGEDIT4) &&
  2085. ((Type == REG_EXPAND_SZ) || (Type == REG_MULTI_SZ))) {
  2086. lpBuffer++;
  2087. }
  2088. PutChar(g_HexConversion[Byte >> 4]);
  2089. PutChar(g_HexConversion[Byte & 0x0F]);
  2090. fFirstByteOnLine = FALSE;
  2091. }
  2092. }
  2093. /*******************************************************************************
  2094. *
  2095. * PutChar
  2096. *
  2097. * DESCRIPTION:
  2098. * Writes a 32-bit word to the registry file stream.
  2099. *
  2100. * PARAMETERS:
  2101. * Dword, dword to write to file.
  2102. *
  2103. *******************************************************************************/
  2104. VOID
  2105. NEAR PASCAL
  2106. PutDword(
  2107. DWORD Dword,
  2108. BOOL fLeadingZeroes
  2109. )
  2110. {
  2111. int CurrentNibble;
  2112. TCHAR Char;
  2113. BOOL fWroteNonleadingChar;
  2114. fWroteNonleadingChar = fLeadingZeroes;
  2115. for (CurrentNibble = 7; CurrentNibble >= 0; CurrentNibble--) {
  2116. Char = g_HexConversion[(Dword >> (CurrentNibble * 4)) & 0x0F];
  2117. if (fWroteNonleadingChar || Char != TEXT('0')) {
  2118. PutChar(Char);
  2119. fWroteNonleadingChar = TRUE;
  2120. }
  2121. }
  2122. //
  2123. // We need to write at least one character, so if we haven't written
  2124. // anything yet, just spit out one zero.
  2125. //
  2126. if (!fWroteNonleadingChar)
  2127. PutChar(TEXT('0'));
  2128. }
  2129. /*******************************************************************************
  2130. *
  2131. * PutChar
  2132. *
  2133. * DESCRIPTION:
  2134. * Writes one character to the registry file stream using an intermediate
  2135. * buffer.
  2136. *
  2137. * PARAMETERS:
  2138. * Char, character to write to file.
  2139. *
  2140. *******************************************************************************/
  2141. VOID
  2142. NEAR PASCAL
  2143. PutChar(
  2144. TCHAR Char
  2145. )
  2146. {
  2147. //
  2148. // Keep track of what column we're currently at. This is useful in cases
  2149. // such as writing a large binary registry record. Instead of writing one
  2150. // very long line, the other Put* routines can break up their output.
  2151. //
  2152. if (Char != TEXT('\n'))
  2153. s_FileIo.CurrentColumn++;
  2154. else {
  2155. //
  2156. // Force a carriage-return, line-feed sequence to keep things like, oh,
  2157. // Notepad happy.
  2158. //
  2159. PutChar(TEXT('\r'));
  2160. s_FileIo.CurrentColumn = 0;
  2161. }
  2162. s_FileIo.Buffer[s_FileIo.BufferOffset++] = Char;
  2163. if (s_FileIo.BufferOffset == SIZE_FILE_IO_BUFFER)
  2164. FlushIoBuffer();
  2165. }
  2166. /*******************************************************************************
  2167. *
  2168. * FlushIoBuffer
  2169. *
  2170. * DESCRIPTION:
  2171. * Flushes the contents of the registry file stream to the disk and resets
  2172. * the buffer pointer.
  2173. *
  2174. * PARAMETERS:
  2175. * (none).
  2176. *
  2177. *******************************************************************************/
  2178. VOID
  2179. NEAR PASCAL
  2180. FlushIoBuffer(
  2181. VOID
  2182. )
  2183. {
  2184. DWORD NumberOfBytesWritten;
  2185. if (s_FileIo.BufferOffset)
  2186. {
  2187. if (g_RegEditData.uExportFormat == FILE_TYPE_REGEDIT4)
  2188. {
  2189. //
  2190. // Convert Unicode to ANSI before writing.
  2191. //
  2192. int i;
  2193. i = WideCharToMultiByte(
  2194. CP_THREAD_ACP,
  2195. 0,
  2196. s_FileIo.Buffer,
  2197. s_FileIo.BufferOffset,
  2198. s_FileIo.ConversionBuffer,
  2199. sizeof(s_FileIo.ConversionBuffer), // Number of bytes...
  2200. NULL,
  2201. NULL
  2202. );
  2203. if (!WRITEFILE(s_FileIo.hFile, s_FileIo.ConversionBuffer, i, &NumberOfBytesWritten)
  2204. || (DWORD) i != NumberOfBytesWritten)
  2205. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  2206. }
  2207. else
  2208. {
  2209. //
  2210. // Write Unicode text
  2211. //
  2212. if (!WRITEFILE(s_FileIo.hFile, s_FileIo.Buffer, s_FileIo.BufferOffset * sizeof(WCHAR),
  2213. &NumberOfBytesWritten)
  2214. || (DWORD) (s_FileIo.BufferOffset * sizeof(WCHAR)) != NumberOfBytesWritten)
  2215. {
  2216. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  2217. }
  2218. }
  2219. }
  2220. s_FileIo.BufferOffset = 0;
  2221. }