OSDN Git Service

- C(Reg|Ini)OptionsMgr::SplitName() -> COptionsMgr::SplitName()
authorTakashi Sawanaka <sdottaka@users.sourceforge.net>
Sun, 9 May 2021 02:06:38 +0000 (11:06 +0900)
committerTakashi Sawanaka <sdottaka@users.sourceforge.net>
Sun, 9 May 2021 02:06:38 +0000 (11:06 +0900)
- Write the settings to the INI file in a separate thread.

Src/Common/IniOptionsMgr.cpp
Src/Common/IniOptionsMgr.h
Src/Common/OptionsMgr.cpp
Src/Common/OptionsMgr.h
Src/Common/RegOptionsMgr.cpp
Src/Common/RegOptionsMgr.h
Src/Merge.cpp

index a213533..0b46735 100644 (file)
 
 #include "pch.h"
 #include "IniOptionsMgr.h"
-#include "OptionsMgr.h"
 #include <Windows.h>
-#include <codecvt>
-#include <filesystem>
-#include <string>
-#include <fstream>
-
-using std::filesystem::current_path;
-
-LPCWSTR CIniOptionsMgr::lpFilePath = NULL;
+#include "OptionsMgr.h"
 
 LPCWSTR lpAppName = TEXT("WinMerge");
-LPCWSTR lpFileName = TEXT("\\winmerge.ini");
 
-CIniOptionsMgr::CIniOptionsMgr()
+struct AsyncWriterThreadParams
+{
+       AsyncWriterThreadParams(const String& name, const varprop::VariantValue& value) : name(name), value(value) {}
+       String name;
+       varprop::VariantValue value;
+};
+
+CIniOptionsMgr::CIniOptionsMgr(const String& filePath)
        : m_serializing(true)
+       , m_filePath{filePath}
+       , m_dwThreadId(0)
+       , m_hThread(nullptr)
 {
-       InitializeCriticalSection(&m_cs);
+       m_iniFileKeyValues = Load(m_filePath);
+       m_hThread = CreateThread(nullptr, 0, AsyncWriterThreadProc, this, 0, &m_dwThreadId);
 }
 
 CIniOptionsMgr::~CIniOptionsMgr()
 {
-       DeleteCriticalSection(&m_cs);
-       delete[] CIniOptionsMgr::lpFilePath;
+       for (;;) {
+               PostThreadMessage(m_dwThreadId, WM_QUIT, 0, 0);
+               if (WaitForSingleObject(m_hThread, 1) != WAIT_TIMEOUT)
+                       break;
+       }
 }
 
-/**
- * @brief Checks wheter INI file exists.
- * @return TRUE if INI file exist,
- *   FALSE otherwise.
- */
-bool CIniOptionsMgr::CheckIfIniFileExist()
+DWORD WINAPI CIniOptionsMgr::AsyncWriterThreadProc(void *pvThis)
 {
-       std::ifstream f(GetFilePath());
-       return f.good();
+       CIniOptionsMgr *pThis = reinterpret_cast<CIniOptionsMgr *>(pvThis);
+       MSG msg;
+       BOOL bRet;
+       while ((bRet = GetMessage(&msg, 0, 0, 0)) != 0)
+       {
+               auto* pParam = reinterpret_cast<AsyncWriterThreadParams *>(msg.wParam);
+               pThis->SaveValueToFile(pParam->name, pParam->value);
+               delete pParam;
+       }
+       return 0;
 }
 
-/**
- * @brief Get path to INI file.
- * @return path to INI file
- */
-LPCWSTR CIniOptionsMgr::GetFilePath()
+std::map<String, String> CIniOptionsMgr::Load(const String& iniFilePath)
+{
+       std::map<String, String> iniFileKeyValues;
+       std::vector<TCHAR> str(32768);
+       if (GetPrivateProfileSection(lpAppName, str.data(), static_cast<DWORD>(str.size()), iniFilePath.c_str()) > 0)
+       {
+               TCHAR* p = str.data();
+               while (*p)
+               {
+                       TCHAR* v = _tcschr(p, '=');
+                       if (!v)
+                               break;
+                       ++v;
+                       size_t vlen = _tcslen(v);
+                       String value{ v, v + vlen };
+                       String key{ p, v - 1 };
+                       iniFileKeyValues.insert_or_assign(key, UnescapeValue(value));
+                       p = v + vlen + 1;
+               }
+       }
+       return iniFileKeyValues;
+}
+
+int CIniOptionsMgr::LoadValueFromBuf(const String& strName, String& textValue, varprop::VariantValue& value)
 {
-       if (CIniOptionsMgr::lpFilePath == NULL)
+       int retVal = COption::OPT_OK;
+       int valType = value.GetType();
+       if (valType == varprop::VT_STRING)
+       {
+               value.SetString(textValue);
+               retVal = Set(strName, value);
+       }
+       else if (valType == varprop::VT_INT)
+       {
+               value.SetInt(std::stoul(textValue));
+               retVal = Set(strName, value);
+       }
+       else if (valType == varprop::VT_BOOL)
        {
-               // create path
-               std::filesystem::path p = current_path();
-               p += lpFileName;
-
-               // change type
-               std::wstring str = p.wstring();
-               size_t length = str.length() + 1;
-               wchar_t* strCp = new wchar_t[length];
-               wcscpy_s(strCp, length, str.c_str());
-
-               // set path
-               CIniOptionsMgr::lpFilePath = strCp;
+               value.SetBool(textValue[0] == '1' ? true : false);
+               retVal = Set(strName, value);
        }
+       else
+               retVal = COption::OPT_WRONG_TYPE;
+
+       return retVal;
+}
+
+int CIniOptionsMgr::SaveValueToFile(const String& name, const varprop::VariantValue& value)
+{
+       BOOL retValReg = TRUE;
+       int valType = value.GetType();
+       int retVal = COption::OPT_OK;
 
-       return CIniOptionsMgr::lpFilePath;
+       if (valType == varprop::VT_STRING)
+       {
+               String strVal = EscapeValue(value.GetString());
+               LPCWSTR text = strVal.c_str();
+               retValReg =WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
+       }
+       else if (valType == varprop::VT_INT)
+       {
+               DWORD dwordVal = value.GetInt();
+               String strVal = strutils::to_str(dwordVal);
+               LPCWSTR text = strVal.c_str();
+               retValReg =WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
+       }
+       else if (valType == varprop::VT_BOOL)
+       {
+               DWORD dwordVal = value.GetBool() ? 1 : 0;
+               String strVal = strutils::to_str(dwordVal);
+               LPCWSTR text = strVal.c_str();
+               retValReg = WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
+       }
+       else if (valType == varprop::VT_NULL)
+       {
+               auto [strPath, strValueName] = SplitName(name);
+               if (!strValueName.empty())
+                       retValReg = WritePrivateProfileString(lpAppName, name.c_str(), nullptr, GetFilePath());
+               else
+               {
+                       auto iniFileMap = Load(GetFilePath());
+                       for (auto& [key, value2] : iniFileMap)
+                       {
+                               if (key.find(strPath) == 0 && key.length() > strPath.length() && key[strPath.length()] == '/')
+                                       retValReg = WritePrivateProfileString(lpAppName, key.c_str(), nullptr, GetFilePath());
+                       }
+               }
+       }
+       else
+       {
+               retVal = COption::OPT_UNKNOWN_TYPE;
+       }
+               
+       if (!retValReg)
+       {
+               retVal = COption::OPT_ERR;
+       }
+       return retVal;
 }
 
 int CIniOptionsMgr::InitOption(const String& name, const varprop::VariantValue& defaultValue)
@@ -80,30 +164,22 @@ int CIniOptionsMgr::InitOption(const String& name, const varprop::VariantValue&
        if (!m_serializing)
                return AddOption(name, defaultValue);
 
-       EnterCriticalSection(&m_cs);
-
-       // check if value exist
-       String textValue = ReadValueFromFile(name);
-       bool found = textValue.size() != 0;
-
        // Actually save value into our in-memory options table
        int retVal = AddOption(name, defaultValue);
 
        // Update registry if successfully saved to in-memory table
        if (retVal == COption::OPT_OK)
        {
+               // check if value exist
+               bool found = m_iniFileKeyValues.find(name) != m_iniFileKeyValues.end();
                if (found)
                {
+                       String textValue = m_iniFileKeyValues[name];
                        varprop::VariantValue value(defaultValue);
-                       retVal = ParseValue(name, textValue, value);
-                       if (retVal == COption::OPT_OK)
-                       {
-                               retVal = Set(name, value);
-                       }
+                       retVal = LoadValueFromBuf(name, textValue, value);
                }
        }
 
-       LeaveCriticalSection(&m_cs);
        return retVal;
 }
 
@@ -153,30 +229,8 @@ int CIniOptionsMgr::SaveOption(const String& name)
 
        if (retVal == COption::OPT_OK)
        {
-               if (valType == varprop::VT_STRING)
-               {
-                       String strVal = EscapeValue(value.GetString());
-                       LPCWSTR text = strVal.c_str();
-                       WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
-               }
-               else if (valType == varprop::VT_INT)
-               {
-                       DWORD dwordVal = value.GetInt();
-                       String strVal = strutils::to_str(dwordVal);
-                       LPCWSTR text = strVal.c_str();
-                       WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
-               }
-               else if (valType == varprop::VT_BOOL)
-               {
-                       DWORD dwordVal = value.GetBool() ? 1 : 0;
-                       String strVal = strutils::to_str(dwordVal);
-                       LPCWSTR text = strVal.c_str();
-                       WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
-               }
-               else
-               {
-                       retVal = COption::OPT_UNKNOWN_TYPE;
-               }
+               auto* pParam = new AsyncWriterThreadParams(name, value);
+               PostThreadMessage(m_dwThreadId, WM_USER, (WPARAM)pParam, 0);
        }
        return retVal;
 }
@@ -236,11 +290,7 @@ int CIniOptionsMgr::SaveOption(const String& name, bool value)
 int CIniOptionsMgr::RemoveOption(const String& name)
 {
        int retVal = COption::OPT_OK;
-
-       String strPath;
-       String strValueName;
-
-       SplitName(name, strPath, strValueName);
+       auto [strPath, strValueName] = SplitName(name);
 
        if (!strValueName.empty())
        {
@@ -250,7 +300,8 @@ int CIniOptionsMgr::RemoveOption(const String& name)
        {
                for (auto it = m_optionsMap.begin(); it != m_optionsMap.end(); )
                {
-                       if (it->first.find(strPath) == 0)
+                       const String& key = it->first;
+                       if (key.find(strPath) == 0 && key.length() > strPath.length() && key[strPath.length()] == '/')
                                it = m_optionsMap.erase(it);
                        else
                                ++it;
@@ -258,89 +309,8 @@ int CIniOptionsMgr::RemoveOption(const String& name)
                retVal = COption::OPT_OK;
        }
 
-       EnterCriticalSection(&m_cs);
-
-       WritePrivateProfileString(lpAppName, name.c_str(), NULL, GetFilePath());
-
-       LeaveCriticalSection(&m_cs);
-
-       return retVal;
-}
-
-String CIniOptionsMgr::ReadValueFromFile(const String& name)
-{
-       if (m_iniFileKeyValues.empty())
-       {
-               std::vector<TCHAR> str(32768);
-               if (GetPrivateProfileSection(lpAppName, str.data(), static_cast<DWORD>(str.size()), GetFilePath()) > 0)
-               {
-                       TCHAR* p = str.data();
-                       while (*p)
-                       {
-                               TCHAR* v = _tcschr(p, '=');
-                               if (!v)
-                                       break;
-                               ++v;
-                               size_t vlen = _tcslen(v);
-                               String value{ v, v + vlen };
-                               m_iniFileKeyValues.insert_or_assign(String{ p, v - 1 }, UnescapeValue(value));
-                               p = v + vlen + 1;
-                       }
-               }
-       }
-       return m_iniFileKeyValues[name];
-}
-
-int CIniOptionsMgr::ParseValue(const String& strName, String& textValue, varprop::VariantValue& value)
-{
-       int valType = value.GetType();
-       int retVal = COption::OPT_OK;
-
-       if (valType == varprop::VT_STRING)
-       {
-               value.SetString(textValue);
-               retVal = Set(strName, value);
-       }
-       else if (valType == varprop::VT_INT)
-       {
-               value.SetInt(std::stoul(textValue));
-               retVal = Set(strName, value);
-       }
-       else if (valType == varprop::VT_BOOL)
-       {
-               value.SetBool(textValue[0] == '1' ? true : false);
-               retVal = Set(strName, value);
-       }
-       else
-               retVal = COption::OPT_WRONG_TYPE;
+       auto* pParam = new AsyncWriterThreadParams(name, varprop::VariantValue());
+       PostThreadMessage(m_dwThreadId, WM_USER, (WPARAM)pParam, 0);
 
        return retVal;
 }
-
-/**
- * @brief Split option name to path (in registry) and
- * valuename (in registry).
- *
- * Option names are given as "full path", e.g. "Settings/AutomaticRescan".
- * This function splits that to path "Settings/" and valuename
- * "AutomaticRescan".
- * @param [in] strName Option name
- * @param [out] srPath Path (key) in registry
- * @param [out] strValue Value in registry
- */
-void CIniOptionsMgr::SplitName(const String& strName, String& strPath,
-       String& strValue) const
-{
-       size_t pos = strName.rfind('/');
-       if (pos != String::npos)
-       {
-               size_t len = strName.length();
-               strValue = strName.substr(pos + 1, len - pos - 1); //Right(len - pos - 1);
-               strPath = strName.substr(0, pos);  //Left(pos);
-       }
-       else
-       {
-               strValue = strName;
-               strPath.erase();
-       }
-}
\ No newline at end of file
index cb7e6ad..6cafbd2 100644 (file)
@@ -17,13 +17,9 @@ class COptionsMgr;
 class CIniOptionsMgr : public COptionsMgr
 {
 public:
-       CIniOptionsMgr();
+       CIniOptionsMgr(const String& filePath);
        virtual ~CIniOptionsMgr();
 
-       static bool CheckIfIniFileExist();
-
-       static LPCWSTR GetFilePath();
-
        virtual int InitOption(const String& name, const varprop::VariantValue& defaultValue) override;
        virtual int InitOption(const String& name, const String& defaultValue) override;
        virtual int InitOption(const String& name, const TCHAR* defaultValue) override;
@@ -41,14 +37,18 @@ public:
 
        virtual void SetSerializing(bool serializing = true) override { m_serializing = serializing; }
 
+protected:
+       static std::map<String, String> Load(const String& iniFilePath);
+       int LoadValueFromBuf(const String& strName, String& textValue, varprop::VariantValue& value);
+       const TCHAR *GetFilePath() const { return m_filePath.c_str(); }
+       int SaveValueToFile(const String& strValueName,
+               const varprop::VariantValue& value);
+       static DWORD WINAPI AsyncWriterThreadProc(void *pParam);
+
 private:
-       CRITICAL_SECTION m_cs;
        bool m_serializing;
-       static LPCWSTR lpFilePath;
        std::map<String, String> m_iniFileKeyValues;
-
-       String ReadValueFromFile(const String& name);
-       int ParseValue(const String& strName, String& textValue, varprop::VariantValue& value);
-
-       void SplitName(const String& strName, String& strPath, String& strValue) const;
-};
\ No newline at end of file
+       String m_filePath;
+       DWORD m_dwThreadId;
+       HANDLE m_hThread;
+};
index fee124a..7e0589b 100644 (file)
@@ -779,3 +779,32 @@ String COptionsMgr::UnescapeValue(const String& text)
        return text2;
 }
 
+/**
+ * @brief Split option name to path (in registry) and
+ * valuename (in registry).
+ *
+ * Option names are given as "full path", e.g. "Settings/AutomaticRescan".
+ * This function splits that to path "Settings/" and valuename
+ * "AutomaticRescan".
+ * @param [in] strName Option name
+ * @param [out] srPath Path (key) in registry
+ * @param [out] strValue Value in registry
+ */
+std::pair<String, String> COptionsMgr::SplitName(const String& strName)
+{
+       String strValue, strPath;
+       size_t pos = strName.rfind('/');
+       if (pos != String::npos)
+       {
+               size_t len = strName.length();
+               strValue = strName.substr(pos + 1, len - pos - 1); //Right(len - pos - 1);
+               strPath = strName.substr(0, pos);  //Left(pos);
+       }
+       else
+       {
+               strValue = strName;
+               strPath.erase();
+       }
+       return { strPath, strValue };
+}
+
index 0b63cea..1d04713 100644 (file)
@@ -154,6 +154,7 @@ public:
 protected:
        static String EscapeValue(const String& text);
        static String UnescapeValue(const String& text);
+       static std::pair<String, String> SplitName(const String& strName);
 
        OptionsMap m_optionsMap; /**< Map where options are stored. */
 
index a852607..0145ecc 100644 (file)
@@ -9,7 +9,6 @@
 #include "RegOptionsMgr.h"
 #include <windows.h>
 #include <Shlwapi.h>
-#include <vector>
 #include "varprop.h"
 #include "OptionsMgr.h"
 
@@ -17,6 +16,7 @@
 
 struct AsyncWriterThreadParams
 {
+       AsyncWriterThreadParams(const String& name, const varprop::VariantValue& value) : name(name), value(value) {}
        String name;
        varprop::VariantValue value;
 };
@@ -42,34 +42,6 @@ CRegOptionsMgr::~CRegOptionsMgr()
        DeleteCriticalSection(&m_cs);
 }
 
-/**
- * @brief Split option name to path (in registry) and
- * valuename (in registry).
- *
- * Option names are given as "full path", e.g. "Settings/AutomaticRescan".
- * This function splits that to path "Settings/" and valuename
- * "AutomaticRescan".
- * @param [in] strName Option name
- * @param [out] srPath Path (key) in registry
- * @param [out] strValue Value in registry
- */
-void CRegOptionsMgr::SplitName(const String &strName, String &strPath,
-       String &strValue) const
-{
-       size_t pos = strName.rfind('/');
-       if (pos != String::npos)
-       {
-               size_t len = strName.length();
-               strValue = strName.substr(pos + 1, len - pos - 1); //Right(len - pos - 1);
-               strPath = strName.substr(0, pos);  //Left(pos);
-       }
-       else
-       {
-               strValue = strName;
-               strPath.erase();
-       }
-}
-
 HKEY CRegOptionsMgr::OpenKey(const String& strPath, bool bAlwaysCreate)
 {
        String strRegPath(m_registryRoot);
@@ -128,10 +100,8 @@ DWORD WINAPI CRegOptionsMgr::AsyncWriterThreadProc(void *pvThis)
        BOOL bRet;
        while ((bRet = GetMessage(&msg, 0, 0, 0)) != 0)
        {
-               AsyncWriterThreadParams *pParam = reinterpret_cast<AsyncWriterThreadParams *>(msg.wParam);
-               String strPath;
-               String strValueName;
-               pThis->SplitName(pParam->name, strPath, strValueName);
+               auto* pParam = reinterpret_cast<AsyncWriterThreadParams *>(msg.wParam);
+               auto [strPath, strValueName] = COptionsMgr::SplitName(pParam->name);
                EnterCriticalSection(&pThis->m_cs);
                HKEY hKey = pThis->OpenKey(strPath, true);
                SaveValueToReg(hKey, strValueName, pParam->value);
@@ -194,15 +164,12 @@ int CRegOptionsMgr::LoadValueFromBuf(const String& strName, DWORD type, const BY
 int CRegOptionsMgr::LoadValueFromReg(HKEY hKey, const String& strName,
        varprop::VariantValue &value)
 {
-       String strPath;
-       String strValueName;
        LONG retValReg = 0;
        std::vector<BYTE> data;
        DWORD type = 0;
        DWORD size = 0;
        int retVal = COption::OPT_OK;
-
-       SplitName(strName, strPath, strValueName);
+       auto [strPath, strValueName] = SplitName(strName);
 
        // Get type and size of value in registry
        retValReg = RegQueryValueEx(hKey, strValueName.c_str(), 0, &type,
@@ -299,9 +266,7 @@ int CRegOptionsMgr::InitOption(const String& name, const varprop::VariantValue&
                return AddOption(name, defaultValue);
 
        // Figure out registry path, for saving value
-       String strPath;
-       String strValueName;
-       SplitName(name, strPath, strValueName);
+       auto [strPath, strValueName] = SplitName(name);
 
        // Open key.
        EnterCriticalSection(&m_cs);
@@ -379,7 +344,7 @@ int CRegOptionsMgr::InitOption(const String& name, int defaultValue, bool serial
 {
        varprop::VariantValue defValue;
        int retVal = COption::OPT_OK;
-       
+
        defValue.SetInt(defaultValue);
        if (serializable)
                retVal = InitOption(name, defValue);
@@ -416,12 +381,10 @@ int CRegOptionsMgr::SaveOption(const String& name)
        int valType = value.GetType();
        if (valType == varprop::VT_NULL)
                retVal = COption::OPT_NOTFOUND;
-       
+
        if (retVal == COption::OPT_OK)
        {
-               AsyncWriterThreadParams *pParam = new AsyncWriterThreadParams();
-               pParam->name = name;
-               pParam->value = value;
+               auto* pParam = new AsyncWriterThreadParams(name, value);
                InterlockedIncrement(&m_dwQueueCount);
                PostThreadMessage(m_dwThreadId, WM_USER, (WPARAM)pParam, 0);
        }
@@ -489,11 +452,7 @@ int CRegOptionsMgr::SaveOption(const String& name, bool value)
 int CRegOptionsMgr::RemoveOption(const String& name)
 {
        int retVal = COption::OPT_OK;
-
-       String strPath;
-       String strValueName;
-
-       SplitName(name, strPath, strValueName);
+       auto [strPath, strValueName] = SplitName(name);
 
        if (!strValueName.empty())
        {
@@ -503,7 +462,8 @@ int CRegOptionsMgr::RemoveOption(const String& name)
        {
                for (auto it = m_optionsMap.begin(); it != m_optionsMap.end(); )
                {
-                       if (it->first.find(strPath) == 0)
+                       const String& key = it->first;
+                       if (key.find(strPath) == 0 && key.length() > strPath.length() && key[strPath.length()] == '/')
                                it = m_optionsMap.erase(it);
                        else 
                                ++it;
index 6f1dd75..88e2a24 100644 (file)
@@ -42,7 +42,6 @@ public:
 protected:
        HKEY OpenKey(const String& strPath, bool bAlwaysCreate);
        void CloseKey(HKEY hKey, const String& strPath);
-       void SplitName(const String &strName, String &strPath, String &strValue) const;
        int LoadValueFromBuf(const String& strName, DWORD type, const BYTE* data, varprop::VariantValue &value);
        int LoadValueFromReg(HKEY hKey, const String& strName,
                varprop::VariantValue &value);
index bbda58a..36c8aa6 100644 (file)
@@ -123,9 +123,10 @@ CMergeApp::CMergeApp() :
  */
 COptionsMgr *CreateOptionManager()
 {
-       if (CIniOptionsMgr::CheckIfIniFileExist())
+       String iniFilePath = paths::ConcatPath(env::GetProgPath(), _T("winmerge.ini"));
+       if (paths::DoesPathExist(iniFilePath) == paths::IS_EXISTING_FILE)
        {
-               return new CIniOptionsMgr();
+               return new CIniOptionsMgr(iniFilePath);
        }
        else
        {