xpcom/ds/nsWindowsRegKey.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <windows.h>
     8 #include <shlwapi.h>
     9 #include <stdlib.h>
    10 #include "nsWindowsRegKey.h"
    11 #include "nsString.h"
    12 #include "nsCOMPtr.h"
    13 #include "mozilla/Attributes.h"
    14 #include "nsAutoPtr.h"
    16 //-----------------------------------------------------------------------------
    18 // According to MSDN, the following limits apply (in characters excluding room
    19 // for terminating null character):
    20 #define MAX_KEY_NAME_LEN     255
    21 #define MAX_VALUE_NAME_LEN   16383
    23 class nsWindowsRegKey MOZ_FINAL : public nsIWindowsRegKey
    24 {
    25 public:
    26   NS_DECL_ISUPPORTS
    27   NS_DECL_NSIWINDOWSREGKEY
    29   nsWindowsRegKey()
    30     : mKey(nullptr)
    31     , mWatchEvent(nullptr)
    32     , mWatchRecursive(FALSE)
    33   {
    34   }
    36 private:
    37   ~nsWindowsRegKey()
    38   {
    39     Close();
    40   }
    42   HKEY   mKey;
    43   HANDLE mWatchEvent;
    44   BOOL   mWatchRecursive;
    45 };
    47 NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey)
    49 NS_IMETHODIMP
    50 nsWindowsRegKey::GetKey(HKEY *key)
    51 {
    52   *key = mKey;
    53   return NS_OK;
    54 }
    56 NS_IMETHODIMP
    57 nsWindowsRegKey::SetKey(HKEY key)
    58 {
    59   // We do not close the older key!
    60   StopWatching();
    62   mKey = key;
    63   return NS_OK;
    64 }
    66 NS_IMETHODIMP
    67 nsWindowsRegKey::Close()
    68 {
    69   StopWatching();
    71   if (mKey) {
    72     RegCloseKey(mKey);
    73     mKey = nullptr;
    74   }
    75   return NS_OK;
    76 }
    78 NS_IMETHODIMP
    79 nsWindowsRegKey::Open(uint32_t rootKey, const nsAString &path, uint32_t mode)
    80 {
    81   Close();
    83   LONG rv = RegOpenKeyExW((HKEY)(intptr_t) rootKey, PromiseFlatString(path).get(), 0,
    84                           (REGSAM) mode, &mKey);
    86   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
    87 }
    89 NS_IMETHODIMP
    90 nsWindowsRegKey::Create(uint32_t rootKey, const nsAString &path, uint32_t mode)
    91 {
    92   Close();
    94   DWORD disposition;
    95   LONG rv = RegCreateKeyExW((HKEY)(intptr_t) rootKey, PromiseFlatString(path).get(), 0,
    96                             nullptr, REG_OPTION_NON_VOLATILE, (REGSAM) mode, nullptr,
    97                             &mKey, &disposition);
    99   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   100 }
   102 NS_IMETHODIMP
   103 nsWindowsRegKey::OpenChild(const nsAString &path, uint32_t mode,
   104                            nsIWindowsRegKey **result)
   105 {
   106   if (NS_WARN_IF(!mKey))
   107     return NS_ERROR_NOT_INITIALIZED;
   109   nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
   111   nsresult rv = child->Open((uintptr_t) mKey, path, mode);
   112   if (NS_FAILED(rv))
   113     return rv;
   115   child.swap(*result);
   116   return NS_OK;
   117 }
   119 NS_IMETHODIMP
   120 nsWindowsRegKey::CreateChild(const nsAString &path, uint32_t mode,
   121                              nsIWindowsRegKey **result)
   122 {
   123   if (NS_WARN_IF(!mKey))
   124     return NS_ERROR_NOT_INITIALIZED;
   126   nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
   128   nsresult rv = child->Create((uintptr_t) mKey, path, mode);
   129   if (NS_FAILED(rv))
   130     return rv;
   132   child.swap(*result);
   133   return NS_OK;
   134 }
   136 NS_IMETHODIMP
   137 nsWindowsRegKey::GetChildCount(uint32_t *result)
   138 {
   139   if (NS_WARN_IF(!mKey))
   140     return NS_ERROR_NOT_INITIALIZED;
   142   DWORD numSubKeys;
   143   LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys,
   144                              nullptr, nullptr, nullptr, nullptr, nullptr,
   145                              nullptr, nullptr);
   146   if (rv != ERROR_SUCCESS)
   147     return NS_ERROR_FAILURE;
   149   *result = numSubKeys;
   150   return NS_OK;
   151 }
   153 NS_IMETHODIMP
   154 nsWindowsRegKey::GetChildName(uint32_t index, nsAString &result)
   155 {
   156   if (NS_WARN_IF(!mKey))
   157     return NS_ERROR_NOT_INITIALIZED;
   159   FILETIME lastWritten;
   161   wchar_t nameBuf[MAX_KEY_NAME_LEN + 1];
   162   DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
   164   LONG rv = RegEnumKeyExW(mKey, index, nameBuf, &nameLen, nullptr, nullptr,
   165                           nullptr, &lastWritten);
   166   if (rv != ERROR_SUCCESS)
   167     return NS_ERROR_NOT_AVAILABLE;  // XXX what's the best error code here?
   169   result.Assign(nameBuf, nameLen);
   171   return NS_OK;
   172 }
   174 NS_IMETHODIMP
   175 nsWindowsRegKey::HasChild(const nsAString &name, bool *result)
   176 {
   177   if (NS_WARN_IF(!mKey))
   178     return NS_ERROR_NOT_INITIALIZED;
   180   // Check for the existence of a child key by opening the key with minimal
   181   // rights.  Perhaps there is a more efficient way to do this?
   183   HKEY key;
   184   LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(name).get(), 0,
   185                           STANDARD_RIGHTS_READ, &key);
   187   if ((*result = (rv == ERROR_SUCCESS && key)))
   188     RegCloseKey(key);
   190   return NS_OK;
   191 }
   193 NS_IMETHODIMP
   194 nsWindowsRegKey::GetValueCount(uint32_t *result)
   195 {
   196   if (NS_WARN_IF(!mKey))
   197     return NS_ERROR_NOT_INITIALIZED;
   199   DWORD numValues;
   200   LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr,
   201                              nullptr, nullptr, &numValues, nullptr, nullptr,
   202                              nullptr, nullptr);
   203   if (rv != ERROR_SUCCESS)
   204     return NS_ERROR_FAILURE;
   206   *result = numValues;
   207   return NS_OK;
   208 }
   210 NS_IMETHODIMP
   211 nsWindowsRegKey::GetValueName(uint32_t index, nsAString &result)
   212 {
   213   if (NS_WARN_IF(!mKey))
   214     return NS_ERROR_NOT_INITIALIZED;
   216   wchar_t nameBuf[MAX_VALUE_NAME_LEN];
   217   DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
   219   LONG rv = RegEnumValueW(mKey, index, nameBuf, &nameLen, nullptr, nullptr,
   220                           nullptr, nullptr);
   221   if (rv != ERROR_SUCCESS)
   222     return NS_ERROR_NOT_AVAILABLE;  // XXX what's the best error code here?
   224   result.Assign(nameBuf, nameLen);
   226   return NS_OK;
   227 }
   229 NS_IMETHODIMP
   230 nsWindowsRegKey::HasValue(const nsAString &name, bool *result)
   231 {
   232   if (NS_WARN_IF(!mKey))
   233     return NS_ERROR_NOT_INITIALIZED;
   235   LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
   236                              nullptr, nullptr);
   238   *result = (rv == ERROR_SUCCESS);
   239   return NS_OK;
   240 }
   242 NS_IMETHODIMP
   243 nsWindowsRegKey::RemoveChild(const nsAString &name)
   244 {
   245   if (NS_WARN_IF(!mKey))
   246     return NS_ERROR_NOT_INITIALIZED;
   248   LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(name).get());
   250   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   251 }
   253 NS_IMETHODIMP
   254 nsWindowsRegKey::RemoveValue(const nsAString &name)
   255 {
   256   if (NS_WARN_IF(!mKey))
   257     return NS_ERROR_NOT_INITIALIZED;
   259   LONG rv = RegDeleteValueW(mKey, PromiseFlatString(name).get());
   261   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   262 }
   264 NS_IMETHODIMP
   265 nsWindowsRegKey::GetValueType(const nsAString &name, uint32_t *result)
   266 {
   267   if (NS_WARN_IF(!mKey))
   268     return NS_ERROR_NOT_INITIALIZED;
   270   LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0,
   271                              (LPDWORD) result, nullptr, nullptr);
   273   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   274 }
   276 NS_IMETHODIMP
   277 nsWindowsRegKey::ReadStringValue(const nsAString &name, nsAString &result)
   278 {
   279   if (NS_WARN_IF(!mKey))
   280     return NS_ERROR_NOT_INITIALIZED;
   282   DWORD type, size;
   284   const nsString &flatName = PromiseFlatString(name);
   286   LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size);
   287   if (rv != ERROR_SUCCESS)
   288     return NS_ERROR_FAILURE;
   290   // This must be a string type in order to fetch the value as a string.
   291   // We're being a bit forgiving here by allowing types other than REG_SZ.
   292   if (type != REG_SZ && type == REG_EXPAND_SZ && type == REG_MULTI_SZ)
   293     return NS_ERROR_FAILURE;
   295   // The buffer size must be a multiple of 2.
   296   if (size % 2 != 0)
   297     return NS_ERROR_UNEXPECTED;
   299   if (size == 0) {
   300     result.Truncate();
   301     return NS_OK;
   302   }
   304   // |size| may or may not include the terminating null character.
   305   DWORD resultLen = size / 2;
   307   result.SetLength(resultLen);
   308   nsAString::iterator begin;
   309   result.BeginWriting(begin);
   310   if (begin.size_forward() != resultLen)
   311     return NS_ERROR_OUT_OF_MEMORY;
   313   rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE) begin.get(),
   314                         &size);
   316   if (!result.CharAt(resultLen-1)) {
   317     // The string passed to us had a null terminator in the final position.
   318     result.Truncate(resultLen-1);
   319   }
   321   // Expand the environment variables if needed
   322   if (type == REG_EXPAND_SZ) {
   323     const nsString &flatSource = PromiseFlatString(result);
   324     resultLen = ExpandEnvironmentStringsW(flatSource.get(), nullptr, 0);
   325     if (resultLen > 1) {
   326       nsAutoString expandedResult;
   327       // |resultLen| includes the terminating null character
   328       --resultLen;
   329       expandedResult.SetLength(resultLen);
   330       nsAString::iterator begin;
   331       expandedResult.BeginWriting(begin);
   332       if (begin.size_forward() != resultLen)
   333         return NS_ERROR_OUT_OF_MEMORY;
   335       resultLen = ExpandEnvironmentStringsW(flatSource.get(),
   336                                             wwc(begin.get()),
   337                                             resultLen + 1);
   338       if (resultLen <= 0) {
   339         rv = ERROR_UNKNOWN_FEATURE;
   340         result.Truncate();
   341       } else {
   342         rv = ERROR_SUCCESS;
   343         result = expandedResult;
   344       }
   345     } else if (resultLen == 1) {
   346       // It apparently expands to nothing (just a null terminator).
   347       result.Truncate();
   348     }
   349   }
   351   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   352 }
   354 NS_IMETHODIMP
   355 nsWindowsRegKey::ReadIntValue(const nsAString &name, uint32_t *result)
   356 {
   357   if (NS_WARN_IF(!mKey))
   358     return NS_ERROR_NOT_INITIALIZED;
   360   DWORD size = sizeof(*result);
   361   LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
   362                              (LPBYTE) result, &size);
   364   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   365 }
   367 NS_IMETHODIMP
   368 nsWindowsRegKey::ReadInt64Value(const nsAString &name, uint64_t *result)
   369 {
   370   if (NS_WARN_IF(!mKey))
   371     return NS_ERROR_NOT_INITIALIZED;
   373   DWORD size = sizeof(*result);
   374   LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
   375                              (LPBYTE) result, &size);
   377   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   378 }
   380 NS_IMETHODIMP
   381 nsWindowsRegKey::ReadBinaryValue(const nsAString &name, nsACString &result)
   382 {
   383   if (NS_WARN_IF(!mKey))
   384     return NS_ERROR_NOT_INITIALIZED;
   386   DWORD size;
   387   LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0,
   388                              nullptr, nullptr, &size);
   390   if (rv != ERROR_SUCCESS)
   391     return NS_ERROR_FAILURE;
   393   if (!size) {
   394     result.Truncate();
   395     return NS_OK;
   396   }
   398   result.SetLength(size);
   399   nsACString::iterator begin;
   400   result.BeginWriting(begin);
   401   if (begin.size_forward() != size)
   402     return NS_ERROR_OUT_OF_MEMORY;
   404   rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
   405                         (LPBYTE) begin.get(), &size);
   407   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   408 }
   410 NS_IMETHODIMP
   411 nsWindowsRegKey::WriteStringValue(const nsAString &name, const nsAString &value)
   412 {
   413   if (NS_WARN_IF(!mKey))
   414     return NS_ERROR_NOT_INITIALIZED;
   416   // Need to indicate complete size of buffer including null terminator.
   417   const nsString &flatValue = PromiseFlatString(value);
   419   LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_SZ,
   420                            (const BYTE *) flatValue.get(),
   421                            (flatValue.Length() + 1) * sizeof(char16_t));
   423   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   424 }
   426 NS_IMETHODIMP
   427 nsWindowsRegKey::WriteIntValue(const nsAString &name, uint32_t value)
   428 {
   429   if (NS_WARN_IF(!mKey))
   430     return NS_ERROR_NOT_INITIALIZED;
   432   LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_DWORD,
   433                            (const BYTE *) &value, sizeof(value));
   435   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   436 }
   438 NS_IMETHODIMP
   439 nsWindowsRegKey::WriteInt64Value(const nsAString &name, uint64_t value)
   440 {
   441   if (NS_WARN_IF(!mKey))
   442     return NS_ERROR_NOT_INITIALIZED;
   444   LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_QWORD,
   445                            (const BYTE *) &value, sizeof(value));
   447   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   448 }
   450 NS_IMETHODIMP
   451 nsWindowsRegKey::WriteBinaryValue(const nsAString &name, const nsACString &value)
   452 {
   453   if (NS_WARN_IF(!mKey))
   454     return NS_ERROR_NOT_INITIALIZED;
   456   const nsCString &flatValue = PromiseFlatCString(value);
   457   LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_BINARY,
   458                            (const BYTE *) flatValue.get(), flatValue.Length());
   460   return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
   461 }
   463 NS_IMETHODIMP
   464 nsWindowsRegKey::StartWatching(bool recurse)
   465 {
   466   if (NS_WARN_IF(!mKey))
   467     return NS_ERROR_NOT_INITIALIZED;
   469   if (mWatchEvent)
   470     return NS_OK;
   472   mWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
   473   if (!mWatchEvent)
   474     return NS_ERROR_OUT_OF_MEMORY;
   476   DWORD filter = REG_NOTIFY_CHANGE_NAME |
   477                  REG_NOTIFY_CHANGE_ATTRIBUTES |
   478                  REG_NOTIFY_CHANGE_LAST_SET |
   479                  REG_NOTIFY_CHANGE_SECURITY;
   481   LONG rv = RegNotifyChangeKeyValue(mKey, recurse, filter, mWatchEvent, TRUE);
   482   if (rv != ERROR_SUCCESS) {
   483     StopWatching();
   484     // On older versions of Windows, this call is not implemented, so simply
   485     // return NS_OK in those cases and pretend that the watching is happening.
   486     return (rv == ERROR_CALL_NOT_IMPLEMENTED) ? NS_OK : NS_ERROR_FAILURE;
   487   }
   489   mWatchRecursive = recurse;
   490   return NS_OK;
   491 }
   493 NS_IMETHODIMP
   494 nsWindowsRegKey::StopWatching()
   495 {
   496   if (mWatchEvent) {
   497     CloseHandle(mWatchEvent);
   498     mWatchEvent = nullptr;
   499   }
   500   return NS_OK;
   501 }
   503 NS_IMETHODIMP
   504 nsWindowsRegKey::HasChanged(bool *result)
   505 {
   506   if (mWatchEvent && WaitForSingleObject(mWatchEvent, 0) == WAIT_OBJECT_0) {
   507     // An event only gets signaled once, then it's done, so we have to set up
   508     // another event to watch.
   509     StopWatching();
   510     StartWatching(mWatchRecursive);
   511     *result = true;
   512   } else {
   513     *result = false;
   514   }
   515   return NS_OK;
   516 }
   518 NS_IMETHODIMP
   519 nsWindowsRegKey::IsWatching(bool *result)
   520 {
   521   *result = (mWatchEvent != nullptr);
   522   return NS_OK;
   523 }
   525 //-----------------------------------------------------------------------------
   527 nsresult
   528 NS_NewWindowsRegKey(nsIWindowsRegKey **result)
   529 {
   530   nsRefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
   531   key.forget(result);
   532   return NS_OK;
   533 }
   535 //-----------------------------------------------------------------------------
   537 nsresult
   538 nsWindowsRegKeyConstructor(nsISupports *delegate, const nsIID &iid,
   539                            void **result)
   540 {
   541   if (delegate)
   542     return NS_ERROR_NO_AGGREGATION;
   544   nsCOMPtr<nsIWindowsRegKey> key;
   545   nsresult rv = NS_NewWindowsRegKey(getter_AddRefs(key));
   546   if (NS_SUCCEEDED(rv))
   547     rv = key->QueryInterface(iid, result);
   548   return rv;
   549 }

mercurial