Skip to content

Commit

Permalink
Add progress dialog for long startup enumerations
Browse files Browse the repository at this point in the history
  • Loading branch information
chrdavis committed Feb 12, 2021
1 parent 81fbb8c commit 0616d6b
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 111 deletions.
85 changes: 0 additions & 85 deletions SmartRenameLib/Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,91 +90,6 @@ HBITMAP CreateBitmapFromIcon(_In_ HICON hIcon, _In_opt_ UINT width, _In_opt_ UIN
return hBitmapResult;
}

HRESULT _ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ ISmartRenameManager* psrm, _In_opt_ IProgressDialog* ppd, _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<IShellItem> spsi;
while ((S_OK == pesi->Next(1, &spsi, &celtFetched)) && (SUCCEEDED(hr)))
{
if (ppd && ppd->HasUserCancelled())
{
// Cancelled by user
hr = E_ABORT;
break;
}

CComPtr<ISmartRenameItemFactory> spsrif;
hr = psrm->get_renameItemFactory(&spsrif);
if (SUCCEEDED(hr))
{
CComPtr<ISmartRenameItem> spNewItem;
hr = spsrif->Create(spsi, &spNewItem);
if (SUCCEEDED(hr))
{
spNewItem->put_depth(depth);
hr = psrm->AddItem(spNewItem);
if (SUCCEEDED(hr) && ppd)
{
// Update the progress dialog
PWSTR pathDisplay = nullptr;
if (SUCCEEDED(spNewItem->get_path(&pathDisplay)))
{
ppd->SetLine(2, pathDisplay, TRUE, nullptr);
CoTaskMemFree(pathDisplay);
}
}
}

if (SUCCEEDED(hr))
{
bool isFolder = false;
if (SUCCEEDED(spNewItem->get_isFolder(&isFolder)) && isFolder)
{
// Bind to the IShellItem for the IEnumShellItems interface
CComPtr<IEnumShellItems> spesiNext;
hr = spsi->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&spesiNext));
if (SUCCEEDED(hr))
{
// Parse the folder contents recursively
hr = _ParseEnumItems(spesiNext, psrm, ppd, depth + 1);
}
}
}
}

spsi = nullptr;
}
}

return hr;
}

// Iterate through the data object and add paths to the rotation manager
HRESULT EnumerateDataObject(_In_ IDataObject* pdo, _In_ ISmartRenameManager* psrm, _In_opt_ IProgressDialog* ppd)
{
CComPtr<IShellItemArray> spsia;
HRESULT hr = SHCreateShellItemArrayFromDataObject(pdo, IID_PPV_ARGS(&spsia));
if (SUCCEEDED(hr))
{
CComPtr<IEnumShellItems> spesi;
hr = spsia->EnumItems(&spesi);
if (SUCCEEDED(hr))
{
hr = _ParseEnumItems(spesi, psrm, ppd);
}
}

return hr;
}

HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p)
{
WNDCLASS wc = { 0 };
Expand Down
1 change: 0 additions & 1 deletion SmartRenameLib/Helpers.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once

HRESULT EnumerateDataObject(_In_ IDataObject* pdo, _In_ ISmartRenameManager* psrm, _In_opt_ IProgressDialog* ppd);
HRESULT GetIconIndexFromPath(_In_ PCWSTR path, _Out_ int* index);
HBITMAP CreateBitmapFromIcon(_In_ HICON hIcon, _In_opt_ UINT width = 0, _In_opt_ UINT height = 0);
HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p);
Expand Down
226 changes: 226 additions & 0 deletions SmartRenameLib/SmartRenameEnum.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#include "stdafx.h"
#include "SmartRenameEnum.h"
#include <ShlGuid.h>

IFACEMETHODIMP_(ULONG) CSmartRenameEnum::AddRef()
{
return InterlockedIncrement(&m_refCount);
}

IFACEMETHODIMP_(ULONG) CSmartRenameEnum::Release()
{
long refCount = InterlockedDecrement(&m_refCount);

if (refCount == 0)
{
delete this;
}
return refCount;
}

IFACEMETHODIMP CSmartRenameEnum::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv)
{
static const QITAB qit[] = {
QITABENT(CSmartRenameEnum, ISmartRenameEnum),
{ 0 }
};
return QISearch(this, qit, riid, ppv);
}

IFACEMETHODIMP CSmartRenameEnum::Advise(_In_ ISmartRenameEnumEvents* events, _Out_ DWORD* cookie)
{
CSRWExclusiveAutoLock lock(&m_lockEvents);
m_cookie++;
RENAME_ENUM_EVENT srme;
srme.cookie = m_cookie;
srme.pEvents = events;
events->AddRef();
m_renameEnumEvents.push_back(srme);

*cookie = m_cookie;

return S_OK;
}

IFACEMETHODIMP CSmartRenameEnum::UnAdvise(_In_ DWORD cookie)
{
HRESULT hr = E_FAIL;
CSRWExclusiveAutoLock lock(&m_lockEvents);

for (std::vector<RENAME_ENUM_EVENT>::iterator it = m_renameEnumEvents.begin(); it != m_renameEnumEvents.end(); ++it)
{
if (it->cookie == cookie)
{
hr = S_OK;
it->cookie = 0;
if (it->pEvents)
{
it->pEvents->Release();
it->pEvents = nullptr;
}
break;
}
}

return hr;
}


IFACEMETHODIMP CSmartRenameEnum::Start()
{
_OnStarted();
m_canceled = false;
CComPtr<IShellItemArray> spsia;
HRESULT hr = SHCreateShellItemArrayFromDataObject(m_spdo, IID_PPV_ARGS(&spsia));
if (SUCCEEDED(hr))
{
CComPtr<IEnumShellItems> spesi;
hr = spsia->EnumItems(&spesi);
if (SUCCEEDED(hr))
{
hr = _ParseEnumItems(spesi);
}
}

_OnCompleted();
return hr;
}

IFACEMETHODIMP CSmartRenameEnum::Cancel()
{
m_canceled = true;
return S_OK;
}

HRESULT CSmartRenameEnum::s_CreateInstance(_In_ IDataObject* pdo, _In_ _In_ ISmartRenameManager* psrm, _In_ REFIID iid, _Outptr_ void** resultInterface)
{
*resultInterface = nullptr;

CSmartRenameEnum* newRenameEnum = new CSmartRenameEnum();
HRESULT hr = newRenameEnum ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = newRenameEnum->_Init(pdo, psrm);
if (SUCCEEDED(hr))
{
hr = newRenameEnum->QueryInterface(iid, resultInterface);
}

newRenameEnum->Release();
}
return hr;
}

CSmartRenameEnum::CSmartRenameEnum() :
m_refCount(1)
{
}

CSmartRenameEnum::~CSmartRenameEnum()
{
}


void CSmartRenameEnum::_OnStarted()
{
CSRWSharedAutoLock lock(&m_lockEvents);

for (std::vector<RENAME_ENUM_EVENT>::iterator it = m_renameEnumEvents.begin(); it != m_renameEnumEvents.end(); ++it)
{
if (it->pEvents)
{
it->pEvents->OnStarted();
}
}
}

void CSmartRenameEnum::_OnCompleted()
{
CSRWSharedAutoLock lock(&m_lockEvents);

for (std::vector<RENAME_ENUM_EVENT>::iterator it = m_renameEnumEvents.begin(); it != m_renameEnumEvents.end(); ++it)
{
if (it->pEvents)
{
it->pEvents->OnCompleted(m_canceled);
}
}
}

void CSmartRenameEnum::_OnFoundItem(_In_ ISmartRenameItem* item)
{
CSRWSharedAutoLock lock(&m_lockEvents);

for (std::vector<RENAME_ENUM_EVENT>::iterator it = m_renameEnumEvents.begin(); it != m_renameEnumEvents.end(); ++it)
{
if (it->pEvents)
{
it->pEvents->OnFoundItem(item);
}
}
}

HRESULT CSmartRenameEnum::_Init(_In_ IDataObject* pdo, _In_ ISmartRenameManager* psrm)
{
m_spdo = pdo;
m_spsrm = psrm;
return S_OK;
}

HRESULT CSmartRenameEnum::_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<IShellItem> spsi;
while ((S_OK == pesi->Next(1, &spsi, &celtFetched)) && (SUCCEEDED(hr)))
{
if (m_canceled)
{
return E_ABORT;
}

CComPtr<ISmartRenameItemFactory> spsrif;
hr = m_spsrm->get_renameItemFactory(&spsrif);
if (SUCCEEDED(hr))
{
CComPtr<ISmartRenameItem> 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(spsrif->Create(spsi, &spNewItem)))
{
spNewItem->put_depth(depth);
_OnFoundItem(spNewItem);
hr = m_spsrm->AddItem(spNewItem);
if (SUCCEEDED(hr))
{
bool isFolder = false;
if (SUCCEEDED(spNewItem->get_isFolder(&isFolder)) && isFolder)
{
// Bind to the IShellItem for the IEnumShellItems interface
CComPtr<IEnumShellItems> 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;
}

51 changes: 51 additions & 0 deletions SmartRenameLib/SmartRenameEnum.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once
#include "stdafx.h"
#include "SmartRenameInterfaces.h"
#include <vector>
#include "srwlock.h"

class CSmartRenameEnum :
public ISmartRenameEnum
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(_In_ REFIID iid, _Outptr_ void** resultInterface);
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();

// ISmartRenameEnum
IFACEMETHODIMP Advise(_In_ ISmartRenameEnumEvents* events, _Out_ DWORD* cookie);
IFACEMETHODIMP UnAdvise(_In_ DWORD cookie);
IFACEMETHODIMP Start();
IFACEMETHODIMP Cancel();

public:
static HRESULT s_CreateInstance(_In_ IDataObject* pdo, _In_ _In_ ISmartRenameManager* psrm, _In_ REFIID iid, _Outptr_ void** resultInterface);

protected:
CSmartRenameEnum();
virtual ~CSmartRenameEnum();

HRESULT _Init(_In_ IDataObject* pdo, _In_ ISmartRenameManager* psrm);
HRESULT _ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ int depth = 0);

void _OnStarted();
void _OnCompleted();
void _OnFoundItem(_In_ ISmartRenameItem* item);

struct RENAME_ENUM_EVENT
{
ISmartRenameEnumEvents* pEvents;
DWORD cookie;
};

DWORD m_cookie = 0;

CSRWLock m_lockEvents;
_Guarded_by_(m_lockEvents) std::vector<RENAME_ENUM_EVENT> m_renameEnumEvents;

CComPtr<ISmartRenameManager> m_spsrm;
CComPtr<IDataObject> m_spdo;
bool m_canceled = false;
long m_refCount = 0;
};
17 changes: 17 additions & 0 deletions SmartRenameLib/SmartRenameInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,20 @@ interface __declspec(uuid("04AAFABE-B76E-4E13-993A-B5941F52B139")) ISmartRenameM
IFACEMETHOD(AddMRUString)(_In_ PCWSTR entry) = 0;
};

interface __declspec(uuid("7ABDE437-7AAF-4545-AA52-EC47A6F428C1")) ISmartRenameEnumEvents : public IUnknown
{
public:
IFACEMETHOD(OnStarted)() = 0;
IFACEMETHOD(OnCompleted)(_In_ bool canceled) = 0;
IFACEMETHOD(OnFoundItem)(_In_ ISmartRenameItem* item) = 0;
};

interface __declspec(uuid("2EFBAB41-A841-47B5-898B-B1CFBF151855")) ISmartRenameEnum : public IUnknown
{
public:
IFACEMETHOD(Advise)(_In_ ISmartRenameEnumEvents* events, _Out_ DWORD* cookie) = 0;
IFACEMETHOD(UnAdvise)(_In_ DWORD cookie) = 0;
IFACEMETHOD(Start)() = 0;
IFACEMETHOD(Cancel)() = 0;
};

Loading

0 comments on commit 0616d6b

Please sign in to comment.