Use JSON data file for storing PowerRename settings instead of registry (#1909)

* Use JSON data file for storing PowerRename settings instead of registry

* Address PR comments and made several improvements

* Remove WindowsApp.lib dependencies in test app and unit tests

* Revert changes in vcxproj for unit test

* Solve linker warnings generated while linking WindowsApp.lib

* Don't migrate enabled flag. Always read / write from registry.
This commit is contained in:
vldmr11080
2020-04-08 19:12:46 +02:00
committed by GitHub
parent 17022d50d4
commit c355a2b61e
8 changed files with 314 additions and 224 deletions

View File

@@ -85,13 +85,14 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -99,7 +100,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -107,6 +108,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -116,7 +118,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>Create</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@@ -124,6 +126,7 @@
<SDLCheck>true</SDLCheck>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -134,7 +137,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>Create</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@@ -143,6 +146,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View File

@@ -1,171 +1,70 @@
#include "stdafx.h"
#include <commctrl.h>
#include "Settings.h"
#include "PowerRenameInterfaces.h"
#include "settings_helpers.h"
const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename";
const wchar_t c_mruSearchRegPath[] = L"SearchMRU";
const wchar_t c_mruReplaceRegPath[] = L"ReplaceMRU";
#include <filesystem>
#include <commctrl.h>
const wchar_t c_enabled[] = L"Enabled";
const wchar_t c_showIconOnMenu[] = L"ShowIcon";
const wchar_t c_extendedContextMenuOnly[] = L"ExtendedContextMenuOnly";
const wchar_t c_persistState[] = L"PersistState";
const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
const wchar_t c_flags[] = L"Flags";
const wchar_t c_searchText[] = L"SearchText";
const wchar_t c_replaceText[] = L"ReplaceText";
const wchar_t c_mruEnabled[] = L"MRUEnabled";
const bool c_enabledDefault = true;
const bool c_showIconOnMenuDefault = true;
const bool c_extendedContextMenuOnlyDefaut = false;
const bool c_persistStateDefault = true;
const bool c_mruEnabledDefault = true;
const DWORD c_maxMRUSizeDefault = 10;
const DWORD c_flagsDefault = 0;
bool CSettings::GetEnabled()
namespace
{
return GetRegBoolValue(c_enabled, c_enabledDefault);
}
const wchar_t c_powerRenameDataFilePath[] = L"power-rename-settings.json";
bool CSettings::SetEnabled(_In_ bool enabled)
{
return SetRegBoolValue(c_enabled, enabled);
}
const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename";
const wchar_t c_mruSearchRegPath[] = L"SearchMRU";
const wchar_t c_mruReplaceRegPath[] = L"ReplaceMRU";
bool CSettings::GetShowIconOnMenu()
{
return GetRegBoolValue(c_showIconOnMenu, c_showIconOnMenuDefault);
}
const wchar_t c_enabled[] = L"Enabled";
const wchar_t c_showIconOnMenu[] = L"ShowIcon";
const wchar_t c_extendedContextMenuOnly[] = L"ExtendedContextMenuOnly";
const wchar_t c_persistState[] = L"PersistState";
const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
const wchar_t c_flags[] = L"Flags";
const wchar_t c_searchText[] = L"SearchText";
const wchar_t c_replaceText[] = L"ReplaceText";
const wchar_t c_mruEnabled[] = L"MRUEnabled";
bool CSettings::SetShowIconOnMenu(_In_ bool show)
{
return SetRegBoolValue(c_showIconOnMenu, show);
}
bool CSettings::GetExtendedContextMenuOnly()
{
return GetRegBoolValue(c_extendedContextMenuOnly, c_extendedContextMenuOnlyDefaut);
}
bool CSettings::SetExtendedContextMenuOnly(_In_ bool extendedOnly)
{
return SetRegBoolValue(c_extendedContextMenuOnly, extendedOnly);
}
bool CSettings::GetPersistState()
{
return GetRegBoolValue(c_persistState, c_persistStateDefault);
}
bool CSettings::SetPersistState(_In_ bool persistState)
{
return SetRegBoolValue(c_persistState, persistState);
}
bool CSettings::GetMRUEnabled()
{
return GetRegBoolValue(c_mruEnabled, c_mruEnabledDefault);
}
bool CSettings::SetMRUEnabled(_In_ bool enabled)
{
return SetRegBoolValue(c_mruEnabled, enabled);
}
DWORD CSettings::GetMaxMRUSize()
{
return GetRegDWORDValue(c_maxMRUSize, c_maxMRUSizeDefault);
}
bool CSettings::SetMaxMRUSize(_In_ DWORD maxMRUSize)
{
return SetRegDWORDValue(c_maxMRUSize, maxMRUSize);
}
DWORD CSettings::GetFlags()
{
return GetRegDWORDValue(c_flags, c_flagsDefault);
}
bool CSettings::SetFlags(_In_ DWORD flags)
{
return SetRegDWORDValue(c_flags, flags);
}
bool CSettings::GetSearchText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf)
{
return GetRegStringValue(c_searchText, text, cchBuf);
}
bool CSettings::SetSearchText(_In_ PCWSTR text)
{
return SetRegStringValue(c_searchText, text);
}
bool CSettings::GetReplaceText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf)
{
return GetRegStringValue(c_replaceText, text, cchBuf);
}
bool CSettings::SetReplaceText(_In_ PCWSTR text)
{
return SetRegStringValue(c_replaceText, text);
}
bool CSettings::SetRegBoolValue(_In_ PCWSTR valueName, _In_ bool value)
{
DWORD dwValue = value ? 1 : 0;
return SetRegDWORDValue(valueName, dwValue);
}
bool CSettings::GetRegBoolValue(_In_ PCWSTR valueName, _In_ bool defaultValue)
{
DWORD value = GetRegDWORDValue(valueName, (defaultValue == 0) ? false : true);
return (value == 0) ? false : true;
}
bool CSettings::SetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD value)
{
return (SUCCEEDED(HRESULT_FROM_WIN32(SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, REG_DWORD, &value, sizeof(value)))));
}
DWORD CSettings::GetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD defaultValue)
{
DWORD retVal = defaultValue;
DWORD type = REG_DWORD;
DWORD dwEnabled = 0;
DWORD cb = sizeof(dwEnabled);
if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, &type, &dwEnabled, &cb) == ERROR_SUCCESS)
long GetRegNumber(const std::wstring& valueName, long defaultValue)
{
retVal = dwEnabled;
DWORD type = REG_DWORD;
DWORD data = 0;
DWORD size = sizeof(DWORD);
if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), &type, &data, &size) == ERROR_SUCCESS)
{
return data;
}
return defaultValue;
}
return retVal;
}
bool CSettings::SetRegStringValue(_In_ PCWSTR valueName, _In_ PCWSTR value)
{
ULONG cb = (DWORD)((wcslen(value) + 1) * sizeof(*value));
return (SUCCEEDED(HRESULT_FROM_WIN32(SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, REG_SZ, (const BYTE*)value, cb))));
}
bool CSettings::GetRegStringValue(_In_ PCWSTR valueName, __out_ecount(cchBuf) PWSTR value, DWORD cchBuf)
{
if (cchBuf > 0)
void SetRegNumber(const std::wstring& valueName, long value)
{
SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), REG_DWORD, &value, sizeof(value));
}
bool GetRegBoolean(const std::wstring& valueName, bool defaultValue)
{
DWORD value = GetRegNumber(valueName.c_str(), defaultValue ? 1 : 0);
return (value == 0) ? false : true;
}
void SetRegBoolean(const std::wstring& valueName, bool value)
{
SetRegNumber(valueName, value ? 1 : 0);
}
std::wstring GetRegString(const std::wstring& valueName) {
wchar_t value[CSettings::MAX_INPUT_STRING_LEN];
value[0] = L'\0';
DWORD type = REG_SZ;
DWORD size = CSettings::MAX_INPUT_STRING_LEN * sizeof(wchar_t);
if (SUCCEEDED(HRESULT_FROM_WIN32(SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), &type, value, &size) == ERROR_SUCCESS)))
{
return std::wstring(value);
}
return std::wstring{};
}
DWORD type = REG_SZ;
ULONG cb = cchBuf * sizeof(*value);
return (SUCCEEDED(HRESULT_FROM_WIN32(SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, &type, value, &cb) == ERROR_SUCCESS)));
}
typedef int (CALLBACK* MRUCMPPROC)(LPCWSTR, LPCWSTR);
typedef struct {
@@ -470,12 +369,121 @@ void CRenameMRU::_FreeMRUList()
}
}
CSettings::CSettings()
{
std::wstring result = PTSettingsHelper::get_module_save_folder_location(L"PowerRename");
jsonFilePath = result + L"\\" + std::wstring(c_powerRenameDataFilePath);
}
bool CSettings::GetEnabled()
{
return GetRegBoolean(c_enabled, true);
}
void CSettings::SetEnabled(bool enabled)
{
SetRegBoolean(c_enabled, enabled);
}
void CSettings::LoadPowerRenameData()
{
if (!std::filesystem::exists(jsonFilePath))
{
MigrateSettingsFromRegistry();
SavePowerRenameData();
}
else
{
ParseJsonSettings();
}
}
void CSettings::SavePowerRenameData() const
{
json::JsonObject jsonData;
jsonData.SetNamedValue(c_showIconOnMenu, json::value(settings.showIconOnMenu));
jsonData.SetNamedValue(c_extendedContextMenuOnly, json::value(settings.extendedContextMenuOnly));
jsonData.SetNamedValue(c_persistState, json::value(settings.persistState));
jsonData.SetNamedValue(c_mruEnabled, json::value(settings.MRUEnabled));
jsonData.SetNamedValue(c_maxMRUSize, json::value(settings.maxMRUSize));
jsonData.SetNamedValue(c_flags, json::value(settings.flags));
jsonData.SetNamedValue(c_searchText, json::value(settings.searchText));
jsonData.SetNamedValue(c_replaceText, json::value(settings.replaceText));
json::to_file(jsonFilePath, jsonData);
}
void CSettings::MigrateSettingsFromRegistry()
{
settings.showIconOnMenu = GetRegBoolean(c_showIconOnMenu, true);
settings.extendedContextMenuOnly = GetRegBoolean(c_extendedContextMenuOnly, false); // Disabled by default.
settings.persistState = GetRegBoolean(c_persistState, true);
settings.MRUEnabled = GetRegBoolean(c_mruEnabled, true);
settings.maxMRUSize = GetRegNumber(c_maxMRUSize, 10);
settings.flags = GetRegNumber(c_flags, 0);
settings.searchText = GetRegString(c_searchText);
settings.replaceText = GetRegString(c_replaceText);
}
void CSettings::ParseJsonSettings()
{
auto json = json::from_file(jsonFilePath);
if (json)
{
const json::JsonObject& jsonSettings = json.value();
try
{
if (json::has(jsonSettings, c_showIconOnMenu, json::JsonValueType::Boolean))
{
settings.showIconOnMenu = jsonSettings.GetNamedBoolean(c_showIconOnMenu);
}
if (json::has(jsonSettings, c_extendedContextMenuOnly, json::JsonValueType::Boolean))
{
settings.extendedContextMenuOnly = jsonSettings.GetNamedBoolean(c_extendedContextMenuOnly);
}
if (json::has(jsonSettings, c_persistState, json::JsonValueType::Boolean))
{
settings.persistState = jsonSettings.GetNamedBoolean(c_persistState);
}
if (json::has(jsonSettings, c_mruEnabled, json::JsonValueType::Boolean))
{
settings.MRUEnabled = jsonSettings.GetNamedBoolean(c_mruEnabled);
}
if (json::has(jsonSettings, c_maxMRUSize, json::JsonValueType::Number))
{
settings.maxMRUSize = (long)jsonSettings.GetNamedNumber(c_maxMRUSize);
}
if (json::has(jsonSettings, c_flags, json::JsonValueType::Number))
{
settings.flags = (long)jsonSettings.GetNamedNumber(c_flags);
}
if (json::has(jsonSettings, c_searchText, json::JsonValueType::String))
{
settings.searchText = jsonSettings.GetNamedString(c_searchText);
}
if (json::has(jsonSettings, c_replaceText, json::JsonValueType::String))
{
settings.replaceText = jsonSettings.GetNamedString(c_replaceText);
}
}
catch (const winrt::hresult_error&) { }
}
}
CSettings& CSettingsInstance()
{
static CSettings instance;
return instance;
}
HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk)
{
return CRenameMRU::CreateInstance(c_mruSearchRegPath, CSettings::GetMaxMRUSize(), ppUnk);
return CRenameMRU::CreateInstance(c_mruSearchRegPath, CSettingsInstance().GetMaxMRUSize(), ppUnk);
}
HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk)
{
return CRenameMRU::CreateInstance(c_mruReplaceRegPath, CSettings::GetMaxMRUSize(), ppUnk);
return CRenameMRU::CreateInstance(c_mruReplaceRegPath, CSettingsInstance().GetMaxMRUSize(), ppUnk);
}

View File

@@ -1,45 +1,124 @@
#pragma once
#include "json.h"
#include <string>
class CSettings
{
public:
static const int MAX_INPUT_STRING_LEN = 1024;
static bool GetEnabled();
static bool SetEnabled(_In_ bool enabled);
CSettings();
static bool GetShowIconOnMenu();
static bool SetShowIconOnMenu(_In_ bool show);
bool GetEnabled();
static bool GetExtendedContextMenuOnly();
static bool SetExtendedContextMenuOnly(_In_ bool extendedOnly);
void SetEnabled(bool enabled);
static bool GetPersistState();
static bool SetPersistState(_In_ bool extendedOnly);
inline bool GetShowIconOnMenu() const
{
return settings.showIconOnMenu;
}
static bool GetMRUEnabled();
static bool SetMRUEnabled(_In_ bool enabled);
inline void SetShowIconOnMenu(bool show)
{
settings.showIconOnMenu = show;
}
static DWORD GetMaxMRUSize();
static bool SetMaxMRUSize(_In_ DWORD maxMRUSize);
inline bool GetExtendedContextMenuOnly() const
{
return settings.extendedContextMenuOnly;
}
static DWORD GetFlags();
static bool SetFlags(_In_ DWORD flags);
inline void SetExtendedContextMenuOnly(bool extendedOnly)
{
settings.extendedContextMenuOnly = extendedOnly;
}
static bool GetSearchText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf);
static bool SetSearchText(_In_ PCWSTR text);
inline bool GetPersistState() const
{
return settings.persistState;
}
static bool GetReplaceText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf);
static bool SetReplaceText(_In_ PCWSTR text);
inline void SetPersistState(bool persistState)
{
settings.persistState = persistState;
}
inline bool GetMRUEnabled() const
{
return settings.MRUEnabled;
}
inline void SetMRUEnabled(bool MRUEnabled)
{
settings.MRUEnabled = MRUEnabled;
}
inline long GetMaxMRUSize() const
{
return settings.maxMRUSize;
}
inline void SetMaxMRUSize(long maxMRUSize)
{
settings.maxMRUSize = maxMRUSize;
}
inline long GetFlags() const
{
return settings.flags;
}
inline void SetFlags(long flags)
{
settings.flags = flags;
}
inline const std::wstring& GetSearchText() const
{
return settings.searchText;
}
inline void SetSearchText(const std::wstring& text)
{
settings.searchText = text;
}
inline const std::wstring& GetReplaceText() const
{
return settings.replaceText;
}
inline void SetReplaceText(const std::wstring& text)
{
settings.replaceText = text;
}
void LoadPowerRenameData();
void SavePowerRenameData() const;
private:
static bool GetRegBoolValue(_In_ PCWSTR valueName, _In_ bool defaultValue);
static bool SetRegBoolValue(_In_ PCWSTR valueName, _In_ bool value);
static bool SetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD value);
static DWORD GetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD defaultValue);
static bool SetRegStringValue(_In_ PCWSTR valueName, _In_ PCWSTR value);
static bool GetRegStringValue(_In_ PCWSTR valueName, __out_ecount(cchBuf) PWSTR value, DWORD cchBuf);
struct Settings
{
bool showIconOnMenu{ true };
bool extendedContextMenuOnly{ false }; // Disabled by default.
bool persistState{ true };
bool MRUEnabled{ true };
long maxMRUSize{ 10 };
long flags{ 0 };
std::wstring searchText{};
std::wstring replaceText{};
};
void MigrateSettingsFromRegistry();
void ParseJsonSettings();
Settings settings;
std::wstring jsonFilePath;
};
CSettings& CSettingsInstance();
HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk);
HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk);

View File

@@ -19,3 +19,5 @@
#include <shlwapi.h>
#include <ProjectTelemetry.h>
#pragma comment(lib, "windowsapp")

View File

@@ -79,11 +79,11 @@ void Trace::SettingsChanged() noexcept
"PowerRename_SettingsChanged",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingBoolean(CSettings::GetEnabled(), "IsEnabled"),
TraceLoggingBoolean(CSettings::GetShowIconOnMenu(), "ShowIconOnMenu"),
TraceLoggingBoolean(CSettings::GetExtendedContextMenuOnly(), "ExtendedContextMenuOnly"),
TraceLoggingBoolean(CSettings::GetPersistState(), "PersistState"),
TraceLoggingBoolean(CSettings::GetMRUEnabled(), "IsMRUEnabled"),
TraceLoggingUInt64(CSettings::GetMaxMRUSize(), "MaxMRUSize"),
TraceLoggingUInt64(CSettings::GetFlags(), "Flags"));
TraceLoggingBoolean(CSettingsInstance().GetEnabled(), "IsEnabled"),
TraceLoggingBoolean(CSettingsInstance().GetShowIconOnMenu(), "ShowIconOnMenu"),
TraceLoggingBoolean(CSettingsInstance().GetExtendedContextMenuOnly(), "ExtendedContextMenuOnly"),
TraceLoggingBoolean(CSettingsInstance().GetPersistState(), "PersistState"),
TraceLoggingBoolean(CSettingsInstance().GetMRUEnabled(), "IsMRUEnabled"),
TraceLoggingUInt64(CSettingsInstance().GetMaxMRUSize(), "MaxMRUSize"),
TraceLoggingUInt64(CSettingsInstance().GetFlags(), "Flags"));
}