|
|
#include "pch.h"
#include "thisdll.h"
#include "cowsite.h"
#include <shlobj.h>
#include "ids.h"
// Context menu offset IDs
enum { CMD_ORGANIZE = 0, CMD_ORGANIZE_DEEP = 1, CMD_ORGANIZE_FLAT = 2, CMD_MAX = 3, };
class COrganizeFiles;
class COrganizeFiles : public IContextMenu, IShellExtInit, INamespaceWalkCB, CObjectWithSite { public: COrganizeFiles();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IShellExtInit
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hKeyID);
// IContextMenu
STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags); STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pcmici); STDMETHODIMP GetCommandString(UINT_PTR uID, UINT uFlags, UINT *res, LPSTR pName, UINT ccMax);
// INamespaceWalkCB
STDMETHODIMP FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHODIMP EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHODIMP LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHOD(InitializeProgressDialog)(LPWSTR *ppszTitle, LPWSTR *ppszCancel) { *ppszTitle = NULL; *ppszCancel = NULL; return E_NOTIMPL; }
private: ~COrganizeFiles();
class CMD_THREAD_DATA { public: CMD_THREAD_DATA(COrganizeFiles *pof, UINT idCmd) : _pof(pof), _idCmd(idCmd) { _pof->AddRef(); _pstmDataObj = NULL; } ~CMD_THREAD_DATA() { _pof->Release(); ATOMICRELEASE(_pstmDataObj); }
COrganizeFiles *_pof; // back ptr to object
UINT _idCmd; IStream *_pstmDataObj; // the IDataObject marshalled to the background thread
};
static DWORD CALLBACK COrganizeFiles::_CmdThreadProc(void *pv); void _CreateCmdThread(UINT idCmd); void _DoCmd(UINT idCmd, IDataObject *pdtobj); void _DoOrganizeMusic(UINT idCmd, IDataObject *pdtobj); HRESULT _GetPropertyUI(IPropertyUI **pppui); HRESULT _NameFromPropertiesString(LPCTSTR pszProps, IPropertySetStorage *ppss, LPTSTR pszRoot, LPTSTR pszName, UINT cchName);
LONG _cRef; IDataObject *_pdtobj;
IPropertyUI *_ppui; BOOL _bCountFiles; // call back is in "count files" mode
UINT _cFilesTotal; // total computed in the count
UINT _cFileCur; // current, for progress UI
IProgressDialog *_ppd; TCHAR _szRootFolder[MAX_PATH]; LPCTSTR _pszProps; LPTSTR _pszTemplateFlat; LPTSTR _pszTemplate; };
COrganizeFiles::COrganizeFiles() : _cRef(1) { }
COrganizeFiles::~COrganizeFiles() { CoTaskMemFree(_pszTemplateFlat); CoTaskMemFree(_pszTemplate);
IUnknown_Set(&_punkSite, NULL); IUnknown_Set((IUnknown**)&_pdtobj, NULL); IUnknown_Set((IUnknown**)&_ppui, NULL); }
STDAPI COrganizeFiles_CreateInstance(IUnknown *pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi) { COrganizeFiles *psid = new COrganizeFiles(); if (!psid) { *ppunk = NULL; // incase of failure
return E_OUTOFMEMORY; }
HRESULT hr = psid->QueryInterface(IID_PPV_ARG(IUnknown, ppunk)); psid->Release(); return hr; }
STDMETHODIMP COrganizeFiles::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(COrganizeFiles, IShellExtInit), QITABENT(COrganizeFiles, IContextMenu), QITABENT(COrganizeFiles, IObjectWithSite), QITABENT(COrganizeFiles, INamespaceWalkCB), { 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) COrganizeFiles::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) COrganizeFiles::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
// IShellExtInit
STDMETHODIMP COrganizeFiles::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hKeyID) { IUnknown_Set((IUnknown**)&_pdtobj, pdtobj); return S_OK; }
// IContextMenu
STDMETHODIMP COrganizeFiles::QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags) { if (NULL == _pszTemplate) { IAssociationArray *paa; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_CtxQueryAssociations, IID_PPV_ARG(IAssociationArray, &paa)))) { paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQN_NAMED_VALUE, L"OrganizeTemplate", &_pszTemplate); paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQN_NAMED_VALUE, L"OrganizeTemplateFlat", &_pszTemplateFlat); paa->Release(); } }
if (!(uFlags & CMF_DEFAULTONLY)) { InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
TCHAR szBuffer[128]; LoadString(m_hInst, IDS_ORGANIZE_MUSIC, szBuffer, ARRAYSIZE(szBuffer)); InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_STRING, uIDFirst + CMD_ORGANIZE, szBuffer);
// if (uFlags & CMF_EXTENDEDVERBS)
{ LoadString(m_hInst, IDS_ORGANIZE_MUSIC_FLAT, szBuffer, ARRAYSIZE(szBuffer)); InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_STRING, uIDFirst + CMD_ORGANIZE_FLAT, szBuffer); } }
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, CMD_MAX); }
typedef struct { LPCTSTR psz; UINT id; } CMD_MAP;
const CMD_MAP c_szVerbs[] = { {TEXT("OrganizeMusic"), CMD_ORGANIZE}, {TEXT("OrganizeMusicFlat"), CMD_ORGANIZE_FLAT}, {TEXT("OrganizeMusicDeep"), CMD_ORGANIZE_DEEP}, };
UINT GetCommandId(LPCMINVOKECOMMANDINFO pcmici, const CMD_MAP rgMap[], UINT cMap) { UINT id = -1;
if (IS_INTRESOURCE(pcmici->lpVerb)) { id = LOWORD((UINT_PTR)pcmici->lpVerb); } else { TCHAR szCmd[64]; szCmd[0] = 0;
// Check first whether the caller passed a EX structure by checking
// the size...
if (pcmici->cbSize == sizeof(CMINVOKECOMMANDINFOEX)) { LPCMINVOKECOMMANDINFOEX pcmiciEX = (LPCMINVOKECOMMANDINFOEX)pcmici; if (pcmici->fMask & CMIC_MASK_UNICODE) { SHUnicodeToTChar(pcmiciEX->lpVerbW, szCmd, ARRAYSIZE(szCmd)); } } // If we don't yet have a command verb, it must have been passed
// ANSI or in a regular CMINVOKECOMMANDINFO structure (which means
// it is by default ANSI). In either case, we need to convert it to
// UNICODE before proceeding...
if (szCmd[0] == 0) { SHAnsiToTChar(pcmici->lpVerb, szCmd, ARRAYSIZE(szCmd)); }
for (UINT i = 0; i < cMap; i++) { if (!StrCmpIC(szCmd, rgMap[i].psz)) { id = rgMap[i].id; break; } } } return id; }
STDMETHODIMP COrganizeFiles::InvokeCommand(LPCMINVOKECOMMANDINFO pcmici) { HRESULT hr = S_OK;
UINT idCmd = GetCommandId(pcmici, c_szVerbs, ARRAYSIZE(c_szVerbs)); switch (idCmd) { case CMD_ORGANIZE: case CMD_ORGANIZE_DEEP: case CMD_ORGANIZE_FLAT: _CreateCmdThread(idCmd); break;
default: hr = E_INVALIDARG; break; }
return hr; }
STDMETHODIMP COrganizeFiles::GetCommandString(UINT_PTR uID, UINT uFlags, UINT *res, LPSTR pName, UINT cchMax) { HRESULT hr = S_OK; UINT idSel = (UINT)uID;
switch (uFlags) { case GCS_VERBW: case GCS_VERBA: if (idSel < ARRAYSIZE(c_szVerbs)) { if (uFlags == GCS_VERBW) { SHTCharToUnicode(c_szVerbs[idSel].psz, (LPWSTR)pName, cchMax); } else { SHTCharToAnsi(c_szVerbs[idSel].psz, pName, cchMax); } } break;
case GCS_HELPTEXTA: case GCS_HELPTEXTW: case GCS_VALIDATEA: case GCS_VALIDATEW: hr = E_NOTIMPL; break; }
return hr; }
DWORD CALLBACK COrganizeFiles::_CmdThreadProc(void *pv) { CMD_THREAD_DATA *potd = (CMD_THREAD_DATA *)pv; IDataObject *pdtobj; HRESULT hr = CoGetInterfaceAndReleaseStream(potd->_pstmDataObj, IID_PPV_ARG(IDataObject, &pdtobj)); potd->_pstmDataObj = NULL; if (SUCCEEDED(hr)) { potd->_pof->_DoCmd(potd->_idCmd, pdtobj); pdtobj->Release(); }
delete potd; return 0; }
void COrganizeFiles::_CreateCmdThread(UINT idCmd) { CMD_THREAD_DATA *potd = new CMD_THREAD_DATA(this, idCmd); if (potd) { if (FAILED(CoMarshalInterThreadInterfaceInStream(IID_IDataObject, _pdtobj, &potd->_pstmDataObj)) || !SHCreateThread(_CmdThreadProc, potd, CTF_COINIT, NULL)) { delete potd; } } }
void COrganizeFiles::_DoCmd(UINT idCmd, IDataObject *pdtobj) { switch (idCmd) { case CMD_ORGANIZE: case CMD_ORGANIZE_DEEP: case CMD_ORGANIZE_FLAT: _DoOrganizeMusic(idCmd, pdtobj); break; } }
HRESULT ReadPropertyAsString(IPropertyUI *ppui, IPropertySetStorage *ppss, LPCSHCOLUMNID pscid, LPTSTR psz, UINT cch) { *psz = 0;
IPropertyStorage *pps; HRESULT hr = ppss->Open(pscid->fmtid, STGM_READ | STGM_SHARE_EXCLUSIVE, &pps); if (SUCCEEDED(hr)) { PROPSPEC ps = {PRSPEC_PROPID, pscid->pid}; PROPVARIANT v = {0}; hr = pps->ReadMultiple(1, &ps, &v); if (SUCCEEDED(hr)) { hr = ppui->FormatForDisplay(pscid->fmtid, pscid->pid, &v, PUIFFDF_DEFAULT, psz, cch); if (SUCCEEDED(hr) && (0 == *psz)) { hr = E_FAIL; } PropVariantClear(&v); } pps->Release(); } return hr; }
void FixBadFilenameChars(LPTSTR pszName) { while (*pszName) { switch (*pszName) { case '?': case '*': case '/': case '\\': case '!': *pszName = '_'; break; case '\"': *pszName = '\''; break; case ':': *pszName = '-'; break; } pszName++; } }
LPTSTR PathCombineRemoveDups(LPTSTR pszNewPath, LPCTSTR pszRoot, LPCTSTR pszTail) { for (LPCTSTR psz = pszTail; (psz && *psz); psz = PathFindNextComponent(psz)) { LPCTSTR pszNext = PathFindNextComponent(psz); if (pszNext && *pszNext) { TCHAR szPart[MAX_PATH]; UINT_PTR len = pszNext - psz; StrCpyN(szPart, psz, (int)min(ARRAYSIZE(szPart), len)); LPCTSTR pszFound = StrStr(pszRoot, szPart); if ((pszFound > pszRoot) && (*(pszFound - 1) == TEXT('\\')) && ((*(pszFound + len - 1) == TEXT('\\')) || (*(pszFound + len - 1) == 0))) { pszTail = pszNext; } } else { break; } } return PathCombine(pszNewPath, pszRoot, pszTail); }
LPTSTR PathRemovePart(LPTSTR pszPath, LPCTSTR pszToRemove) { LPTSTR pszFound = StrStr(pszPath, pszToRemove); if (pszFound && (pszFound > pszPath) && (*(pszFound - 1) == TEXT('\\'))) { LPTSTR pszTail = pszFound + lstrlen(pszToRemove); if ((*pszTail == TEXT('\\')) || (*pszTail == 0)) { *pszFound = 0; if (*pszTail) { PathAppend(pszPath, pszTail + 1); } else { PathRemoveBackslash(pszPath); // trim extra stuff
} } } return pszPath; }
// pszProps == "%Artist% - %Album% - %Track% - %DocTitle%"
HRESULT COrganizeFiles::_NameFromPropertiesString(LPCTSTR pszProps, IPropertySetStorage *ppss, LPTSTR pszRoot, LPTSTR pszName, UINT cchName) { *pszName = 0; // start empty, we build this up
BOOL bSomeProps = FALSE; IPropertyUI *ppui; if (SUCCEEDED(_GetPropertyUI(&ppui))) { LPCTSTR psz = StrChr(pszProps, TEXT('%')); while (psz) { psz++; // skip first %
LPCTSTR pszTail = StrChr(psz, TEXT('%')); if (pszTail) { TCHAR szPropName[128]; StrCpyN(szPropName, psz, (int)min(ARRAYSIZE(szPropName), pszTail - psz + 1));
psz = pszTail + 1; // just past second %, make sure we advance every time through the loop
if (szPropName[0]) { SHCOLUMNID scid; ULONG chEaten = 0; // gets incremented by ParsePropertyName
if (SUCCEEDED(ppui->ParsePropertyName(szPropName, &scid.fmtid, &scid.pid, &chEaten))) { TCHAR szValue[128]; szValue[0] = 0;
if (SUCCEEDED(ReadPropertyAsString(ppui, ppss, &scid, szValue, ARRAYSIZE(szValue)))) { bSomeProps = TRUE; FixBadFilenameChars(szValue);
} else { TCHAR szUnknown[64], szPropDisplayName[128]; LoadString(m_hInst, IDS_UNKNOWN, szUnknown, ARRAYSIZE(szUnknown));
if (SUCCEEDED(ppui->GetDisplayName(scid.fmtid, scid.pid, PUIFNF_DEFAULT, szPropDisplayName, ARRAYSIZE(szPropDisplayName)))) { wnsprintf(szValue, ARRAYSIZE(szValue), TEXT("%s %s"), szUnknown, szPropDisplayName); } else { StrCpyN(szValue, szUnknown, ARRAYSIZE(szValue)); } }
// clean stuff out of the root path that we find
PathRemovePart(pszRoot, szValue);
if (szValue[0]) { StrCatBuff(pszName, szValue, cchName); // the value
// add extra formatting stuff next
LPCTSTR pszNext = StrChr(psz, TEXT('%')); if (NULL == pszNext) pszNext = psz + lstrlen(psz); // end of string case
// get extra stuff
TCHAR szExtra[64]; StrCpyN(szExtra, psz, (int)min((UINT)(pszNext - psz + 1), ARRAYSIZE(szExtra))); StrCatBuff(pszName, szExtra, cchName);
psz = *pszNext ? pszNext : NULL; // maybe end, maybe more
} } } } else { psz = NULL; } } ppui->Release(); } return bSomeProps ? S_OK : S_FALSE; }
// do both files binary compare
BOOL IsSameFile(LPCTSTR pszFile1, LPCTSTR pszFile2) { BOOL bSame = FALSE; IStream *pstm1; HRESULT hr = SHCreateStreamOnFileEx(pszFile1, STGM_READ | STGM_SHARE_DENY_NONE, 0, FALSE, NULL, &pstm1); if (SUCCEEDED(hr)) { IStream *pstm2; hr = SHCreateStreamOnFileEx(pszFile2, STGM_READ | STGM_SHARE_DENY_NONE, 0, FALSE, NULL, &pstm2); if (SUCCEEDED(hr)) { ULONG cb1; do { char buf1[4096]; hr = pstm1->Read(buf1, sizeof(buf1), &cb1); if (SUCCEEDED(hr)) { char buf2[4096]; ULONG cb2; hr = pstm2->Read(buf2, sizeof(buf2), &cb2); if (SUCCEEDED(hr)) { if (cb1 == cb2) { if (0 == memcmp(buf1, buf2, cb1)) bSame = TRUE; } else { bSame = FALSE; } } } } while (bSame && SUCCEEDED(hr) && cb1);
pstm2->Release(); } pstm1->Release(); } return bSame; }
// returns win32 error code
// 0 == success
int MoveFileAndCreateDirectory(LPCTSTR pszOldPath, LPCTSTR pszNewPath) { int err = ERROR_SUCCESS; if (!MoveFile(pszOldPath, pszNewPath)) { err = GetLastError(); if (ERROR_PATH_NOT_FOUND == err) { // maybe the target folder does not exist, lets
// create it and try again
TCHAR szNewPath[MAX_PATH]; StrCpyN(szNewPath, pszNewPath, ARRAYSIZE(szNewPath)); PathRemoveFileSpec(szNewPath);
if (ERROR_SUCCESS == SHCreateDirectoryEx(NULL, szNewPath, NULL)) { if (MoveFile(pszOldPath, pszNewPath)) { // failure now success
err = ERROR_SUCCESS; } } } } return err; }
LPCWSTR LoadStr(UINT id, LPWSTR psz, UINT cch) { LoadString(m_hInst, id, psz, cch); return psz; }
// INamespaceWalkCB
STDMETHODIMP COrganizeFiles::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl) { if (_bCountFiles) { _cFilesTotal++; } else { _cFileCur++;
// if we were invoked on just a file we never got an ::EnterFolder()
if (0 == _szRootFolder[0]) { DisplayNameOf(psf, pidl, SHGDN_FORPARSING, _szRootFolder, ARRAYSIZE(_szRootFolder)); PathRemoveFileSpec(_szRootFolder); }
TCHAR szOldPath[MAX_PATH]; DisplayNameOf(psf, pidl, SHGDN_FORPARSING, szOldPath, ARRAYSIZE(szOldPath)); _ppd->SetLine(2, szOldPath, TRUE, NULL); _ppd->SetProgress64(_cFileCur, _cFilesTotal);
IPropertySetStorage *ppss; if (SUCCEEDED(psf->BindToObject(pidl, NULL, IID_PPV_ARG(IPropertySetStorage, &ppss)))) { TCHAR szRoot[MAX_PATH]; TCHAR szNewName[MAX_PATH];
StrCpyN(szRoot, _szRootFolder, ARRAYSIZE(szRoot));
if (S_OK == _NameFromPropertiesString(_pszProps, ppss, szRoot, szNewName, ARRAYSIZE(szNewName))) { PathRenameExtension(szNewName, PathFindExtension(szOldPath)); TCHAR szNewPath[MAX_PATH]; // PathCombineRemoveDups(szNewPath, szRoot, szNewName);
PathCombine(szNewPath, szRoot, szNewName);
ASSERT(0 != *PathFindExtension(szNewPath)); if (StrCmpC(szNewPath, szOldPath)) { int err = MoveFileAndCreateDirectory(szOldPath, szNewPath); if (ERROR_ALREADY_EXISTS == err) { // maybe the source and target are the same file
// if so lets get rid of one of them
WCHAR szBuf[64];
_ppd->SetLine(2, LoadStr(IDS_COMPARING_FILES, szBuf, ARRAYSIZE(szBuf)), FALSE, NULL);
if (IsSameFile(szOldPath, szNewPath)) { DeleteFile(szOldPath); }
_ppd->SetLine(2, L"", FALSE, NULL); } } } ppss->Release(); } } return S_OK; }
STDMETHODIMP COrganizeFiles::EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { if (0 == _szRootFolder[0]) { DisplayNameOf(psf, pidl, SHGDN_FORPARSING, _szRootFolder, ARRAYSIZE(_szRootFolder)); } return S_OK; }
BOOL IsEmptyFolder(IShellFolder *psf) { // don't look for SHCONTF_INCLUDEHIDDEN items, as we want to still delete the folder
// if there are some of these
IEnumIDList *penum; HRESULT hr = psf->EnumObjects(NULL, SHCONTF_NONFOLDERS | SHCONTF_FOLDERS, &penum); if (S_OK == hr) { LPITEMIDLIST pidl; hr = penum->Next(1, &pidl, NULL); if (S_OK == hr) { ILFree(pidl); } penum->Release(); }
return S_FALSE == hr; }
void RemoveEmptyFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { IShellFolder *psfItem; if (SUCCEEDED(psf->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfItem)))) { if (IsEmptyFolder(psfItem)) { TCHAR szPath[MAX_PATH] = {0}; // zero init to dbl null terminate
DisplayNameOf(psf, pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath));
SHFILEOPSTRUCT fo = { NULL, FO_DELETE, szPath, NULL, FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI }; SHFileOperation(&fo); } psfItem->Release(); } }
STDMETHODIMP COrganizeFiles::LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { if (!_bCountFiles) { // if the folder is not empty we try to remove it knowing that this call will
// fail if there are files in the folder
RemoveEmptyFolder(psf, pidl); } return S_OK; }
HRESULT COrganizeFiles::_GetPropertyUI(IPropertyUI **pppui) { if (!_ppui) SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &_ppui));
return _ppui ? _ppui->QueryInterface(IID_PPV_ARG(IPropertyUI, pppui)) : E_NOTIMPL; }
void COrganizeFiles::_DoOrganizeMusic(UINT idCmd, IDataObject *pdtobj) { _pszProps = (idCmd == CMD_ORGANIZE_FLAT) ? (_pszTemplateFlat ? _pszTemplateFlat : TEXT("%Artist% - %Album% - %Track% - %DocTitle%")) : (_pszTemplate ? _pszTemplate : TEXT("%Artist%\\%Album%\\%Track% - %DocTitle%"));
INamespaceWalk *pnsw; HRESULT hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw)); if (SUCCEEDED(hr)) { IProgressDialog *ppd; hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IProgressDialog, &ppd)); if (SUCCEEDED(hr)) { ppd->StartProgressDialog(NULL, NULL, PROGDLG_AUTOTIME, NULL);
WCHAR szBuf[64]; ppd->SetTitle(LoadStr(IDS_ORGANIZE_MUSIC, szBuf, ARRAYSIZE(szBuf))); ppd->SetLine(1, LoadStr(IDS_FINDING_FILES, szBuf, ARRAYSIZE(szBuf)), FALSE, NULL);
_ppd = ppd; _bCountFiles = TRUE; _cFileCur = _cFilesTotal = 0; // everything happens in our callback interface
hr = pnsw->Walk(pdtobj, NSWF_DONT_TRAVERSE_LINKS | NSWF_DONT_ACCUMULATE_RESULT, 8, SAFECAST(this, INamespaceWalkCB *)); if (SUCCEEDED(hr)) { ppd->SetLine(1, LoadStr(IDS_ORGANIZING_FILES, szBuf, ARRAYSIZE(szBuf)), FALSE, NULL); _bCountFiles = FALSE; hr = pnsw->Walk(pdtobj, NSWF_DONT_TRAVERSE_LINKS | NSWF_DONT_ACCUMULATE_RESULT, 8, SAFECAST(this, INamespaceWalkCB *)); } ppd->StopProgressDialog(); ppd->Release(); } pnsw->Release(); }
if (_szRootFolder[0]) { SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, _szRootFolder, NULL); } }
|