[PowerRename] Fix tests inconsistency, improve test performance (#8129)

* Move retrieveing file attibutes to PowerRenameRegex
Move file attributes unit tests to PowerRenameRegexTests
Add file time field to MockPowerRenameItem

* Add file attributes unittests to PowerRenameManagerTests

* Change variable name

* Rearrange function arguments

* Check if file attributes are used only once

* Change variable name LocalTime -> fileTime, date -> time

* Set fileTime as a member of PowerRenameRegEx rather than passing as an argument

* Change function name isFileAttributesUsed() -> isFileTimeUsed()
Check before resetting fileTime

* Fix small bugs

* Fix typos

* Refactor for readability, move free calls to reachable places

* Fix search for area empty bug
searchTerm being empty is not an invalid argument rather it must return OK without any operation
Tests must check if Replace()  returns S_OK becuase later it checks its result

* Check return values of method calls in PowerRenameManager
Remove received argments checks from some methods because argument being null or empty string doesnt mean it is invalid or method fails

* Fix formatting. Remove overlooked comment. Fix error message.

* Change HRESULT declarations according to coding style

* Fix unhandled case. Refactor.
This commit is contained in:
Mehmet Murat Akburak 2020-12-14 12:28:12 +03:00 committed by GitHub
parent 4403876320
commit da22e21a0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 552 additions and 334 deletions

View File

@ -2126,6 +2126,7 @@ tif
TILEDWINDOW TILEDWINDOW
timeinfo timeinfo
Timeline Timeline
timeunion
timeutil timeutil
titlecase titlecase
tlb tlb
@ -2191,6 +2192,7 @@ uint
UIPI UIPI
UIs UIs
ul ul
ULARGE
ULLONG ULLONG
ulong ulong
unchecks unchecks

View File

@ -123,8 +123,8 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
{ {
Trace::Invoked(); Trace::Invoked();
InvokeStruct* pInvokeData = new (std::nothrow) InvokeStruct; InvokeStruct* pInvokeData = new (std::nothrow) InvokeStruct;
hr = pInvokeData ? S_OK : E_OUTOFMEMORY; hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (pInvokeData)
{ {
pInvokeData->hwndParent = pici->hwnd; pInvokeData->hwndParent = pici->hwnd;
hr = CoMarshalInterThreadInterfaceInStream(__uuidof(m_spdo), m_spdo, &(pInvokeData->pstrm)); hr = CoMarshalInterThreadInterfaceInStream(__uuidof(m_spdo), m_spdo, &(pInvokeData->pstrm));
@ -248,8 +248,8 @@ HRESULT __stdcall CPowerRenameMenu::Invoke(IShellItemArray* psiItemArray, IBindC
#endif #endif
Trace::Invoked(); Trace::Invoked();
InvokeStruct* pInvokeData = new (std::nothrow) InvokeStruct; InvokeStruct* pInvokeData = new (std::nothrow) InvokeStruct;
HRESULT hr = pInvokeData ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (pInvokeData)
{ {
pInvokeData->hwndParent = nullptr; pInvokeData->hwndParent = nullptr;
hr = CoMarshalInterThreadInterfaceInStream(__uuidof(psiItemArray), psiItemArray, &(pInvokeData->pstrm)); hr = CoMarshalInterThreadInterfaceInStream(__uuidof(psiItemArray), psiItemArray, &(pInvokeData->pstrm));

View File

@ -123,9 +123,10 @@ STDAPI DllCanUnloadNow(void)
// //
STDAPI DllGetClassObject(_In_ REFCLSID clsid, _In_ REFIID riid, _Outptr_ void** ppv) STDAPI DllGetClassObject(_In_ REFCLSID clsid, _In_ REFIID riid, _Outptr_ void** ppv)
{ {
HRESULT hr = E_FAIL;
*ppv = NULL; *ppv = NULL;
CPowerRenameClassFactory* pClassFactory = new CPowerRenameClassFactory(clsid); CPowerRenameClassFactory* pClassFactory = new CPowerRenameClassFactory(clsid);
HRESULT hr = pClassFactory->QueryInterface(riid, ppv); hr = pClassFactory->QueryInterface(riid, ppv);
pClassFactory->Release(); pClassFactory->Release();
return hr; return hr;
} }

View File

@ -9,8 +9,8 @@ namespace fs = std::filesystem;
HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source) HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source)
{ {
HRESULT hr = (source && wcslen(source) > 0) ? S_OK : E_INVALIDARG; HRESULT hr = E_INVALIDARG;
if (SUCCEEDED(hr)) if (source)
{ {
PWSTR newName = nullptr; PWSTR newName = nullptr;
hr = SHStrDup(source, &newName); hr = SHStrDup(source, &newName);
@ -38,8 +38,8 @@ HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source)
HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, DWORD flags) HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, DWORD flags)
{ {
std::locale::global(std::locale("")); std::locale::global(std::locale(""));
HRESULT hr = (source && wcslen(source) > 0 && flags) ? S_OK : E_INVALIDARG; HRESULT hr = E_INVALIDARG;
if (SUCCEEDED(hr)) if (source && flags)
{ {
if (flags & Uppercase) if (flags & Uppercase)
{ {
@ -169,7 +169,7 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour
return hr; return hr;
} }
bool isFileAttributesUsed(_In_ PCWSTR source) bool isFileTimeUsed(_In_ PCWSTR source)
{ {
bool used = false; bool used = false;
std::wstring patterns[] = { L"(([^\\$]|^)(\\$\\$)*)\\$Y", L"(([^\\$]|^)(\\$\\$)*)\\$M", L"(([^\\$]|^)(\\$\\$)*)\\$D", std::wstring patterns[] = { L"(([^\\$]|^)(\\$\\$)*)\\$Y", L"(([^\\$]|^)(\\$\\$)*)\\$M", L"(([^\\$]|^)(\\$\\$)*)\\$D",
@ -185,11 +185,11 @@ bool isFileAttributesUsed(_In_ PCWSTR source)
return used; return used;
} }
HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME LocalTime) HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME fileTime)
{ {
std::locale::global(std::locale("")); std::locale::global(std::locale(""));
HRESULT hr = (source && wcslen(source) > 0) ? S_OK : E_INVALIDARG; HRESULT hr = E_INVALIDARG;
if (SUCCEEDED(hr)) if (source && wcslen(source) > 0)
{ {
std::wstring res(source); std::wstring res(source);
wchar_t replaceTerm[MAX_PATH] = { 0 }; wchar_t replaceTerm[MAX_PATH] = { 0 };
@ -201,72 +201,72 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY
StringCchCopy(localeName, LOCALE_NAME_MAX_LENGTH, L"en_US"); StringCchCopy(localeName, LOCALE_NAME_MAX_LENGTH, L"en_US");
} }
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%04d"), L"$01", LocalTime.wYear); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%04d"), L"$01", fileTime.wYear);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$YYYY"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$YYYY"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", (LocalTime.wYear % 100)); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", (fileTime.wYear % 100));
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$YY"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$YY"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", (LocalTime.wYear % 10)); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", (fileTime.wYear % 10));
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$Y"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$Y"), replaceTerm);
GetDateFormatEx(localeName, NULL, &LocalTime, L"MMMM", formattedDate, MAX_PATH, NULL); GetDateFormatEx(localeName, NULL, &fileTime, L"MMMM", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]); formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMMM"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMMM"), replaceTerm);
GetDateFormatEx(localeName, NULL, &LocalTime, L"MMM", formattedDate, MAX_PATH, NULL); GetDateFormatEx(localeName, NULL, &fileTime, L"MMM", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]); formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMM"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMM"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", LocalTime.wMonth); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMonth);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MM"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MM"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", LocalTime.wMonth); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMonth);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$M"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$M"), replaceTerm);
GetDateFormatEx(localeName, NULL, &LocalTime, L"dddd", formattedDate, MAX_PATH, NULL); GetDateFormatEx(localeName, NULL, &fileTime, L"dddd", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]); formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DDDD"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DDDD"), replaceTerm);
GetDateFormatEx(localeName, NULL, &LocalTime, L"ddd", formattedDate, MAX_PATH, NULL); GetDateFormatEx(localeName, NULL, &fileTime, L"ddd", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]); formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DDD"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DDD"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", LocalTime.wDay); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wDay);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DD"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DD"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", LocalTime.wDay); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wDay);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$D"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$D"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", LocalTime.wHour); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wHour);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$hh"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$hh"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", LocalTime.wHour); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wHour);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$h"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$h"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", LocalTime.wMinute); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMinute);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$mm"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$mm"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", LocalTime.wMinute); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMinute);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$m"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$m"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", LocalTime.wSecond); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wSecond);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ss"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ss"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", LocalTime.wSecond); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wSecond);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$s"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$s"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%03d"), L"$01", LocalTime.wMilliseconds); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%03d"), L"$01", fileTime.wMilliseconds);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$fff"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$fff"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", LocalTime.wMilliseconds/10); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMilliseconds/10);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ff"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ff"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", LocalTime.wMilliseconds/100); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMilliseconds/100);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$f"), replaceTerm); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$f"), replaceTerm);
hr = StringCchCopy(result, cchMax, res.c_str()); hr = StringCchCopy(result, cchMax, res.c_str());
@ -346,12 +346,11 @@ HRESULT _ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ IPowerRenameManager* ps
HRESULT EnumerateDataObject(_In_ IUnknown* dataSource, _In_ IPowerRenameManager* psrm) HRESULT EnumerateDataObject(_In_ IUnknown* dataSource, _In_ IPowerRenameManager* psrm)
{ {
CComPtr<IShellItemArray> spsia; CComPtr<IShellItemArray> spsia;
HRESULT hr = _GetShellItemArrayFromDataOject(dataSource, &spsia); HRESULT hr = E_FAIL;
if (SUCCEEDED(hr)) if (SUCCEEDED(_GetShellItemArrayFromDataOject(dataSource, &spsia)))
{ {
CComPtr<IEnumShellItems> spesi; CComPtr<IEnumShellItems> spesi;
hr = spsia->EnumItems(&spesi); if (SUCCEEDED(spsia->EnumItems(&spesi)))
if (SUCCEEDED(hr))
{ {
hr = _ParseEnumItems(spesi, psrm); hr = _ParseEnumItems(spesi, psrm);
} }

View File

@ -5,8 +5,8 @@
HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source); HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source);
HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, DWORD flags); HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, DWORD flags);
HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME LocalTime); HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME fileTime);
bool isFileAttributesUsed(_In_ PCWSTR source); bool isFileTimeUsed(_In_ PCWSTR source);
bool DataObjectContainsRenamableItem(_In_ IUnknown* dataSource); bool DataObjectContainsRenamableItem(_In_ IUnknown* dataSource);
HRESULT EnumerateDataObject(_In_ IUnknown* pdo, _In_ IPowerRenameManager* psrm); HRESULT EnumerateDataObject(_In_ IUnknown* pdo, _In_ IPowerRenameManager* psrm);
BOOL GetEnumeratedFileName( BOOL GetEnumeratedFileName(

View File

@ -31,6 +31,7 @@ public:
IFACEMETHOD(OnSearchTermChanged)(_In_ PCWSTR searchTerm) = 0; IFACEMETHOD(OnSearchTermChanged)(_In_ PCWSTR searchTerm) = 0;
IFACEMETHOD(OnReplaceTermChanged)(_In_ PCWSTR replaceTerm) = 0; IFACEMETHOD(OnReplaceTermChanged)(_In_ PCWSTR replaceTerm) = 0;
IFACEMETHOD(OnFlagsChanged)(_In_ DWORD flags) = 0; IFACEMETHOD(OnFlagsChanged)(_In_ DWORD flags) = 0;
IFACEMETHOD(OnFileTimeChanged)(_In_ SYSTEMTIME fileTime) = 0;
}; };
interface __declspec(uuid("E3ED45B5-9CE0-47E2-A595-67EB950B9B72")) IPowerRenameRegEx : public IUnknown interface __declspec(uuid("E3ED45B5-9CE0-47E2-A595-67EB950B9B72")) IPowerRenameRegEx : public IUnknown
@ -44,6 +45,8 @@ public:
IFACEMETHOD(PutReplaceTerm)(_In_ PCWSTR replaceTerm) = 0; IFACEMETHOD(PutReplaceTerm)(_In_ PCWSTR replaceTerm) = 0;
IFACEMETHOD(GetFlags)(_Out_ DWORD* flags) = 0; IFACEMETHOD(GetFlags)(_Out_ DWORD* flags) = 0;
IFACEMETHOD(PutFlags)(_In_ DWORD flags) = 0; IFACEMETHOD(PutFlags)(_In_ DWORD flags) = 0;
IFACEMETHOD(PutFileTime)(_In_ SYSTEMTIME fileTime) = 0;
IFACEMETHOD(ResetFileTime)() = 0;
IFACEMETHOD(Replace)(_In_ PCWSTR source, _Outptr_ PWSTR* result) = 0; IFACEMETHOD(Replace)(_In_ PCWSTR source, _Outptr_ PWSTR* result) = 0;
}; };
@ -51,7 +54,7 @@ interface __declspec(uuid("C7F59201-4DE1-4855-A3A2-26FC3279C8A5")) IPowerRenameI
{ {
public: public:
IFACEMETHOD(GetPath)(_Outptr_ PWSTR* path) = 0; IFACEMETHOD(GetPath)(_Outptr_ PWSTR* path) = 0;
IFACEMETHOD(GetDate)(_Outptr_ SYSTEMTIME* date) = 0; IFACEMETHOD(GetTime)(_Outptr_ SYSTEMTIME* time) = 0;
IFACEMETHOD(GetShellItem)(_Outptr_ IShellItem** ppsi) = 0; IFACEMETHOD(GetShellItem)(_Outptr_ IShellItem** ppsi) = 0;
IFACEMETHOD(GetOriginalName)(_Outptr_ PWSTR* originalName) = 0; IFACEMETHOD(GetOriginalName)(_Outptr_ PWSTR* originalName) = 0;
IFACEMETHOD(GetNewName)(_Outptr_ PWSTR* newName) = 0; IFACEMETHOD(GetNewName)(_Outptr_ PWSTR* newName) = 0;

View File

@ -34,19 +34,24 @@ IFACEMETHODIMP CPowerRenameItem::GetPath(_Outptr_ PWSTR* path)
{ {
*path = nullptr; *path = nullptr;
CSRWSharedAutoLock lock(&m_lock); CSRWSharedAutoLock lock(&m_lock);
HRESULT hr = m_path ? S_OK : E_FAIL; HRESULT hr = E_FAIL;
if (SUCCEEDED(hr)) if (m_path)
{ {
hr = SHStrDup(m_path, path); hr = SHStrDup(m_path, path);
} }
return hr; return hr;
} }
IFACEMETHODIMP CPowerRenameItem::GetDate(_Outptr_ SYSTEMTIME* date) IFACEMETHODIMP CPowerRenameItem::GetTime(_Outptr_ SYSTEMTIME* time)
{ {
CSRWSharedAutoLock lock(&m_lock); CSRWSharedAutoLock lock(&m_lock);
HRESULT hr = m_isDateParsed ? S_OK : E_FAIL ; HRESULT hr = E_FAIL ;
if (!m_isDateParsed)
if (m_isTimeParsed)
{
hr = S_OK;
}
else
{ {
HANDLE hFile = CreateFileW(m_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); HANDLE hFile = CreateFileW(m_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hFile != INVALID_HANDLE_VALUE) if (hFile != INVALID_HANDLE_VALUE)
@ -59,8 +64,8 @@ IFACEMETHODIMP CPowerRenameItem::GetDate(_Outptr_ SYSTEMTIME* date)
{ {
if (SystemTimeToTzSpecificLocalTime(NULL, &SystemTime, &LocalTime)) if (SystemTimeToTzSpecificLocalTime(NULL, &SystemTime, &LocalTime))
{ {
m_date = LocalTime; m_time = LocalTime;
m_isDateParsed = true; m_isTimeParsed = true;
hr = S_OK; hr = S_OK;
} }
} }
@ -68,7 +73,7 @@ IFACEMETHODIMP CPowerRenameItem::GetDate(_Outptr_ SYSTEMTIME* date)
} }
CloseHandle(hFile); CloseHandle(hFile);
} }
*date = m_date; *time = m_time;
return hr; return hr;
} }
@ -80,8 +85,8 @@ IFACEMETHODIMP CPowerRenameItem::GetShellItem(_Outptr_ IShellItem** ppsi)
IFACEMETHODIMP CPowerRenameItem::GetOriginalName(_Outptr_ PWSTR* originalName) IFACEMETHODIMP CPowerRenameItem::GetOriginalName(_Outptr_ PWSTR* originalName)
{ {
CSRWSharedAutoLock lock(&m_lock); CSRWSharedAutoLock lock(&m_lock);
HRESULT hr = m_originalName ? S_OK : E_FAIL; HRESULT hr = E_FAIL;
if (SUCCEEDED(hr)) if (m_originalName)
{ {
hr = SHStrDup(m_originalName, originalName); hr = SHStrDup(m_originalName, originalName);
} }
@ -104,8 +109,8 @@ IFACEMETHODIMP CPowerRenameItem::PutNewName(_In_opt_ PCWSTR newName)
IFACEMETHODIMP CPowerRenameItem::GetNewName(_Outptr_ PWSTR* newName) IFACEMETHODIMP CPowerRenameItem::GetNewName(_Outptr_ PWSTR* newName)
{ {
CSRWSharedAutoLock lock(&m_lock); CSRWSharedAutoLock lock(&m_lock);
HRESULT hr = m_newName ? S_OK : E_FAIL; HRESULT hr = S_OK;
if (SUCCEEDED(hr)) if (m_newName)
{ {
hr = SHStrDup(m_newName, newName); hr = SHStrDup(m_newName, newName);
} }
@ -217,9 +222,10 @@ HRESULT CPowerRenameItem::s_CreateInstance(_In_opt_ IShellItem* psi, _In_ REFIID
*resultInterface = nullptr; *resultInterface = nullptr;
CPowerRenameItem *newRenameItem = new CPowerRenameItem(); CPowerRenameItem *newRenameItem = new CPowerRenameItem();
HRESULT hr = newRenameItem ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (newRenameItem)
{ {
hr = S_OK ;
if (psi != nullptr) if (psi != nullptr)
{ {
hr = newRenameItem->_Init(psi); hr = newRenameItem->_Init(psi);

View File

@ -15,7 +15,7 @@ public:
// IPowerRenameItem // IPowerRenameItem
IFACEMETHODIMP GetPath(_Outptr_ PWSTR* path); IFACEMETHODIMP GetPath(_Outptr_ PWSTR* path);
IFACEMETHODIMP GetDate(_Outptr_ SYSTEMTIME* date); IFACEMETHODIMP GetTime(_Outptr_ SYSTEMTIME* time);
IFACEMETHODIMP GetShellItem(_Outptr_ IShellItem** ppsi); IFACEMETHODIMP GetShellItem(_Outptr_ IShellItem** ppsi);
IFACEMETHODIMP GetOriginalName(_Outptr_ PWSTR* originalName); IFACEMETHODIMP GetOriginalName(_Outptr_ PWSTR* originalName);
IFACEMETHODIMP PutNewName(_In_opt_ PCWSTR newName); IFACEMETHODIMP PutNewName(_In_opt_ PCWSTR newName);
@ -50,7 +50,7 @@ protected:
bool m_selected = true; bool m_selected = true;
bool m_isFolder = false; bool m_isFolder = false;
bool m_isDateParsed = false; bool m_isTimeParsed = false;
bool m_canRename = true; bool m_canRename = true;
int m_id = -1; int m_id = -1;
int m_iconIndex = -1; int m_iconIndex = -1;
@ -59,7 +59,7 @@ protected:
PWSTR m_path = nullptr; PWSTR m_path = nullptr;
PWSTR m_originalName = nullptr; PWSTR m_originalName = nullptr;
PWSTR m_newName = nullptr; PWSTR m_newName = nullptr;
SYSTEMTIME m_date; SYSTEMTIME m_time = {0};
CSRWLock m_lock; CSRWLock m_lock;
long m_refCount = 0; long m_refCount = 0;
}; };

View File

@ -8,6 +8,7 @@
#include "window_helpers.h" #include "window_helpers.h"
#include <filesystem> #include <filesystem>
#include "trace.h" #include "trace.h"
#include <winrt/base.h>
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -415,12 +416,18 @@ IFACEMETHODIMP CPowerRenameManager::OnFlagsChanged(_In_ DWORD flags)
return S_OK; return S_OK;
} }
IFACEMETHODIMP CPowerRenameManager::OnFileTimeChanged(_In_ SYSTEMTIME /*fileTime*/)
{
_PerformRegExRename();
return S_OK;
}
HRESULT CPowerRenameManager::s_CreateInstance(_Outptr_ IPowerRenameManager** ppsrm) HRESULT CPowerRenameManager::s_CreateInstance(_Outptr_ IPowerRenameManager** ppsrm)
{ {
*ppsrm = nullptr; *ppsrm = nullptr;
CPowerRenameManager *psrm = new CPowerRenameManager(); CPowerRenameManager *psrm = new CPowerRenameManager();
HRESULT hr = psrm ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (psrm)
{ {
hr = psrm->_Init(); hr = psrm->_Init();
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
@ -645,16 +652,20 @@ HRESULT CPowerRenameManager::_PerformFileOperation()
HRESULT CPowerRenameManager::_CreateFileOpWorkerThread() HRESULT CPowerRenameManager::_CreateFileOpWorkerThread()
{ {
WorkerThreadData* pwtd = new WorkerThreadData; WorkerThreadData* pwtd = new WorkerThreadData;
HRESULT hr = pwtd ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (pwtd)
{ {
pwtd->hwndManager = m_hwndMessage; pwtd->hwndManager = m_hwndMessage;
pwtd->startEvent = m_startRegExWorkerEvent; pwtd->startEvent = m_startRegExWorkerEvent;
pwtd->cancelEvent = nullptr; pwtd->cancelEvent = nullptr;
pwtd->spsrm = this; pwtd->spsrm = this;
m_fileOpWorkerThreadHandle = CreateThread(nullptr, 0, s_fileOpWorkerThread, pwtd, 0, nullptr); m_fileOpWorkerThreadHandle = CreateThread(nullptr, 0, s_fileOpWorkerThread, pwtd, 0, nullptr);
hr = (m_fileOpWorkerThreadHandle) ? S_OK : E_FAIL; hr = E_FAIL;
if (FAILED(hr)) if (m_fileOpWorkerThreadHandle)
{
hr = S_OK;
}
else
{ {
delete pwtd; delete pwtd;
} }
@ -791,8 +802,8 @@ HRESULT CPowerRenameManager::_PerformRegExRename()
HRESULT CPowerRenameManager::_CreateRegExWorkerThread() HRESULT CPowerRenameManager::_CreateRegExWorkerThread()
{ {
WorkerThreadData* pwtd = new WorkerThreadData; WorkerThreadData* pwtd = new WorkerThreadData;
HRESULT hr = pwtd ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (pwtd)
{ {
pwtd->hwndManager = m_hwndMessage; pwtd->hwndManager = m_hwndMessage;
pwtd->startEvent = m_startRegExWorkerEvent; pwtd->startEvent = m_startRegExWorkerEvent;
@ -800,8 +811,12 @@ HRESULT CPowerRenameManager::_CreateRegExWorkerThread()
pwtd->hwndParent = m_hwndParent; pwtd->hwndParent = m_hwndParent;
pwtd->spsrm = this; pwtd->spsrm = this;
m_regExWorkerThreadHandle = CreateThread(nullptr, 0, s_regexWorkerThread, pwtd, 0, nullptr); m_regExWorkerThreadHandle = CreateThread(nullptr, 0, s_regexWorkerThread, pwtd, 0, nullptr);
hr = (m_regExWorkerThreadHandle) ? S_OK : E_FAIL; hr = E_FAIL;
if (FAILED(hr)) if (m_regExWorkerThreadHandle)
{
hr = S_OK;
}
else
{ {
delete pwtd; delete pwtd;
} }
@ -812,8 +827,9 @@ HRESULT CPowerRenameManager::_CreateRegExWorkerThread()
DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv) DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv)
{ {
if (SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) try
{ {
winrt::check_hresult(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
WorkerThreadData* pwtd = reinterpret_cast<WorkerThreadData*>(pv); WorkerThreadData* pwtd = reinterpret_cast<WorkerThreadData*>(pv);
if (pwtd) if (pwtd)
{ {
@ -823,192 +839,201 @@ DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv)
if (WaitForSingleObject(pwtd->startEvent, INFINITE) == WAIT_OBJECT_0) if (WaitForSingleObject(pwtd->startEvent, INFINITE) == WAIT_OBJECT_0)
{ {
CComPtr<IPowerRenameRegEx> spRenameRegEx; CComPtr<IPowerRenameRegEx> spRenameRegEx;
if (SUCCEEDED(pwtd->spsrm->GetRenameRegEx(&spRenameRegEx)))
winrt::check_hresult(pwtd->spsrm->GetRenameRegEx(&spRenameRegEx));
DWORD flags = 0;
winrt::check_hresult(spRenameRegEx->GetFlags(&flags));
PWSTR replaceTerm = nullptr;
bool useFileTime = false;
winrt::check_hresult(spRenameRegEx->GetReplaceTerm(&replaceTerm));
if (isFileTimeUsed(replaceTerm))
{ {
DWORD flags = 0; useFileTime = true;
spRenameRegEx->GetFlags(&flags); }
UINT itemCount = 0; UINT itemCount = 0;
unsigned long itemEnumIndex = 1; unsigned long itemEnumIndex = 1;
pwtd->spsrm->GetItemCount(&itemCount); winrt::check_hresult(pwtd->spsrm->GetItemCount(&itemCount));
for (UINT u = 0; u <= itemCount; u++) for (UINT u = 0; u < itemCount; u++)
{
// Check if cancel event is signaled
if (WaitForSingleObject(pwtd->cancelEvent, 0) == WAIT_OBJECT_0)
{ {
// Check if cancel event is signaled // Canceled from manager
if (WaitForSingleObject(pwtd->cancelEvent, 0) == WAIT_OBJECT_0) // Send the manager thread the canceled message
PostMessage(pwtd->hwndManager, SRM_REGEX_CANCELED, GetCurrentThreadId(), 0);
break;
}
CComPtr<IPowerRenameItem> spItem;
winrt::check_hresult(pwtd->spsrm->GetItemByIndex(u, &spItem));
int id = -1;
winrt::check_hresult(spItem->GetId(&id));
bool isFolder = false;
bool isSubFolderContent = false;
winrt::check_hresult(spItem->GetIsFolder(&isFolder));
winrt::check_hresult(spItem->GetIsSubFolderContent(&isSubFolderContent));
if ((isFolder && (flags & PowerRenameFlags::ExcludeFolders)) ||
(!isFolder && (flags & PowerRenameFlags::ExcludeFiles)) ||
(isSubFolderContent && (flags & PowerRenameFlags::ExcludeSubfolders)))
{
// Exclude this item from renaming. Ensure new name is cleared.
winrt::check_hresult(spItem->PutNewName(nullptr));
// Send the manager thread the item processed message
PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);
continue;
}
PWSTR originalName = nullptr;
winrt::check_hresult(spItem->GetOriginalName(&originalName));
PWSTR currentNewName = nullptr;
winrt::check_hresult(spItem->GetNewName(&currentNewName));
wchar_t sourceName[MAX_PATH] = { 0 };
if (flags & NameOnly)
{
StringCchCopy(sourceName, ARRAYSIZE(sourceName), fs::path(originalName).stem().c_str());
}
else if (flags & ExtensionOnly)
{
std::wstring extension = fs::path(originalName).extension().wstring();
if (!extension.empty() && extension.front() == '.')
{ {
// Canceled from manager extension = extension.erase(0, 1);
// Send the manager thread the canceled message
PostMessage(pwtd->hwndManager, SRM_REGEX_CANCELED, GetCurrentThreadId(), 0);
break;
} }
StringCchCopy(sourceName, ARRAYSIZE(sourceName), extension.c_str());
}
else
{
StringCchCopy(sourceName, ARRAYSIZE(sourceName), originalName);
}
CComPtr<IPowerRenameItem> spItem; SYSTEMTIME fileTime = { 0 };
if (SUCCEEDED(pwtd->spsrm->GetItemByIndex(u, &spItem)))
if (useFileTime)
{
winrt::check_hresult(spItem->GetTime(&fileTime));
winrt::check_hresult(spRenameRegEx->PutFileTime(fileTime));
}
PWSTR newName = nullptr;
// Failure here means we didn't match anything or had nothing to match
// Call put_newName with null in that case to reset it
winrt::check_hresult(spRenameRegEx->Replace(sourceName, &newName));
if (useFileTime)
{
winrt::check_hresult(spRenameRegEx->ResetFileTime());
}
wchar_t resultName[MAX_PATH] = { 0 };
PWSTR newNameToUse = nullptr;
// newName == nullptr likely means we have an empty search string. We should leave newNameToUse
// as nullptr so we clear the renamed column
// Except string transformation is selected.
if (newName == nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase))
{
SHStrDup(sourceName, &newName);
}
if (newName != nullptr)
{
newNameToUse = resultName;
if (flags & NameOnly)
{ {
int id = -1; StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s%s", newName, fs::path(originalName).extension().c_str());
spItem->GetId(&id); }
else if (flags & ExtensionOnly)
bool isFolder = false; {
bool isSubFolderContent = false; std::wstring extension = fs::path(originalName).extension().wstring();
spItem->GetIsFolder(&isFolder); if (!extension.empty())
spItem->GetIsSubFolderContent(&isSubFolderContent);
if ((isFolder && (flags & PowerRenameFlags::ExcludeFolders)) ||
(!isFolder && (flags & PowerRenameFlags::ExcludeFiles)) ||
(isSubFolderContent && (flags & PowerRenameFlags::ExcludeSubfolders)))
{ {
// Exclude this item from renaming. Ensure new name is cleared. StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s.%s", fs::path(originalName).stem().c_str(), newName);
spItem->PutNewName(nullptr);
// Send the manager thread the item processed message
PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);
continue;
} }
else
PWSTR originalName = nullptr;
if (SUCCEEDED(spItem->GetOriginalName(&originalName)))
{ {
PWSTR currentNewName = nullptr; StringCchCopy(resultName, ARRAYSIZE(resultName), originalName);
spItem->GetNewName(&currentNewName);
wchar_t sourceName[MAX_PATH] = { 0 };
if (flags & NameOnly)
{
StringCchCopy(sourceName, ARRAYSIZE(sourceName), fs::path(originalName).stem().c_str());
}
else if (flags & ExtensionOnly)
{
std::wstring extension = fs::path(originalName).extension().wstring();
if (!extension.empty() && extension.front() == '.')
{
extension = extension.erase(0, 1);
}
StringCchCopy(sourceName, ARRAYSIZE(sourceName), extension.c_str());
}
else
{
StringCchCopy(sourceName, ARRAYSIZE(sourceName), originalName);
}
wchar_t newReplaceTerm[MAX_PATH] = { 0 };
PWSTR replaceTerm = nullptr;
SYSTEMTIME LocalTime;
if (SUCCEEDED(spRenameRegEx->GetReplaceTerm(&replaceTerm)) && isFileAttributesUsed(replaceTerm))
{
if (SUCCEEDED(spItem->GetDate(&LocalTime)))
{
if (SUCCEEDED(GetDatedFileName(newReplaceTerm, ARRAYSIZE(newReplaceTerm), replaceTerm, LocalTime)))
{
spRenameRegEx->PutReplaceTerm(newReplaceTerm);
}
}
}
PWSTR newName = nullptr;
// Failure here means we didn't match anything or had nothing to match
// Call put_newName with null in that case to reset it
spRenameRegEx->Replace(sourceName, &newName);
spRenameRegEx->PutReplaceTerm(replaceTerm);
wchar_t resultName[MAX_PATH] = { 0 };
PWSTR newNameToUse = nullptr;
// newName == nullptr likely means we have an empty search string. We should leave newNameToUse
// as nullptr so we clear the renamed column
// Except string transformation is selected.
if (newName == nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase))
{
SHStrDup(sourceName, &newName);
}
if (newName != nullptr)
{
newNameToUse = resultName;
if (flags & NameOnly)
{
StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s%s", newName, fs::path(originalName).extension().c_str());
}
else if (flags & ExtensionOnly)
{
std::wstring extension = fs::path(originalName).extension().wstring();
if (!extension.empty())
{
StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s.%s", fs::path(originalName).stem().c_str(), newName);
}
else
{
StringCchCopy(resultName, ARRAYSIZE(resultName), originalName);
}
}
else
{
StringCchCopy(resultName, ARRAYSIZE(resultName), newName);
}
}
wchar_t trimmedName[MAX_PATH] = { 0 };
if (newNameToUse != nullptr && SUCCEEDED(GetTrimmedFileName(trimmedName, ARRAYSIZE(trimmedName), newNameToUse)))
{
newNameToUse = trimmedName;
}
wchar_t transformedName[MAX_PATH] = { 0 };
if (newNameToUse != nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase))
{
if (SUCCEEDED(GetTransformedFileName(transformedName, ARRAYSIZE(transformedName), newNameToUse, flags)))
{
newNameToUse = transformedName;
}
}
// No change from originalName so set newName to
// null so we clear it from our UI as well.
if (lstrcmp(originalName, newNameToUse) == 0)
{
newNameToUse = nullptr;
}
wchar_t uniqueName[MAX_PATH] = { 0 };
if (newNameToUse != nullptr && (flags & EnumerateItems))
{
unsigned long countUsed = 0;
if (GetEnumeratedFileName(uniqueName, ARRAYSIZE(uniqueName), newNameToUse, nullptr, itemEnumIndex, &countUsed))
{
newNameToUse = uniqueName;
}
itemEnumIndex++;
}
spItem->PutNewName(newNameToUse);
// Was there a change?
if (lstrcmp(currentNewName, newNameToUse) != 0)
{
// Send the manager thread the item processed message
PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);
}
CoTaskMemFree(newName);
CoTaskMemFree(replaceTerm);
CoTaskMemFree(currentNewName);
CoTaskMemFree(originalName);
} }
} }
else
{
StringCchCopy(resultName, ARRAYSIZE(resultName), newName);
}
} }
wchar_t trimmedName[MAX_PATH] = { 0 };
if (newNameToUse != nullptr)
{
winrt::check_hresult(GetTrimmedFileName(trimmedName, ARRAYSIZE(trimmedName), newNameToUse));
newNameToUse = trimmedName;
}
wchar_t transformedName[MAX_PATH] = { 0 };
if (newNameToUse != nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase))
{
winrt::check_hresult(GetTransformedFileName(transformedName, ARRAYSIZE(transformedName), newNameToUse, flags));
newNameToUse = transformedName;
}
// No change from originalName so set newName to
// null so we clear it from our UI as well.
if (lstrcmp(originalName, newNameToUse) == 0)
{
newNameToUse = nullptr;
}
wchar_t uniqueName[MAX_PATH] = { 0 };
if (newNameToUse != nullptr && (flags & EnumerateItems))
{
unsigned long countUsed = 0;
if (GetEnumeratedFileName(uniqueName, ARRAYSIZE(uniqueName), newNameToUse, nullptr, itemEnumIndex, &countUsed))
{
newNameToUse = uniqueName;
}
itemEnumIndex++;
}
winrt::check_hresult(spItem->PutNewName(newNameToUse));
// Was there a change?
if (lstrcmp(currentNewName, newNameToUse) != 0)
{
// Send the manager thread the item processed message
PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);
}
CoTaskMemFree(newName);
CoTaskMemFree(currentNewName);
CoTaskMemFree(originalName);
} }
CoTaskMemFree(replaceTerm);
} }
// Send the manager thread the completion message // Send the manager thread the completion message
PostMessage(pwtd->hwndManager, SRM_REGEX_COMPLETE, GetCurrentThreadId(), 0); PostMessage(pwtd->hwndManager, SRM_REGEX_COMPLETE, GetCurrentThreadId(), 0);
delete pwtd; delete pwtd;
} }
CoUninitialize(); CoUninitialize();
} }
catch (...)
{
MessageBox(NULL, L"RegexWorkerThread failed to execute.\nPlease report the bug to https://aka.ms/powerToysReportBug", L"PowerRename Error", MB_OK);
}
return 0; return 0;
} }

View File

@ -46,6 +46,7 @@ public:
IFACEMETHODIMP OnSearchTermChanged(_In_ PCWSTR searchTerm); IFACEMETHODIMP OnSearchTermChanged(_In_ PCWSTR searchTerm);
IFACEMETHODIMP OnReplaceTermChanged(_In_ PCWSTR replaceTerm); IFACEMETHODIMP OnReplaceTermChanged(_In_ PCWSTR replaceTerm);
IFACEMETHODIMP OnFlagsChanged(_In_ DWORD flags); IFACEMETHODIMP OnFlagsChanged(_In_ DWORD flags);
IFACEMETHODIMP OnFileTimeChanged(_In_ SYSTEMTIME fileTime);
static HRESULT s_CreateInstance(_Outptr_ IPowerRenameManager** ppsrm); static HRESULT s_CreateInstance(_Outptr_ IPowerRenameManager** ppsrm);

View File

@ -5,7 +5,7 @@
#include <string> #include <string>
#include <algorithm> #include <algorithm>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <helpers.cpp>
using namespace std; using namespace std;
using std::regex_error; using std::regex_error;
@ -76,8 +76,8 @@ IFACEMETHODIMP CPowerRenameRegEx::UnAdvise(_In_ DWORD cookie)
IFACEMETHODIMP CPowerRenameRegEx::GetSearchTerm(_Outptr_ PWSTR* searchTerm) IFACEMETHODIMP CPowerRenameRegEx::GetSearchTerm(_Outptr_ PWSTR* searchTerm)
{ {
*searchTerm = nullptr; *searchTerm = nullptr;
HRESULT hr = m_searchTerm ? S_OK : E_FAIL; HRESULT hr = S_OK;
if (SUCCEEDED(hr)) if (m_searchTerm)
{ {
CSRWSharedAutoLock lock(&m_lock); CSRWSharedAutoLock lock(&m_lock);
hr = SHStrDup(m_searchTerm, searchTerm); hr = SHStrDup(m_searchTerm, searchTerm);
@ -88,8 +88,8 @@ IFACEMETHODIMP CPowerRenameRegEx::GetSearchTerm(_Outptr_ PWSTR* searchTerm)
IFACEMETHODIMP CPowerRenameRegEx::PutSearchTerm(_In_ PCWSTR searchTerm) IFACEMETHODIMP CPowerRenameRegEx::PutSearchTerm(_In_ PCWSTR searchTerm)
{ {
bool changed = false; bool changed = false;
HRESULT hr = searchTerm ? S_OK : E_INVALIDARG; HRESULT hr = S_OK;
if (SUCCEEDED(hr)) if (searchTerm)
{ {
CSRWExclusiveAutoLock lock(&m_lock); CSRWExclusiveAutoLock lock(&m_lock);
if (m_searchTerm == nullptr || lstrcmp(searchTerm, m_searchTerm) != 0) if (m_searchTerm == nullptr || lstrcmp(searchTerm, m_searchTerm) != 0)
@ -111,8 +111,8 @@ IFACEMETHODIMP CPowerRenameRegEx::PutSearchTerm(_In_ PCWSTR searchTerm)
IFACEMETHODIMP CPowerRenameRegEx::GetReplaceTerm(_Outptr_ PWSTR* replaceTerm) IFACEMETHODIMP CPowerRenameRegEx::GetReplaceTerm(_Outptr_ PWSTR* replaceTerm)
{ {
*replaceTerm = nullptr; *replaceTerm = nullptr;
HRESULT hr = m_replaceTerm ? S_OK : E_FAIL; HRESULT hr = S_OK;
if (SUCCEEDED(hr)) if (m_replaceTerm)
{ {
CSRWSharedAutoLock lock(&m_lock); CSRWSharedAutoLock lock(&m_lock);
hr = SHStrDup(m_replaceTerm, replaceTerm); hr = SHStrDup(m_replaceTerm, replaceTerm);
@ -123,8 +123,8 @@ IFACEMETHODIMP CPowerRenameRegEx::GetReplaceTerm(_Outptr_ PWSTR* replaceTerm)
IFACEMETHODIMP CPowerRenameRegEx::PutReplaceTerm(_In_ PCWSTR replaceTerm) IFACEMETHODIMP CPowerRenameRegEx::PutReplaceTerm(_In_ PCWSTR replaceTerm)
{ {
bool changed = false; bool changed = false;
HRESULT hr = replaceTerm ? S_OK : E_INVALIDARG; HRESULT hr = S_OK;
if (SUCCEEDED(hr)) if (replaceTerm)
{ {
CSRWExclusiveAutoLock lock(&m_lock); CSRWExclusiveAutoLock lock(&m_lock);
if (m_replaceTerm == nullptr || lstrcmp(replaceTerm, m_replaceTerm) != 0) if (m_replaceTerm == nullptr || lstrcmp(replaceTerm, m_replaceTerm) != 0)
@ -159,13 +159,45 @@ IFACEMETHODIMP CPowerRenameRegEx::PutFlags(_In_ DWORD flags)
return S_OK; return S_OK;
} }
IFACEMETHODIMP CPowerRenameRegEx::PutFileTime(_In_ SYSTEMTIME fileTime)
{
union timeunion
{
FILETIME fileTime;
ULARGE_INTEGER ul;
};
timeunion ft1;
timeunion ft2;
SystemTimeToFileTime(&m_fileTime, &ft1.fileTime);
SystemTimeToFileTime(&fileTime, &ft2.fileTime);
if (ft2.ul.QuadPart != ft1.ul.QuadPart)
{
m_fileTime = fileTime;
m_useFileTime = true;
_OnFileTimeChanged();
}
return S_OK;
}
IFACEMETHODIMP CPowerRenameRegEx::ResetFileTime()
{
SYSTEMTIME ZERO = { 0 };
m_fileTime = ZERO;
m_useFileTime = false;
_OnFileTimeChanged();
return S_OK;
}
HRESULT CPowerRenameRegEx::s_CreateInstance(_Outptr_ IPowerRenameRegEx** renameRegEx) HRESULT CPowerRenameRegEx::s_CreateInstance(_Outptr_ IPowerRenameRegEx** renameRegEx)
{ {
*renameRegEx = nullptr; *renameRegEx = nullptr;
CPowerRenameRegEx *newRenameRegEx = new CPowerRenameRegEx(); CPowerRenameRegEx *newRenameRegEx = new CPowerRenameRegEx();
HRESULT hr = newRenameRegEx ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (newRenameRegEx)
{ {
hr = newRenameRegEx->QueryInterface(IID_PPV_ARGS(renameRegEx)); hr = newRenameRegEx->QueryInterface(IID_PPV_ARGS(renameRegEx));
newRenameRegEx->Release(); newRenameRegEx->Release();
@ -194,73 +226,90 @@ HRESULT CPowerRenameRegEx::Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result)
*result = nullptr; *result = nullptr;
CSRWSharedAutoLock lock(&m_lock); CSRWSharedAutoLock lock(&m_lock);
HRESULT hr = (source && wcslen(source) > 0 && m_searchTerm && wcslen(m_searchTerm) > 0) ? S_OK : E_INVALIDARG; HRESULT hr = S_OK;
if (SUCCEEDED(hr)) if (!(m_searchTerm && wcslen(m_searchTerm) > 0 && source && wcslen(source) > 0))
{ {
wstring res = source; return hr;
try }
wstring res = source;
try
{
// TODO: creating the regex could be costly. May want to cache this.
wchar_t newReplaceTerm[MAX_PATH] = { 0 };
bool fileTimeErrorOccurred = false;
if (m_useFileTime)
{ {
// TODO: creating the regex could be costly. May want to cache this. if (FAILED(GetDatedFileName(newReplaceTerm, ARRAYSIZE(newReplaceTerm), m_replaceTerm, m_fileTime)))
std::wstring sourceToUse(source); fileTimeErrorOccurred = true;
std::wstring searchTerm(m_searchTerm); }
std::wstring replaceTerm(m_replaceTerm ? wstring(m_replaceTerm) : wstring(L""));
replaceTerm = regex_replace(replaceTerm, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$[0]"), L"$1$$$0"); std::wstring sourceToUse(source);
replaceTerm = regex_replace(replaceTerm, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$([1-9])"), L"$1$0$4"); std::wstring searchTerm(m_searchTerm);
std::wstring replaceTerm(L"");
if (m_useFileTime && !fileTimeErrorOccurred)
{
replaceTerm = wstring(newReplaceTerm);
}
else if (m_replaceTerm)
{
replaceTerm = wstring(m_replaceTerm);
}
if (m_flags & UseRegularExpressions) replaceTerm = regex_replace(replaceTerm, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$[0]"), L"$1$$$0");
replaceTerm = regex_replace(replaceTerm, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$([1-9])"), L"$1$0$4");
if (m_flags & UseRegularExpressions)
{
if (_useBoostLib)
{ {
if (_useBoostLib) boost::wregex pattern(m_searchTerm, (!(m_flags & CaseSensitive)) ? boost::regex::icase | boost::regex::ECMAScript : boost::regex::ECMAScript);
if (m_flags & MatchAllOccurences)
{ {
boost::wregex pattern(m_searchTerm, (!(m_flags & CaseSensitive)) ? boost::regex::icase | boost::regex::ECMAScript : boost::regex::ECMAScript); res = boost::regex_replace(wstring(source), pattern, replaceTerm);
if (m_flags & MatchAllOccurences)
{
res = boost::regex_replace(wstring(source), pattern, replaceTerm);
}
else
{
res = boost::regex_replace(wstring(source), pattern, replaceTerm, boost::regex_constants::format_first_only);
}
} }
else else
{ {
std::wregex pattern(m_searchTerm, (!(m_flags & CaseSensitive)) ? regex_constants::icase | regex_constants::ECMAScript : regex_constants::ECMAScript); res = boost::regex_replace(wstring(source), pattern, replaceTerm, boost::regex_constants::format_first_only);
if (m_flags & MatchAllOccurences)
{
res = regex_replace(wstring(source), pattern, replaceTerm);
}
else
{
res = regex_replace(wstring(source), pattern, replaceTerm, regex_constants::format_first_only);
}
} }
} }
else else
{ {
// Simple search and replace std::wregex pattern(m_searchTerm, (!(m_flags & CaseSensitive)) ? regex_constants::icase | regex_constants::ECMAScript : regex_constants::ECMAScript);
size_t pos = 0; if (m_flags & MatchAllOccurences)
do
{ {
pos = _Find(sourceToUse, searchTerm, (!(m_flags & CaseSensitive)), pos); res = regex_replace(wstring(source), pattern, replaceTerm);
if (pos != std::string::npos) }
{ else
res = sourceToUse.replace(pos, searchTerm.length(), replaceTerm); {
pos += replaceTerm.length(); res = regex_replace(wstring(source), pattern, replaceTerm, regex_constants::format_first_only);
} }
if (!(m_flags & MatchAllOccurences))
{
break;
}
} while (pos != std::string::npos);
} }
hr = SHStrDup(res.c_str(), result);
} }
catch (regex_error e) else
{ {
hr = E_FAIL; // Simple search and replace
size_t pos = 0;
do
{
pos = _Find(sourceToUse, searchTerm, (!(m_flags & CaseSensitive)), pos);
if (pos != std::string::npos)
{
res = sourceToUse.replace(pos, searchTerm.length(), replaceTerm);
pos += replaceTerm.length();
}
if (!(m_flags & MatchAllOccurences))
{
break;
}
} while (pos != std::string::npos);
} }
hr = SHStrDup(res.c_str(), result);
}
catch (regex_error e)
{
hr = E_FAIL;
} }
return hr; return hr;
} }
@ -316,3 +365,16 @@ void CPowerRenameRegEx::_OnFlagsChanged()
} }
} }
} }
void CPowerRenameRegEx::_OnFileTimeChanged()
{
CSRWSharedAutoLock lock(&m_lockEvents);
for (auto it : m_renameRegExEvents)
{
if (it.pEvents)
{
it.pEvents->OnFileTimeChanged(m_fileTime);
}
}
}

View File

@ -25,6 +25,8 @@ public:
IFACEMETHODIMP PutReplaceTerm(_In_ PCWSTR replaceTerm); IFACEMETHODIMP PutReplaceTerm(_In_ PCWSTR replaceTerm);
IFACEMETHODIMP GetFlags(_Out_ DWORD* flags); IFACEMETHODIMP GetFlags(_Out_ DWORD* flags);
IFACEMETHODIMP PutFlags(_In_ DWORD flags); IFACEMETHODIMP PutFlags(_In_ DWORD flags);
IFACEMETHODIMP PutFileTime(_In_ SYSTEMTIME fileTime);
IFACEMETHODIMP ResetFileTime();
IFACEMETHODIMP Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result); IFACEMETHODIMP Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result);
static HRESULT s_CreateInstance(_Outptr_ IPowerRenameRegEx **renameRegEx); static HRESULT s_CreateInstance(_Outptr_ IPowerRenameRegEx **renameRegEx);
@ -36,6 +38,7 @@ protected:
void _OnSearchTermChanged(); void _OnSearchTermChanged();
void _OnReplaceTermChanged(); void _OnReplaceTermChanged();
void _OnFlagsChanged(); void _OnFlagsChanged();
void _OnFileTimeChanged();
size_t _Find(std::wstring data, std::wstring toSearch, bool caseInsensitive, size_t pos); size_t _Find(std::wstring data, std::wstring toSearch, bool caseInsensitive, size_t pos);
@ -44,6 +47,9 @@ protected:
PWSTR m_searchTerm = nullptr; PWSTR m_searchTerm = nullptr;
PWSTR m_replaceTerm = nullptr; PWSTR m_replaceTerm = nullptr;
SYSTEMTIME m_fileTime = {0};
bool m_useFileTime = false;
CSRWLock m_lock; CSRWLock m_lock;
CSRWLock m_lockEvents; CSRWLock m_lockEvents;

View File

@ -312,15 +312,16 @@ HRESULT CRenameMRU::CreateInstance(_In_ const std::wstring& filePath, _In_ const
{ {
*ppUnk = nullptr; *ppUnk = nullptr;
unsigned int maxMRUSize = CSettingsInstance().GetMaxMRUSize(); unsigned int maxMRUSize = CSettingsInstance().GetMaxMRUSize();
HRESULT hr = maxMRUSize > 0 ? S_OK : E_FAIL; HRESULT hr = E_FAIL;
if (SUCCEEDED(hr)) if (maxMRUSize > 0)
{ {
CRenameMRU* renameMRU = new CRenameMRU(maxMRUSize, filePath, regPath); CRenameMRU* renameMRU = new CRenameMRU(maxMRUSize, filePath, regPath);
hr = renameMRU ? S_OK : E_OUTOFMEMORY; hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (renameMRU)
{ {
renameMRU->QueryInterface(IID_PPV_ARGS(ppUnk)); renameMRU->QueryInterface(IID_PPV_ARGS(ppUnk));
renameMRU->Release(); renameMRU->Release();
hr = S_OK;
} }
} }

View File

@ -120,8 +120,8 @@ HRESULT CPowerRenameUI::s_CreateInstance(_In_ IPowerRenameManager* psrm, _In_opt
{ {
*ppsrui = nullptr; *ppsrui = nullptr;
CPowerRenameUI* prui = new CPowerRenameUI(); CPowerRenameUI* prui = new CPowerRenameUI();
HRESULT hr = prui ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (prui)
{ {
// Pass the IPowerRenameManager to the IPowerRenameUI so it can subscribe to events // Pass the IPowerRenameManager to the IPowerRenameUI so it can subscribe to events
hr = prui->_Initialize(psrm, dataSource, enableDragDrop); hr = prui->_Initialize(psrm, dataSource, enableDragDrop);

View File

@ -1,14 +1,14 @@
#include "pch.h" #include "pch.h"
#include "MockPowerRenameItem.h" #include "MockPowerRenameItem.h"
HRESULT CMockPowerRenameItem::CreateInstance(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalName, _In_ UINT depth, _In_ bool isFolder, _Outptr_ IPowerRenameItem** ppItem) HRESULT CMockPowerRenameItem::CreateInstance(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalName, _In_ UINT depth, _In_ bool isFolder, _In_ SYSTEMTIME time, _Outptr_ IPowerRenameItem** ppItem)
{ {
*ppItem = nullptr; *ppItem = nullptr;
CMockPowerRenameItem* newItem = new CMockPowerRenameItem(); CMockPowerRenameItem* newItem = new CMockPowerRenameItem();
HRESULT hr = newItem ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (newItem)
{ {
newItem->Init(path, originalName, depth, isFolder); newItem->Init(path, originalName, depth, isFolder, time);
hr = newItem->QueryInterface(IID_PPV_ARGS(ppItem)); hr = newItem->QueryInterface(IID_PPV_ARGS(ppItem));
newItem->Release(); newItem->Release();
} }
@ -16,7 +16,7 @@ HRESULT CMockPowerRenameItem::CreateInstance(_In_opt_ PCWSTR path, _In_opt_ PCWS
return hr; return hr;
} }
void CMockPowerRenameItem::Init(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalName, _In_ UINT depth, _In_ bool isFolder) void CMockPowerRenameItem::Init(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalName, _In_ UINT depth, _In_ bool isFolder, _In_ SYSTEMTIME time)
{ {
if (path != nullptr) if (path != nullptr)
{ {
@ -30,4 +30,6 @@ void CMockPowerRenameItem::Init(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalNa
m_depth = depth; m_depth = depth;
m_isFolder = isFolder; m_isFolder = isFolder;
m_time = time;
m_isTimeParsed = true;
} }

View File

@ -7,6 +7,6 @@ class CMockPowerRenameItem :
public CPowerRenameItem public CPowerRenameItem
{ {
public: public:
static HRESULT CreateInstance(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalName, _In_ UINT depth, _In_ bool isFolder, _Outptr_ IPowerRenameItem** ppItem); static HRESULT CreateInstance(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalName, _In_ UINT depth, _In_ bool isFolder, _In_ SYSTEMTIME time, _Outptr_ IPowerRenameItem** ppItem);
void Init(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalName, _In_ UINT depth, _In_ bool isFolder); void Init(_In_opt_ PCWSTR path, _In_opt_ PCWSTR originalName, _In_ UINT depth, _In_ bool isFolder, _In_ SYSTEMTIME time);
}; };

View File

@ -81,8 +81,8 @@ HRESULT CMockPowerRenameManagerEvents::s_CreateInstance(_In_ IPowerRenameManager
{ {
*ppsrui = nullptr; *ppsrui = nullptr;
CMockPowerRenameManagerEvents* events = new CMockPowerRenameManagerEvents(); CMockPowerRenameManagerEvents* events = new CMockPowerRenameManagerEvents();
HRESULT hr = events != nullptr ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (events != nullptr)
{ {
hr = events->QueryInterface(IID_PPV_ARGS(ppsrui)); hr = events->QueryInterface(IID_PPV_ARGS(ppsrui));
events->Release(); events->Release();

View File

@ -56,12 +56,18 @@ IFACEMETHODIMP CMockPowerRenameRegExEvents::OnFlagsChanged(_In_ DWORD flags)
return S_OK; return S_OK;
} }
IFACEMETHODIMP CMockPowerRenameRegExEvents::OnFileTimeChanged(_In_ SYSTEMTIME fileTime)
{
m_fileTime = fileTime;
return S_OK;
}
HRESULT CMockPowerRenameRegExEvents::s_CreateInstance(_Outptr_ IPowerRenameRegExEvents** ppsrree) HRESULT CMockPowerRenameRegExEvents::s_CreateInstance(_Outptr_ IPowerRenameRegExEvents** ppsrree)
{ {
*ppsrree = nullptr; *ppsrree = nullptr;
CMockPowerRenameRegExEvents* psrree = new CMockPowerRenameRegExEvents(); CMockPowerRenameRegExEvents* psrree = new CMockPowerRenameRegExEvents();
HRESULT hr = psrree ? S_OK : E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) if (psrree)
{ {
hr = psrree->QueryInterface(IID_PPV_ARGS(ppsrree)); hr = psrree->QueryInterface(IID_PPV_ARGS(ppsrree));
psrree->Release(); psrree->Release();

View File

@ -18,6 +18,7 @@ public:
IFACEMETHODIMP OnSearchTermChanged(_In_ PCWSTR searchTerm); IFACEMETHODIMP OnSearchTermChanged(_In_ PCWSTR searchTerm);
IFACEMETHODIMP OnReplaceTermChanged(_In_ PCWSTR replaceTerm); IFACEMETHODIMP OnReplaceTermChanged(_In_ PCWSTR replaceTerm);
IFACEMETHODIMP OnFlagsChanged(_In_ DWORD flags); IFACEMETHODIMP OnFlagsChanged(_In_ DWORD flags);
IFACEMETHODIMP OnFileTimeChanged(_In_ SYSTEMTIME fileTime);
static HRESULT s_CreateInstance(_Outptr_ IPowerRenameRegExEvents** ppsrree); static HRESULT s_CreateInstance(_Outptr_ IPowerRenameRegExEvents** ppsrree);
@ -35,5 +36,6 @@ public:
PWSTR m_searchTerm = nullptr; PWSTR m_searchTerm = nullptr;
PWSTR m_replaceTerm = nullptr; PWSTR m_replaceTerm = nullptr;
DWORD m_flags = 0; DWORD m_flags = 0;
SYSTEMTIME m_fileTime = { 0 };
long m_refCount; long m_refCount;
}; };

View File

@ -32,7 +32,7 @@ namespace PowerRenameManagerTests
int depth; int depth;
}; };
void RenameHelper(_In_ rename_pairs * renamePairs, _In_ int numPairs, _In_ std::wstring searchTerm, _In_ std::wstring replaceTerm, SYSTEMTIME LocalTime, _In_ DWORD flags) void RenameHelper(_In_ rename_pairs * renamePairs, _In_ int numPairs, _In_ std::wstring searchTerm, _In_ std::wstring replaceTerm, SYSTEMTIME fileTime, _In_ DWORD flags)
{ {
// Create a single item (in a temp directory) and verify rename works as expected // Create a single item (in a temp directory) and verify rename works as expected
CTestFileHelper testFileHelper; CTestFileHelper testFileHelper;
@ -59,12 +59,11 @@ namespace PowerRenameManagerTests
for (int i = 0; i < numPairs; i++) for (int i = 0; i < numPairs; i++)
{ {
CComPtr<IPowerRenameItem> item; CComPtr<IPowerRenameItem> item;
CMockPowerRenameItem::CreateInstance(testFileHelper.GetFullPath( CMockPowerRenameItem::CreateInstance(testFileHelper.GetFullPath(renamePairs[i].originalName).c_str(),
renamePairs[i].originalName)
.c_str(),
renamePairs[i].originalName.c_str(), renamePairs[i].originalName.c_str(),
renamePairs[i].depth, renamePairs[i].depth,
!renamePairs[i].isFile, !renamePairs[i].isFile,
fileTime,
&item); &item);
int itemId = 0; int itemId = 0;
@ -84,20 +83,21 @@ namespace PowerRenameManagerTests
Assert::IsTrue(mgr->GetRenameRegEx(&renRegEx) == S_OK); Assert::IsTrue(mgr->GetRenameRegEx(&renRegEx) == S_OK);
renRegEx->PutFlags(flags); renRegEx->PutFlags(flags);
renRegEx->PutSearchTerm(searchTerm.c_str()); renRegEx->PutSearchTerm(searchTerm.c_str());
if (isFileAttributesUsed(replaceTerm.c_str()) && SUCCEEDED(GetDatedFileName(newReplaceTerm, ARRAYSIZE(newReplaceTerm), replaceTerm.c_str(), LocalTime))) renRegEx->PutReplaceTerm(replaceTerm.c_str());
{
renRegEx->PutReplaceTerm(newReplaceTerm);
}
else
{
renRegEx->PutReplaceTerm(replaceTerm.c_str());
}
Sleep(1000);
// Perform the rename // Perform the rename
Assert::IsTrue(mgr->Rename(0) == S_OK); bool replaceSuccess = false;
for (int step = 0; step < 20; step++)
{
replaceSuccess = mgr->Rename(0) == S_OK;
if (replaceSuccess)
{
break;
}
Sleep(10);
}
Sleep(1000); Assert::IsTrue(replaceSuccess);
// Verify the rename occurred // Verify the rename occurred
for (int i = 0; i < numPairs; i++) for (int i = 0; i < numPairs; i++)
@ -128,7 +128,7 @@ namespace PowerRenameManagerTests
CComPtr<IPowerRenameManager> mgr; CComPtr<IPowerRenameManager> mgr;
Assert::IsTrue(CPowerRenameManager::s_CreateInstance(&mgr) == S_OK); Assert::IsTrue(CPowerRenameManager::s_CreateInstance(&mgr) == S_OK);
CComPtr<IPowerRenameItem> item; CComPtr<IPowerRenameItem> item;
CMockPowerRenameItem::CreateInstance(L"foo", L"foo", 0, false, &item); CMockPowerRenameItem::CreateInstance(L"foo", L"foo", 0, false, SYSTEMTIME{0}, &item);
mgr->AddItem(item); mgr->AddItem(item);
Assert::IsTrue(mgr->Shutdown() == S_OK); Assert::IsTrue(mgr->Shutdown() == S_OK);
} }
@ -143,7 +143,7 @@ namespace PowerRenameManagerTests
DWORD cookie = 0; DWORD cookie = 0;
Assert::IsTrue(mgr->Advise(mgrEvents, &cookie) == S_OK); Assert::IsTrue(mgr->Advise(mgrEvents, &cookie) == S_OK);
CComPtr<IPowerRenameItem> item; CComPtr<IPowerRenameItem> item;
CMockPowerRenameItem::CreateInstance(L"foo", L"foo", 0, false, &item); CMockPowerRenameItem::CreateInstance(L"foo", L"foo", 0, false, SYSTEMTIME{0}, &item);
int itemId = 0; int itemId = 0;
Assert::IsTrue(item->GetId(&itemId) == S_OK); Assert::IsTrue(item->GetId(&itemId) == S_OK);
mgr->AddItem(item); mgr->AddItem(item);
@ -290,6 +290,7 @@ namespace PowerRenameManagerTests
RenameHelper(renamePairs, ARRAYSIZE(renamePairs), L"foo", L"bar", SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 }, DEFAULT_FLAGS | Lowercase | ExtensionOnly); RenameHelper(renamePairs, ARRAYSIZE(renamePairs), L"foo", L"bar", SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 }, DEFAULT_FLAGS | Lowercase | ExtensionOnly);
} }
TEST_METHOD (VerifyFileAttributesNoPadding) TEST_METHOD (VerifyFileAttributesNoPadding)
{ {
rename_pairs renamePairs[] = { rename_pairs renamePairs[] = {
@ -311,26 +312,26 @@ namespace PowerRenameManagerTests
TEST_METHOD (VerifyFileAttributesMonthandDayNames) TEST_METHOD (VerifyFileAttributesMonthandDayNames)
{ {
std::locale::global(std::locale("")); std::locale::global(std::locale(""));
SYSTEMTIME LocalTime = { 2020, 1, 3, 1, 15, 6, 42, 453 }; SYSTEMTIME fileTime = { 2020, 1, 3, 1, 15, 6, 42, 453 };
wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
wchar_t result[MAX_PATH] = L"bar"; wchar_t result[MAX_PATH] = L"bar";
wchar_t formattedDate[MAX_PATH]; wchar_t formattedDate[MAX_PATH];
if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0) if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
StringCchCopy(localeName, LOCALE_NAME_MAX_LENGTH, L"en_US"); StringCchCopy(localeName, LOCALE_NAME_MAX_LENGTH, L"en_US");
GetDateFormatEx(localeName, NULL, &LocalTime, L"MMM", formattedDate, MAX_PATH, NULL); GetDateFormatEx(localeName, NULL, &fileTime, L"MMM", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]); formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(result, MAX_PATH, TEXT("%s%s"), result, formattedDate); StringCchPrintf(result, MAX_PATH, TEXT("%s%s"), result, formattedDate);
GetDateFormatEx(localeName, NULL, &LocalTime, L"MMMM", formattedDate, MAX_PATH, NULL); GetDateFormatEx(localeName, NULL, &fileTime, L"MMMM", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]); formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate); StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate);
GetDateFormatEx(localeName, NULL, &LocalTime, L"ddd", formattedDate, MAX_PATH, NULL); GetDateFormatEx(localeName, NULL, &fileTime, L"ddd", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]); formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate); StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate);
GetDateFormatEx(localeName, NULL, &LocalTime, L"dddd", formattedDate, MAX_PATH, NULL); GetDateFormatEx(localeName, NULL, &fileTime, L"dddd", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]); formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate); StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate);

View File

@ -59,7 +59,7 @@ TEST_METHOD(ReplaceNoSearchOrReplaceTerm)
CComPtr<IPowerRenameRegEx> renameRegEx; CComPtr<IPowerRenameRegEx> renameRegEx;
Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
PWSTR result = nullptr; PWSTR result = nullptr;
Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) != S_OK); Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK);
Assert::IsTrue(result == nullptr); Assert::IsTrue(result == nullptr);
CoTaskMemFree(result); CoTaskMemFree(result);
} }
@ -427,6 +427,8 @@ TEST_METHOD(VerifyEventsFire)
Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
Assert::IsTrue(renameRegEx->PutSearchTerm(L"FOO") == S_OK); Assert::IsTrue(renameRegEx->PutSearchTerm(L"FOO") == S_OK);
Assert::IsTrue(renameRegEx->PutReplaceTerm(L"BAR") == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(L"BAR") == S_OK);
Assert::IsTrue(renameRegEx->PutFileTime(SYSTEMTIME{0}) == S_OK);
Assert::IsTrue(renameRegEx->ResetFileTime() == S_OK);
Assert::IsTrue(lstrcmpi(L"FOO", mockEvents->m_searchTerm) == 0); Assert::IsTrue(lstrcmpi(L"FOO", mockEvents->m_searchTerm) == 0);
Assert::IsTrue(lstrcmpi(L"BAR", mockEvents->m_replaceTerm) == 0); Assert::IsTrue(lstrcmpi(L"BAR", mockEvents->m_replaceTerm) == 0);
Assert::IsTrue(flags == mockEvents->m_flags); Assert::IsTrue(flags == mockEvents->m_flags);

View File

@ -53,7 +53,7 @@ TEST_METHOD(ReplaceNoSearchOrReplaceTerm)
CComPtr<IPowerRenameRegEx> renameRegEx; CComPtr<IPowerRenameRegEx> renameRegEx;
Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
PWSTR result = nullptr; PWSTR result = nullptr;
Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) != S_OK); Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK);
Assert::IsTrue(result == nullptr); Assert::IsTrue(result == nullptr);
CoTaskMemFree(result); CoTaskMemFree(result);
} }
@ -369,6 +369,103 @@ TEST_METHOD(VerifyHandleCapturingGroups)
} }
} }
TEST_METHOD (VerifyFileAttributesNoPadding)
{
CComPtr<IPowerRenameRegEx> renameRegEx;
Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
DWORD flags = MatchAllOccurences | UseRegularExpressions ;
SYSTEMTIME fileTime = SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 };
Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
SearchReplaceExpected sreTable[] = {
//search, replace, test, result
{ L"foo", L"bar$YY-$M-$D-$h-$m-$s-$f", L"foo", L"bar20-7-22-15-6-42-4" },
};
for (int i = 0; i < ARRAYSIZE(sreTable); i++)
{
PWSTR result = nullptr;
Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK);
Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK);
Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0);
CoTaskMemFree(result);
}
}
TEST_METHOD (VerifyFileAttributesPadding)
{
CComPtr<IPowerRenameRegEx> renameRegEx;
Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
DWORD flags = MatchAllOccurences | UseRegularExpressions;
Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
SYSTEMTIME fileTime = SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 };
SearchReplaceExpected sreTable[] = {
//search, replace, test, result
{ L"foo", L"bar$YYYY-$MM-$DD-$hh-$mm-$ss-$fff", L"foo", L"bar2020-07-22-15-06-42-453" },
};
for (int i = 0; i < ARRAYSIZE(sreTable); i++)
{
PWSTR result = nullptr;
Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK);
Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK);
Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0);
CoTaskMemFree(result);
}
}
TEST_METHOD (VerifyFileAttributesMonthandDayNames)
{
CComPtr<IPowerRenameRegEx> renameRegEx;
Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
DWORD flags = MatchAllOccurences | UseRegularExpressions;
Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
std::locale::global(std::locale(""));
SYSTEMTIME fileTime = { 2020, 1, 3, 1, 15, 6, 42, 453 };
wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
wchar_t result[MAX_PATH] = L"bar";
wchar_t formattedDate[MAX_PATH];
if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
StringCchCopy(localeName, LOCALE_NAME_MAX_LENGTH, L"en_US");
GetDateFormatEx(localeName, NULL, &fileTime, L"MMM", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(result, MAX_PATH, TEXT("%s%s"), result, formattedDate);
GetDateFormatEx(localeName, NULL, &fileTime, L"MMMM", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate);
GetDateFormatEx(localeName, NULL, &fileTime, L"ddd", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate);
GetDateFormatEx(localeName, NULL, &fileTime, L"dddd", formattedDate, MAX_PATH, NULL);
formattedDate[0] = towupper(formattedDate[0]);
StringCchPrintf(result, MAX_PATH, TEXT("%s-%s"), result, formattedDate);
SearchReplaceExpected sreTable[] = {
//search, replace, test, result
{ L"foo", L"bar$MMM-$MMMM-$DDD-$DDDD", L"foo", result },
};
for (int i = 0; i < ARRAYSIZE(sreTable); i++)
{
PWSTR result = nullptr;
Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK);
Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK);
Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0);
CoTaskMemFree(result);
}
}
TEST_METHOD(VerifyLookbehindFails) TEST_METHOD(VerifyLookbehindFails)
{ {
// Standard Library Regex Engine does not support lookbehind, thus test should fail. // Standard Library Regex Engine does not support lookbehind, thus test should fail.
@ -406,6 +503,8 @@ TEST_METHOD(VerifyEventsFire)
Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
Assert::IsTrue(renameRegEx->PutSearchTerm(L"FOO") == S_OK); Assert::IsTrue(renameRegEx->PutSearchTerm(L"FOO") == S_OK);
Assert::IsTrue(renameRegEx->PutReplaceTerm(L"BAR") == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(L"BAR") == S_OK);
Assert::IsTrue(renameRegEx->PutFileTime(SYSTEMTIME{ 0 }) == S_OK);
Assert::IsTrue(renameRegEx->ResetFileTime() == S_OK);
Assert::IsTrue(lstrcmpi(L"FOO", mockEvents->m_searchTerm) == 0); Assert::IsTrue(lstrcmpi(L"FOO", mockEvents->m_searchTerm) == 0);
Assert::IsTrue(lstrcmpi(L"BAR", mockEvents->m_replaceTerm) == 0); Assert::IsTrue(lstrcmpi(L"BAR", mockEvents->m_replaceTerm) == 0);
Assert::IsTrue(flags == mockEvents->m_flags); Assert::IsTrue(flags == mockEvents->m_flags);