1 ///////////////////////////////////////////////////////////////////////////////
\r
3 // Module: WriteRegistry.cpp
\r
5 // Desc: Functions to write a registry hive to a file.
\r
7 // Copyright (c) 2003 Grant McDorman
\r
8 // This file is licensed using a BSD-type license:
\r
9 // This software is provided 'as-is', without any express or implied
\r
10 // warranty. In no event will the authors be held liable for any damages
\r
11 // arising from the use of this software.
\r
13 // Permission is granted to anyone to use this software for any purpose,
\r
14 // including commercial applications, and to alter it and redistribute it
\r
15 // freely, subject to the following restrictions:
\r
17 // 1. The origin of this software must not be misrepresented; you must not
\r
18 // claim that you wrote the original software. If you use this software
\r
19 // in a product, an acknowledgment in the product documentation would be
\r
20 // appreciated but is not required.
\r
21 // 2. Altered source versions must be plainly marked as such, and must not be
\r
22 // misrepresented as being the original software.
\r
23 // 3. This notice may not be removed or altered from any source distribution.
\r
25 ///////////////////////////////////////////////////////////////////////////////
\r
28 #include <windows.h> // CreateFile, WriteFile, CloseHandle, DeleteFile, Reg* functions
\r
29 #include <stdio.h> // _snprintf
\r
31 #include "WriteRegistry.h"
\r
33 static bool WriteRegValue(HANDLE hFile, const char *key_path, const char *name, int name_len, DWORD type, const unsigned char *data, DWORD data_len);
\r
34 static bool WriteValuesAndSubkeys(const char *key_path, HKEY parent_key, const char *subkey, HANDLE hFile);
\r
35 static void WriteFileString(HANDLE hFile, const char *string);
\r
37 bool WriteRegistryTreeToFile(const char *key, const char *filename)
\r
39 const char *cp = strchr(key, '\\');
\r
43 ptrdiff_t len = cp - key;
\r
46 #define IS_PATH(id, short_id) if (strncmp(key, #id, len) == 0 || strncmp(key, #short_id, len) == 0) hKey = id
\r
47 IS_PATH(HKEY_CLASSES_ROOT, HKCR);
\r
48 else IS_PATH(HKEY_CURRENT_USER, HKCU);
\r
49 else IS_PATH(HKEY_LOCAL_MACHINE, HKLM);
\r
50 else IS_PATH(HKEY_CURRENT_CONFIG, HKCC);
\r
51 else IS_PATH(HKEY_USERS, HKU);
\r
52 else IS_PATH(HKEY_PERFORMANCE_DATA, HKPD);
\r
53 else IS_PATH(HKEY_DYN_DATA, HKDD);
\r
57 return WriteRegistryTreeToFile(hKey, cp + 1, filename);
\r
60 bool WriteRegistryTreeToFile(HKEY section, const char *subkey, const char *filename)
\r
62 bool status = false;
\r
63 HANDLE hFile = ::CreateFile(
\r
65 GENERIC_READ | GENERIC_WRITE,
\r
66 FILE_SHARE_READ | FILE_SHARE_WRITE,
\r
69 FILE_ATTRIBUTE_NORMAL,
\r
71 if (INVALID_HANDLE_VALUE != hFile) {
\r
72 char * key_path = "UNKNOWN";
\r
73 #define SET_PATH(id) if (id == section) key_path = #id
\r
74 SET_PATH(HKEY_CLASSES_ROOT);
\r
75 else SET_PATH(HKEY_CURRENT_USER);
\r
76 else SET_PATH(HKEY_LOCAL_MACHINE);
\r
77 else SET_PATH(HKEY_CURRENT_CONFIG);
\r
78 else SET_PATH(HKEY_USERS);
\r
79 else SET_PATH(HKEY_PERFORMANCE_DATA);
\r
80 else SET_PATH(HKEY_DYN_DATA);
\r
81 WriteFileString(hFile, "REGEDIT4\r\n");
\r
84 status = WriteValuesAndSubkeys(key_path, section, subkey, hFile);
\r
90 DeleteFile(filename);
\r
96 static bool WriteValuesAndSubkeys(const char *key_path, HKEY parent_key, const char *subkey, HANDLE hFile)
\r
100 if (RegOpenKeyEx(parent_key, subkey, 0, KEY_READ, &key) != ERROR_SUCCESS) {
\r
101 OutputDebugString("RegOpenKeyEx failed, key:\n");
\r
102 OutputDebugString(subkey);
\r
106 DWORD max_subkey_len;
\r
108 DWORD max_name_len;
\r
109 DWORD max_value_len;
\r
112 if (RegQueryInfoKey(key,
\r
116 &num_subkeys, &max_subkey_len,
\r
117 NULL, // MaxClassLen
\r
118 &num_values, &max_name_len, &max_value_len, NULL, NULL) != ERROR_SUCCESS) {
\r
119 OutputDebugString("RegQueryInfoKey failed, key:\n");
\r
120 OutputDebugString(subkey);
\r
124 max_id_len = (max_name_len > max_subkey_len) ? max_name_len : max_subkey_len;
\r
125 char *this_path = reinterpret_cast<char *>(alloca(strlen(key_path) + strlen(subkey) + 2));
\r
126 // strcpy/strcat safe because of above alloca
\r
127 strcpy(this_path, key_path);
\r
128 strcat(this_path, "\\");
\r
129 strcat(this_path, subkey);
\r
131 WriteFileString(hFile, "\r\n[");
\r
132 WriteFileString(hFile, this_path);
\r
133 WriteFileString(hFile, "]\r\n");
\r
135 // enumerate values
\r
136 char *name = reinterpret_cast<char *>(alloca(max_id_len*2 + 2));
\r
137 unsigned char *data = reinterpret_cast<unsigned char *>(alloca(max_value_len*2 + 2));
\r
139 bool status = true;
\r
141 for (index = 0; index < num_values && status; index++) {
\r
142 DWORD name_len = max_id_len + 1;
\r
143 DWORD value_len = max_value_len + 1;
\r
145 if (RegEnumValue(key, index, name, &name_len, NULL, &type, data, &value_len) == ERROR_SUCCESS) {
\r
146 status = WriteRegValue(hFile, this_path, name, name_len, type, data, value_len);
\r
150 // enumerate subkeys
\r
151 for (index = 0; index < num_subkeys && status; index++) {
\r
152 DWORD name_len = max_id_len + 1;
\r
153 if (RegEnumKeyEx(key, index, name, &name_len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
\r
154 status = WriteValuesAndSubkeys(this_path, key, name, hFile);
\r
163 static bool WriteRegValue(HANDLE hFile, const char * /*key_path*/, const char *name, int /* name_len */, DWORD type, const unsigned char *data, DWORD data_len)
\r
165 WriteFileString(hFile, "\"");
\r
166 WriteFileString(hFile, name);
\r
168 char string_type[64];
\r
171 case REG_DWORD: // A 32-bit number.
\r
172 strncpy(string_type, "\"=dword:", sizeof string_type);
\r
175 case REG_SZ: // A null terminated string.
\r
176 strncpy(string_type, "\"=\"", sizeof string_type);
\r
179 case REG_BINARY: // Binary data in any form.
\r
180 strncpy(string_type, "\"=hex:", sizeof string_type);
\r
183 case REG_EXPAND_SZ: // A null-terminated string that contains unexpanded references to environment variables (for example, "%PATH%"). It will be a Unicode or ANSI string depending on whether you use the Unicode or ANSI functions. To expand the environment variable references, use the ExpandEnvironmentStrings function.
\r
184 case REG_LINK: // A Unicode symbolic link. Used internally; applications should not use this type.
\r
185 case REG_MULTI_SZ: // An array of null-terminated strings, terminated by two null characters.
\r
186 case REG_NONE: // No defined value type.
\r
187 case REG_DWORD_BIG_ENDIAN: // A 64-bit number in big-endian format.
\r
188 case REG_RESOURCE_LIST: // A device-driver resource list.
\r
190 _snprintf(string_type, sizeof string_type, "\"=hex(%x):", type);
\r
194 WriteFileString(hFile, string_type);
\r
196 if (type == REG_SZ || type == REG_EXPAND_SZ) {
\r
197 // escape special characters; length includes trailing NUL
\r
199 // don't crash'n'burn if data_len is 0
\r
200 for (i = 0; i < static_cast<int>(data_len) - 1; i++) {
\r
201 if (data[i] == '\\' || data[i] == '"') {
\r
202 WriteFileString(hFile, "\\");
\r
204 if (isprint(data[i])) {
\r
206 if (!WriteFile(hFile, &data[i], 1, &written, NULL) || written != 1) {
\r
210 _snprintf(string_type, sizeof string_type, "\\%02x", data[i]);
\r
211 WriteFileString(hFile, string_type);
\r
214 WriteFileString(hFile, "\"");
\r
215 } else if (type == REG_DWORD) {
\r
216 // write as hex, MSB first
\r
218 for (i = static_cast<int>(data_len) - 1; i >= 0; i--) {
\r
219 _snprintf(string_type, sizeof string_type, "%02x", data[i]);
\r
220 WriteFileString(hFile, string_type);
\r
223 // write as comma-separated hex values
\r
225 for (i = 0; i < data_len; i++) {
\r
226 _snprintf(string_type, sizeof string_type, "%s%02x", i > 0 ? "," : "", data[i]);
\r
227 WriteFileString(hFile, string_type);
\r
228 if (i > 0 && i % 16 == 0) {
\r
229 WriteFileString(hFile, "\r\n");
\r
233 WriteFileString(hFile, "\r\n");
\r
240 static void WriteFileString(HANDLE hFile, const char *string)
\r
243 if (!WriteFile(hFile, string, strlen(string), &written, NULL) || written != strlen(string)) {
\r
244 OutputDebugString("WriteFile failed\n");
\r