Browse Source

cmWindowsRegistry: enhance unicode conversions

Marc Chevrier 3 years ago
parent
commit
769f25aa3c
2 changed files with 79 additions and 14 deletions
  1. 1 1
      Source/CMakeLists.txt
  2. 78 13
      Source/cmWindowsRegistry.cxx

+ 1 - 1
Source/CMakeLists.txt

@@ -734,7 +734,7 @@ set(SRCS
   bindexplib.cxx
   )
 
-SET_PROPERTY(SOURCE cmProcessOutput.cxx APPEND PROPERTY COMPILE_DEFINITIONS
+SET_PROPERTY(SOURCE cmProcessOutput.cxx cmWindowsRegistry.cxx APPEND PROPERTY COMPILE_DEFINITIONS
   KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE})
 
 # Xcode only works on Apple

+ 78 - 13
Source/cmWindowsRegistry.cxx

@@ -75,6 +75,8 @@ public:
 
 private:
   static std::string FormatSystemError(LSTATUS status);
+  static std::wstring ToWide(cm::string_view str);
+  static std::string ToNarrow(const wchar_t* str, int size = -1);
 
   HKEY Handler;
 };
@@ -104,7 +106,7 @@ KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
   }
   std::wstring subKey;
   if (start != cm::string_view::npos) {
-    subKey = cmsys::Encoding::ToWide(key.substr(start + 1).data());
+    subKey = ToWide(key.substr(start + 1));
   }
   // Update path format
   std::replace(subKey.begin(), subKey.end(), L'/', L'\\');
@@ -125,21 +127,84 @@ KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
 
 std::string KeyHandler::FormatSystemError(LSTATUS status)
 {
-  std::string formattedMessage;
+  std::string formattedMessage{ "Windows Registry: unexpected error." };
   LPWSTR message = nullptr;
   DWORD size = 1024;
   if (FormatMessageW(
         FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr,
-        status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) == 0) {
-    formattedMessage = "Windows Registry: unexpected error.";
-  } else {
-    formattedMessage = cmTrimWhitespace(cmsys::Encoding::ToNarrow(message));
+        status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) != 0) {
+    try {
+      formattedMessage = cmTrimWhitespace(ToNarrow(message));
+    } catch (...) {
+      // ignore any exception because this method can be called
+      // as part of the raise of an exception
+    }
   }
   LocalFree(message);
 
   return formattedMessage;
 }
 
+std::wstring KeyHandler::ToWide(cm::string_view str)
+{
+  std::wstring wstr;
+
+  if (str.empty()) {
+    return wstr;
+  }
+
+  const auto wlength =
+    MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
+                        int(str.size()), nullptr, 0);
+  if (wlength > 0) {
+    auto wdata = cm::make_unique<wchar_t[]>(wlength);
+    const auto r =
+      MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
+                          int(str.size()), wdata.get(), wlength);
+    if (r > 0) {
+      wstr = std::wstring(wdata.get(), wlength);
+    } else {
+      throw registry_error(FormatSystemError(GetLastError()));
+    }
+  } else {
+    throw registry_error(FormatSystemError(GetLastError()));
+  }
+
+  return wstr;
+}
+
+std::string KeyHandler::ToNarrow(const wchar_t* wstr, int size)
+{
+  std::string str;
+
+  if (size == 0 || (size == -1 && wstr[0] == L'\0')) {
+    return str;
+  }
+
+  const auto length =
+    WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
+                        nullptr, 0, nullptr, nullptr);
+  if (length > 0) {
+    auto data = cm::make_unique<char[]>(length);
+    const auto r =
+      WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
+                          data.get(), length, nullptr, nullptr);
+    if (r > 0) {
+      if (size == -1) {
+        str = std::string(data.get());
+      } else {
+        str = std::string(data.get(), length);
+      }
+    } else {
+      throw registry_error(FormatSystemError(GetLastError()));
+    }
+  } else {
+    throw registry_error(FormatSystemError(GetLastError()));
+  }
+
+  return str;
+}
+
 std::string KeyHandler::ReadValue(cm::string_view name,
                                   cm::string_view separator)
 {
@@ -153,14 +218,14 @@ std::string KeyHandler::ReadValue(cm::string_view name,
   }
   auto data = cm::make_unique<BYTE[]>(size);
   DWORD type;
-  auto valueName = cmsys::Encoding::ToWide(name.data());
+  auto valueName = this->ToWide(name);
   if ((status = RegQueryValueExW(this->Handler, valueName.c_str(), nullptr,
                                  &type, data.get(), &size)) != ERROR_SUCCESS) {
     throw registry_error(this->FormatSystemError(status));
   }
   switch (type) {
     case REG_SZ:
-      return cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
+      return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
       break;
     case REG_EXPAND_SZ: {
       auto expandSize = ExpandEnvironmentStringsW(
@@ -170,7 +235,7 @@ std::string KeyHandler::ReadValue(cm::string_view name,
                                     expandData.get(), expandSize + 1) == 0) {
         throw registry_error(this->FormatSystemError(GetLastError()));
       } else {
-        return cmsys::Encoding::ToNarrow(expandData.get());
+        return this->ToNarrow(expandData.get());
       }
     } break;
     case REG_DWORD:
@@ -181,12 +246,12 @@ std::string KeyHandler::ReadValue(cm::string_view name,
       break;
     case REG_MULTI_SZ: {
       // replace separator with semicolon
-      auto sep = cmsys::Encoding::ToWide(separator.data())[0];
+      auto sep = this->ToWide(separator)[0];
       std::replace(reinterpret_cast<wchar_t*>(data.get()),
                    reinterpret_cast<wchar_t*>(data.get()) +
                      (size / sizeof(wchar_t)) - 1,
                    sep, L';');
-      return cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
+      return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
     } break;
     default:
       throw registry_error(cmStrCat(type, ": unsupported type."));
@@ -213,7 +278,7 @@ std::vector<std::string> KeyHandler::GetValueNames()
   while ((status = RegEnumValueW(this->Handler, index, data.get(), &size,
                                  nullptr, nullptr, nullptr, nullptr)) ==
          ERROR_SUCCESS) {
-    auto name = cmsys::Encoding::ToNarrow(data.get());
+    auto name = this->ToNarrow(data.get());
     valueNames.push_back(name.empty() ? "(default)" : name);
     size = maxSize;
     ++index;
@@ -243,7 +308,7 @@ std::vector<std::string> KeyHandler::GetSubKeys()
 
   while ((status = RegEnumKeyW(this->Handler, index, data.get(), size)) ==
          ERROR_SUCCESS) {
-    subKeys.push_back(cmsys::Encoding::ToNarrow(data.get()));
+    subKeys.push_back(this->ToNarrow(data.get()));
     ++index;
   }
   if (status != ERROR_NO_MORE_ITEMS) {