[SHELL32] Optimize change notification (#3030)

- Keep the directory lists only.
- Don't remember file sizes and normal file paths.
CORE-13950
This commit is contained in:
Katayama Hirofumi MZ 2020-08-10 13:34:48 +09:00 committed by GitHub
parent 1c40070561
commit 1f31905ecd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 227 additions and 330 deletions

View file

@ -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;
}

View file

@ -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;
};

View 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;
}

View 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();

View file

@ -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;
}

View file

@ -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;
};

View file

@ -13,7 +13,7 @@ list(APPEND SOURCE
CChangeNotifyServer.cpp
CDesktopBrowser.cpp
CDirectoryWatcher.cpp
CFilePathList.cpp
CDirectoryList.cpp
dde.cpp)
add_library(shelldesktop ${SOURCE})