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 
24 using 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  */
32 bool WindowsRegistry::
33 set_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  */
77 bool WindowsRegistry::
78 set_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  */
89 WindowsRegistry::Type WindowsRegistry::
90 get_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  */
117 string WindowsRegistry::
118 get_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  */
175 int WindowsRegistry::
176 get_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  */
201 bool WindowsRegistry::
202 do_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  */
247 bool WindowsRegistry::
248 do_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  */
345 string WindowsRegistry::
346 format_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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:33
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
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