Reactos

Revert "[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)" (#6800)

Reverts #6784 that was a guilty commit of CORE-19531 that causing performance regression.

JIRA issue: CORE-19531

authored by

Katayama Hirofumi MZ and committed by
GitHub
d55f4997 91acf823

+147 -264
+74 -250
dll/win32/shell32/shelldesktop/CDirectoryList.cpp
··· 2 2 * PROJECT: shell32 3 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 4 * PURPOSE: Shell change notification 5 - * COPYRIGHT: Copyright 2020-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 5 + * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 6 6 */ 7 7 #include "shelldesktop.h" 8 8 #include "CDirectoryList.h" 9 - #include <atlstr.h> 9 + #include <assert.h> // for assert 10 10 11 11 WINE_DEFAULT_DEBUG_CHANNEL(shcn); 12 12 13 - /////////////////////////////////////////////////////////////////////////////////////// 14 - // File-system path iterator 15 - 16 - class CFSPathIterator 13 + BOOL CDirectoryList::ContainsPath(LPCWSTR pszPath) const 17 14 { 18 - public: 19 - CStringW m_strFullName; 20 - INT m_ich; 15 + assert(!PathIsRelativeW(pszPath)); 21 16 22 - CFSPathIterator(CStringW strFullName) : m_strFullName(strFullName), m_ich(0) 17 + for (INT i = 0; i < m_items.GetSize(); ++i) 23 18 { 24 - } 19 + if (m_items[i].IsEmpty()) 20 + continue; 25 21 26 - bool Next(CStringW& strNext); 27 - }; 28 - 29 - /////////////////////////////////////////////////////////////////////////////////////// 30 - 31 - bool CFSPathIterator::Next(CStringW& strNext) 32 - { 33 - if (m_ich >= m_strFullName.GetLength()) 34 - return false; 35 - 36 - auto ich = m_strFullName.Find(L'\\', m_ich); 37 - if (ich < 0) 38 - { 39 - ich = m_strFullName.GetLength(); 40 - strNext = m_strFullName.Mid(m_ich, ich - m_ich); 41 - m_ich = ich; 42 - } 43 - else 44 - { 45 - strNext = m_strFullName.Mid(m_ich, ich - m_ich); 46 - m_ich = ich + 1; 22 + if (m_items[i].EqualPath(pszPath)) 23 + return TRUE; // matched 47 24 } 48 - return true; 49 - } 50 - 51 - /////////////////////////////////////////////////////////////////////////////////////// 52 - // File-system node 53 - 54 - class CFSNode 55 - { 56 - public: 57 - CStringW m_strName; 58 - 59 - CFSNode(const CStringW& strName, CFSNode* pParent = NULL); 60 - ~CFSNode(); 61 - 62 - CStringW GetFullName(); 63 - CFSNode* BuildPath(const CStringW& strFullName, BOOL bMarkNotExpanded = TRUE); 64 - 65 - CFSNode* FindChild(const CStringW& strName); 66 - CFSNode* Find(const CStringW& strFullName); 67 - 68 - BOOL RemoveChild(CFSNode *pNode); 69 - BOOL Remove(); 70 - 71 - void MarkNotExpanded(); 72 - 73 - void Expand(); 74 - void clear(); 75 - 76 - protected: 77 - BOOL m_bExpand; 78 - CFSNode* m_pParent; 79 - CSimpleArray<CFSNode*> m_children; 80 - }; 81 - 82 - /////////////////////////////////////////////////////////////////////////////////////// 83 - 84 - CFSNode::CFSNode(const CStringW& strName, CFSNode* pParent) 85 - : m_strName(strName) 86 - , m_bExpand(FALSE) 87 - , m_pParent(pParent) 88 - { 25 + return FALSE; 89 26 } 90 27 91 - CFSNode::~CFSNode() 28 + BOOL CDirectoryList::AddPath(LPCWSTR pszPath) 92 29 { 93 - clear(); 94 - } 95 - 96 - CStringW CFSNode::GetFullName() 97 - { 98 - CStringW ret; 99 - if (m_pParent) 100 - ret = m_pParent->GetFullName(); 101 - if (ret.GetLength()) 102 - ret += L'\\'; 103 - ret += m_strName; 104 - return ret; 105 - } 106 - 107 - CFSNode* CFSNode::FindChild(const CStringW& strName) 108 - { 109 - for (INT iItem = 0; iItem < m_children.GetSize(); ++iItem) 30 + assert(!PathIsRelativeW(pszPath)); 31 + if (ContainsPath(pszPath)) 32 + return FALSE; 33 + for (INT i = 0; i < m_items.GetSize(); ++i) 110 34 { 111 - auto pChild = m_children[iItem]; 112 - if (pChild && 113 - pChild->m_strName.GetLength() == strName.GetLength() && 114 - lstrcmpiW(pChild->m_strName, strName) == 0) 35 + if (m_items[i].IsEmpty()) 115 36 { 116 - return pChild; 37 + m_items[i].SetPath(pszPath); 38 + return TRUE; 117 39 } 118 40 } 119 - return NULL; 41 + return m_items.Add(pszPath); 120 42 } 121 43 122 - BOOL CFSNode::RemoveChild(CFSNode *pNode) 44 + BOOL CDirectoryList::RenamePath(LPCWSTR pszPath1, LPCWSTR pszPath2) 123 45 { 124 - for (INT iItem = 0; iItem < m_children.GetSize(); ++iItem) 46 + assert(!PathIsRelativeW(pszPath1)); 47 + assert(!PathIsRelativeW(pszPath2)); 48 + 49 + for (INT i = 0; i < m_items.GetSize(); ++i) 125 50 { 126 - auto& pChild = m_children[iItem]; 127 - if (pChild == pNode) 51 + if (m_items[i].EqualPath(pszPath1)) 128 52 { 129 - auto pOld = pChild; 130 - pChild = NULL; 131 - delete pOld; 53 + // matched 54 + m_items[i].SetPath(pszPath2); 132 55 return TRUE; 133 56 } 134 57 } 135 58 return FALSE; 136 59 } 137 60 138 - BOOL CFSNode::Remove() 61 + BOOL CDirectoryList::DeletePath(LPCWSTR pszPath) 139 62 { 140 - if (m_pParent) 141 - return m_pParent->RemoveChild(this); 142 - return FALSE; 143 - } 63 + assert(!PathIsRelativeW(pszPath)); 144 64 145 - CFSNode* CFSNode::Find(const CStringW& strFullName) 146 - { 147 - CFSPathIterator it(strFullName); 148 - CStringW strName; 149 - CFSNode *pChild, *pNode; 150 - for (pNode = this; it.Next(strName); pNode = pChild) 65 + for (INT i = 0; i < m_items.GetSize(); ++i) 151 66 { 152 - pChild = pNode->FindChild(strName); 153 - if (!pChild) 154 - return NULL; 67 + if (m_items[i].EqualPath(pszPath)) 68 + { 69 + // matched 70 + m_items[i].SetPath(NULL); 71 + return TRUE; 72 + } 155 73 } 156 - return pNode; 157 - } 158 - 159 - void CFSNode::MarkNotExpanded() 160 - { 161 - for (auto pNode = this; pNode; pNode = pNode->m_pParent) 162 - pNode->m_bExpand = FALSE; 74 + return FALSE; 163 75 } 164 76 165 - CFSNode* CFSNode::BuildPath(const CStringW& strFullName, BOOL bMarkNotExpanded) 77 + BOOL CDirectoryList::AddPathsFromDirectory(LPCWSTR pszDirectoryPath) 166 78 { 167 - CFSPathIterator it(strFullName); 168 - CStringW strName; 169 - CFSNode *pNode, *pChild = NULL; 170 - for (pNode = this; it.Next(strName); pNode = pChild) 171 - { 172 - pChild = pNode->FindChild(strName); 173 - if (pChild) 174 - continue; 79 + // get the full path 80 + WCHAR szPath[MAX_PATH]; 81 + lstrcpynW(szPath, pszDirectoryPath, _countof(szPath)); 82 + assert(!PathIsRelativeW(szPath)); 175 83 176 - pChild = new CFSNode(strName, pNode); 177 - pNode->m_children.Add(pChild); 178 - if (bMarkNotExpanded) 179 - pNode->MarkNotExpanded(); 180 - } 181 - return pNode; 182 - } 84 + // is it a directory? 85 + if (!PathIsDirectoryW(szPath)) 86 + return FALSE; 183 87 184 - void CFSNode::Expand() 185 - { 186 - if (m_bExpand) 187 - return; 188 - 189 - auto strSpec = GetFullName(); 190 - strSpec += L"\\*"; 88 + // add the path 89 + if (!AddPath(szPath)) 90 + return FALSE; 191 91 92 + // enumerate the file items to remember 93 + PathAppendW(szPath, L"*"); 192 94 WIN32_FIND_DATAW find; 193 - HANDLE hFind = ::FindFirstFileW(strSpec, &find); 95 + HANDLE hFind = FindFirstFileW(szPath, &find); 194 96 if (hFind == INVALID_HANDLE_VALUE) 195 - return; 97 + { 98 + ERR("FindFirstFileW failed\n"); 99 + return FALSE; 100 + } 196 101 102 + LPWSTR pch; 197 103 do 198 104 { 199 - if (lstrcmpW(find.cFileName, L".") == 0 || 200 - lstrcmpW(find.cFileName, L"..") == 0 || 201 - !(find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || 202 - (find.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 105 + // ignore "." and ".." 106 + pch = find.cFileName; 107 + if (pch[0] == L'.' && (pch[1] == 0 || (pch[1] == L'.' && pch[2] == 0))) 108 + continue; 109 + 110 + // build a path 111 + PathRemoveFileSpecW(szPath); 112 + if (lstrlenW(szPath) + lstrlenW(find.cFileName) + 1 > MAX_PATH) 203 113 { 114 + ERR("szPath is too long\n"); 204 115 continue; 205 116 } 117 + PathAppendW(szPath, find.cFileName); 206 118 207 - auto pNode = FindChild(find.cFileName); 208 - if (!pNode) 119 + // add the path and do recurse 120 + if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 209 121 { 210 - pNode = new CFSNode(find.cFileName, this); 211 - m_children.Add(pNode); 122 + if (m_fRecursive) 123 + AddPathsFromDirectory(szPath); 124 + else 125 + AddPath(szPath); 212 126 } 213 - pNode->Expand(); 214 - } while (::FindNextFileW(hFind, &find)); 215 - ::FindClose(hFind); 216 - 217 - m_bExpand = TRUE; 218 - } 219 - 220 - void CFSNode::clear() 221 - { 222 - for (INT iItem = 0; iItem < m_children.GetSize(); ++iItem) 223 - { 224 - auto& pChild = m_children[iItem]; 225 - delete pChild; 226 - pChild = NULL; 227 - } 228 - m_children.RemoveAll(); 229 - } 127 + } while (FindNextFileW(hFind, &find)); 230 128 231 - /////////////////////////////////////////////////////////////////////////////////////// 232 - // CDirectoryList 233 - 234 - CDirectoryList::CDirectoryList(CFSNode *pRoot) 235 - : m_pRoot(pRoot ? pRoot : (new CFSNode(L""))) 236 - , m_fRecursive(FALSE) 237 - { 238 - } 239 - 240 - CDirectoryList::CDirectoryList(CFSNode *pRoot, LPCWSTR pszDirectoryPath, BOOL fRecursive) 241 - : m_pRoot(pRoot ? pRoot : (new CFSNode(L""))) 242 - , m_fRecursive(fRecursive) 243 - { 244 - AddPathsFromDirectory(pszDirectoryPath); 245 - } 246 - 247 - CDirectoryList::~CDirectoryList() 248 - { 249 - delete m_pRoot; 250 - } 251 - 252 - BOOL CDirectoryList::ContainsPath(LPCWSTR pszPath) const 253 - { 254 - ATLASSERT(!PathIsRelativeW(pszPath)); 255 - 256 - return !!m_pRoot->Find(pszPath); 257 - } 258 - 259 - BOOL CDirectoryList::AddPath(LPCWSTR pszPath) 260 - { 261 - ATLASSERT(!PathIsRelativeW(pszPath)); 262 - 263 - auto pNode = m_pRoot->BuildPath(pszPath); 264 - if (pNode && m_fRecursive) 265 - pNode->Expand(); 266 - 267 - return TRUE; 268 - } 269 - 270 - BOOL CDirectoryList::RenamePath(LPCWSTR pszPath1, LPCWSTR pszPath2) 271 - { 272 - ATLASSERT(!PathIsRelativeW(pszPath1)); 273 - ATLASSERT(!PathIsRelativeW(pszPath2)); 274 - 275 - auto pNode = m_pRoot->Find(pszPath1); 276 - if (!pNode) 277 - return FALSE; 278 - 279 - LPWSTR pch = wcsrchr(pszPath2, L'\\'); 280 - if (!pch) 281 - return FALSE; 282 - 283 - pNode->m_strName = pch + 1; 284 - return TRUE; 285 - } 286 - 287 - BOOL CDirectoryList::DeletePath(LPCWSTR pszPath) 288 - { 289 - ATLASSERT(!PathIsRelativeW(pszPath)); 290 - 291 - auto pNode = m_pRoot->Find(pszPath); 292 - if (!pNode) 293 - return FALSE; 294 - 295 - pNode->Remove(); 296 - return TRUE; 297 - } 298 - 299 - BOOL CDirectoryList::AddPathsFromDirectory(LPCWSTR pszDirectoryPath) 300 - { 301 - ATLASSERT(!PathIsRelativeW(pszPath)); 302 - 303 - auto pNode = m_pRoot->BuildPath(pszDirectoryPath); 304 - if (pNode) 305 - pNode->Expand(); 129 + FindClose(hFind); 306 130 307 131 return TRUE; 308 132 }
+72 -13
dll/win32/shell32/shelldesktop/CDirectoryList.h
··· 1 - /* 2 - * PROJECT: shell32 3 - * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 - * PURPOSE: Shell change notification 5 - * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 6 - */ 7 - 8 1 #pragma once 9 2 10 3 #include <atlsimpcoll.h> // for CSimpleArray 11 4 12 5 ////////////////////////////////////////////////////////////////////////////// 13 6 14 - class CFSNode; 7 + // A pathname with info 8 + class CDirectoryItem 9 + { 10 + public: 11 + CDirectoryItem() : m_pszPath(NULL) 12 + { 13 + } 14 + 15 + CDirectoryItem(LPCWSTR pszPath) 16 + { 17 + m_pszPath = _wcsdup(pszPath); 18 + } 19 + 20 + CDirectoryItem(const CDirectoryItem& item) 21 + : m_pszPath(_wcsdup(item.m_pszPath)) 22 + { 23 + } 24 + 25 + CDirectoryItem& operator=(const CDirectoryItem& item) 26 + { 27 + if (this != &item) 28 + { 29 + free(m_pszPath); 30 + m_pszPath = _wcsdup(item.m_pszPath); 31 + } 32 + return *this; 33 + } 34 + 35 + ~CDirectoryItem() 36 + { 37 + free(m_pszPath); 38 + } 39 + 40 + BOOL IsEmpty() const 41 + { 42 + return m_pszPath == NULL; 43 + } 44 + 45 + LPCWSTR GetPath() const 46 + { 47 + return m_pszPath; 48 + } 49 + 50 + void SetPath(LPCWSTR pszPath) 51 + { 52 + free(m_pszPath); 53 + m_pszPath = _wcsdup(pszPath); 54 + } 55 + 56 + BOOL EqualPath(LPCWSTR pszPath) const 57 + { 58 + return m_pszPath != NULL && lstrcmpiW(m_pszPath, pszPath) == 0; 59 + } 60 + 61 + protected: 62 + LPWSTR m_pszPath; // A full path, malloc'ed 63 + }; 15 64 16 65 // the directory list 17 66 class CDirectoryList 18 67 { 19 68 public: 20 - CDirectoryList(CFSNode *pRoot); 21 - CDirectoryList(CFSNode *pRoot, LPCWSTR pszDirectoryPath, BOOL fRecursive); 22 - ~CDirectoryList(); 69 + CDirectoryList() : m_fRecursive(FALSE) 70 + { 71 + } 72 + 73 + CDirectoryList(LPCWSTR pszDirectoryPath, BOOL fRecursive) 74 + : m_fRecursive(fRecursive) 75 + { 76 + AddPathsFromDirectory(pszDirectoryPath); 77 + } 23 78 24 79 BOOL ContainsPath(LPCWSTR pszPath) const; 25 80 BOOL AddPath(LPCWSTR pszPath); 26 81 BOOL AddPathsFromDirectory(LPCWSTR pszDirectoryPath); 27 82 BOOL RenamePath(LPCWSTR pszPath1, LPCWSTR pszPath2); 28 83 BOOL DeletePath(LPCWSTR pszPath); 29 - void RemoveAll(); 84 + 85 + void RemoveAll() 86 + { 87 + m_items.RemoveAll(); 88 + } 30 89 31 90 protected: 32 - CFSNode *m_pRoot; 33 91 BOOL m_fRecursive; 92 + CSimpleArray<CDirectoryItem> m_items; 34 93 };
+1 -1
dll/win32/shell32/shelldesktop/CDirectoryWatcher.cpp
··· 71 71 CDirectoryWatcher::CDirectoryWatcher(LPCWSTR pszDirectoryPath, BOOL fSubTree) 72 72 : m_fDead(FALSE) 73 73 , m_fRecursive(fSubTree) 74 - , m_dir_list(NULL, pszDirectoryPath, fSubTree) 74 + , m_dir_list(pszDirectoryPath, fSubTree) 75 75 { 76 76 TRACE("CDirectoryWatcher::CDirectoryWatcher: %p, '%S'\n", this, pszDirectoryPath); 77 77