2020-04-29 22:02:18 +02:00
|
|
|
#include "pch.h"
|
2019-10-18 05:57:19 +02:00
|
|
|
#include "PowerRenameManager.h"
|
|
|
|
#include "PowerRenameRegEx.h" // Default RegEx handler
|
|
|
|
#include <algorithm>
|
|
|
|
#include <shlobj.h>
|
2020-07-02 10:52:01 +02:00
|
|
|
#include <cstring>
|
2019-10-18 05:57:19 +02:00
|
|
|
#include "helpers.h"
|
2020-01-16 08:06:22 +01:00
|
|
|
#include "window_helpers.h"
|
2019-10-18 05:57:19 +02:00
|
|
|
#include <filesystem>
|
2019-10-28 18:14:59 +01:00
|
|
|
#include "trace.h"
|
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
|
|
extern HINSTANCE g_hInst;
|
|
|
|
|
|
|
|
// The default FOF flags to use in the rename operations
|
|
|
|
#define FOF_DEFAULTFLAGS (FOF_ALLOWUNDO | FOFX_ADDUNDORECORD | FOFX_SHOWELEVATIONPROMPT | FOF_RENAMEONCOLLISION)
|
|
|
|
|
|
|
|
IFACEMETHODIMP_(ULONG) CPowerRenameManager::AddRef()
|
|
|
|
{
|
|
|
|
return InterlockedIncrement(&m_refCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP_(ULONG) CPowerRenameManager::Release()
|
|
|
|
{
|
|
|
|
long refCount = InterlockedDecrement(&m_refCount);
|
|
|
|
|
|
|
|
if (refCount == 0)
|
|
|
|
{
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
return refCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv)
|
|
|
|
{
|
|
|
|
static const QITAB qit[] = {
|
|
|
|
QITABENT(CPowerRenameManager, IPowerRenameManager),
|
|
|
|
QITABENT(CPowerRenameManager, IPowerRenameRegExEvents),
|
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppv);
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::Advise(_In_ IPowerRenameManagerEvents* renameOpEvents, _Out_ DWORD* cookie)
|
|
|
|
{
|
|
|
|
CSRWExclusiveAutoLock lock(&m_lockEvents);
|
|
|
|
m_cookie++;
|
2019-11-11 20:00:42 +01:00
|
|
|
RENAME_MGR_EVENT srme;
|
2019-10-18 05:57:19 +02:00
|
|
|
srme.cookie = m_cookie;
|
|
|
|
srme.pEvents = renameOpEvents;
|
|
|
|
renameOpEvents->AddRef();
|
2019-11-12 05:58:39 +01:00
|
|
|
m_powerRenameManagerEvents.push_back(srme);
|
2019-10-18 05:57:19 +02:00
|
|
|
|
|
|
|
*cookie = m_cookie;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::UnAdvise(_In_ DWORD cookie)
|
|
|
|
{
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
CSRWExclusiveAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (std::vector<RENAME_MGR_EVENT>::iterator it = m_powerRenameManagerEvents.begin(); it != m_powerRenameManagerEvents.end(); ++it)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-12 05:58:39 +01:00
|
|
|
if (it->cookie == cookie)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
hr = S_OK;
|
2019-11-12 05:58:39 +01:00
|
|
|
it->cookie = 0;
|
|
|
|
if (it->pEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-12 05:58:39 +01:00
|
|
|
it->pEvents->Release();
|
|
|
|
it->pEvents = nullptr;
|
2019-10-18 05:57:19 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::Start()
|
|
|
|
{
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::Stop()
|
|
|
|
{
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::Rename(_In_ HWND hwndParent)
|
|
|
|
{
|
|
|
|
m_hwndParent = hwndParent;
|
|
|
|
return _PerformFileOperation();
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::Reset()
|
|
|
|
{
|
|
|
|
// Stop all threads and wait
|
|
|
|
// Reset all rename items
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::Shutdown()
|
|
|
|
{
|
|
|
|
_ClearRegEx();
|
|
|
|
_Cleanup();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::AddItem(_In_ IPowerRenameItem* pItem)
|
|
|
|
{
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// Scope lock
|
|
|
|
{
|
|
|
|
CSRWExclusiveAutoLock lock(&m_lockItems);
|
|
|
|
int id = 0;
|
2020-08-25 07:22:05 +02:00
|
|
|
pItem->GetId(&id);
|
2019-10-18 05:57:19 +02:00
|
|
|
// Verify the item isn't already added
|
2019-11-11 20:00:42 +01:00
|
|
|
if (m_renameItems.find(id) == m_renameItems.end())
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
m_renameItems[id] = pItem;
|
2020-08-25 07:22:05 +02:00
|
|
|
m_isVisible.push_back(true);
|
2019-10-18 05:57:19 +02:00
|
|
|
pItem->AddRef();
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
_OnItemAdded(pItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetItemByIndex(_In_ UINT index, _COM_Outptr_ IPowerRenameItem** ppItem)
|
|
|
|
{
|
|
|
|
*ppItem = nullptr;
|
|
|
|
CSRWSharedAutoLock lock(&m_lockItems);
|
|
|
|
HRESULT hr = E_FAIL;
|
2019-11-11 20:00:42 +01:00
|
|
|
if (index < m_renameItems.size())
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
std::map<int, IPowerRenameItem*>::iterator it = m_renameItems.begin();
|
2019-10-18 05:57:19 +02:00
|
|
|
std::advance(it, index);
|
|
|
|
*ppItem = it->second;
|
|
|
|
(*ppItem)->AddRef();
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetVisibleItemByIndex(_In_ UINT index, _COM_Outptr_ IPowerRenameItem** ppItem)
|
|
|
|
{
|
|
|
|
*ppItem = nullptr;
|
|
|
|
CSRWSharedAutoLock lock(&m_lockItems);
|
|
|
|
UINT count = 0;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
|
|
|
|
if (m_filter == PowerRenameFilters::None)
|
|
|
|
{
|
|
|
|
hr = GetItemByIndex(index, ppItem);
|
|
|
|
}
|
|
|
|
else if (SUCCEEDED(GetVisibleItemCount(&count)) && index < count)
|
|
|
|
{
|
2020-11-04 12:13:07 +01:00
|
|
|
UINT realIndex = 0, visibleIndex = 0;
|
2020-08-25 07:22:05 +02:00
|
|
|
for (size_t i = 0; i < m_isVisible.size(); i++)
|
|
|
|
{
|
2020-11-04 12:13:07 +01:00
|
|
|
if (m_isVisible[i] && visibleIndex == index)
|
2020-08-25 07:22:05 +02:00
|
|
|
{
|
|
|
|
realIndex = static_cast<UINT>(i);
|
|
|
|
break;
|
|
|
|
}
|
2020-11-04 12:13:07 +01:00
|
|
|
if (m_isVisible[i])
|
|
|
|
{
|
|
|
|
visibleIndex++;
|
|
|
|
}
|
2020-08-25 07:22:05 +02:00
|
|
|
}
|
|
|
|
hr = GetItemByIndex(realIndex, ppItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetItemById(_In_ int id, _COM_Outptr_ IPowerRenameItem** ppItem)
|
|
|
|
{
|
|
|
|
*ppItem = nullptr;
|
|
|
|
|
|
|
|
CSRWSharedAutoLock lock(&m_lockItems);
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
std::map<int, IPowerRenameItem*>::iterator it;
|
2019-11-11 20:00:42 +01:00
|
|
|
it = m_renameItems.find(id);
|
|
|
|
if (it != m_renameItems.end())
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
*ppItem = m_renameItems[id];
|
2019-10-18 05:57:19 +02:00
|
|
|
(*ppItem)->AddRef();
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetItemCount(_Out_ UINT* count)
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockItems);
|
2019-11-11 20:00:42 +01:00
|
|
|
*count = static_cast<UINT>(m_renameItems.size());
|
2019-10-18 05:57:19 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::SetVisible()
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockItems);
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
UINT lastVisibleDepth = 0;
|
|
|
|
size_t i = m_isVisible.size() - 1;
|
|
|
|
PWSTR searchTerm = nullptr;
|
|
|
|
for (auto rit = m_renameItems.rbegin(); rit != m_renameItems.rend(); ++rit, --i)
|
|
|
|
{
|
|
|
|
bool isVisible = false;
|
|
|
|
if (m_filter == PowerRenameFilters::ShouldRename &&
|
|
|
|
(FAILED(m_spRegEx->GetSearchTerm(&searchTerm)) || searchTerm && wcslen(searchTerm) == 0))
|
|
|
|
{
|
|
|
|
isVisible = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rit->second->IsItemVisible(m_filter, m_flags, &isVisible);
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT itemDepth = 0;
|
|
|
|
rit->second->GetDepth(&itemDepth);
|
|
|
|
|
|
|
|
//Make an item visible if it has a least one visible subitem
|
|
|
|
if (isVisible)
|
|
|
|
{
|
|
|
|
lastVisibleDepth = itemDepth;
|
|
|
|
}
|
|
|
|
else if (lastVisibleDepth == itemDepth+1)
|
|
|
|
{
|
|
|
|
isVisible = true;
|
|
|
|
lastVisibleDepth = itemDepth;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_isVisible[i] = isVisible;
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetVisibleItemCount(_Out_ UINT* count)
|
|
|
|
{
|
|
|
|
*count = 0;
|
|
|
|
CSRWSharedAutoLock lock(&m_lockItems);
|
|
|
|
|
|
|
|
if (m_filter != PowerRenameFilters::None)
|
|
|
|
{
|
|
|
|
SetVisible();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < m_isVisible.size(); i++)
|
|
|
|
{
|
|
|
|
if (m_isVisible[i])
|
|
|
|
{
|
|
|
|
(*count)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GetItemCount(count);
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetSelectedItemCount(_Out_ UINT* count)
|
|
|
|
{
|
|
|
|
*count = 0;
|
|
|
|
CSRWSharedAutoLock lock(&m_lockItems);
|
|
|
|
|
2019-11-11 20:00:42 +01:00
|
|
|
for (auto it : m_renameItems)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
IPowerRenameItem* pItem = it.second;
|
|
|
|
bool selected = false;
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(pItem->GetSelected(&selected)) && selected)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
(*count)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetRenameItemCount(_Out_ UINT* count)
|
|
|
|
{
|
|
|
|
*count = 0;
|
|
|
|
CSRWSharedAutoLock lock(&m_lockItems);
|
|
|
|
|
2019-11-11 20:00:42 +01:00
|
|
|
for (auto it : m_renameItems)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
IPowerRenameItem* pItem = it.second;
|
|
|
|
bool shouldRename = false;
|
|
|
|
if (SUCCEEDED(pItem->ShouldRenameItem(m_flags, &shouldRename)) && shouldRename)
|
|
|
|
{
|
|
|
|
(*count)++;
|
|
|
|
}
|
|
|
|
}
|
2020-08-25 07:22:05 +02:00
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetFlags(_Out_ DWORD* flags)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
_EnsureRegEx();
|
|
|
|
*flags = m_flags;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::PutFlags(_In_ DWORD flags)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (flags != m_flags)
|
|
|
|
{
|
|
|
|
m_flags = flags;
|
|
|
|
_EnsureRegEx();
|
2020-08-25 07:22:05 +02:00
|
|
|
m_spRegEx->PutFlags(flags);
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetFilter(_Out_ DWORD* filter)
|
|
|
|
{
|
|
|
|
*filter = m_filter;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::SwitchFilter(_In_ int columnNumber)
|
|
|
|
{
|
|
|
|
switch (m_filter)
|
|
|
|
{
|
|
|
|
case PowerRenameFilters::None:
|
|
|
|
m_filter = (columnNumber == 0) ? PowerRenameFilters::Selected : PowerRenameFilters::ShouldRename;
|
|
|
|
break;
|
|
|
|
case PowerRenameFilters::Selected:
|
|
|
|
m_filter = (columnNumber == 0) ? PowerRenameFilters::FlagsApplicable : PowerRenameFilters::ShouldRename;
|
|
|
|
break;
|
|
|
|
case PowerRenameFilters::FlagsApplicable:
|
|
|
|
m_filter = (columnNumber == 0) ? PowerRenameFilters::None : PowerRenameFilters::ShouldRename;
|
|
|
|
break;
|
|
|
|
case PowerRenameFilters::ShouldRename:
|
|
|
|
m_filter = (columnNumber == 0) ? PowerRenameFilters::Selected : PowerRenameFilters::None;
|
|
|
|
break;
|
2019-10-18 05:57:19 +02:00
|
|
|
}
|
2020-08-25 07:22:05 +02:00
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetRenameRegEx(_COM_Outptr_ IPowerRenameRegEx** ppRegEx)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
*ppRegEx = nullptr;
|
|
|
|
HRESULT hr = _EnsureRegEx();
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
*ppRegEx = m_spRegEx;
|
|
|
|
(*ppRegEx)->AddRef();
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::PutRenameRegEx(_In_ IPowerRenameRegEx* pRegEx)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
_ClearRegEx();
|
|
|
|
m_spRegEx = pRegEx;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::GetRenameItemFactory(_COM_Outptr_ IPowerRenameItemFactory** ppItemFactory)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
*ppItemFactory = nullptr;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (m_spItemFactory)
|
|
|
|
{
|
|
|
|
hr = S_OK;
|
|
|
|
*ppItemFactory = m_spItemFactory;
|
|
|
|
(*ppItemFactory)->AddRef();
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
IFACEMETHODIMP CPowerRenameManager::PutRenameItemFactory(_In_ IPowerRenameItemFactory* pItemFactory)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
m_spItemFactory = pItemFactory;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::OnSearchTermChanged(_In_ PCWSTR /*searchTerm*/)
|
|
|
|
{
|
|
|
|
_PerformRegExRename();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::OnReplaceTermChanged(_In_ PCWSTR /*replaceTerm*/)
|
|
|
|
{
|
|
|
|
_PerformRegExRename();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFACEMETHODIMP CPowerRenameManager::OnFlagsChanged(_In_ DWORD flags)
|
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
// Flags were updated in the rename regex. Update our preview.
|
2019-10-18 05:57:19 +02:00
|
|
|
m_flags = flags;
|
|
|
|
_PerformRegExRename();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CPowerRenameManager::s_CreateInstance(_Outptr_ IPowerRenameManager** ppsrm)
|
|
|
|
{
|
|
|
|
*ppsrm = nullptr;
|
|
|
|
CPowerRenameManager *psrm = new CPowerRenameManager();
|
|
|
|
HRESULT hr = psrm ? S_OK : E_OUTOFMEMORY;
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
hr = psrm->_Init();
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
hr = psrm->QueryInterface(IID_PPV_ARGS(ppsrm));
|
|
|
|
}
|
|
|
|
psrm->Release();
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPowerRenameManager::CPowerRenameManager() :
|
|
|
|
m_refCount(1)
|
|
|
|
{
|
|
|
|
InitializeCriticalSection(&m_critsecReentrancy);
|
|
|
|
}
|
|
|
|
|
|
|
|
CPowerRenameManager::~CPowerRenameManager()
|
|
|
|
{
|
|
|
|
DeleteCriticalSection(&m_critsecReentrancy);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CPowerRenameManager::_Init()
|
|
|
|
{
|
|
|
|
// Guaranteed to succeed
|
|
|
|
m_startFileOpWorkerEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
|
|
m_startRegExWorkerEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
|
|
m_cancelRegExWorkerEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
|
|
|
|
|
|
m_hwndMessage = CreateMsgWindow(g_hInst, s_msgWndProc, this);
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Custom messages for worker threads
|
|
|
|
enum
|
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
SRM_REGEX_ITEM_UPDATED = (WM_APP + 1), // Single rename item processed by regex worker thread
|
2019-10-18 05:57:19 +02:00
|
|
|
SRM_REGEX_STARTED, // RegEx operation was started
|
|
|
|
SRM_REGEX_CANCELED, // Regex operation was canceled
|
|
|
|
SRM_REGEX_COMPLETE, // Regex worker thread completed
|
|
|
|
SRM_FILEOP_COMPLETE // File Operation worker thread completed
|
|
|
|
};
|
|
|
|
|
|
|
|
struct WorkerThreadData
|
|
|
|
{
|
|
|
|
HWND hwndManager = nullptr;
|
|
|
|
HANDLE startEvent = nullptr;
|
|
|
|
HANDLE cancelEvent = nullptr;
|
|
|
|
HWND hwndParent = nullptr;
|
|
|
|
CComPtr<IPowerRenameManager> spsrm;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Msg-only worker window proc for communication from our worker threads
|
|
|
|
LRESULT CALLBACK CPowerRenameManager::s_msgWndProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
|
|
|
|
{
|
|
|
|
LRESULT lRes = 0;
|
|
|
|
|
|
|
|
CPowerRenameManager* pThis = (CPowerRenameManager*)GetWindowLongPtr(hwnd, 0);
|
|
|
|
if (pThis != nullptr)
|
|
|
|
{
|
|
|
|
lRes = pThis->_WndProc(hwnd, uMsg, wParam, lParam);
|
|
|
|
if (uMsg == WM_NCDESTROY)
|
|
|
|
{
|
|
|
|
SetWindowLongPtr(hwnd, 0, NULL);
|
|
|
|
pThis->m_hwndMessage = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lRes = DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
return lRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CPowerRenameManager::_WndProc(_In_ HWND hwnd, _In_ UINT msg, _In_ WPARAM wParam, _In_ LPARAM lParam)
|
|
|
|
{
|
|
|
|
LRESULT lRes = 0;
|
|
|
|
|
|
|
|
AddRef();
|
|
|
|
|
|
|
|
switch (msg)
|
|
|
|
{
|
|
|
|
case SRM_REGEX_ITEM_UPDATED:
|
|
|
|
{
|
|
|
|
int id = static_cast<int>(lParam);
|
|
|
|
CComPtr<IPowerRenameItem> spItem;
|
|
|
|
if (SUCCEEDED(GetItemById(id, &spItem)))
|
|
|
|
{
|
|
|
|
_OnUpdate(spItem);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SRM_REGEX_STARTED:
|
|
|
|
_OnRegExStarted(static_cast<DWORD>(wParam));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SRM_REGEX_CANCELED:
|
|
|
|
_OnRegExCanceled(static_cast<DWORD>(wParam));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SRM_REGEX_COMPLETE:
|
|
|
|
_OnRegExCompleted(static_cast<DWORD>(wParam));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
lRes = DefWindowProc(hwnd, msg, wParam, lParam);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Release();
|
|
|
|
|
|
|
|
return lRes;
|
|
|
|
}
|
|
|
|
|
2019-10-28 18:14:59 +01:00
|
|
|
void CPowerRenameManager::_LogOperationTelemetry()
|
|
|
|
{
|
|
|
|
UINT renameItemCount = 0;
|
|
|
|
UINT selectedItemCount = 0;
|
|
|
|
UINT totalItemCount = 0;
|
|
|
|
DWORD flags = 0;
|
|
|
|
|
|
|
|
GetItemCount(&totalItemCount);
|
|
|
|
GetSelectedItemCount(&selectedItemCount);
|
|
|
|
GetRenameItemCount(&renameItemCount);
|
2020-08-25 07:22:05 +02:00
|
|
|
GetFlags(&flags);
|
2019-10-28 18:14:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
// Enumerate extensions used into a map
|
|
|
|
std::map<std::wstring, int> extensionsMap;
|
|
|
|
for (UINT i = 0; i < totalItemCount; i++)
|
|
|
|
{
|
|
|
|
CComPtr<IPowerRenameItem> spItem;
|
|
|
|
if (SUCCEEDED(GetItemByIndex(i, &spItem)))
|
|
|
|
{
|
|
|
|
PWSTR originalName;
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(spItem->GetOriginalName(&originalName)))
|
2019-10-28 18:14:59 +01:00
|
|
|
{
|
|
|
|
std::wstring extension = fs::path(originalName).extension().wstring();
|
|
|
|
std::map<std::wstring, int>::iterator it = extensionsMap.find(extension);
|
|
|
|
if (it == extensionsMap.end())
|
|
|
|
{
|
|
|
|
extensionsMap.insert({ extension, 1 });
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
it->second++;
|
|
|
|
}
|
|
|
|
|
|
|
|
CoTaskMemFree(originalName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring extensionList = L"";
|
|
|
|
for (auto elem : extensionsMap)
|
|
|
|
{
|
|
|
|
extensionList.append(elem.first);
|
|
|
|
extensionList.append(L":");
|
|
|
|
extensionList.append(std::to_wstring(elem.second));
|
|
|
|
extensionList.append(L",");
|
|
|
|
}
|
|
|
|
|
|
|
|
Trace::RenameOperation(totalItemCount, selectedItemCount, renameItemCount, flags, extensionList.c_str());
|
|
|
|
}
|
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
HRESULT CPowerRenameManager::_PerformFileOperation()
|
|
|
|
{
|
|
|
|
// Do we have items to rename?
|
|
|
|
UINT renameItemCount = 0;
|
|
|
|
if (FAILED(GetRenameItemCount(&renameItemCount)) || renameItemCount == 0)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
2019-10-28 18:14:59 +01:00
|
|
|
_LogOperationTelemetry();
|
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
// Wait for existing regex thread to finish
|
|
|
|
_WaitForRegExWorkerThread();
|
|
|
|
|
|
|
|
// Create worker thread which will perform the actual rename
|
|
|
|
HRESULT hr = _CreateFileOpWorkerThread();
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
_OnRenameStarted();
|
|
|
|
|
|
|
|
// Signal the worker thread that they can start working. We needed to wait until we
|
|
|
|
// were ready to process thread messages.
|
|
|
|
SetEvent(m_startFileOpWorkerEvent);
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
// Check if worker thread has exited
|
|
|
|
if (WaitForSingleObject(m_fileOpWorkerThreadHandle, 0) == WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
MSG msg;
|
|
|
|
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
|
|
|
|
{
|
|
|
|
if (msg.message == SRM_FILEOP_COMPLETE)
|
|
|
|
{
|
|
|
|
// Worker thread completed
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_OnRenameCompleted();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CPowerRenameManager::_CreateFileOpWorkerThread()
|
|
|
|
{
|
|
|
|
WorkerThreadData* pwtd = new WorkerThreadData;
|
|
|
|
HRESULT hr = pwtd ? S_OK : E_OUTOFMEMORY;
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
pwtd->hwndManager = m_hwndMessage;
|
|
|
|
pwtd->startEvent = m_startRegExWorkerEvent;
|
|
|
|
pwtd->cancelEvent = nullptr;
|
|
|
|
pwtd->spsrm = this;
|
|
|
|
m_fileOpWorkerThreadHandle = CreateThread(nullptr, 0, s_fileOpWorkerThread, pwtd, 0, nullptr);
|
|
|
|
hr = (m_fileOpWorkerThreadHandle) ? S_OK : E_FAIL;
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
|
|
|
delete pwtd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD WINAPI CPowerRenameManager::s_fileOpWorkerThread(_In_ void* pv)
|
|
|
|
{
|
|
|
|
if (SUCCEEDED(CoInitializeEx(NULL, 0)))
|
|
|
|
{
|
|
|
|
WorkerThreadData* pwtd = reinterpret_cast<WorkerThreadData*>(pv);
|
|
|
|
if (pwtd)
|
|
|
|
{
|
|
|
|
// Wait to be told we can begin
|
|
|
|
if (WaitForSingleObject(pwtd->startEvent, INFINITE) == WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
CComPtr<IPowerRenameRegEx> spRenameRegEx;
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(pwtd->spsrm->GetRenameRegEx(&spRenameRegEx)))
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
// Create IFileOperation interface
|
|
|
|
CComPtr<IFileOperation> spFileOp;
|
|
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spFileOp))))
|
|
|
|
{
|
|
|
|
DWORD flags = 0;
|
2020-08-25 07:22:05 +02:00
|
|
|
spRenameRegEx->GetFlags(&flags);
|
2019-10-18 05:57:19 +02:00
|
|
|
|
|
|
|
UINT itemCount = 0;
|
|
|
|
pwtd->spsrm->GetItemCount(&itemCount);
|
2019-11-11 20:00:42 +01:00
|
|
|
|
|
|
|
// We add the items to the operation in depth-first order. This allows child items to be
|
|
|
|
// renamed before parent items.
|
|
|
|
|
|
|
|
// Creating a vector of vectors of items of the same depth
|
|
|
|
std::vector<std::vector<UINT>> matrix(itemCount);
|
|
|
|
|
|
|
|
for (UINT u = 0; u < itemCount; u++)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
CComPtr<IPowerRenameItem> spItem;
|
|
|
|
if (SUCCEEDED(pwtd->spsrm->GetItemByIndex(u, &spItem)))
|
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
UINT depth = 0;
|
2020-08-25 07:22:05 +02:00
|
|
|
spItem->GetDepth(&depth);
|
2019-11-11 20:00:42 +01:00
|
|
|
matrix[depth].push_back(u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// From the greatest depth first, add all items of that depth to the operation
|
|
|
|
for (LONG v = itemCount - 1; v >= 0; v--)
|
|
|
|
{
|
|
|
|
for (auto it : matrix[v])
|
|
|
|
{
|
|
|
|
CComPtr<IPowerRenameItem> spItem;
|
|
|
|
if (SUCCEEDED(pwtd->spsrm->GetItemByIndex(it, &spItem)))
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
bool shouldRename = false;
|
|
|
|
if (SUCCEEDED(spItem->ShouldRenameItem(flags, &shouldRename)) && shouldRename)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
PWSTR newName = nullptr;
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(spItem->GetNewName(&newName)))
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-11 20:00:42 +01:00
|
|
|
CComPtr<IShellItem> spShellItem;
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(spItem->GetShellItem(&spShellItem)))
|
2019-11-11 20:00:42 +01:00
|
|
|
{
|
|
|
|
spFileOp->RenameItem(spShellItem, newName, nullptr);
|
|
|
|
}
|
|
|
|
CoTaskMemFree(newName);
|
2019-10-18 05:57:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the operation flags
|
|
|
|
if (SUCCEEDED(spFileOp->SetOperationFlags(FOF_DEFAULTFLAGS)))
|
|
|
|
{
|
|
|
|
// Set the parent window
|
|
|
|
if (pwtd->hwndParent)
|
|
|
|
{
|
|
|
|
spFileOp->SetOwnerWindow(pwtd->hwndParent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform the operation
|
|
|
|
// We don't care about the return code here. We would rather
|
|
|
|
// return control back to explorer so the user can cleanly
|
|
|
|
// undo the operation if it failed halfway through.
|
|
|
|
spFileOp->PerformOperations();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the manager thread the completion message
|
|
|
|
PostMessage(pwtd->hwndManager, SRM_FILEOP_COMPLETE, GetCurrentThreadId(), 0);
|
|
|
|
|
|
|
|
delete pwtd;
|
|
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CPowerRenameManager::_PerformRegExRename()
|
|
|
|
{
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
|
|
|
|
if (!TryEnterCriticalSection(&m_critsecReentrancy))
|
|
|
|
{
|
|
|
|
// Ensure we do not re-enter since we pump messages here.
|
|
|
|
// TODO: If we do, post a message back to ourselves
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Ensure previous thread is canceled
|
|
|
|
_CancelRegExWorkerThread();
|
|
|
|
|
|
|
|
// Create worker thread which will message us progress and completion.
|
|
|
|
hr = _CreateRegExWorkerThread();
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
ResetEvent(m_cancelRegExWorkerEvent);
|
|
|
|
|
|
|
|
// Signal the worker thread that they can start working. We needed to wait until we
|
|
|
|
// were ready to process thread messages.
|
|
|
|
SetEvent(m_startRegExWorkerEvent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CPowerRenameManager::_CreateRegExWorkerThread()
|
|
|
|
{
|
|
|
|
WorkerThreadData* pwtd = new WorkerThreadData;
|
|
|
|
HRESULT hr = pwtd ? S_OK : E_OUTOFMEMORY;
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
pwtd->hwndManager = m_hwndMessage;
|
|
|
|
pwtd->startEvent = m_startRegExWorkerEvent;
|
|
|
|
pwtd->cancelEvent = m_cancelRegExWorkerEvent;
|
|
|
|
pwtd->hwndParent = m_hwndParent;
|
|
|
|
pwtd->spsrm = this;
|
|
|
|
m_regExWorkerThreadHandle = CreateThread(nullptr, 0, s_regexWorkerThread, pwtd, 0, nullptr);
|
|
|
|
hr = (m_regExWorkerThreadHandle) ? S_OK : E_FAIL;
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
|
|
|
delete pwtd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv)
|
|
|
|
{
|
|
|
|
if (SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
|
|
|
|
{
|
|
|
|
WorkerThreadData* pwtd = reinterpret_cast<WorkerThreadData*>(pv);
|
|
|
|
if (pwtd)
|
|
|
|
{
|
|
|
|
PostMessage(pwtd->hwndManager, SRM_REGEX_STARTED, GetCurrentThreadId(), 0);
|
|
|
|
|
|
|
|
// Wait to be told we can begin
|
|
|
|
if (WaitForSingleObject(pwtd->startEvent, INFINITE) == WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
CComPtr<IPowerRenameRegEx> spRenameRegEx;
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(pwtd->spsrm->GetRenameRegEx(&spRenameRegEx)))
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
DWORD flags = 0;
|
2020-08-25 07:22:05 +02:00
|
|
|
spRenameRegEx->GetFlags(&flags);
|
2019-10-18 05:57:19 +02:00
|
|
|
|
|
|
|
UINT itemCount = 0;
|
|
|
|
unsigned long itemEnumIndex = 1;
|
|
|
|
pwtd->spsrm->GetItemCount(&itemCount);
|
|
|
|
for (UINT u = 0; u <= itemCount; u++)
|
|
|
|
{
|
|
|
|
// Check if cancel event is signaled
|
|
|
|
if (WaitForSingleObject(pwtd->cancelEvent, 0) == WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
// Canceled from manager
|
|
|
|
// Send the manager thread the canceled message
|
|
|
|
PostMessage(pwtd->hwndManager, SRM_REGEX_CANCELED, GetCurrentThreadId(), 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
CComPtr<IPowerRenameItem> spItem;
|
|
|
|
if (SUCCEEDED(pwtd->spsrm->GetItemByIndex(u, &spItem)))
|
|
|
|
{
|
|
|
|
int id = -1;
|
2020-08-25 07:22:05 +02:00
|
|
|
spItem->GetId(&id);
|
2019-10-18 05:57:19 +02:00
|
|
|
|
|
|
|
bool isFolder = false;
|
|
|
|
bool isSubFolderContent = false;
|
2020-08-25 07:22:05 +02:00
|
|
|
spItem->GetIsFolder(&isFolder);
|
|
|
|
spItem->GetIsSubFolderContent(&isSubFolderContent);
|
2019-10-18 05:57:19 +02:00
|
|
|
if ((isFolder && (flags & PowerRenameFlags::ExcludeFolders)) ||
|
|
|
|
(!isFolder && (flags & PowerRenameFlags::ExcludeFiles)) ||
|
|
|
|
(isSubFolderContent && (flags & PowerRenameFlags::ExcludeSubfolders)))
|
|
|
|
{
|
|
|
|
// Exclude this item from renaming. Ensure new name is cleared.
|
2020-08-25 07:22:05 +02:00
|
|
|
spItem->PutNewName(nullptr);
|
2019-10-18 05:57:19 +02:00
|
|
|
|
|
|
|
// Send the manager thread the item processed message
|
|
|
|
PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
PWSTR originalName = nullptr;
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(spItem->GetOriginalName(&originalName)))
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
PWSTR currentNewName = nullptr;
|
2020-08-25 07:22:05 +02:00
|
|
|
spItem->GetNewName(¤tNewName);
|
2019-10-18 05:57:19 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-08-20 01:53:09 +02:00
|
|
|
wchar_t newReplaceTerm[MAX_PATH] = { 0 };
|
|
|
|
PWSTR replaceTerm = nullptr;
|
|
|
|
SYSTEMTIME LocalTime;
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(spRenameRegEx->GetReplaceTerm(&replaceTerm)) && isFileAttributesUsed(replaceTerm))
|
2020-08-20 01:53:09 +02:00
|
|
|
{
|
2020-08-25 07:22:05 +02:00
|
|
|
if (SUCCEEDED(spItem->GetDate(&LocalTime)))
|
2020-08-20 01:53:09 +02:00
|
|
|
{
|
|
|
|
if (SUCCEEDED(GetDatedFileName(newReplaceTerm, ARRAYSIZE(newReplaceTerm), replaceTerm, LocalTime)))
|
|
|
|
{
|
2020-08-25 07:22:05 +02:00
|
|
|
spRenameRegEx->PutReplaceTerm(newReplaceTerm);
|
2020-08-20 01:53:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
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);
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
spRenameRegEx->PutReplaceTerm(replaceTerm);
|
2020-08-20 01:53:09 +02:00
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
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
|
2020-07-02 10:52:01 +02:00
|
|
|
// Except string transformation is selected.
|
|
|
|
|
|
|
|
if (newName == nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase))
|
|
|
|
{
|
|
|
|
SHStrDup(sourceName, &newName);
|
|
|
|
}
|
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 10:52:01 +02:00
|
|
|
wchar_t trimmedName[MAX_PATH] = { 0 };
|
|
|
|
if (newNameToUse != nullptr && SUCCEEDED(GetTrimmedFileName(trimmedName, ARRAYSIZE(trimmedName), newNameToUse)))
|
|
|
|
{
|
|
|
|
newNameToUse = trimmedName;
|
|
|
|
}
|
2020-07-16 13:24:49 +02:00
|
|
|
|
2020-07-02 10:52:01 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2020-07-16 13:24:49 +02:00
|
|
|
|
2019-10-18 05:57:19 +02:00
|
|
|
// 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++;
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:22:05 +02:00
|
|
|
spItem->PutNewName(newNameToUse);
|
2019-10-18 05:57:19 +02:00
|
|
|
|
|
|
|
// 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);
|
2020-08-20 01:53:09 +02:00
|
|
|
CoTaskMemFree(replaceTerm);
|
2019-10-18 05:57:19 +02:00
|
|
|
CoTaskMemFree(currentNewName);
|
|
|
|
CoTaskMemFree(originalName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the manager thread the completion message
|
|
|
|
PostMessage(pwtd->hwndManager, SRM_REGEX_COMPLETE, GetCurrentThreadId(), 0);
|
|
|
|
|
|
|
|
delete pwtd;
|
|
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_CancelRegExWorkerThread()
|
|
|
|
{
|
|
|
|
if (m_startRegExWorkerEvent)
|
|
|
|
{
|
|
|
|
SetEvent(m_startRegExWorkerEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_cancelRegExWorkerEvent)
|
|
|
|
{
|
|
|
|
SetEvent(m_cancelRegExWorkerEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
_WaitForRegExWorkerThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_WaitForRegExWorkerThread()
|
|
|
|
{
|
|
|
|
if (m_regExWorkerThreadHandle)
|
|
|
|
{
|
|
|
|
WaitForSingleObject(m_regExWorkerThreadHandle, INFINITE);
|
|
|
|
CloseHandle(m_regExWorkerThreadHandle);
|
|
|
|
m_regExWorkerThreadHandle = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_Cancel()
|
|
|
|
{
|
|
|
|
SetEvent(m_startFileOpWorkerEvent);
|
|
|
|
_CancelRegExWorkerThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CPowerRenameManager::_EnsureRegEx()
|
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!m_spRegEx)
|
|
|
|
{
|
|
|
|
// Create the default regex handler
|
|
|
|
hr = CPowerRenameRegEx::s_CreateInstance(&m_spRegEx);
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
hr = _InitRegEx();
|
|
|
|
// Get the flags
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
2020-08-25 07:22:05 +02:00
|
|
|
m_spRegEx->GetFlags(&m_flags);
|
2019-10-18 05:57:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CPowerRenameManager::_InitRegEx()
|
|
|
|
{
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (m_spRegEx)
|
|
|
|
{
|
|
|
|
hr = m_spRegEx->Advise(this, &m_regExAdviseCookie);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_ClearRegEx()
|
|
|
|
{
|
|
|
|
if (m_spRegEx)
|
|
|
|
{
|
|
|
|
m_spRegEx->UnAdvise(m_regExAdviseCookie);
|
|
|
|
m_regExAdviseCookie = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_OnItemAdded(_In_ IPowerRenameItem* renameItem)
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (auto it : m_powerRenameManagerEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (it.pEvents)
|
|
|
|
{
|
|
|
|
it.pEvents->OnItemAdded(renameItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_OnUpdate(_In_ IPowerRenameItem* renameItem)
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (auto it : m_powerRenameManagerEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (it.pEvents)
|
|
|
|
{
|
|
|
|
it.pEvents->OnUpdate(renameItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_OnError(_In_ IPowerRenameItem* renameItem)
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (auto it : m_powerRenameManagerEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (it.pEvents)
|
|
|
|
{
|
|
|
|
it.pEvents->OnError(renameItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_OnRegExStarted(_In_ DWORD threadId)
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (auto it : m_powerRenameManagerEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (it.pEvents)
|
|
|
|
{
|
|
|
|
it.pEvents->OnRegExStarted(threadId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_OnRegExCanceled(_In_ DWORD threadId)
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (auto it : m_powerRenameManagerEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (it.pEvents)
|
|
|
|
{
|
|
|
|
it.pEvents->OnRegExCanceled(threadId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_OnRegExCompleted(_In_ DWORD threadId)
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (auto it : m_powerRenameManagerEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (it.pEvents)
|
|
|
|
{
|
|
|
|
it.pEvents->OnRegExCompleted(threadId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_OnRenameStarted()
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (auto it : m_powerRenameManagerEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (it.pEvents)
|
|
|
|
{
|
|
|
|
it.pEvents->OnRenameStarted();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_OnRenameCompleted()
|
|
|
|
{
|
|
|
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
for (auto it : m_powerRenameManagerEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
|
|
|
if (it.pEvents)
|
|
|
|
{
|
|
|
|
it.pEvents->OnRenameCompleted();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_ClearEventHandlers()
|
|
|
|
{
|
|
|
|
CSRWExclusiveAutoLock lock(&m_lockEvents);
|
|
|
|
|
|
|
|
// Cleanup event handlers
|
2019-11-12 05:58:39 +01:00
|
|
|
for (std::vector<RENAME_MGR_EVENT>::iterator it = m_powerRenameManagerEvents.begin(); it != m_powerRenameManagerEvents.end(); ++it)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-12 05:58:39 +01:00
|
|
|
it->cookie = 0;
|
|
|
|
if (it->pEvents)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-12 05:58:39 +01:00
|
|
|
it->pEvents->Release();
|
|
|
|
it->pEvents = nullptr;
|
2019-10-18 05:57:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-12 05:58:39 +01:00
|
|
|
m_powerRenameManagerEvents.clear();
|
2019-10-18 05:57:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_ClearPowerRenameItems()
|
|
|
|
{
|
|
|
|
CSRWExclusiveAutoLock lock(&m_lockItems);
|
|
|
|
|
2019-11-11 20:00:42 +01:00
|
|
|
// Cleanup rename items
|
2019-11-12 05:58:39 +01:00
|
|
|
for (std::map<int, IPowerRenameItem*>::iterator it = m_renameItems.begin(); it != m_renameItems.end(); ++it)
|
2019-10-18 05:57:19 +02:00
|
|
|
{
|
2019-11-12 05:58:39 +01:00
|
|
|
IPowerRenameItem* pItem = it->second;
|
|
|
|
if (pItem)
|
|
|
|
{
|
|
|
|
pItem->Release();
|
|
|
|
it->second = nullptr;
|
|
|
|
}
|
2019-10-18 05:57:19 +02:00
|
|
|
}
|
|
|
|
|
2019-11-11 20:00:42 +01:00
|
|
|
m_renameItems.clear();
|
2019-10-18 05:57:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPowerRenameManager::_Cleanup()
|
|
|
|
{
|
|
|
|
if (m_hwndMessage)
|
|
|
|
{
|
|
|
|
DestroyWindow(m_hwndMessage);
|
|
|
|
m_hwndMessage = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseHandle(m_startFileOpWorkerEvent);
|
|
|
|
m_startFileOpWorkerEvent = nullptr;
|
|
|
|
|
|
|
|
CloseHandle(m_startRegExWorkerEvent);
|
|
|
|
m_startRegExWorkerEvent = nullptr;
|
|
|
|
|
|
|
|
CloseHandle(m_cancelRegExWorkerEvent);
|
|
|
|
m_cancelRegExWorkerEvent = nullptr;
|
|
|
|
|
|
|
|
_ClearRegEx();
|
|
|
|
_ClearEventHandlers();
|
|
|
|
_ClearPowerRenameItems();
|
|
|
|
}
|