[SHELL32] Optimize change notification (#3030)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Mon, 10 Aug 2020 04:34:48 +0000 (13:34 +0900)
committerGitHub <noreply@github.com>
Mon, 10 Aug 2020 04:34:48 +0000 (13:34 +0900)
- Keep the directory lists only.
- Don't remember file sizes and normal file paths.
CORE-13950

dll/win32/shell32/shelldesktop/CDirectoryList.cpp [new file with mode: 0644]
dll/win32/shell32/shelldesktop/CDirectoryList.h [new file with mode: 0644]
dll/win32/shell32/shelldesktop/CDirectoryWatcher.cpp
dll/win32/shell32/shelldesktop/CDirectoryWatcher.h
dll/win32/shell32/shelldesktop/CFilePathList.cpp [deleted file]
dll/win32/shell32/shelldesktop/CFilePathList.h [deleted file]
dll/win32/shell32/shelldesktop/CMakeLists.txt

diff --git a/dll/win32/shell32/shelldesktop/CDirectoryList.cpp b/dll/win32/shell32/shelldesktop/CDirectoryList.cpp
new file mode 100644 (file)
index 0000000..eff446d
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * PROJECT:     shell32
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Shell change notification
+ * COPYRIGHT:   Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
+ */
+#include "shelldesktop.h"
+#include "CDirectoryList.h"
+#include <assert.h>      // for assert
+
+WINE_DEFAULT_DEBUG_CHANNEL(shcn);
+
+BOOL CDirectoryList::ContainsPath(LPCWSTR pszPath) const
+{
+    assert(!PathIsRelativeW(pszPath));
+
+    for (INT i = 0; i < m_items.GetSize(); ++i)
+    {
+        if (m_items[i].IsEmpty())
+            continue;
+
+        if (m_items[i].EqualPath(pszPath))
+            return TRUE; // matched
+    }
+    return FALSE;
+}
+
+BOOL CDirectoryList::AddPath(LPCWSTR pszPath)
+{
+    assert(!PathIsRelativeW(pszPath));
+    return m_items.Add(pszPath);
+}
+
+BOOL CDirectoryList::RenamePath(LPCWSTR pszPath1, LPCWSTR pszPath2)
+{
+    assert(!PathIsRelativeW(pszPath1));
+    assert(!PathIsRelativeW(pszPath2));
+
+    for (INT i = 0; i < m_items.GetSize(); ++i)
+    {
+        if (m_items[i].EqualPath(pszPath1))
+        {
+            // matched
+            m_items[i].SetPath(pszPath2);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+BOOL CDirectoryList::DeletePath(LPCWSTR pszPath)
+{
+    assert(!PathIsRelativeW(pszPath));
+
+    for (INT i = 0; i < m_items.GetSize(); ++i)
+    {
+        if (m_items[i].EqualPath(pszPath))
+        {
+            // matched
+            m_items[i].SetPath(NULL);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+BOOL CDirectoryList::AddPathsFromDirectory(LPCWSTR pszDirectoryPath)
+{
+    // get the full path
+    WCHAR szPath[MAX_PATH];
+    lstrcpynW(szPath, pszDirectoryPath, _countof(szPath));
+    assert(!PathIsRelativeW(szPath));
+
+    // is it a directory?
+    if (!PathIsDirectoryW(szPath))
+        return FALSE;
+
+    // add the path
+    if (!AddPath(szPath))
+        return FALSE;
+
+    // enumerate the file items to remember
+    PathAppendW(szPath, L"*");
+    WIN32_FIND_DATAW find;
+    HANDLE hFind = FindFirstFileW(szPath, &find);
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        ERR("FindFirstFileW failed\n");
+        return FALSE;
+    }
+
+    LPWSTR pch;
+    do
+    {
+        // ignore "." and ".."
+        pch = find.cFileName;
+        if (pch[0] == L'.' && (pch[1] == 0 || (pch[1] == L'.' && pch[2] == 0)))
+            continue;
+
+        // build a path
+        PathRemoveFileSpecW(szPath);
+        if (lstrlenW(szPath) + lstrlenW(find.cFileName) + 1 > MAX_PATH)
+        {
+            ERR("szPath is too long\n");
+            continue;
+        }
+        PathAppendW(szPath, find.cFileName);
+
+        // add the path and do recurse
+        if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        {
+            if (m_fRecursive)
+                AddPathsFromDirectory(szPath);
+            else
+                AddPath(szPath);
+        }
+    } while (FindNextFileW(hFind, &find));
+
+    FindClose(hFind);
+
+    return TRUE;
+}
diff --git a/dll/win32/shell32/shelldesktop/CDirectoryList.h b/dll/win32/shell32/shelldesktop/CDirectoryList.h
new file mode 100644 (file)
index 0000000..1e9559c
--- /dev/null
@@ -0,0 +1,93 @@
+#pragma once
+
+#include <atlsimpcoll.h> // for CSimpleArray
+
+//////////////////////////////////////////////////////////////////////////////
+
+// A pathname with info
+class CDirectoryItem
+{
+public:
+    CDirectoryItem() : m_pszPath(NULL)
+    {
+    }
+
+    CDirectoryItem(LPCWSTR pszPath)
+    {
+        m_pszPath = _wcsdup(pszPath);
+    }
+
+    CDirectoryItem(const CDirectoryItem& item)
+        : m_pszPath(_wcsdup(item.m_pszPath))
+    {
+    }
+
+    CDirectoryItem& operator=(const CDirectoryItem& item)
+    {
+        if (this != &item)
+        {
+            free(m_pszPath);
+            m_pszPath = _wcsdup(item.m_pszPath);
+        }
+        return *this;
+    }
+
+    ~CDirectoryItem()
+    {
+        free(m_pszPath);
+    }
+
+    BOOL IsEmpty() const
+    {
+        return m_pszPath == NULL;
+    }
+
+    LPCWSTR GetPath() const
+    {
+        return m_pszPath;
+    }
+
+    void SetPath(LPCWSTR pszPath)
+    {
+        free(m_pszPath);
+        m_pszPath = _wcsdup(pszPath);
+    }
+
+    BOOL EqualPath(LPCWSTR pszPath) const
+    {
+        return m_pszPath != NULL && lstrcmpiW(m_pszPath, pszPath) == 0;
+    }
+
+protected:
+    LPWSTR m_pszPath;    // A full path, malloc'ed
+};
+
+// the directory list
+class CDirectoryList
+{
+public:
+    CDirectoryList() : m_fRecursive(FALSE)
+    {
+    }
+
+    CDirectoryList(LPCWSTR pszDirectoryPath, BOOL fRecursive)
+        : m_fRecursive(fRecursive)
+    {
+        AddPathsFromDirectory(pszDirectoryPath);
+    }
+
+    BOOL ContainsPath(LPCWSTR pszPath) const;
+    BOOL AddPath(LPCWSTR pszPath);
+    BOOL AddPathsFromDirectory(LPCWSTR pszDirectoryPath);
+    BOOL RenamePath(LPCWSTR pszPath1, LPCWSTR pszPath2);
+    BOOL DeletePath(LPCWSTR pszPath);
+
+    void RemoveAll()
+    {
+        m_items.RemoveAll();
+    }
+
+protected:
+    BOOL m_fRecursive;
+    CSimpleArray<CDirectoryItem> m_items;
+};
index d2ed158..9a206ac 100644 (file)
@@ -71,7 +71,7 @@ static void NTAPI _RequestAllTerminationAPC(ULONG_PTR Parameter)
 CDirectoryWatcher::CDirectoryWatcher(LPCWSTR pszDirectoryPath, BOOL fSubTree)
     : m_fDead(FALSE)
     , m_fRecursive(fSubTree)
-    , m_file_list(pszDirectoryPath, fSubTree)
+    , m_dir_list(pszDirectoryPath, fSubTree)
 {
     TRACE("CDirectoryWatcher::CDirectoryWatcher: %p, '%S'\n", this, pszDirectoryPath);
 
@@ -139,24 +139,6 @@ void CDirectoryWatcher::ProcessNotification()
     DWORD dwEvent, cbName;
     BOOL fDir;
 
-    // if the watch is recursive
-    if (m_fRecursive)
-    {
-        // get the first change
-        if (!m_file_list.GetFirstChange(szPath))
-            return;
-
-        // then, notify a SHCNE_UPDATEDIR
-        if (lstrcmpiW(m_szDirectoryPath, szPath) != 0)
-            PathRemoveFileSpecW(szPath);
-        NotifyFileSystemChange(SHCNE_UPDATEDIR, szPath, NULL);
-
-        // refresh directory list
-        m_file_list.RemoveAll();
-        m_file_list.AddPathsFromDirectory(m_szDirectoryPath, TRUE);
-        return;
-    }
-
     // for each entry in s_buffer
     szPath[0] = szTempPath[0] = 0;
     for (;;)
@@ -188,37 +170,37 @@ void CDirectoryWatcher::ProcessNotification()
         dwEvent = ConvertActionToEvent(pInfo->Action, fDir);
 
         // convert SHCNE_DELETE to SHCNE_RMDIR if the path is a directory
-        if (!fDir && (dwEvent == SHCNE_DELETE) && m_file_list.ContainsPath(szPath, TRUE))
+        if (!fDir && (dwEvent == SHCNE_DELETE) && m_dir_list.ContainsPath(szPath))
         {
             fDir = TRUE;
             dwEvent = SHCNE_RMDIR;
         }
 
-        // update m_file_list
+        // update m_dir_list
         switch (dwEvent)
         {
             case SHCNE_MKDIR:
-                if (!m_file_list.AddPath(szPath, 0, TRUE))
+                if (!PathIsDirectoryW(szPath) || !m_dir_list.AddPath(szPath))
                     dwEvent = 0;
                 break;
             case SHCNE_CREATE:
-                if (!m_file_list.AddPath(szPath, INVALID_FILE_SIZE, FALSE))
+                if (!PathFileExistsW(szPath) || PathIsDirectoryW(szPath))
                     dwEvent = 0;
                 break;
             case SHCNE_RENAMEFOLDER:
-                if (!m_file_list.RenamePath(szTempPath, szPath, TRUE))
+                if (!PathIsDirectoryW(szPath) || !m_dir_list.RenamePath(szTempPath, szPath))
                     dwEvent = 0;
                 break;
             case SHCNE_RENAMEITEM:
-                if (!m_file_list.RenamePath(szTempPath, szPath, FALSE))
+                if (!PathFileExistsW(szPath) || PathIsDirectoryW(szPath))
                     dwEvent = 0;
                 break;
             case SHCNE_RMDIR:
-                if (!m_file_list.DeletePath(szPath, TRUE))
+                if (PathIsDirectoryW(szPath) || !m_dir_list.DeletePath(szPath))
                     dwEvent = 0;
                 break;
             case SHCNE_DELETE:
-                if (!m_file_list.DeletePath(szPath, FALSE))
+                if (PathFileExistsW(szPath))
                     dwEvent = 0;
                 break;
         }
index 667d5fa..a8789b2 100644 (file)
@@ -6,7 +6,7 @@
  */
 #pragma once
 
-#include "CFilePathList.h"
+#include "CDirectoryList.h"
 
 // NOTE: Regard to asynchronous procedure call (APC), please see:
 // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleepex
@@ -31,7 +31,7 @@ public:
 protected:
     BOOL m_fDead;
     BOOL m_fRecursive;
-    CFilePathList m_file_list;
+    CDirectoryList m_dir_list;
     OVERLAPPED m_overlapped;
 
     BOOL CreateAPCThread();
diff --git a/dll/win32/shell32/shelldesktop/CFilePathList.cpp b/dll/win32/shell32/shelldesktop/CFilePathList.cpp
deleted file mode 100644 (file)
index cb4d505..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * PROJECT:     shell32
- * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
- * PURPOSE:     Shell change notification
- * COPYRIGHT:   Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
- */
-#include "shelldesktop.h"
-#include "CFilePathList.h"
-#include <assert.h>      // for assert
-
-WINE_DEFAULT_DEBUG_CHANNEL(shcn);
-
-BOOL CFilePathList::ContainsPath(LPCWSTR pszPath, BOOL fIsDirectory) const
-{
-    assert(!PathIsRelativeW(pszPath));
-
-    for (INT i = 0; i < m_items.GetSize(); ++i)
-    {
-        if (m_items[i].IsEmpty() || fIsDirectory != m_items[i].IsDirectory())
-            continue;
-
-        if (m_items[i].EqualPath(pszPath))
-            return TRUE; // matched
-    }
-    return FALSE;
-}
-
-static DWORD GetSizeOfFile(LPCWSTR pszPath)
-{
-    WIN32_FIND_DATAW find;
-    HANDLE hFind = FindFirstFileW(pszPath, &find);
-    if (hFind == INVALID_HANDLE_VALUE)
-        return FALSE;
-    FindClose(hFind);
-    return find.nFileSizeLow;
-}
-
-BOOL CFilePathList::AddPath(LPCWSTR pszPath, DWORD dwFileSize, BOOL fIsDirectory)
-{
-    assert(!PathIsRelativeW(pszPath));
-
-    if (dwFileSize == INVALID_FILE_SIZE)
-    {
-        dwFileSize = GetSizeOfFile(pszPath);
-    }
-
-    CFilePathItem item(pszPath, dwFileSize, fIsDirectory);
-    return m_items.Add(item);
-}
-
-BOOL CFilePathList::RenamePath(LPCWSTR pszPath1, LPCWSTR pszPath2, BOOL fIsDirectory)
-{
-    assert(!PathIsRelativeW(pszPath1));
-    assert(!PathIsRelativeW(pszPath2));
-
-    for (INT i = 0; i < m_items.GetSize(); ++i)
-    {
-        if (m_items[i].IsDirectory() == fIsDirectory && m_items[i].EqualPath(pszPath1))
-        {
-            // matched
-            m_items[i].SetPath(pszPath2);
-            return TRUE;
-        }
-    }
-    return FALSE;
-}
-
-BOOL CFilePathList::DeletePath(LPCWSTR pszPath, BOOL fIsDirectory)
-{
-    assert(!PathIsRelativeW(pszPath));
-
-    for (INT i = 0; i < m_items.GetSize(); ++i)
-    {
-        if (m_items[i].IsDirectory() == fIsDirectory && m_items[i].EqualPath(pszPath))
-        {
-            // matched
-            m_items[i].SetPath(NULL);
-            return TRUE;
-        }
-    }
-    return FALSE;
-}
-
-BOOL CFilePathList::AddPathsFromDirectory(LPCWSTR pszDirectoryPath, BOOL fRecursive)
-{
-    // get the full path
-    WCHAR szPath[MAX_PATH];
-    lstrcpynW(szPath, pszDirectoryPath, _countof(szPath));
-    assert(!PathIsRelativeW(szPath));
-
-    // is it a directory?
-    if (!PathIsDirectoryW(szPath))
-        return FALSE;
-
-    // add the path
-    if (!AddPath(szPath, 0, TRUE))
-        return FALSE;
-
-    // enumerate the file items to remember
-    PathAppendW(szPath, L"*");
-    WIN32_FIND_DATAW find;
-    HANDLE hFind = FindFirstFileW(szPath, &find);
-    if (hFind == INVALID_HANDLE_VALUE)
-    {
-        ERR("FindFirstFileW failed\n");
-        return FALSE;
-    }
-
-    do
-    {
-        // ignore "." and ".."
-        if (lstrcmpW(find.cFileName, L".") == 0 ||
-            lstrcmpW(find.cFileName, L"..") == 0)
-        {
-            continue;
-        }
-
-        // build a path
-        PathRemoveFileSpecW(szPath);
-        if (lstrlenW(szPath) + lstrlenW(find.cFileName) + 1 > MAX_PATH)
-        {
-            ERR("szPath is too long\n");
-            continue;
-        }
-        PathAppendW(szPath, find.cFileName);
-
-        BOOL fIsDirectory = !!(find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
-
-        // add the path and do recurse
-        if (fRecursive && fIsDirectory)
-            AddPathsFromDirectory(szPath, fRecursive);
-        else
-            AddPath(szPath, find.nFileSizeLow, fIsDirectory);
-    } while (FindNextFileW(hFind, &find));
-
-    FindClose(hFind);
-
-    return TRUE;
-}
-
-BOOL CFilePathList::GetFirstChange(LPWSTR pszPath) const
-{
-    // validate paths
-    for (INT i = 0; i < m_items.GetSize(); ++i)
-    {
-        if (m_items[i].IsEmpty())
-            continue;
-
-        if (m_items[i].IsDirectory()) // item is a directory
-        {
-            if (!PathIsDirectoryW(m_items[i].GetPath()))
-            {
-                // mismatched
-                lstrcpynW(pszPath, m_items[i].GetPath(), MAX_PATH);
-                return TRUE;
-            }
-        }
-        else // item is a normal file
-        {
-            if (!PathFileExistsW(m_items[i].GetPath()) ||
-                PathIsDirectoryW(m_items[i].GetPath()))
-            {
-                // mismatched
-                lstrcpynW(pszPath, m_items[i].GetPath(), MAX_PATH);
-                return TRUE;
-            }
-        }
-    }
-
-    // check sizes
-    HANDLE hFind;
-    WIN32_FIND_DATAW find;
-    for (INT i = 0; i < m_items.GetSize(); ++i)
-    {
-        if (m_items[i].IsEmpty() || m_items[i].IsDirectory())
-            continue;
-
-        // get size
-        hFind = FindFirstFileW(m_items[i].GetPath(), &find);
-        FindClose(hFind);
-
-        if (hFind == INVALID_HANDLE_VALUE ||
-            find.nFileSizeLow != m_items[i].GetSize())
-        {
-            // different size
-            lstrcpynW(pszPath, m_items[i].GetPath(), MAX_PATH);
-            return TRUE;
-        }
-    }
-
-    return FALSE;
-}
diff --git a/dll/win32/shell32/shelldesktop/CFilePathList.h b/dll/win32/shell32/shelldesktop/CFilePathList.h
deleted file mode 100644 (file)
index 815fdab..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-#pragma once
-
-#include <atlsimpcoll.h> // for CSimpleArray
-
-//////////////////////////////////////////////////////////////////////////////
-
-// A pathname with info
-class CFilePathItem
-{
-public:
-    CFilePathItem() : m_pszPath(NULL)
-    {
-    }
-
-    CFilePathItem(LPCWSTR pszPath, DWORD dwFileSize, BOOL IsDirectory)
-    {
-        m_pszPath = _wcsdup(pszPath);
-        m_dwFileSize = dwFileSize;
-        m_fIsDirectory = IsDirectory;
-    }
-
-    CFilePathItem(const CFilePathItem& item)
-        : m_pszPath(_wcsdup(item.m_pszPath))
-        , m_dwFileSize(item.m_dwFileSize)
-        , m_fIsDirectory(item.m_fIsDirectory)
-    {
-    }
-
-    CFilePathItem& operator=(const CFilePathItem& item)
-    {
-        free(m_pszPath);
-        m_pszPath = _wcsdup(item.m_pszPath);
-        m_dwFileSize = item.m_dwFileSize;
-        m_fIsDirectory = item.m_fIsDirectory;
-        return *this;
-    }
-
-    ~CFilePathItem()
-    {
-        free(m_pszPath);
-    }
-
-    BOOL IsEmpty() const
-    {
-        return m_pszPath == NULL;
-    }
-
-    BOOL IsDirectory() const
-    {
-        return m_fIsDirectory;
-    }
-
-    LPCWSTR GetPath() const
-    {
-        return m_pszPath;
-    }
-
-    void SetPath(LPCWSTR pszPath)
-    {
-        free(m_pszPath);
-        m_pszPath = _wcsdup(pszPath);
-    }
-
-    BOOL EqualPath(LPCWSTR pszPath) const
-    {
-        return m_pszPath != NULL && lstrcmpiW(m_pszPath, pszPath) == 0;
-    }
-
-    DWORD GetSize() const
-    {
-        return m_dwFileSize;
-    }
-
-protected:
-    LPWSTR m_pszPath;    // A full path, malloc'ed
-    DWORD m_dwFileSize;  // the size of a file
-    BOOL m_fIsDirectory; // is it a directory?
-};
-
-// the file list
-class CFilePathList
-{
-public:
-    CFilePathList()
-    {
-    }
-
-    CFilePathList(LPCWSTR pszDirectoryPath, BOOL fRecursive)
-    {
-        AddPathsFromDirectory(pszDirectoryPath, fRecursive);
-    }
-
-    BOOL ContainsPath(LPCWSTR pszPath, BOOL fIsDirectory) const;
-    BOOL GetFirstChange(LPWSTR pszPath) const;
-
-    BOOL AddPath(LPCWSTR pszPath, DWORD dwFileSize, BOOL fIsDirectory);
-    BOOL AddPathsFromDirectory(LPCWSTR pszDirectoryPath, BOOL fRecursive);
-    BOOL RenamePath(LPCWSTR pszPath1, LPCWSTR pszPath2, BOOL fIsDirectory);
-    BOOL DeletePath(LPCWSTR pszPath, BOOL fIsDirectory);
-
-    void RemoveAll()
-    {
-        m_items.RemoveAll();
-    }
-
-protected:
-    CSimpleArray<CFilePathItem> m_items;
-};
index 498eb70..2ea167b 100644 (file)
@@ -13,7 +13,7 @@ list(APPEND SOURCE
     CChangeNotifyServer.cpp
     CDesktopBrowser.cpp
     CDirectoryWatcher.cpp
-    CFilePathList.cpp
+    CDirectoryList.cpp
     dde.cpp)
 
 add_library(shelldesktop ${SOURCE})