Panda3D
windowsRegistry.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file windowsRegistry.cxx
10 * @author drose
11 * @date 2001-08-06
12 */
13
14#include "windowsRegistry.h"
15#include "config_express.h"
16#include "textEncoder.h"
17
18#if defined(WIN32_VC)
19#ifndef WIN32_LEAN_AND_MEAN
20#define WIN32_LEAN_AND_MEAN 1
21#endif
22#include <windows.h>
23
24using std::string;
25
26/**
27 * Sets the registry key to the indicated value as a string. The supplied
28 * string value is automatically converted from whatever encoding is set by
29 * TextEncoder::set_default_encoding() and written as a Unicode string. The
30 * registry key must already exist prior to calling this function.
31 */
32bool WindowsRegistry::
33set_string_value(const string &key, const string &name, const string &value,
34 WindowsRegistry::RegLevel rl)
35{
36 TextEncoder encoder;
37 std::wstring wvalue = encoder.decode_text(value);
38
39 // Now convert the string to Windows' idea of the correct wide-char
40 // encoding, so we can store it in the registry. This might well be the
41 // same string we just decoded from, but it might not.
42
43 // Windows likes to have a null character trailing the string (even though
44 // we also pass a length).
45 wvalue += (wchar_t)0;
46 int mb_result_len =
47 WideCharToMultiByte(CP_ACP, 0,
48 wvalue.data(), wvalue.length(),
49 nullptr, 0,
50 nullptr, nullptr);
51 if (mb_result_len == 0) {
52 express_cat.error()
53 << "Unable to convert '" << value
54 << "' from Unicode to MultiByte form.\n";
55 return false;
56 }
57
58 char *mb_result = (char *)alloca(mb_result_len);
59 WideCharToMultiByte(CP_ACP, 0,
60 wvalue.data(), wvalue.length(),
61 mb_result, mb_result_len,
62 nullptr, nullptr);
63
64 if (express_cat.is_debug()) {
65 express_cat.debug()
66 << "Converted '" << value << "' to '" << mb_result
67 << "' for storing in registry.\n";
68 }
69
70 return do_set(key, name, REG_SZ, mb_result, mb_result_len, rl);
71}
72
73/**
74 * Sets the registry key to the indicated value as an integer. The registry
75 * key must already exist prior to calling this function.
76 */
77bool WindowsRegistry::
78set_int_value(const string &key, const string &name, int value,
79 WindowsRegistry::RegLevel rl)
80{
81 DWORD dw = value;
82 return do_set(key, name, REG_DWORD, &dw, sizeof(dw), rl);
83}
84
85/**
86 * Returns the type of the indicated key, or T_none if the key is not known or
87 * is some unsupported type.
88 */
89WindowsRegistry::Type WindowsRegistry::
90get_key_type(const string &key, const string &name,
91 WindowsRegistry::RegLevel rl)
92{
93 int data_type;
94 string data;
95 if (!do_get(key, name, data_type, data, rl)) {
96 return T_none;
97 }
98
99 switch (data_type) {
100 case REG_SZ:
101 return T_string;
102
103 case REG_DWORD:
104 return T_int;
105
106 default:
107 return T_none;
108 }
109}
110
111/**
112 * Returns the value associated with the indicated registry key, assuming it
113 * is a string value. The string value is automatically encoded using
114 * TextEncoder::get_default_encoding(). If the key is not defined or is not a
115 * string type value, default_value is returned instead.
116 */
117string WindowsRegistry::
118get_string_value(const string &key, const string &name,
119 const string &default_value,
120 WindowsRegistry::RegLevel rl)
121{
122 int data_type;
123 string data;
124 if (!do_get(key, name, data_type, data, rl)) {
125 return default_value;
126 }
127
128 if (data_type != REG_SZ) {
129 express_cat.warning()
130 << "Registry key " << key << " does not contain a string value.\n";
131 return default_value;
132 }
133
134 // Now we have to decode the MultiByte string to Unicode, and re-encode it
135 // according to our own encoding.
136
137 if (data.empty()) {
138 return data;
139 }
140
141 int wide_result_len =
142 MultiByteToWideChar(CP_ACP, 0,
143 data.data(), data.length(),
144 nullptr, 0);
145 if (wide_result_len == 0) {
146 express_cat.error()
147 << "Unable to convert '" << data
148 << "' from MultiByte to Unicode form.\n";
149 return data;
150 }
151
152 wchar_t *wide_result = (wchar_t *)alloca(wide_result_len * sizeof(wchar_t));
153 MultiByteToWideChar(CP_ACP, 0,
154 data.data(), data.length(),
155 wide_result, wide_result_len);
156
157 std::wstring wdata(wide_result, wide_result_len);
158
159 TextEncoder encoder;
160 string result = encoder.encode_wtext(wdata);
161
162 if (express_cat.is_debug()) {
163 express_cat.debug()
164 << "Converted '" << data << "' from registry to '" << result << "'\n";
165 }
166
167 return result;
168}
169
170/**
171 * Returns the value associated with the indicated registry key, assuming it
172 * is an integer value. If the key is not defined or is not an integer type
173 * value, default_value is returned instead.
174 */
175int WindowsRegistry::
176get_int_value(const string &key, const string &name, int default_value,
177 WindowsRegistry::RegLevel rl)
178{
179 int data_type;
180 string data;
181 if (!do_get(key, name, data_type, data, rl)) {
182 return default_value;
183 }
184
185 if (data_type != REG_DWORD) {
186 express_cat.warning()
187 << "Registry key " << key << " does not contain an integer value.\n";
188 return default_value;
189 }
190
191 // Now we have a DWORD encoded in a string.
192 nassertr(data.length() == sizeof(DWORD), default_value);
193 DWORD dw = *(DWORD *)data.data();
194 return dw;
195}
196
197/**
198 * The internal function to actually make all of the appropriate windows calls
199 * to set the registry value.
200 */
201bool WindowsRegistry::
202do_set(const string &key, const string &name,
203 int data_type, const void *data, int data_length,
204 const WindowsRegistry::RegLevel rl)
205{
206 HKEY hkey, regkey = HKEY_LOCAL_MACHINE;
207 LONG error;
208
209 if (rl == rl_user) // switch to user local settings
210 regkey = HKEY_CURRENT_USER;
211
212 error =
213 RegOpenKeyEx(regkey, key.c_str(), 0, KEY_SET_VALUE, &hkey);
214 if (error != ERROR_SUCCESS) {
215 express_cat.error()
216 << "Unable to open registry key " << key
217 << ": " << format_message(error) << "\n";
218 return false;
219 }
220
221 bool okflag = true;
222
223 error =
224 RegSetValueEx(hkey, name.c_str(), 0, data_type,
225 (CONST BYTE *)data, data_length);
226 if (error != ERROR_SUCCESS) {
227 express_cat.error()
228 << "Unable to set registry key " << key << " name " << name
229 << ": " << format_message(error) << "\n";
230 okflag = false;
231 }
232
233 error = RegCloseKey(hkey);
234 if (error != ERROR_SUCCESS) {
235 express_cat.warning()
236 << "Unable to close opened registry key " << key
237 << ": " << format_message(error) << "\n";
238 }
239
240 return okflag;
241}
242
243/**
244 * The internal function to actually make all of the appropriate windows calls
245 * to retrieve the registry value.
246 */
247bool WindowsRegistry::
248do_get(const string &key, const string &name, int &data_type, string &data,
249 const WindowsRegistry::RegLevel rl)
250{
251 HKEY hkey, regkey = HKEY_LOCAL_MACHINE;
252 LONG error;
253
254 if (rl == rl_user) // switch to user local settings
255 regkey = HKEY_CURRENT_USER;
256
257 error =
258 RegOpenKeyEx(regkey, key.c_str(), 0, KEY_QUERY_VALUE, &hkey);
259 if (error != ERROR_SUCCESS) {
260 express_cat.debug()
261 << "Unable to open registry key " << key
262 << ": " << format_message(error) << "\n";
263 return false;
264 }
265
266 bool okflag = true;
267
268 // We start with a 1K buffer; presumably that will be big enough most of the
269 // time.
270 static const size_t init_buffer_size = 1024;
271 char init_buffer[init_buffer_size];
272 DWORD buffer_size = init_buffer_size;
273 DWORD dw_data_type;
274
275 error =
276 RegQueryValueEx(hkey, name.c_str(), 0, &dw_data_type,
277 (BYTE *)init_buffer, &buffer_size);
278 if (error == ERROR_SUCCESS) {
279 data_type = dw_data_type;
280 if (data_type == REG_SZ ||
281 data_type == REG_MULTI_SZ ||
282 data_type == REG_EXPAND_SZ) {
283 // Eliminate the trailing null character for non-zero lengths.
284 if (buffer_size > 0) // if zero, leave it
285 buffer_size--;
286 }
287 data = string(init_buffer, buffer_size);
288
289 } else if (error == ERROR_MORE_DATA) {
290 // Huh, 1K wasn't big enough. Ok, get a bigger buffer.
291
292 // If we were querying HKEY_PERFORMANCE_DATA, we'd have to keep guessing
293 // bigger and bigger until we got it. Since we're querying static data
294 // for now, we can just use the size Windows tells us.
295 char *new_buffer = (char *)PANDA_MALLOC_ARRAY(buffer_size);
296 error =
297 RegQueryValueEx(hkey, name.c_str(), 0, &dw_data_type,
298 (BYTE *)new_buffer, &buffer_size);
299 if (error == ERROR_SUCCESS) {
300 data_type = dw_data_type;
301 if (data_type == REG_SZ ||
302 data_type == REG_MULTI_SZ ||
303 data_type == REG_EXPAND_SZ) {
304 // Eliminate the trailing null character for non-zero lengths.
305 if (buffer_size > 0) // if zero, leave it
306 buffer_size--;
307 }
308 data = string(new_buffer, buffer_size);
309 }
310 PANDA_FREE_ARRAY(new_buffer);
311 }
312
313 if (error != ERROR_SUCCESS) {
314 express_cat.debug()
315 << "Unable to get registry value " << name
316 << ": " << format_message(error) << "\n";
317 okflag = false;
318 }
319
320 error = RegCloseKey(hkey);
321 if (error != ERROR_SUCCESS) {
322 express_cat.warning()
323 << "Unable to close opened registry key " << key
324 << ": " << format_message(error) << "\n";
325 }
326
327 if (okflag) {
328 if (data_type == REG_EXPAND_SZ) {
329 // Expand the string.
330 DWORD destSize=ExpandEnvironmentStrings(data.c_str(), 0, 0);
331 char *dest = (char *)PANDA_MALLOC_ARRAY(destSize);
332 ExpandEnvironmentStrings(data.c_str(), dest, destSize);
333 data = dest;
334 PANDA_FREE_ARRAY(dest);
335 data_type = REG_SZ;
336 }
337 }
338
339 return okflag;
340}
341
342/**
343 * Returns the Windows error message associated with the given error code.
344 */
345string WindowsRegistry::
346format_message(int error_code) {
347 PVOID buffer;
348 DWORD length =
349 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
350 nullptr, error_code, 0, (LPTSTR)&buffer, 0, nullptr);
351 if (length == 0) {
352 return "Unknown error message";
353 }
354
355 const char *text = (const char *)buffer;
356
357 // Strip off \n's and \r's trailing the string.
358 while (length > 0 &&
359 (text[length - 1] == '\r' || text[length - 1] == '\n')) {
360 length--;
361 }
362
363 string result((const char *)text, length);
364 LocalFree(buffer);
365 return result;
366}
367
368#endif
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:33
std::wstring decode_text(const std::string &text) const
Returns the given wstring decoded to a single-byte string, via the current encoding system.
Definition: textEncoder.I:490
std::string encode_wtext(const std::wstring &wtext) const
Encodes a wide-text string into a single-char string, according to the current encoding.
Definition: textEncoder.I:481
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.