[SHELL32] Big fix for change notification (#3048)

- Reduced the failures of SHChangeNotify testcase.
- Simplified change notification mechanism.
- Realized PIDL aliasing.
CORE-13950
This commit is contained in:
Katayama Hirofumi MZ 2020-09-03 13:36:31 +09:00 committed by GitHub
parent 7ffb6a09c3
commit 7c134e4d14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 244 additions and 151 deletions

View file

@ -1166,39 +1166,14 @@ LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
SetShellWindowEx(hwndSB, m_ListView);
}
INT nRegCount;
SHChangeNotifyEntry ntreg[3];
PIDLIST_ABSOLUTE pidls[3];
if (_ILIsDesktop(m_pidlParent))
{
nRegCount = 3;
SHGetSpecialFolderLocation(m_hWnd, CSIDL_DESKTOPDIRECTORY, &pidls[0]);
SHGetSpecialFolderLocation(m_hWnd, CSIDL_COMMON_DESKTOPDIRECTORY, &pidls[1]);
SHGetSpecialFolderLocation(m_hWnd, CSIDL_BITBUCKET, &pidls[2]);
ntreg[0].fRecursive = FALSE;
ntreg[0].pidl = pidls[0];
ntreg[1].fRecursive = FALSE;
ntreg[1].pidl = pidls[1];
ntreg[2].fRecursive = FALSE;
ntreg[2].pidl = pidls[2];
}
else
{
nRegCount = 1;
ntreg[0].fRecursive = FALSE;
ntreg[0].pidl = m_pidlParent;
}
SHChangeNotifyEntry ntreg[1];
ntreg[0].fRecursive = FALSE;
ntreg[0].pidl = m_pidlParent;
m_hNotify = SHChangeNotifyRegister(m_hWnd,
SHCNRF_InterruptLevel | SHCNRF_ShellLevel |
SHCNRF_NewDelivery,
SHCNE_ALLEVENTS, SHV_CHANGE_NOTIFY,
nRegCount, ntreg);
if (nRegCount == 3)
{
ILFree(pidls[0]);
ILFree(pidls[1]);
ILFree(pidls[2]);
}
1, ntreg);
/* _DoFolderViewCB(SFVM_GETNOTIFY, ?? ??) */

View file

@ -294,13 +294,16 @@ CreateRegistrationParam(ULONG nRegID, HWND hwnd, UINT wMsg, INT fSources, LONG f
// It creates a delivery ticket and send CN_DELIVER_NOTIFICATION message to
// transport the change.
static void
CreateNotificationParamAndSend(LONG wEventId, UINT uFlags, LPITEMIDLIST pidl1, LPITEMIDLIST pidl2,
CreateNotificationParamAndSend(LONG wEventId, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2,
DWORD dwTick)
{
// get server window
HWND hwndServer = GetNotificationServer(FALSE);
if (hwndServer == NULL)
{
ERR("hwndServer == NULL\n");
return;
}
// the ticket owner is the process of the notification server
DWORD pid;
@ -314,10 +317,79 @@ CreateNotificationParamAndSend(LONG wEventId, UINT uFlags, LPITEMIDLIST pidl1, L
TRACE("hTicket: %p, 0x%lx\n", hTicket, pid);
// send the ticket by using CN_DELIVER_NOTIFICATION
if ((uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH)
if (pid != GetCurrentProcessId() ||
(uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH)
{
SendMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket, pid);
}
else
{
SendNotifyMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket, pid);
}
}
struct ALIAS_PIDL
{
INT csidl1; // from
INT csidl2; // to
LPITEMIDLIST pidl1; // from
LPITEMIDLIST pidl2; // to
WCHAR szPath1[MAX_PATH]; // from
WCHAR szPath2[MAX_PATH]; // to
};
static ALIAS_PIDL AliasPIDLs[] =
{
{ CSIDL_PERSONAL, CSIDL_PERSONAL },
{ CSIDL_DESKTOP, CSIDL_COMMON_DESKTOPDIRECTORY, },
{ CSIDL_DESKTOP, CSIDL_DESKTOPDIRECTORY },
};
static VOID DoInitAliasPIDLs(void)
{
static BOOL s_bInit = FALSE;
if (!s_bInit)
{
for (SIZE_T i = 0; i < _countof(AliasPIDLs); ++i)
{
ALIAS_PIDL *alias = &AliasPIDLs[i];
SHGetSpecialFolderLocation(NULL, alias->csidl1, &alias->pidl1);
SHGetPathFromIDListW(alias->pidl1, alias->szPath1);
SHGetSpecialFolderLocation(NULL, alias->csidl2, &alias->pidl2);
SHGetPathFromIDListW(alias->pidl2, alias->szPath2);
}
s_bInit = TRUE;
}
}
static BOOL DoGetAliasPIDLs(LPITEMIDLIST apidls[2], PCIDLIST_ABSOLUTE pidl)
{
DoInitAliasPIDLs();
apidls[0] = apidls[1] = NULL;
INT k = 0;
for (SIZE_T i = 0; i < _countof(AliasPIDLs); ++i)
{
const ALIAS_PIDL *alias = &AliasPIDLs[i];
if (ILIsEqual(pidl, alias->pidl1))
{
if (alias->csidl1 == alias->csidl2)
{
apidls[k++] = ILCreateFromPathW(alias->szPath2);
}
else
{
apidls[k++] = ILClone(alias->pidl2);
}
if (k >= 2)
break;
}
}
return k > 0;
}
/*************************************************************************
@ -349,11 +421,10 @@ SHChangeNotifyRegister(HWND hwnd, INT fSources, LONG wEventMask, UINT uMsg,
if (hwndServer == NULL)
return INVALID_REG_ID;
// disable new delivery method in specific condition
if ((fSources & SHCNRF_RecursiveInterrupt) != 0 &&
(fSources & SHCNRF_InterruptLevel) == 0)
// disable recursive interrupt in specific condition
if ((fSources & SHCNRF_RecursiveInterrupt) && !(fSources & SHCNRF_InterruptLevel))
{
fSources &= ~SHCNRF_NewDelivery;
fSources &= ~SHCNRF_RecursiveInterrupt;
}
// if it is old delivery method, then create a broker window
@ -393,13 +464,62 @@ SHChangeNotifyRegister(HWND hwnd, INT fSources, LONG wEventMask, UINT uMsg,
SHFreeShared(hRegEntry, dwOwnerPID);
}
// if failed, then destroy the broker
if (nRegID == INVALID_REG_ID && (fSources & SHCNRF_NewDelivery) == 0)
if (nRegID == INVALID_REG_ID)
{
ERR("Delivery failed\n");
DestroyWindow(hwndBroker);
if (hwndBroker)
{
// destroy the broker
DestroyWindow(hwndBroker);
}
break;
}
// PIDL alias
LPITEMIDLIST apidlAlias[2];
if (DoGetAliasPIDLs(apidlAlias, lpItems[iItem].pidl))
{
if (apidlAlias[0])
{
// create another registration entry
hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask,
lpItems[iItem].fRecursive, apidlAlias[0],
dwOwnerPID, hwndBroker);
if (hRegEntry)
{
TRACE("CN_REGISTER: hwnd:%p, hRegEntry:%p, pid:0x%lx\n",
hwndServer, hRegEntry, dwOwnerPID);
// send CN_REGISTER to the server
SendMessageW(hwndServer, CN_REGISTER, (WPARAM)hRegEntry, dwOwnerPID);
// free registration entry
SHFreeShared(hRegEntry, dwOwnerPID);
}
ILFree(apidlAlias[0]);
}
if (apidlAlias[1])
{
// create another registration entry
hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask,
lpItems[iItem].fRecursive, apidlAlias[1],
dwOwnerPID, hwndBroker);
if (hRegEntry)
{
TRACE("CN_REGISTER: hwnd:%p, hRegEntry:%p, pid:0x%lx\n",
hwndServer, hRegEntry, dwOwnerPID);
// send CN_REGISTER to the server
SendMessageW(hwndServer, CN_REGISTER, (WPARAM)hRegEntry, dwOwnerPID);
// free registration entry
SHFreeShared(hRegEntry, dwOwnerPID);
}
ILFree(apidlAlias[1]);
}
}
}
LeaveCriticalSection(&SHELL32_ChangenotifyCS);
@ -472,6 +592,39 @@ static LPCSTR DumpEvent(LONG event)
#undef DUMPEV
}
/*************************************************************************
* SHChangeRegistrationReceive [SHELL32.646]
*/
EXTERN_C BOOL WINAPI
SHChangeRegistrationReceive(LPVOID lpUnknown1, DWORD dwUnknown2)
{
FIXME("SHChangeRegistrationReceive() stub\n");
return FALSE;
}
EXTERN_C VOID WINAPI
SHChangeNotifyReceiveEx(LONG lEvent, UINT uFlags,
LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwTick)
{
// TODO: Queueing notifications
CreateNotificationParamAndSend(lEvent, uFlags, pidl1, pidl2, dwTick);
}
/*************************************************************************
* SHChangeNotifyReceive [SHELL32.643]
*/
EXTERN_C VOID WINAPI
SHChangeNotifyReceive(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
SHChangeNotifyReceiveEx(lEvent, uFlags, pidl1, pidl2, GetTickCount());
}
EXTERN_C VOID WINAPI
SHChangeNotifyTransmit(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwTick)
{
SHChangeNotifyReceiveEx(lEvent, uFlags, pidl1, pidl2, dwTick);
}
/*************************************************************************
* SHChangeNotify [SHELL32.@]
*/
@ -532,7 +685,7 @@ SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
if (wEventId == 0 || (wEventId & SHCNE_ASSOCCHANGED) || pidl1 != NULL)
{
TRACE("notifying event %s(%x)\n", DumpEvent(wEventId), wEventId);
CreateNotificationParamAndSend(wEventId, uFlags, pidl1, pidl2, dwTick);
SHChangeNotifyTransmit(wEventId, uFlags, pidl1, pidl2, dwTick);
}
if (pidlTemp1)
@ -610,3 +763,17 @@ NTSHChangeNotifyDeregister(ULONG hNotify)
FIXME("(0x%08x):semi stub.\n", hNotify);
return SHChangeNotifyDeregister(hNotify);
}
/*************************************************************************
* SHChangeNotifySuspendResume [SHELL32.277]
*/
EXTERN_C BOOL
WINAPI
SHChangeNotifySuspendResume(BOOL bSuspend,
LPITEMIDLIST pidl,
BOOL bRecursive,
DWORD dwReserved)
{
FIXME("SHChangeNotifySuspendResume() stub\n");
return FALSE;
}

View file

@ -11,8 +11,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(shcn);
// TODO: SHCNRF_RecursiveInterrupt
//////////////////////////////////////////////////////////////////////////////
// notification target item
@ -188,10 +186,6 @@ CreateDirectoryWatcherFromRegEntry(LPREGENTRY pRegEntry)
if (pRegEntry->ibPidl == 0)
return NULL;
// it must be interrupt level if pRegEntry is a filesystem watch
if (!(pRegEntry->fSources & SHCNRF_InterruptLevel))
return NULL;
// get the path
WCHAR szPath[MAX_PATH];
LPITEMIDLIST pidl = (LPITEMIDLIST)((LPBYTE)pRegEntry + pRegEntry->ibPidl);
@ -249,13 +243,18 @@ LRESULT CChangeNotifyServer::OnRegister(UINT uMsg, WPARAM wParam, LPARAM lParam,
}
// create a directory watch if necessary
CDirectoryWatcher *pDirWatch = CreateDirectoryWatcherFromRegEntry(pRegEntry);
if (pDirWatch && !pDirWatch->RequestAddWatcher())
CDirectoryWatcher *pDirWatch = NULL;
if (pRegEntry->ibPidl && (pRegEntry->fSources & SHCNRF_InterruptLevel))
{
pRegEntry->nRegID = INVALID_REG_ID;
SHUnlockShared(pRegEntry);
delete pDirWatch;
return FALSE;
pDirWatch = CreateDirectoryWatcherFromRegEntry(pRegEntry);
if (pDirWatch && !pDirWatch->RequestAddWatcher())
{
ERR("RequestAddWatcher failed: %u\n", pRegEntry->nRegID);
pRegEntry->nRegID = INVALID_REG_ID;
SHUnlockShared(pRegEntry);
delete pDirWatch;
return FALSE;
}
}
// unlock the registry entry
@ -386,6 +385,7 @@ BOOL CChangeNotifyServer::DeliverNotification(HANDLE hTicket, DWORD dwOwnerPID)
TRACE("Notifying: %p, 0x%x, %p, %lu\n",
pRegEntry->hwnd, pRegEntry->uMsg, hTicket, dwOwnerPID);
SendMessageW(pRegEntry->hwnd, pRegEntry->uMsg, (WPARAM)hTicket, dwOwnerPID);
TRACE("GetLastError(): %ld\n", ::GetLastError());
}
// unlock the registration entry
@ -400,91 +400,58 @@ BOOL CChangeNotifyServer::DeliverNotification(HANDLE hTicket, DWORD dwOwnerPID)
BOOL CChangeNotifyServer::ShouldNotify(LPDELITICKET pTicket, LPREGENTRY pRegEntry)
{
LPITEMIDLIST pidl, pidl1 = NULL, pidl2 = NULL;
WCHAR szPath[MAX_PATH], szPath1[MAX_PATH], szPath2[MAX_PATH];
INT cch, cch1, cch2;
#define RETURN(x) do { \
TRACE("ShouldNotify return %d\n", (x)); \
return (x); \
} while (0)
// check fSources
if (pTicket->uFlags & SHCNE_INTERRUPT)
if (pTicket->wEventId & SHCNE_INTERRUPT)
{
if (!(pRegEntry->fSources & SHCNRF_InterruptLevel))
return FALSE;
RETURN(FALSE);
if (!pRegEntry->ibPidl)
RETURN(FALSE);
}
else
{
if (!(pRegEntry->fSources & SHCNRF_ShellLevel))
return FALSE;
RETURN(FALSE);
}
if (pRegEntry->ibPidl == 0)
return TRUE; // there is no PIDL
if (!(pTicket->wEventId & pRegEntry->fEvents))
RETURN(FALSE);
// get the stored pidl
pidl = (LPITEMIDLIST)((LPBYTE)pRegEntry + pRegEntry->ibPidl);
if (pidl->mkid.cb == 0 && pRegEntry->fRecursive)
return TRUE; // desktop is the root
// check pidl1
LPITEMIDLIST pidl = NULL, pidl1 = NULL, pidl2 = NULL;
if (pRegEntry->ibPidl)
pidl = (LPITEMIDLIST)((LPBYTE)pRegEntry + pRegEntry->ibPidl);
if (pTicket->ibOffset1)
{
pidl1 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset1);
if (ILIsEqual(pidl, pidl1) || ILIsParent(pidl, pidl1, !pRegEntry->fRecursive))
return TRUE;
}
// check pidl2
if (pTicket->ibOffset2)
{
pidl2 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset2);
if (ILIsEqual(pidl, pidl2) || ILIsParent(pidl, pidl2, !pRegEntry->fRecursive))
return TRUE;
}
// The paths:
// "C:\\Path\\To\\File1"
// "C:\\Path\\To\\File1Test"
// should be distinguished in comparison, so we add backslash at last as follows:
// "C:\\Path\\To\\File1\\"
// "C:\\Path\\To\\File1Test\\"
if (SHGetPathFromIDListW(pidl, szPath))
if (pidl == NULL || (pTicket->wEventId & SHCNE_GLOBALEVENTS))
RETURN(TRUE);
if (pRegEntry->fRecursive)
{
PathAddBackslashW(szPath);
cch = lstrlenW(szPath);
if (pidl1 && SHGetPathFromIDListW(pidl1, szPath1))
if (ILIsParent(pidl, pidl1, FALSE) ||
(pidl2 && ILIsParent(pidl, pidl2, FALSE)))
{
PathAddBackslashW(szPath1);
cch1 = lstrlenW(szPath1);
// Is szPath1 a subfile or subdirectory of szPath?
if (cch < cch1 &&
(pRegEntry->fRecursive ||
wcschr(&szPath1[cch], L'\\') == &szPath1[cch1 - 1]))
{
szPath1[cch] = 0;
if (lstrcmpiW(szPath, szPath1) == 0)
return TRUE;
}
RETURN(TRUE);
}
if (pidl2 && SHGetPathFromIDListW(pidl2, szPath2))
}
else
{
if (ILIsEqual(pidl, pidl1) ||
ILIsParent(pidl, pidl1, TRUE) ||
(pidl2 && ILIsParent(pidl, pidl2, TRUE)))
{
PathAddBackslashW(szPath2);
cch2 = lstrlenW(szPath2);
// Is szPath2 a subfile or subdirectory of szPath?
if (cch < cch2 &&
(pRegEntry->fRecursive ||
wcschr(&szPath2[cch], L'\\') == &szPath2[cch2 - 1]))
{
szPath2[cch] = 0;
if (lstrcmpiW(szPath, szPath2) == 0)
return TRUE;
}
RETURN(TRUE);
}
}
return FALSE;
RETURN(FALSE);
#undef RETURN
}
HRESULT WINAPI CChangeNotifyServer::GetWindow(HWND* phwnd)

View file

@ -28,6 +28,16 @@ BOOL CDirectoryList::ContainsPath(LPCWSTR pszPath) const
BOOL CDirectoryList::AddPath(LPCWSTR pszPath)
{
assert(!PathIsRelativeW(pszPath));
if (ContainsPath(pszPath))
return FALSE;
for (INT i = 0; i < m_items.GetSize(); ++i)
{
if (m_items[i].IsEmpty())
{
m_items[i].SetPath(pszPath);
return TRUE;
}
}
return m_items.Add(pszPath);
}

View file

@ -15,7 +15,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(shcn);
static inline void
NotifyFileSystemChange(LONG wEventId, LPCWSTR path1, LPCWSTR path2)
{
SHChangeNotify(wEventId | SHCNE_INTERRUPT, SHCNF_PATHW, path1, path2);
SHChangeNotify(wEventId | SHCNE_INTERRUPT, SHCNF_PATHW | SHCNF_FLUSH, path1, path2);
}
// The handle of the APC thread
@ -138,6 +138,7 @@ void CDirectoryWatcher::ProcessNotification()
WCHAR szName[MAX_PATH], szPath[MAX_PATH], szTempPath[MAX_PATH];
DWORD dwEvent, cbName;
BOOL fDir;
TRACE("CDirectoryWatcher::ProcessNotification: enter\n");
// for each entry in s_buffer
szPath[0] = szTempPath[0] = 0;
@ -225,6 +226,8 @@ void CDirectoryWatcher::ProcessNotification()
// go next entry
pInfo = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pInfo + pInfo->NextEntryOffset);
}
TRACE("CDirectoryWatcher::ProcessNotification: leave\n");
}
void CDirectoryWatcher::ReadCompletion(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered)

View file

@ -329,17 +329,6 @@ CDefFolderMenu_Create(LPITEMIDLIST pidlFolder,
return E_FAIL;
}
/*
* Unimplemented
*/
EXTERN_C BOOL
WINAPI
SHChangeRegistrationReceive(LPVOID lpUnknown1, DWORD dwUnknown2)
{
FIXME("SHChangeRegistrationReceive() stub\n");
return FALSE;
}
/*
* Unimplemented
*/
@ -350,16 +339,6 @@ SHWaitOp_Operate(LPVOID lpUnknown1, DWORD dwUnknown2)
FIXME("SHWaitOp_Operate() stub\n");
}
/*
* Unimplemented
*/
EXTERN_C VOID
WINAPI
SHChangeNotifyReceive(LONG lUnknown, UINT uUnknown, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
FIXME("SHChangeNotifyReceive() stub\n");
}
/*
* Unimplemented
*/
@ -625,20 +604,6 @@ SHCreateProcessAsUserW(PSHCREATEPROCESSINFOW pscpi)
return FALSE;
}
/*
* Unimplemented
*/
EXTERN_C BOOL
WINAPI
SHChangeNotifySuspendResume(BOOL bSuspend,
LPITEMIDLIST pidl,
BOOL bRecursive,
DWORD dwReserved)
{
FIXME("SHChangeNotifySuspendResume() stub\n");
return FALSE;
}
/*
* Unimplemented
*/

View file

@ -1101,8 +1101,13 @@ LPITEMIDLIST WINAPI SHSimpleIDListFromPathA(LPCSTR lpszPath)
MultiByteToWideChar(CP_ACP, 0, lpszPath, -1, wPath, len);
}
#ifdef __REACTOS__
// FIXME: Needs folder attribute
if (PathFileExistsW(wPath))
return ILCreateFromPathW(wPath);
{
pidl = ILCreateFromPathW(wPath);
HeapFree(GetProcessHeap(), 0, wPath);
return pidl;
}
#endif
_ILParsePathW(wPath, NULL, TRUE, &pidl, NULL);
@ -1118,6 +1123,7 @@ LPITEMIDLIST WINAPI SHSimpleIDListFromPathW(LPCWSTR lpszPath)
TRACE("%s\n", debugstr_w(lpszPath));
#ifdef __REACTOS__
// FIXME: Needs folder attribute
if (PathFileExistsW(lpszPath))
return ILCreateFromPathW(lpszPath);
#endif