From d128939227c14b5f35faea4681eebf0d5f3af4c7 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 2 Apr 2021 03:07:12 -0700 Subject: [PATCH] Show progress dialog during startup (#9255) * Show progress dialog during startup for selection enumeration that can take a long time. * Updated with better code organization and a timer to ensure the progress dialog does not appear in most cases. * Update based on PR feedback * Change progress dialog delay from 1500ms to 2500ms * Move progress dialog invocation off the main UI thread Co-authored-by: Chris Davis (EDGE) --- .gitignore | 2 + src/modules/powerrename/lib/Helpers.cpp | 98 +++------ src/modules/powerrename/lib/Helpers.h | 3 +- .../powerrename/lib/PowerRenameEnum.cpp | 144 +++++++++++++ src/modules/powerrename/lib/PowerRenameEnum.h | 34 +++ .../powerrename/lib/PowerRenameInterfaces.h | 6 + .../powerrename/lib/PowerRenameLib.vcxproj | 2 + .../powerrename/lib/PowerRenameManager.cpp | 28 --- src/modules/powerrename/lib/pch.h | 1 + src/modules/powerrename/ui/PowerRenameUI.cpp | 204 +++++++++++++++++- src/modules/powerrename/ui/PowerRenameUI.h | 41 +++- src/modules/powerrename/ui/Resources.resx | 6 + 12 files changed, 463 insertions(+), 106 deletions(-) create mode 100644 src/modules/powerrename/lib/PowerRenameEnum.cpp create mode 100644 src/modules/powerrename/lib/PowerRenameEnum.h diff --git a/.gitignore b/.gitignore index bfded31434..1c39ddc996 100644 --- a/.gitignore +++ b/.gitignore @@ -342,3 +342,5 @@ src/common/Telemetry/*.etl !**/MergeModules/Release/ !**/MergeModules/Debug/ /src/modules/previewpane/SvgThumbnailProvider/$(SolutionDir)$(Platform)/$(Configuration)/modules/FileExplorerPreview/SvgThumbnailProvider.xml +/src/modules/powerrename/ui/RCa24464 +/src/modules/powerrename/ui/RCb24464 diff --git a/src/modules/powerrename/lib/Helpers.cpp b/src/modules/powerrename/lib/Helpers.cpp index 0c01f06597..30447d34f8 100644 --- a/src/modules/powerrename/lib/Helpers.cpp +++ b/src/modules/powerrename/lib/Helpers.cpp @@ -275,7 +275,7 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY return hr; } -HRESULT _GetShellItemArrayFromDataOject(_In_ IUnknown* dataSource, _COM_Outptr_ IShellItemArray** items) +HRESULT GetShellItemArrayFromDataObject(_In_ IUnknown* dataSource, _COM_Outptr_ IShellItemArray** items) { *items = nullptr; CComPtr dataObj; @@ -292,73 +292,6 @@ HRESULT _GetShellItemArrayFromDataOject(_In_ IUnknown* dataSource, _COM_Outptr_ return hr; } -HRESULT _ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ IPowerRenameManager* psrm, _In_ int depth = 0) -{ - HRESULT hr = E_INVALIDARG; - - // We shouldn't get this deep since we only enum the contents of - // regular folders but adding just in case - if ((pesi) && (depth < (MAX_PATH / 2))) - { - hr = S_OK; - - ULONG celtFetched; - CComPtr spsi; - while ((S_OK == pesi->Next(1, &spsi, &celtFetched)) && (SUCCEEDED(hr))) - { - CComPtr spsrif; - hr = psrm->GetRenameItemFactory(&spsrif); - if (SUCCEEDED(hr)) - { - CComPtr spNewItem; - hr = spsrif->Create(spsi, &spNewItem); - if (SUCCEEDED(hr)) - { - spNewItem->PutDepth(depth); - hr = psrm->AddItem(spNewItem); - } - - if (SUCCEEDED(hr)) - { - bool isFolder = false; - if (SUCCEEDED(spNewItem->GetIsFolder(&isFolder)) && isFolder) - { - // Bind to the IShellItem for the IEnumShellItems interface - CComPtr spesiNext; - hr = spsi->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&spesiNext)); - if (SUCCEEDED(hr)) - { - // Parse the folder contents recursively - hr = _ParseEnumItems(spesiNext, psrm, depth + 1); - } - } - } - } - - spsi = nullptr; - } - } - - return hr; -} - -// Iterate through the data source and add paths to the rotation manager -HRESULT EnumerateDataObject(_In_ IUnknown* dataSource, _In_ IPowerRenameManager* psrm) -{ - CComPtr spsia; - HRESULT hr = E_FAIL; - if (SUCCEEDED(_GetShellItemArrayFromDataOject(dataSource, &spsia))) - { - CComPtr spesi; - if (SUCCEEDED(spsia->EnumItems(&spesi))) - { - hr = _ParseEnumItems(spesi, psrm); - } - } - - return hr; -} - BOOL GetEnumeratedFileName(__out_ecount(cchMax) PWSTR pszUniqueName, UINT cchMax, __in PCWSTR pszTemplate, __in_opt PCWSTR pszDir, unsigned long ulMinLong, __inout unsigned long* pulNumUsed) { PWSTR pszName = nullptr; @@ -528,7 +461,7 @@ bool DataObjectContainsRenamableItem(_In_ IUnknown* dataSource) { bool hasRenamable = false; CComPtr spsia; - if (SUCCEEDED(_GetShellItemArrayFromDataOject(dataSource, &spsia))) + if (SUCCEEDED(GetShellItemArrayFromDataObject(dataSource, &spsia))) { CComPtr spesi; if (SUCCEEDED(spsia->EnumItems(&spesi))) @@ -549,3 +482,30 @@ bool DataObjectContainsRenamableItem(_In_ IUnknown* dataSource) } return hasRenamable; } + +HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p) +{ + WNDCLASS wc = { 0 }; + PCWSTR wndClassName = L"MsgWindow"; + + wc.lpfnWndProc = DefWindowProc; + wc.cbWndExtra = sizeof(void*); + wc.hInstance = hInst; + wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wc.lpszClassName = wndClassName; + + RegisterClass(&wc); + + HWND hwnd = CreateWindowEx( + 0, wndClassName, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInst, nullptr); + if (hwnd) + { + SetWindowLongPtr(hwnd, 0, (LONG_PTR)p); + if (pfnWndProc) + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnWndProc); + } + } + + return hwnd; +} diff --git a/src/modules/powerrename/lib/Helpers.h b/src/modules/powerrename/lib/Helpers.h index e035c65b66..a803ba15f5 100644 --- a/src/modules/powerrename/lib/Helpers.h +++ b/src/modules/powerrename/lib/Helpers.h @@ -7,7 +7,7 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME fileTime); bool isFileTimeUsed(_In_ PCWSTR source); bool DataObjectContainsRenamableItem(_In_ IUnknown* dataSource); -HRESULT EnumerateDataObject(_In_ IUnknown* pdo, _In_ IPowerRenameManager* psrm); +HRESULT GetShellItemArrayFromDataObject(_In_ IUnknown* dataSource, _COM_Outptr_ IShellItemArray** items); BOOL GetEnumeratedFileName( __out_ecount(cchMax) PWSTR pszUniqueName, UINT cchMax, @@ -15,3 +15,4 @@ BOOL GetEnumeratedFileName( __in_opt PCWSTR pszDir, unsigned long ulMinLong, __inout unsigned long* pulNumUsed); +HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p); diff --git a/src/modules/powerrename/lib/PowerRenameEnum.cpp b/src/modules/powerrename/lib/PowerRenameEnum.cpp new file mode 100644 index 0000000000..94b575226a --- /dev/null +++ b/src/modules/powerrename/lib/PowerRenameEnum.cpp @@ -0,0 +1,144 @@ +#include "pch.h" +#include "PowerRenameEnum.h" +#include +#include + +IFACEMETHODIMP_(ULONG) CPowerRenameEnum::AddRef() +{ + return InterlockedIncrement(&m_refCount); +} + +IFACEMETHODIMP_(ULONG) CPowerRenameEnum::Release() +{ + long refCount = InterlockedDecrement(&m_refCount); + + if (refCount == 0) + { + delete this; + } + return refCount; +} + +IFACEMETHODIMP CPowerRenameEnum::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv) +{ + static const QITAB qit[] = { + QITABENT(CPowerRenameEnum, IPowerRenameEnum), + { 0 } + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP CPowerRenameEnum::Start() +{ + m_canceled = false; + CComPtr spsia; + HRESULT hr = GetShellItemArrayFromDataObject(m_spdo, &spsia); + if (SUCCEEDED(hr)) + { + CComPtr spesi; + hr = spsia->EnumItems(&spesi); + if (SUCCEEDED(hr)) + { + hr = _ParseEnumItems(spesi); + } + } + + return hr; +} + +IFACEMETHODIMP CPowerRenameEnum::Cancel() +{ + m_canceled = true; + return S_OK; +} + +HRESULT CPowerRenameEnum::s_CreateInstance(_In_ IUnknown* pdo, _In_ IPowerRenameManager* pManager, _In_ REFIID iid, _Outptr_ void** resultInterface) +{ + *resultInterface = nullptr; + + CPowerRenameEnum* newRenameEnum = new CPowerRenameEnum(); + HRESULT hr = newRenameEnum ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) + { + hr = newRenameEnum->_Init(pdo, pManager); + if (SUCCEEDED(hr)) + { + hr = newRenameEnum->QueryInterface(iid, resultInterface); + } + + newRenameEnum->Release(); + } + return hr; +} + +CPowerRenameEnum::CPowerRenameEnum() : + m_refCount(1) +{ +} + +CPowerRenameEnum::~CPowerRenameEnum() +{ +} + +HRESULT CPowerRenameEnum::_Init(_In_ IUnknown* pdo, _In_ IPowerRenameManager* pManager) +{ + m_spdo = pdo; + m_spsrm = pManager; + return S_OK; +} + +HRESULT CPowerRenameEnum::_ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ int depth) +{ + HRESULT hr = E_INVALIDARG; + + // We shouldn't get this deep since we only enum the contents of + // regular folders but adding just in case + if ((pesi) && (depth < (MAX_PATH / 2))) + { + hr = S_OK; + + ULONG celtFetched; + CComPtr spsi; + while ((S_OK == pesi->Next(1, &spsi, &celtFetched)) && (SUCCEEDED(hr))) + { + if (m_canceled) + { + return E_ABORT; + } + + CComPtr spFactory; + hr = m_spsrm->GetRenameItemFactory(&spFactory); + if (SUCCEEDED(hr)) + { + CComPtr spNewItem; + // Failure may be valid if we come across a shell item that does + // not support a file system path. In that case we simply ignore + // the item. + if (SUCCEEDED(spFactory->Create(spsi, &spNewItem))) + { + spNewItem->PutDepth(depth); + hr = m_spsrm->AddItem(spNewItem); + if (SUCCEEDED(hr)) + { + bool isFolder = false; + if (SUCCEEDED(spNewItem->GetIsFolder(&isFolder)) && isFolder) + { + // Bind to the IShellItem for the IEnumShellItems interface + CComPtr spesiNext; + hr = spsi->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&spesiNext)); + if (SUCCEEDED(hr)) + { + // Parse the folder contents recursively + hr = _ParseEnumItems(spesiNext, depth + 1); + } + } + } + } + } + + spsi = nullptr; + } + } + + return hr; +} diff --git a/src/modules/powerrename/lib/PowerRenameEnum.h b/src/modules/powerrename/lib/PowerRenameEnum.h new file mode 100644 index 0000000000..c2923bc587 --- /dev/null +++ b/src/modules/powerrename/lib/PowerRenameEnum.h @@ -0,0 +1,34 @@ +#pragma once +#include "pch.h" +#include "PowerRenameInterfaces.h" +#include +#include "srwlock.h" + +class CPowerRenameEnum : + public IPowerRenameEnum +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(_In_ REFIID iid, _Outptr_ void** resultInterface); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // ISmartRenameEnum + IFACEMETHODIMP Start(); + IFACEMETHODIMP Cancel(); + +public: + static HRESULT s_CreateInstance(_In_ IUnknown* pdo, _In_ IPowerRenameManager* pManager, _In_ REFIID iid, _Outptr_ void** resultInterface); + +protected: + CPowerRenameEnum(); + virtual ~CPowerRenameEnum(); + + HRESULT _Init(_In_ IUnknown* pdo, _In_ IPowerRenameManager* pManager); + HRESULT _ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ int depth = 0); + + CComPtr m_spsrm; + CComPtr m_spdo; + bool m_canceled = false; + long m_refCount = 0; +}; \ No newline at end of file diff --git a/src/modules/powerrename/lib/PowerRenameInterfaces.h b/src/modules/powerrename/lib/PowerRenameInterfaces.h index ee2303c1fd..c274cdb99b 100644 --- a/src/modules/powerrename/lib/PowerRenameInterfaces.h +++ b/src/modules/powerrename/lib/PowerRenameInterfaces.h @@ -134,3 +134,9 @@ public: IFACEMETHOD(AddMRUString)(_In_ PCWSTR entry) = 0; }; +interface __declspec(uuid("CE8C8616-C1A8-457A-9601-10570F5B9F1F")) IPowerRenameEnum : public IUnknown +{ +public: + IFACEMETHOD(Start)() = 0; + IFACEMETHOD(Cancel)() = 0; +}; diff --git a/src/modules/powerrename/lib/PowerRenameLib.vcxproj b/src/modules/powerrename/lib/PowerRenameLib.vcxproj index 25147ebd76..1372c57ba1 100644 --- a/src/modules/powerrename/lib/PowerRenameLib.vcxproj +++ b/src/modules/powerrename/lib/PowerRenameLib.vcxproj @@ -40,6 +40,7 @@ + @@ -52,6 +53,7 @@ + diff --git a/src/modules/powerrename/lib/PowerRenameManager.cpp b/src/modules/powerrename/lib/PowerRenameManager.cpp index 4089a52985..bdf62fe1df 100644 --- a/src/modules/powerrename/lib/PowerRenameManager.cpp +++ b/src/modules/powerrename/lib/PowerRenameManager.cpp @@ -13,34 +13,6 @@ namespace fs = std::filesystem; extern HINSTANCE g_hInst; -HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p) -{ - WNDCLASS wc = { 0 }; - - PCWSTR wndClassName = L"MsgWindow"; - - wc.lpfnWndProc = DefWindowProc; - wc.cbWndExtra = sizeof(void*); - wc.hInstance = hInst; - wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wc.lpszClassName = wndClassName; - - RegisterClass(&wc); - - HWND hwnd = CreateWindowEx( - 0, wndClassName, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInst, nullptr); - if (hwnd) - { - SetWindowLongPtr(hwnd, 0, (LONG_PTR)p); - if (pfnWndProc) - { - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnWndProc); - } - } - - return hwnd; -} - // The default FOF flags to use in the rename operations #define FOF_DEFAULTFLAGS (FOF_ALLOWUNDO | FOFX_ADDUNDORECORD | FOFX_SHOWELEVATIONPROMPT | FOF_RENAMEONCOLLISION) diff --git a/src/modules/powerrename/lib/pch.h b/src/modules/powerrename/lib/pch.h index 607ca6cd92..121c6909fc 100644 --- a/src/modules/powerrename/lib/pch.h +++ b/src/modules/powerrename/lib/pch.h @@ -17,6 +17,7 @@ #include #include #include +#include #include diff --git a/src/modules/powerrename/ui/PowerRenameUI.cpp b/src/modules/powerrename/ui/PowerRenameUI.cpp index 63ffd534e4..c6f1245c38 100644 --- a/src/modules/powerrename/ui/PowerRenameUI.cpp +++ b/src/modules/powerrename/ui/PowerRenameUI.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -167,6 +168,17 @@ IFACEMETHODIMP CPowerRenameUI::GetShowUI(_Out_ bool* showUI) // IPowerRenameManagerEvents IFACEMETHODIMP CPowerRenameUI::OnItemAdded(_In_ IPowerRenameItem*) { + // Check if the user canceled the enumeration from the progress dialog UI + if (m_prpui.IsCanceled()) + { + m_prpui.Stop(); + if (m_sppre) + { + // Cancel the enumeration + m_sppre->Cancel(); + } + } + return S_OK; } @@ -373,21 +385,37 @@ void CPowerRenameUI::_Cleanup() m_hwnd = NULL; } -void CPowerRenameUI::_EnumerateItems(_In_ IUnknown* pdtobj) +HRESULT CPowerRenameUI::_EnumerateItems(_In_ IUnknown* pdtobj) { + HRESULT hr = S_OK; // Enumerate the data object and populate the manager if (m_spsrm) { m_disableCountUpdate = true; - EnumerateDataObject(pdtobj, m_spsrm); + + // Ensure we re-create the enumerator + m_sppre = nullptr; + hr = CPowerRenameEnum::s_CreateInstance(pdtobj, m_spsrm, IID_PPV_ARGS(&m_sppre)); + if (SUCCEEDED(hr)) + { + m_prpui.Start(); + hr = m_sppre->Start(); + m_prpui.Stop(); + } + m_disableCountUpdate = false; - UINT itemCount = 0; - m_spsrm->GetVisibleItemCount(&itemCount); - m_listview.SetItemCount(itemCount); + if (SUCCEEDED(hr)) + { + UINT itemCount = 0; + m_spsrm->GetItemCount(&itemCount); + m_listview.SetItemCount(itemCount); - _UpdateCounts(); + _UpdateCounts(); + } } + + return hr; } HRESULT CPowerRenameUI::_ReadSettings() @@ -607,7 +635,12 @@ void CPowerRenameUI::_OnInitDlg() if (m_dataSource) { // Populate the manager from the data object - _EnumerateItems(m_dataSource); + if (FAILED(_EnumerateItems(m_dataSource))) + { + // Failed during enumeration. Close the dialog. + _OnCloseDlg(); + return; + } } // Initialize from stored settings. Do this now in case we have @@ -1442,3 +1475,160 @@ void CPowerRenameListView::OnColumnClick(_In_ IPowerRenameManager* psrm, _In_ in psrm->GetFilter(&filter); _UpdateHeaderFilterState(filter); } + +IFACEMETHODIMP CPowerRenameProgressUI::QueryInterface(__in REFIID riid, __deref_out void** ppv) +{ + static const QITAB qit[] = { + QITABENT(CPowerRenameProgressUI, IUnknown), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +CPowerRenameProgressUI::AddRef() +{ + return InterlockedIncrement(&m_refCount); +} + +IFACEMETHODIMP_(ULONG) +CPowerRenameProgressUI::Release() +{ + long refCount = InterlockedDecrement(&m_refCount); + if (refCount == 0) + { + delete this; + } + return refCount; +} + +#define TIMERID_CHECKCANCELED 101 +#define CANCEL_CHECK_INTERVAL 500 + +HRESULT CPowerRenameProgressUI::Start() +{ + _Cleanup(); + m_canceled = false; + m_workerThreadHandle = CreateThread(nullptr, 0, s_workerThread, this, 0, nullptr); + return (m_workerThreadHandle) ? S_OK : E_FAIL; +} + +DWORD WINAPI CPowerRenameProgressUI::s_workerThread(_In_ void* pv) +{ + if (SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) + { + CPowerRenameProgressUI* pThis = reinterpret_cast(pv); + if (pThis) + { + HWND hwndMessage = CreateMsgWindow(g_hInst, s_msgWndProc, pThis); + + SetTimer(hwndMessage, TIMERID_CHECKCANCELED, CANCEL_CHECK_INTERVAL, nullptr); + + if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&pThis->m_sppd)))) + { + wchar_t buff[100] = { 0 }; + LoadString(g_hInst, IDS_LOADING, buff, ARRAYSIZE(buff)); + pThis->m_sppd->SetLine(1, buff, FALSE, NULL); + LoadString(g_hInst, IDS_LOADING_MSG, buff, ARRAYSIZE(buff)); + pThis->m_sppd->SetLine(2, buff, FALSE, NULL); + LoadString(g_hInst, IDS_APP_TITLE, buff, ARRAYSIZE(buff)); + pThis->m_sppd->SetTitle(buff); + SetTimer(hwndMessage, TIMERID_CHECKCANCELED, CANCEL_CHECK_INTERVAL, nullptr); + pThis->m_sppd->StartProgressDialog(NULL, NULL, PROGDLG_MARQUEEPROGRESS, NULL); + } + + while (pThis->m_sppd && !pThis->m_canceled) + { + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + KillTimer(hwndMessage, TIMERID_CHECKCANCELED); + DestroyWindow(hwndMessage); + } + + CoUninitialize(); + } + + return S_OK; +} + +HRESULT CPowerRenameProgressUI::Stop() +{ + _Cleanup(); + return S_OK; +} + +void CPowerRenameProgressUI::_Cleanup() +{ + if (m_sppd) + { + m_sppd->StopProgressDialog(); + m_sppd = nullptr; + } + + if (m_workerThreadHandle) + { + CloseHandle(m_workerThreadHandle); + m_workerThreadHandle = nullptr; + } +} + +void CPowerRenameProgressUI::_UpdateCancelState() +{ + m_canceled = (m_sppd && m_sppd->HasUserCancelled()); +} + +LRESULT CALLBACK CPowerRenameProgressUI::s_msgWndProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) +{ + LRESULT lRes = 0; + + CPowerRenameProgressUI* pThis = (CPowerRenameProgressUI*)GetWindowLongPtr(hwnd, 0); + if (pThis != nullptr) + { + lRes = pThis->_WndProc(hwnd, uMsg, wParam, lParam); + if (uMsg == WM_NCDESTROY) + { + SetWindowLongPtr(hwnd, 0, NULL); + } + } + else + { + lRes = DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + return lRes; +} + +LRESULT CPowerRenameProgressUI::_WndProc(_In_ HWND hwnd, _In_ UINT msg, _In_ WPARAM wParam, _In_ LPARAM lParam) +{ + LRESULT lRes = 0; + + AddRef(); + + switch (msg) + { + case WM_TIMER: + if (wParam == TIMERID_CHECKCANCELED) + { + _UpdateCancelState(); + } + break; + + case WM_DESTROY: + KillTimer(hwnd, TIMERID_CHECKCANCELED); + break; + + default: + lRes = DefWindowProc(hwnd, msg, wParam, lParam); + break; + } + + Release(); + + return lRes; +} \ No newline at end of file diff --git a/src/modules/powerrename/ui/PowerRenameUI.h b/src/modules/powerrename/ui/PowerRenameUI.h index c0ef499156..ab036b4db2 100644 --- a/src/modules/powerrename/ui/PowerRenameUI.h +++ b/src/modules/powerrename/ui/PowerRenameUI.h @@ -35,6 +35,43 @@ private: HWND m_hwndLV = nullptr; }; +class CPowerRenameProgressUI : + public IUnknown +{ +public: + CPowerRenameProgressUI() : + m_refCount(1) + { + } + + ~CPowerRenameProgressUI() = default; + + // IUnknown + IFACEMETHODIMP QueryInterface(__in REFIID riid, __deref_out void** ppv); + IFACEMETHODIMP_(ULONG) + AddRef(); + IFACEMETHODIMP_(ULONG) + Release(); + + HRESULT Start(); + HRESULT Stop(); + bool IsCanceled() { return m_canceled; } + +private: + static LRESULT CALLBACK s_msgWndProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); + LRESULT _WndProc(_In_ HWND hwnd, _In_ UINT msg, _In_ WPARAM wParam, _In_ LPARAM lParam); + + static DWORD WINAPI s_workerThread(_In_ void* pv); + + void _UpdateCancelState(); + void _Cleanup(); + + long m_refCount = 0; + bool m_canceled = false; + HANDLE m_workerThreadHandle = nullptr; + CComPtr m_sppd; +}; + class CPowerRenameUI : public IDropTarget, public IPowerRenameUI, @@ -141,7 +178,7 @@ private: void _SetCheckboxesFromFlags(_In_ DWORD flags); void _ValidateFlagCheckbox(_In_ DWORD checkBoxId); - void _EnumerateItems(_In_ IUnknown* pdtobj); + HRESULT _EnumerateItems(_In_ IUnknown* pdtobj); void _UpdateCounts(); void _CollectItemPosition(_In_ DWORD id); @@ -164,8 +201,10 @@ private: int m_initialHeight = 0; int m_lastWidth = 0; int m_lastHeight = 0; + CPowerRenameProgressUI m_prpui; CComPtr m_spsrm; CComPtr m_dataSource; + CComPtr m_sppre; CComPtr m_spdth; CComPtr m_spSearchAC; CComPtr m_spSearchACL; diff --git a/src/modules/powerrename/ui/Resources.resx b/src/modules/powerrename/ui/Resources.resx index 4da3576bbd..28b645e29e 100644 --- a/src/modules/powerrename/ui/Resources.resx +++ b/src/modules/powerrename/ui/Resources.resx @@ -205,4 +205,10 @@ Please select from the options above to show items. Enter the criteria below to rename the items + + Loading... + + + Please wait while the selected items are enumerated. + \ No newline at end of file