WvStreams
uniregistrygen.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2004 Net Integration Technologies, Inc.
00004  * 
00005  * A generator that exposes the windows registry.
00006  */
00007 #include "uniregistrygen.h"
00008 #include "wvmoniker.h"
00009 #include "wvlinkerhack.h"
00010 
00011 WV_LINK(UniRegistryGen);
00012 
00013 
00014 // returns a handle to the key specified by key, or, if key specifies a value,
00015 // a handle to the key containing that value (and setting isValue = true)
00016 static HKEY follow_path(HKEY from, const UniConfKey &key,
00017                         bool create, bool *isValue)
00018 {
00019     const REGSAM samDesired = KEY_READ | KEY_WRITE;
00020     LONG result;
00021     HKEY hLastKey = from; // DuplicateHandle() does not work with regkeys
00022     int n = key.numsegments();
00023 
00024     if (isValue) *isValue = false;
00025 
00026     for (int i=0;i<n;i++)
00027     {
00028         WvString subkey = key.segment(i).printable();
00029         HKEY hNextKey;
00030         
00031         if (create)
00032         {
00033             result = RegCreateKeyEx(hLastKey, subkey, 0, NULL, 0, samDesired, 
00034                 NULL, &hNextKey, NULL);
00035         }
00036         else
00037         {
00038             result = RegOpenKeyEx(hLastKey, subkey, 0, samDesired, &hNextKey);
00039         }
00040 
00041         if ((result == ERROR_FILE_NOT_FOUND) && (i == n-1))
00042         {
00043             WvString xsub(subkey=="." ? WvString::null : subkey);
00044             
00045             // maybe the last segment is a value name
00046             if (RegQueryValueEx(hLastKey, xsub, 0, NULL, NULL, NULL) == ERROR_SUCCESS)
00047             {
00048                 // ... it is a value
00049                 if (isValue) *isValue = true;
00050                 break;
00051             }
00052         }
00053         if (result != ERROR_SUCCESS)
00054         {
00055             return 0;
00056         }
00057         
00058         
00059         if (i > 0)
00060         {
00061             RegCloseKey(hLastKey);
00062         }
00063         hLastKey = hNextKey;
00064     }
00065 
00066     return hLastKey;
00067 }
00068 
00069 
00070 UniRegistryGen::UniRegistryGen(WvString _moniker) :
00071     m_log(_moniker), m_hRoot(0)
00072 {
00073     UniConfKey key = _moniker;
00074     WvString hive = key.first().printable();
00075     if (strcmp("HKEY_CLASSES_ROOT", hive) == 0)
00076     {
00077         m_hRoot = HKEY_CLASSES_ROOT;
00078     } 
00079     else if (strcmp("HKEY_CURRENT_USER", hive) == 0)
00080     {
00081         m_hRoot = HKEY_CURRENT_USER;
00082     }
00083     else if (strcmp("HKEY_LOCAL_MACHINE", hive) == 0)
00084     {
00085         m_hRoot = HKEY_LOCAL_MACHINE;
00086     }
00087     else if (strcmp("HKEY_USERS", hive) == 0)
00088     {
00089         m_hRoot = HKEY_USERS;
00090     }
00091 
00092     m_hRoot = follow_path(m_hRoot, key.range(1, key.numsegments()), true, NULL);
00093 
00094 #if 0
00095     // FIXME: Notifications don't work for external registry changes.
00096     //
00097     hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
00098     RegNotifyChangeKeyValue(
00099         m_hRoot,
00100         TRUE,
00101         REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
00102         REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY,
00103         hEvent,
00104         TRUE
00105     );
00106 #endif
00107 }
00108 
00109 UniRegistryGen::~UniRegistryGen()
00110 {
00111     if (m_hRoot)
00112     {
00113         RegCloseKey(m_hRoot);
00114         m_hRoot = 0;
00115     }
00116 }
00117 
00118 bool UniRegistryGen::isok()
00119 {
00120     return m_hRoot != 0;
00121 }
00122 
00123 WvString UniRegistryGen::get(const UniConfKey &key)
00124 {
00125     WvString retval = WvString::null;
00126     bool isvalue;
00127     HKEY hKey = follow_path(m_hRoot, key, false, &isvalue);
00128 
00129     WvString value;
00130     if (isvalue)
00131     {
00132         // the path ends up at a value so fetch that
00133         value = key.last();
00134         if (value == ".") value = WvString::null;
00135     }
00136     else
00137     {
00138         // the key isn't a value, fetch its default value instead
00139         value = WvString::null;
00140     }
00141     
00142     DWORD type;
00143     TCHAR data[1024];
00144     DWORD size = sizeof(data) / sizeof(data[0]);
00145     LONG result = RegQueryValueEx(
00146         hKey, 
00147         value.cstr(), 
00148         0, 
00149         &type, 
00150         (BYTE *) data, 
00151         &size
00152     );
00153 
00154     if (result == ERROR_SUCCESS)
00155     {
00156         switch (type)
00157         {
00158         case REG_DWORD:
00159             retval.setsize(11);
00160             itoa(*((int *) data), retval.edit(), 10);
00161             break;
00162         case REG_SZ:
00163             retval = data;
00164             break;
00165         default:
00166             break;
00167         };
00168     }
00169 
00170     if (hKey != m_hRoot) RegCloseKey(hKey);
00171     return retval;
00172 }
00173 
00174 void UniRegistryGen::set(const UniConfKey &key, WvStringParm value)
00175 {
00176     LONG result;
00177     HKEY hKey = follow_path(m_hRoot, key.first( key.numsegments()-1 ), true, NULL);
00178     if (hKey)
00179     {
00180         if (value.isnull())
00181         {
00182             result = RegDeleteValue(hKey, key.last().printable());
00183         }
00184         else
00185         {
00186             WvString last = key.last();
00187             if (last == ".") last = WvString::null;
00188             result = RegSetValueEx(
00189                 hKey,
00190                 last,
00191                 0,
00192                 REG_SZ,
00193                 (BYTE *) value.cstr(),
00194                 strlen(value)+1
00195             );
00196         }
00197         if (result == ERROR_SUCCESS)
00198         {
00199             delta(key, value);
00200         }
00201     }
00202     if (hKey != m_hRoot) RegCloseKey(hKey);
00203 }
00204 
00205 void UniRegistryGen::setv(const UniConfPairList &pairs)
00206 {
00207     setv_naive(pairs);
00208 }
00209 
00210 bool UniRegistryGen::exists(const UniConfKey &key)
00211 {
00212     return !get(key).isnull();
00213 }
00214 
00215 bool UniRegistryGen::haschildren(const UniConfKey &key)
00216 {
00217     UniRegistryGenIter iter(*this, key, m_hRoot);
00218     iter.rewind();
00219     return iter.next();
00220 }
00221 
00222 
00223 UniConfGen::Iter *UniRegistryGen::iterator(const UniConfKey &key)
00224 {
00225     return new UniRegistryGenIter(*this, key, m_hRoot);
00226 }
00227 
00228 
00229 UniRegistryGenIter::UniRegistryGenIter(UniRegistryGen &gen,
00230                                        const UniConfKey &key, HKEY base)
00231     : m_hKey(0), m_enumerating(KEYS), m_index(0), gen(gen), parent(key),
00232       m_dontClose(base)
00233 {
00234     bool isValue;
00235     HKEY hKey = follow_path(base, key, false, &isValue);
00236     
00237     // fprintf(stderr, "(iter:%s:%d:%p)\n",
00238     //      key.printable().cstr(), isValue, hKey); fflush(stderr);
00239             
00240     if (isValue)
00241     {
00242         // a value doesn't have subkeys
00243         if (hKey != m_dontClose) RegCloseKey(hKey);
00244         m_enumerating = VALUES;
00245     }
00246     else
00247         m_hKey = hKey;
00248 }
00249 
00250 
00251 UniRegistryGenIter::~UniRegistryGenIter()
00252 {
00253     if (m_hKey && m_hKey != m_dontClose)
00254         RegCloseKey(m_hKey);
00255 }
00256 
00257 
00258 void UniRegistryGenIter::rewind()
00259 {
00260     current_key = "YOU HAVE TO REWIND, DUMMY!";
00261     m_enumerating = KEYS;
00262     m_index = 0;
00263 }
00264 
00265 
00266 bool UniRegistryGenIter::next()
00267 {
00268     if (m_enumerating == KEYS)
00269     {
00270         LONG result = next_key();
00271         if (result == ERROR_SUCCESS)
00272             return true;
00273         else if (result == ERROR_NO_MORE_ITEMS)
00274         {
00275             // done enumerating keys, now enumerate the values
00276             m_enumerating = VALUES;
00277             m_index = 0;
00278         }
00279         else
00280         {
00281             fprintf(stderr, "KEY_ENUM result: %ld\n", result);
00282             fflush(stderr);
00283             return false; // give up
00284         }
00285     }
00286     assert(m_enumerating == VALUES);
00287     LONG result = next_value();
00288     if (result == ERROR_SUCCESS)
00289         return true;
00290     return false;
00291 }
00292 
00293 UniConfKey UniRegistryGenIter::key() const
00294 {
00295     return current_key;
00296 }
00297 
00298 
00299 WvString UniRegistryGenIter::value() const
00300 {
00301     UniConfKey val(parent, current_key);
00302     return gen.get(val);
00303 }
00304 
00305 
00306 LONG UniRegistryGenIter::next_key()
00307 {
00308     if (!m_hKey)
00309         return ERROR_NO_MORE_ITEMS;
00310     
00311     FILETIME dontcare;
00312     TCHAR data[1024];
00313     DWORD size = sizeof(data) / sizeof(data[0]);
00314     LONG result = RegEnumKeyEx(m_hKey, m_index++, data, &size, 0, 0, 0, &dontcare);
00315     if (result == ERROR_SUCCESS)
00316         current_key = data;
00317     return result;
00318 }
00319 
00320 
00321 LONG UniRegistryGenIter::next_value()
00322 {
00323     if (!m_hKey)
00324         return ERROR_NO_MORE_ITEMS;
00325     
00326     TCHAR data[1024] = "";
00327     DWORD size = sizeof(data) / sizeof(data[0]);
00328     while (!*data)
00329     {
00330         LONG result = RegEnumValue(m_hKey, m_index++, data, &size, 0, 0, 0, 0);
00331         if (result != ERROR_SUCCESS)
00332             return result;
00333     }
00334     current_key = data;
00335     return ERROR_SUCCESS;
00336 }
00337 
00338 
00339 static IUniConfGen *creator(WvStringParm s, IObject*)
00340 {
00341     return new UniRegistryGen(s);
00342 }
00343 
00344 #pragma warning(disable : 4073)
00345 #pragma init_seg(lib)
00346 WvMoniker<IUniConfGen> UniRegistryGenMoniker("registry", creator);