Panda3D
 All Classes Functions Variables Enumerations
windowsRegistry.cxx
00001 // Filename: windowsRegistry.cxx
00002 // Created by:  drose (06Aug01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "windowsRegistry.h"
00016 #include "config_express.h"
00017 #include "textEncoder.h"
00018 
00019 #if defined(WIN32_VC)
00020 #define WIN32_LEAN_AND_MEAN
00021 #include <windows.h>
00022 
00023 ////////////////////////////////////////////////////////////////////
00024 //     Function: WindowsRegistry::set_string_value
00025 //       Access: Published, Static
00026 //  Description: Sets the registry key to the indicated value as a
00027 //               string.  The supplied string value is automatically
00028 //               converted from whatever encoding is set by
00029 //               TextEncoder::set_default_encoding() and written as a
00030 //               Unicode string.  The registry key must already exist
00031 //               prior to calling this function.
00032 ////////////////////////////////////////////////////////////////////
00033 bool WindowsRegistry::
00034 set_string_value(const string &key, const string &name, const string &value,
00035         WindowsRegistry::RegLevel rl)
00036 {
00037   TextEncoder encoder;
00038   wstring wvalue = encoder.decode_text(value);
00039 
00040   bool okflag = true;
00041 
00042   // Now convert the string to Windows' idea of the correct wide-char
00043   // encoding, so we can store it in the registry.  This might well be
00044   // the same string we just decoded from, but it might not.
00045 
00046   // Windows likes to have a null character trailing the string (even
00047   // though we also pass a length).
00048   wvalue += (wchar_t)0;
00049   int mb_result_len =
00050     WideCharToMultiByte(CP_ACP, 0,
00051                         wvalue.data(), wvalue.length(),
00052                         NULL, 0,
00053                         NULL, NULL);
00054   if (mb_result_len == 0) {
00055     express_cat.error()
00056       << "Unable to convert '" << value
00057       << "' from Unicode to MultiByte form.\n";
00058     return false;
00059   }
00060 
00061   char *mb_result = (char *)alloca(mb_result_len);
00062   WideCharToMultiByte(CP_ACP, 0,
00063                       wvalue.data(), wvalue.length(),
00064                       mb_result, mb_result_len,
00065                       NULL, NULL);
00066 
00067   if (express_cat.is_debug()) {
00068     express_cat.debug()
00069       << "Converted '" << value << "' to '" << mb_result
00070       << "' for storing in registry.\n";
00071   }
00072 
00073   return do_set(key, name, REG_SZ, mb_result, mb_result_len, rl);
00074 }
00075 
00076 ////////////////////////////////////////////////////////////////////
00077 //     Function: WindowsRegistry::set_int_value
00078 //       Access: Published, Static
00079 //  Description: Sets the registry key to the indicated value as an
00080 //               integer.  The registry key must already exist prior
00081 //               to calling this function.
00082 ////////////////////////////////////////////////////////////////////
00083 bool WindowsRegistry::
00084 set_int_value(const string &key, const string &name, int value,
00085         WindowsRegistry::RegLevel rl)
00086 {
00087   DWORD dw = value;
00088   return do_set(key, name, REG_DWORD, &dw, sizeof(dw), rl);
00089 }
00090 
00091 ////////////////////////////////////////////////////////////////////
00092 //     Function: WindowsRegistry::get_key_type
00093 //       Access: Published, Static
00094 //  Description: Returns the type of the indicated key, or T_none if
00095 //               the key is not known or is some unsupported type.
00096 ////////////////////////////////////////////////////////////////////
00097 WindowsRegistry::Type WindowsRegistry::
00098 get_key_type(const string &key, const string &name,
00099         WindowsRegistry::RegLevel rl)
00100 {
00101   int data_type;
00102   string data;
00103   if (!do_get(key, name, data_type, data, rl)) {
00104     return T_none;
00105   }
00106 
00107   switch (data_type) {
00108   case REG_SZ:
00109     return T_string;
00110 
00111   case REG_DWORD:
00112     return T_int;
00113 
00114   default:
00115     return T_none;
00116   }
00117 }
00118 
00119 ////////////////////////////////////////////////////////////////////
00120 //     Function: WindowsRegistry::get_string_value
00121 //       Access: Published, Static
00122 //  Description: Returns the value associated with the indicated
00123 //               registry key, assuming it is a string value.  The
00124 //               string value is automatically encoded using
00125 //               TextEncoder::get_default_encoding().  If the key is
00126 //               not defined or is not a string type value,
00127 //               default_value is returned instead.
00128 ////////////////////////////////////////////////////////////////////
00129 string WindowsRegistry::
00130 get_string_value(const string &key, const string &name,
00131                  const string &default_value,
00132                  WindowsRegistry::RegLevel rl)
00133 {
00134   int data_type;
00135   string data;
00136   if (!do_get(key, name, data_type, data, rl)) {
00137     return default_value;
00138   }
00139 
00140   if (data_type != REG_SZ) {
00141     express_cat.warning()
00142       << "Registry key " << key << " does not contain a string value.\n";
00143     return default_value;
00144   }
00145 
00146   // Now we have to decode the MultiByte string to Unicode, and re-encode
00147   // it according to our own encoding.
00148 
00149   if (data.empty()) {
00150     return data;
00151   }
00152 
00153   int wide_result_len =
00154     MultiByteToWideChar(CP_ACP, 0,
00155                         data.data(), data.length(),
00156                         NULL, 0);
00157   if (wide_result_len == 0) {
00158     express_cat.error()
00159       << "Unable to convert '" << data 
00160       << "' from MultiByte to Unicode form.\n";
00161     return data;
00162   }
00163     
00164   wchar_t *wide_result = (wchar_t *)alloca(wide_result_len * sizeof(wchar_t));
00165   MultiByteToWideChar(CP_ACP, 0,
00166                       data.data(), data.length(),
00167                       wide_result, wide_result_len);
00168 
00169   wstring wdata(wide_result, wide_result_len);
00170 
00171   TextEncoder encoder;
00172   string result = encoder.encode_wtext(wdata);
00173 
00174   if (express_cat.is_debug()) {
00175     express_cat.debug()
00176       << "Converted '" << data << "' from registry to '" << result << "'\n";
00177   }
00178 
00179   return result;
00180 }
00181 
00182 ////////////////////////////////////////////////////////////////////
00183 //     Function: WindowsRegistry::get_int_value
00184 //       Access: Published, Static
00185 //  Description: Returns the value associated with the indicated
00186 //               registry key, assuming it is an integer value.  If
00187 //               the key is not defined or is not an integer type
00188 //               value, default_value is returned instead.
00189 ////////////////////////////////////////////////////////////////////
00190 int WindowsRegistry::
00191 get_int_value(const string &key, const string &name, int default_value,
00192         WindowsRegistry::RegLevel rl)
00193 {
00194   int data_type;
00195   string data;
00196   if (!do_get(key, name, data_type, data, rl)) {
00197     return default_value;
00198   }
00199 
00200   if (data_type != REG_DWORD) {
00201     express_cat.warning()
00202       << "Registry key " << key << " does not contain an integer value.\n";
00203     return default_value;
00204   }
00205   
00206   // Now we have a DWORD encoded in a string.
00207   nassertr(data.length() == sizeof(DWORD), default_value);
00208   DWORD dw = *(DWORD *)data.data();
00209   return dw;
00210 }
00211 
00212 ////////////////////////////////////////////////////////////////////
00213 //     Function: WindowsRegistry::do_set
00214 //       Access: Private, Static
00215 //  Description: The internal function to actually make all of the
00216 //               appropriate windows calls to set the registry value.
00217 ////////////////////////////////////////////////////////////////////
00218 bool WindowsRegistry::
00219 do_set(const string &key, const string &name,
00220        int data_type, const void *data, int data_length,
00221        const WindowsRegistry::RegLevel rl)
00222 {
00223   HKEY hkey, regkey = HKEY_LOCAL_MACHINE;
00224   LONG error;
00225 
00226   if (rl == rl_user)    // switch to user local settings
00227       regkey = HKEY_CURRENT_USER;
00228 
00229   error =
00230     RegOpenKeyEx(regkey, key.c_str(), 0, KEY_SET_VALUE, &hkey);
00231   if (error != ERROR_SUCCESS) {
00232     express_cat.error()
00233       << "Unable to open registry key " << key
00234       << ": " << format_message(error) << "\n";
00235     return false;
00236   }
00237 
00238   bool okflag = true;
00239 
00240   error =
00241     RegSetValueEx(hkey, name.c_str(), 0, data_type, 
00242                   (CONST BYTE *)data, data_length);
00243   if (error != ERROR_SUCCESS) {
00244     express_cat.error()
00245       << "Unable to set registry key " << key << " name " << name 
00246       << ": " << format_message(error) << "\n";
00247     okflag = false;
00248   }
00249 
00250   error = RegCloseKey(hkey);
00251   if (error != ERROR_SUCCESS) {
00252     express_cat.warning()
00253       << "Unable to close opened registry key " << key
00254       << ": " << format_message(error) << "\n";
00255   }
00256 
00257   return okflag;
00258 }
00259 
00260 ////////////////////////////////////////////////////////////////////
00261 //     Function: WindowsRegistry::do_get
00262 //       Access: Private, Static
00263 //  Description: The internal function to actually make all of the
00264 //               appropriate windows calls to retrieve the registry
00265 //               value.
00266 ////////////////////////////////////////////////////////////////////
00267 bool WindowsRegistry::
00268 do_get(const string &key, const string &name, int &data_type, string &data,
00269        const WindowsRegistry::RegLevel rl)
00270 {
00271   HKEY hkey, regkey = HKEY_LOCAL_MACHINE;
00272   LONG error;
00273 
00274   if (rl == rl_user)    // switch to user local settings
00275       regkey = HKEY_CURRENT_USER;
00276 
00277   error =
00278     RegOpenKeyEx(regkey, key.c_str(), 0, KEY_QUERY_VALUE, &hkey);
00279   if (error != ERROR_SUCCESS) {
00280     express_cat.debug()
00281       << "Unable to open registry key " << key
00282       << ": " << format_message(error) << "\n";
00283     return false;
00284   }
00285 
00286   bool okflag = true;
00287 
00288   // We start with a 1K buffer; presumably that will be big enough
00289   // most of the time.
00290   static const size_t init_buffer_size = 1024;
00291   char init_buffer[init_buffer_size];
00292   DWORD buffer_size = init_buffer_size;
00293   DWORD dw_data_type;
00294 
00295   error =
00296     RegQueryValueEx(hkey, name.c_str(), 0, &dw_data_type, 
00297                     (BYTE *)init_buffer, &buffer_size);
00298   if (error == ERROR_SUCCESS) {
00299     data_type = dw_data_type;
00300     if (data_type == REG_SZ || 
00301         data_type == REG_MULTI_SZ || 
00302         data_type == REG_EXPAND_SZ) {
00303       // Eliminate the trailing null character for non-zero lengths.
00304       if (buffer_size > 0)              // if zero, leave it
00305         buffer_size--;
00306     }
00307     data = string(init_buffer, buffer_size);
00308 
00309   } else if (error == ERROR_MORE_DATA) {
00310     // Huh, 1K wasn't big enough.  Ok, get a bigger buffer.
00311 
00312     // If we were querying HKEY_PERFORMANCE_DATA, we'd have to keep
00313     // guessing bigger and bigger until we got it.  Since we're
00314     // querying static data for now, we can just use the size Windows
00315     // tells us.
00316     char *new_buffer = (char *)PANDA_MALLOC_ARRAY(buffer_size);
00317     error =
00318       RegQueryValueEx(hkey, name.c_str(), 0, &dw_data_type, 
00319                       (BYTE *)new_buffer, &buffer_size);
00320     if (error == ERROR_SUCCESS) {
00321       data_type = dw_data_type;
00322       if (data_type == REG_SZ || 
00323           data_type == REG_MULTI_SZ || 
00324           data_type == REG_EXPAND_SZ) {
00325         // Eliminate the trailing null character for non-zero lengths.
00326         if (buffer_size > 0)            // if zero, leave it
00327           buffer_size--;
00328       }
00329       data = string(new_buffer, buffer_size);
00330     }
00331     PANDA_FREE_ARRAY(new_buffer);
00332   }
00333 
00334   if (error != ERROR_SUCCESS) {
00335     express_cat.debug()
00336       << "Unable to get registry value " << name 
00337       << ": " << format_message(error) << "\n";
00338     okflag = false;
00339   }
00340 
00341   error = RegCloseKey(hkey);
00342   if (error != ERROR_SUCCESS) {
00343     express_cat.warning()
00344       << "Unable to close opened registry key " << key
00345       << ": " << format_message(error) << "\n";
00346   }
00347 
00348   if (okflag) {
00349     if (data_type == REG_EXPAND_SZ) {
00350       // Expand the string.
00351       DWORD destSize=ExpandEnvironmentStrings(data.c_str(), 0, 0);
00352       char *dest = (char *)PANDA_MALLOC_ARRAY(destSize);
00353       ExpandEnvironmentStrings(data.c_str(), dest, destSize);
00354       data = dest;
00355       PANDA_FREE_ARRAY(dest);
00356       data_type = REG_SZ;
00357     }
00358   }
00359 
00360   return okflag;
00361 }
00362 
00363 ////////////////////////////////////////////////////////////////////
00364 //     Function: WindowsRegistry::format_message
00365 //       Access: Private, Static
00366 //  Description: Returns the Windows error message associated with the
00367 //               given error code.
00368 ////////////////////////////////////////////////////////////////////
00369 string WindowsRegistry::
00370 format_message(int error_code) {
00371   PVOID buffer;
00372   DWORD length = 
00373     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00374                   NULL, error_code, 0, (LPTSTR)&buffer, 0, NULL);
00375   if (length == 0) {
00376     return "Unknown error message";
00377   }
00378 
00379   const char *text = (const char *)buffer;
00380 
00381   // Strip off \n's and \r's trailing the string.
00382   while (length > 0 && 
00383          (text[length - 1] == '\r' || text[length - 1] == '\n')) {
00384     length--;
00385   }
00386 
00387   string result((const char *)text, length);
00388   LocalFree(buffer);
00389   return result;
00390 }
00391 
00392 #endif
00393 
00394 
00395 
 All Classes Functions Variables Enumerations