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