diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp index f2cb36153d9..ff3ce600c38 100644 --- a/dll/win32/shell32/CDefView.cpp +++ b/dll/win32/shell32/CDefView.cpp @@ -778,12 +778,8 @@ int CDefView::LV_FindItemByPidl(PCUITEMID_CHILD pidl) for (int i = 0; iCompareIDs(0, pidl, currentpidl); - - if (SUCCEEDED(hr) && !HRESULT_CODE(hr)) - { + if (ILIsEqual(pidl, currentpidl)) return i; - } } return -1; } @@ -819,7 +815,7 @@ BOOLEAN CDefView::LV_DeleteItem(PCUITEMID_CHILD pidl) nIndex = LV_FindItemByPidl(pidl); - return (-1 == m_ListView.DeleteItem(nIndex)) ? FALSE : TRUE; + return m_ListView.DeleteItem(nIndex); } /********************************************************** @@ -1129,7 +1125,6 @@ LRESULT CDefView::OnNCDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHa LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) { CComPtr pdt; - SHChangeNotifyEntry ntreg; CComPtr ppf2; TRACE("%p\n", this); @@ -1145,9 +1140,6 @@ LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl if (ppf2) { ppf2->GetCurFolder(&m_pidlParent); - ntreg.fRecursive = TRUE; - ntreg.pidl = m_pidlParent; - m_hNotify = SHChangeNotifyRegister(m_hWnd, SHCNRF_InterruptLevel | SHCNRF_ShellLevel, SHCNE_ALLEVENTS, SHV_CHANGE_NOTIFY, 1, &ntreg); } if (CreateList()) @@ -1158,6 +1150,19 @@ LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl } } + if (m_FolderSettings.fFlags & FWF_DESKTOP) + { + HWND hwndSB; + m_pShellBrowser->GetWindow(&hwndSB); + SetShellWindowEx(hwndSB, m_ListView); + } + + SHChangeNotifyEntry ntreg; + ntreg.fRecursive = TRUE; + ntreg.pidl = m_pidlParent; + m_hNotify = SHChangeNotifyRegister(m_hWnd, SHCNRF_NewDelivery | SHCNRF_ShellLevel, + SHCNE_ALLEVENTS, SHV_CHANGE_NOTIFY, 1, &ntreg); + /* _DoFolderViewCB(SFVM_GETNOTIFY, ?? ??) */ m_hAccel = LoadAcceleratorsW(shell32_hInstance, MAKEINTRESOURCEW(IDA_SHELLVIEW)); @@ -2100,6 +2105,21 @@ static BOOL ILIsParentOrSpecialParent(PCIDLIST_ABSOLUTE pidl1, PCIDLIST_ABSOLUTE } ILFree(deskpidl); } + + WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; + LPITEMIDLIST pidl2Clone = ILClone(pidl2); + ILRemoveLastID(pidl2Clone); + if (SHGetPathFromIDListW(pidl1, szPath1) && + SHGetPathFromIDListW(pidl2Clone, szPath2)) + { + if (lstrcmpiW(szPath1, szPath2) == 0) + { + ILFree(pidl2Clone); + return TRUE; + } + } + ILFree(pidl2Clone); + return FALSE; } @@ -2108,13 +2128,24 @@ static BOOL ILIsParentOrSpecialParent(PCIDLIST_ABSOLUTE pidl1, PCIDLIST_ABSOLUTE */ LRESULT CDefView::OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) { - PCIDLIST_ABSOLUTE *Pidls = reinterpret_cast(wParam); + HANDLE hChange = (HANDLE)wParam; + DWORD dwProcID = (DWORD)lParam; + PIDLIST_ABSOLUTE *Pidls; + LONG lEvent; + HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &Pidls, &lEvent); + if (hLock == NULL) + { + ERR("hLock == NULL\n"); + return FALSE; + } + BOOL bParent0 = ILIsParentOrSpecialParent(m_pidlParent, Pidls[0]); BOOL bParent1 = ILIsParentOrSpecialParent(m_pidlParent, Pidls[1]); TRACE("(%p)(%p,%p,0x%08x)\n", this, Pidls[0], Pidls[1], lParam); - switch (lParam &~ SHCNE_INTERRUPT) + lEvent &= ~SHCNE_INTERRUPT; + switch (lEvent) { case SHCNE_MKDIR: case SHCNE_CREATE: @@ -2156,6 +2187,8 @@ LRESULT CDefView::OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & Refresh(); break; } + + SHChangeNotification_Unlock(hLock); return TRUE; } diff --git a/dll/win32/shell32/shelldesktop/CChangeNotify.cpp b/dll/win32/shell32/shelldesktop/CChangeNotify.cpp new file mode 100644 index 00000000000..83cb010eac7 --- /dev/null +++ b/dll/win32/shell32/shelldesktop/CChangeNotify.cpp @@ -0,0 +1,594 @@ +/* + * 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 "shlwapi_undoc.h" +#include +#include + +WINE_DEFAULT_DEBUG_CHANNEL(shcn); + +static HWND s_hwndNewWorker = NULL; + +EXTERN_C void +DoNotifyFreeSpace(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) +{ + WCHAR path1[MAX_PATH], path2[MAX_PATH]; + + path1[0] = 0; + if (pidl1) + SHGetPathFromIDListW(pidl1, path1); + + path2[0] = 0; + if (pidl2) + SHGetPathFromIDListW(pidl2, path2); + + if (path1[0]) + { + if (path2[0]) + SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATHW, path1, path2); + else + SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATHW, path1, NULL); + } + else + { + SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATHW, NULL, NULL); + } +} + +EXTERN_C HWND +DoGetNewDeliveryWorker(void) +{ + if (s_hwndNewWorker && IsWindow(s_hwndNewWorker)) + return s_hwndNewWorker; + + HWND hwndShell = GetShellWindow(); + HWND hwndWorker = (HWND)SendMessageW(hwndShell, WM_GETDELIWORKERWND, 0, 0); + if (!IsWindow(hwndWorker)) + { + ERR("Unable to get notification window\n"); + hwndWorker = NULL; + } + + s_hwndNewWorker = hwndWorker; + return hwndWorker; +} + +static LRESULT CALLBACK +OldDeliveryWorkerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HANDLE hLock; + PIDLIST_ABSOLUTE *ppidl; + LONG lEvent; + LPOLDDELIVERYWORKER pWorker; + HANDLE hShared; + DWORD dwOwnerPID; + + switch (uMsg) + { + case WM_OLDDELI_HANDOVER: + hShared = (HANDLE)wParam; + dwOwnerPID = (DWORD)lParam; + TRACE("WM_OLDDELI_HANDOVER: hwnd:%p, hShared:%p, pid:0x%lx\n", + hwnd, hShared, dwOwnerPID); + + pWorker = (LPOLDDELIVERYWORKER)GetWindowLongPtrW(hwnd, 0); + if (!pWorker) + { + ERR("!pWorker\n"); + break; + } + + ppidl = NULL; + hLock = SHChangeNotification_Lock(hShared, dwOwnerPID, &ppidl, &lEvent); + if (!hLock) + { + ERR("!hLock\n"); + break; + } + + TRACE("OldDeliveryWorker notifying: %p, 0x%x, %p, 0x%lx\n", + pWorker->hwnd, pWorker->uMsg, ppidl, lEvent); + SendMessageW(pWorker->hwnd, pWorker->uMsg, (WPARAM)ppidl, lEvent); + SHChangeNotification_Unlock(hLock); + return TRUE; + + case WM_NCDESTROY: + TRACE("WM_NCDESTROY\n"); + pWorker = (LPOLDDELIVERYWORKER)GetWindowLongPtrW(hwnd, 0); + SetWindowLongW(hwnd, 0, 0); + delete pWorker; + break; + + default: + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + return 0; +} + +EXTERN_C HWND +DoHireOldDeliveryWorker(HWND hwnd, UINT wMsg) +{ + LPOLDDELIVERYWORKER pWorker; + HWND hwndOldWorker; + + pWorker = new OLDDELIVERYWORKER; + if (!pWorker) + { + ERR("Out of memory\n"); + return NULL; + } + + pWorker->hwnd = hwnd; + pWorker->uMsg = wMsg; + hwndOldWorker = SHCreateWorkerWindowW(OldDeliveryWorkerWndProc, NULL, 0, 0, + NULL, (LONG_PTR)pWorker); + if (hwndOldWorker == NULL) + { + ERR("hwndOldWorker == NULL\n"); + delete pWorker; + } + + DWORD pid; + GetWindowThreadProcessId(hwndOldWorker, &pid); + TRACE("hwndOldWorker: %p, 0x%lx\n", hwndOldWorker, pid); + return hwndOldWorker; +} + +EXTERN_C HANDLE +DoCreateNotifShare(ULONG nRegID, HWND hwnd, UINT wMsg, INT fSources, LONG fEvents, + LONG fRecursive, LPCITEMIDLIST pidl, DWORD dwOwnerPID, + HWND hwndOldWorker) +{ + DWORD cbPidl = ILGetSize(pidl); + DWORD ibPidl = DWORD_ALIGNMENT(sizeof(NOTIFSHARE)); + DWORD cbSize = ibPidl + cbPidl; + HANDLE hShared = SHAllocShared(NULL, cbSize, dwOwnerPID); + if (!hShared) + { + ERR("Out of memory\n"); + return NULL; + } + + LPNOTIFSHARE pShared = (LPNOTIFSHARE)SHLockSharedEx(hShared, dwOwnerPID, TRUE); + if (pShared == NULL) + { + ERR("SHLockSharedEx failed\n"); + SHFreeShared(hShared, dwOwnerPID); + return NULL; + } + pShared->dwMagic = NOTIFSHARE_MAGIC; + pShared->cbSize = cbSize; + pShared->nRegID = nRegID; + pShared->hwnd = hwnd; + pShared->hwndOldWorker = hwndOldWorker; + pShared->uMsg = wMsg; + pShared->fSources = fSources; + pShared->fEvents = fEvents; + pShared->fRecursive = fRecursive; + pShared->ibPidl = 0; + if (pidl) + { + pShared->ibPidl = ibPidl; + memcpy((LPBYTE)pShared + ibPidl, pidl, cbPidl); + } + SHUnlockShared(pShared); + return hShared; +} + +EXTERN_C HANDLE +DoCreateDeliTicket(LONG wEventId, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, + DWORD dwOwnerPID, DWORD dwTick) +{ + LPDELITICKET pTicket; + HANDLE hTicket = NULL; + DWORD cbPidl1 = 0, cbPidl2 = 0, ibOffset1 = 0, ibOffset2 = 0, cbSize; + + if (pidl1) + { + cbPidl1 = ILGetSize(pidl1); + ibOffset1 = DWORD_ALIGNMENT(sizeof(DELITICKET)); + } + + if (pidl2) + { + cbPidl2 = ILGetSize(pidl2); + ibOffset2 = DWORD_ALIGNMENT(ibOffset1 + cbPidl1); + } + + cbSize = ibOffset2 + cbPidl2; + hTicket = SHAllocShared(NULL, cbSize, dwOwnerPID); + if (hTicket == NULL) + { + ERR("Out of memory\n"); + return NULL; + } + + pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, TRUE); + if (pTicket == NULL) + { + ERR("SHLockSharedEx failed\n"); + SHFreeShared(hTicket, dwOwnerPID); + return NULL; + } + pTicket->dwMagic = DELITICKET_MAGIC; + pTicket->wEventId = wEventId; + pTicket->uFlags = uFlags; + pTicket->ibOffset1 = ibOffset1; + pTicket->ibOffset2 = ibOffset2; + + if (pidl1) + memcpy((LPBYTE)pTicket + ibOffset1, pidl1, cbPidl1); + if (pidl2) + memcpy((LPBYTE)pTicket + ibOffset2, pidl2, cbPidl2); + + SHUnlockShared(pTicket); + return hTicket; +} + +EXTERN_C LPHANDBAG +DoGetHandbagFromTicket(HANDLE hTicket, DWORD dwOwnerPID) +{ + LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, FALSE); + if (!pTicket || pTicket->dwMagic != DELITICKET_MAGIC) + { + ERR("pTicket is invalid\n"); + return NULL; + } + + LPHANDBAG pHandbag = (LPHANDBAG)LocalAlloc(LMEM_FIXED, sizeof(HANDBAG)); + if (pHandbag == NULL) + { + ERR("Out of memory\n"); + SHUnlockShared(pTicket); + return NULL; + } + pHandbag->dwMagic = HANDBAG_MAGIC; + pHandbag->pTicket = pTicket; + + pHandbag->pidls[0] = pHandbag->pidls[1] = NULL; + if (pTicket->ibOffset1) + pHandbag->pidls[0] = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset1); + if (pTicket->ibOffset2) + pHandbag->pidls[1] = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset2); + + return pHandbag; +} + +EXTERN_C void +DoTransportChange(LONG wEventId, UINT uFlags, LPITEMIDLIST pidl1, LPITEMIDLIST pidl2, + DWORD dwTick) +{ + HWND hwndNotif = DoGetNewDeliveryWorker(); + if (!hwndNotif) + return; + + DWORD pid; + GetWindowThreadProcessId(hwndNotif, &pid); + + HANDLE hTicket = DoCreateDeliTicket(wEventId, uFlags, pidl1, pidl2, pid, dwTick); + if (hTicket) + { + TRACE("hTicket: %p, 0x%lx\n", hTicket, pid); + if ((uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH) + SendMessageW(hwndNotif, WM_NOTIF_DELIVERY, (WPARAM)hTicket, pid); + else + SendNotifyMessageW(hwndNotif, WM_NOTIF_DELIVERY, (WPARAM)hTicket, pid); + } +} + +/*static*/ LRESULT CALLBACK +CWorker::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CWorker *pThis = (CWorker *)GetWindowLongPtrW(hwnd, 0); + if (pThis) + { + LRESULT lResult = 0; + pThis->ProcessWindowMessage(hwnd, uMsg, wParam, lParam, lResult, 0); + return lResult; + } + return 0; +} + +BOOL CWorker::CreateWorker(HWND hwndParent, DWORD dwExStyle, DWORD dwStyle) +{ + assert(m_hWnd == NULL); + m_hWnd = SHCreateWorkerWindowW(WindowProc, hwndParent, dwExStyle, dwStyle, + NULL, (LONG_PTR)this); + return m_hWnd != NULL; +} + +struct CChangeNotifyImpl +{ + typedef CChangeNotify::ITEM ITEM; + CSimpleArray m_items; + + BOOL AddItem(UINT nRegID, DWORD dwUserPID, HANDLE hShare, HWND hwndOldWorker) + { + for (INT i = 0; i < m_items.GetSize(); ++i) + { + if (m_items[i].nRegID == INVALID_REG_ID) + { + m_items[i].nRegID = nRegID; + m_items[i].dwUserPID = dwUserPID; + m_items[i].hShare = hShare; + m_items[i].hwndOldWorker = hwndOldWorker; + return TRUE; + } + } + + ITEM item = { nRegID, dwUserPID, hShare, hwndOldWorker }; + m_items.Add(item); + return TRUE; + } + + void DestroyItem(ITEM& item, DWORD dwOwnerPID, HWND *phwndOldWorker) + { + if (item.hwndOldWorker && item.hwndOldWorker != *phwndOldWorker) + { + DestroyWindow(item.hwndOldWorker); + *phwndOldWorker = item.hwndOldWorker; + } + + SHFreeShared(item.hShare, dwOwnerPID); + item.nRegID = INVALID_REG_ID; + item.dwUserPID = 0; + item.hShare = NULL; + item.hwndOldWorker = NULL; + } + + BOOL RemoveItem(UINT nRegID, DWORD dwOwnerPID) + { + BOOL bFound = FALSE; + HWND hwndOldWorker = NULL; + for (INT i = 0; i < m_items.GetSize(); ++i) + { + if (m_items[i].nRegID == nRegID) + { + bFound = TRUE; + DestroyItem(m_items[i], dwOwnerPID, &hwndOldWorker); + } + } + return bFound; + } + + void RemoveItemsByProcess(DWORD dwOwnerPID, DWORD dwUserPID) + { + HWND hwndOldWorker = NULL; + for (INT i = 0; i < m_items.GetSize(); ++i) + { + if (m_items[i].dwUserPID == dwUserPID) + { + DestroyItem(m_items[i], dwOwnerPID, &hwndOldWorker); + } + } + } +}; + +CChangeNotify::CChangeNotify() + : m_nNextRegID(INVALID_REG_ID) + , m_pimpl(new CChangeNotifyImpl) +{ +} + +CChangeNotify::~CChangeNotify() +{ + delete m_pimpl; +} + +BOOL CChangeNotify::AddItem(UINT nRegID, DWORD dwUserPID, HANDLE hShare, HWND hwndOldWorker) +{ + return m_pimpl->AddItem(nRegID, dwUserPID, hShare, hwndOldWorker); +} + +BOOL CChangeNotify::RemoveItem(UINT nRegID, DWORD dwOwnerPID) +{ + return m_pimpl->RemoveItem(nRegID, dwOwnerPID); +} + +void CChangeNotify::RemoveItemsByProcess(DWORD dwOwnerPID, DWORD dwUserPID) +{ + m_pimpl->RemoveItemsByProcess(dwOwnerPID, dwUserPID); +} + +LRESULT CChangeNotify::OnReg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + TRACE("OnReg(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam); + + HANDLE hShared = (HANDLE)wParam; + DWORD dwOwnerPID = (DWORD)lParam; + + LPNOTIFSHARE pShared = (LPNOTIFSHARE)SHLockSharedEx(hShared, dwOwnerPID, TRUE); + if (!pShared || pShared->dwMagic != NOTIFSHARE_MAGIC) + { + ERR("pShared is invalid\n"); + return FALSE; + } + + if (pShared->nRegID == INVALID_REG_ID) + pShared->nRegID = GetNextRegID(); + + TRACE("pShared->nRegID: %u\n", pShared->nRegID); + + DWORD dwUserPID; + GetWindowThreadProcessId(pShared->hwnd, &dwUserPID); + + HWND hwndOldWorker = pShared->hwndOldWorker; + + HANDLE hNewShared = SHAllocShared(pShared, pShared->cbSize, dwOwnerPID); + if (!hNewShared) + { + ERR("Out of memory\n"); + pShared->nRegID = INVALID_REG_ID; + SHUnlockShared(pShared); + return FALSE; + } + + SHUnlockShared(pShared); + + return AddItem(m_nNextRegID, dwUserPID, hNewShared, hwndOldWorker); +} + +LRESULT CChangeNotify::OnUnReg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + TRACE("OnUnReg(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam); + + UINT nRegID = (UINT)wParam; + if (nRegID == INVALID_REG_ID) + { + ERR("INVALID_REG_ID\n"); + return FALSE; + } + + DWORD dwOwnerPID; + GetWindowThreadProcessId(m_hWnd, &dwOwnerPID); + return RemoveItem(nRegID, dwOwnerPID); +} + +LRESULT CChangeNotify::OnDelivery(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + TRACE("OnDelivery(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam); + + HANDLE hTicket = (HANDLE)wParam; + DWORD dwOwnerPID = (DWORD)lParam; + BOOL ret = FALSE; + + LPHANDBAG pHandbag = DoGetHandbagFromTicket(hTicket, dwOwnerPID); + if (pHandbag && pHandbag->dwMagic == HANDBAG_MAGIC) + { + LPDELITICKET pTicket = pHandbag->pTicket; + if (pTicket && pTicket->dwMagic == DELITICKET_MAGIC) + { + ret = DoDelivery(hTicket, dwOwnerPID); + } + } + + SHChangeNotification_Unlock(pHandbag); + SHFreeShared(hTicket, dwOwnerPID); + return ret; +} + +LRESULT CChangeNotify::OnSuspendResume(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + TRACE("OnSuspendResume\n"); + + // FIXME + return FALSE; +} + +LRESULT CChangeNotify::OnRemoveByPID(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + DWORD dwOwnerPID, dwUserPID = (DWORD)wParam; + GetWindowThreadProcessId(m_hWnd, &dwOwnerPID); + RemoveItemsByProcess(dwOwnerPID, dwUserPID); + return 0; +} + +UINT CChangeNotify::GetNextRegID() +{ + m_nNextRegID++; + if (m_nNextRegID == INVALID_REG_ID) + m_nNextRegID++; + return m_nNextRegID; +} + +BOOL CChangeNotify::DoDelivery(HANDLE hTicket, DWORD dwOwnerPID) +{ + TRACE("DoDelivery(%p, %p, 0x%lx)\n", m_hWnd, hTicket, dwOwnerPID); + + for (INT i = 0; i < m_pimpl->m_items.GetSize(); ++i) + { + HANDLE hShare = m_pimpl->m_items[i].hShare; + if (!m_pimpl->m_items[i].nRegID || !hShare) + continue; + + LPNOTIFSHARE pShared = (LPNOTIFSHARE)SHLockSharedEx(hShare, dwOwnerPID, FALSE); + if (!pShared || pShared->dwMagic != NOTIFSHARE_MAGIC) + { + ERR("pShared is invalid\n"); + continue; + } + + LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, FALSE); + if (!pTicket || pTicket->dwMagic != DELITICKET_MAGIC) + { + ERR("pTicket is invalid\n"); + SHUnlockShared(pShared); + continue; + } + BOOL bNotify = ShouldNotify(pTicket, pShared); + SHUnlockShared(pTicket); + + if (bNotify) + { + TRACE("Notifying: %p, 0x%x, %p, %lu\n", + pShared->hwnd, pShared->uMsg, hTicket, dwOwnerPID); + SendMessageW(pShared->hwnd, pShared->uMsg, (WPARAM)hTicket, dwOwnerPID); + } + SHUnlockShared(pShared); + } + + return TRUE; +} + +BOOL CChangeNotify::ShouldNotify(LPDELITICKET pTicket, LPNOTIFSHARE pShared) +{ + BOOL ret = FALSE; + LPITEMIDLIST pidl = NULL, pidl1 = NULL, pidl2 = NULL; + WCHAR szPath[MAX_PATH], szPath1[MAX_PATH], szPath2[MAX_PATH]; + INT cch, cch1, cch2; + + szPath[0] = szPath1[0] = szPath2[0] = 0; + + if (pShared->ibPidl) + { + pidl = (LPITEMIDLIST)((LPBYTE)pShared + pShared->ibPidl); + SHGetPathFromIDListW(pidl, szPath); + PathAddBackslashW(szPath); + } + + if (pTicket->ibOffset1) + { + pidl1 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset1); + if (ILIsEqual(pidl, pidl1)) + ret = TRUE; + SHGetPathFromIDListW(pidl1, szPath1); + PathAddBackslashW(szPath1); + } + + if (pTicket->ibOffset2) + { + pidl2 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset2); + if (ILIsEqual(pidl, pidl2)) + ret = TRUE; + SHGetPathFromIDListW(pidl2, szPath2); + PathAddBackslashW(szPath2); + } + + if (pShared->fRecursive) + { + if (szPath1[0] == 0) + ret = TRUE; + + cch = lstrlenW(szPath); + cch1 = lstrlenW(szPath1); + cch2 = lstrlenW(szPath2); + if (cch < cch1) + { + szPath1[cch] = 0; + if (lstrcmpiW(szPath, szPath1) == 0) + ret = TRUE; + } + if (cch < cch2) + { + szPath2[cch] = 0; + if (lstrcmpiW(szPath, szPath2) == 0) + ret = TRUE; + } + } + + return ret; +} diff --git a/dll/win32/shell32/shelldesktop/CChangeNotify.h b/dll/win32/shell32/shelldesktop/CChangeNotify.h new file mode 100644 index 00000000000..0cbe48bd88a --- /dev/null +++ b/dll/win32/shell32/shelldesktop/CChangeNotify.h @@ -0,0 +1,151 @@ +/* + * 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) + */ +#pragma once + +#define INVALID_REG_ID 0 + +#define WM_GETDELIWORKERWND (WM_USER + 25) /* 0x419 */ +#define WM_OLDDELI_HANDOVER (WM_USER + 1) /* 0x401 */ +#define WM_NOTIF_REG (WM_USER + 1) /* 0x401 */ +#define WM_NOTIF_UNREG (WM_USER + 2) /* 0x402 */ +#define WM_NOTIF_DELIVERY (WM_USER + 3) /* 0x403 */ +#define WM_NOTIF_SUSPEND (WM_USER + 6) /* 0x406 */ +#define WM_NOTIF_REMOVEBYPID (WM_USER + 7) /* 0x407 */ + +#define DWORD_ALIGNMENT(offset) \ + ((((offset) + sizeof(DWORD) - 1) / sizeof(DWORD)) * sizeof(DWORD)) + +typedef struct OLDDELIVERYWORKER +{ + HWND hwnd; + UINT uMsg; +} OLDDELIVERYWORKER, *LPOLDDELIVERYWORKER; + +typedef struct DELITICKET +{ + DWORD dwMagic; + LONG wEventId; + UINT uFlags; + DWORD dwTick; + DWORD ibOffset1; /* offset to pidl1 */ + DWORD ibOffset2; /* offset to pidl2 */ +} DELITICKET, *LPDELITICKET; + +typedef struct NOTIFSHARE +{ + DWORD dwMagic; + DWORD cbSize; + UINT nRegID; + HWND hwnd; + HWND hwndOldWorker; + UINT uMsg; + INT fSources; + LONG fEvents; + BOOL fRecursive; + UINT ibPidl; +} NOTIFSHARE, *LPNOTIFSHARE; + +typedef struct HANDBAG +{ + DWORD dwMagic; + LPITEMIDLIST pidls[2]; + LPDELITICKET pTicket; +} HANDBAG, *LPHANDBAG; + +#define DELITICKET_MAGIC 0xDEADFACE +#define NOTIFSHARE_MAGIC 0xB0B32D1E +#define HANDBAG_MAGIC 0xFACEB00C + +EXTERN_C HWND DoGetNewDeliveryWorker(void); +EXTERN_C void DoNotifyFreeSpace(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); +EXTERN_C HWND DoHireOldDeliveryWorker(HWND hwnd, UINT wMsg); +EXTERN_C LPHANDBAG DoGetHandbagFromTicket(HANDLE hTicket, DWORD dwOwnerPID); + +EXTERN_C HANDLE +DoCreateNotifShare(ULONG nRegID, HWND hwnd, UINT wMsg, INT fSources, + LONG fEvents, LONG fRecursive, LPCITEMIDLIST pidl, + DWORD dwOwnerPID, HWND hwndOldWorker); + +EXTERN_C HANDLE +DoCreateDeliTicket(LONG wEventId, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, + DWORD dwOwnerPID, DWORD dwTick); + +EXTERN_C void +DoTransportChange(LONG wEventId, UINT uFlags, LPITEMIDLIST pidl1, LPITEMIDLIST pidl2, + DWORD dwTick); + +#ifdef __cplusplus +class CWorker : public CMessageMap +{ +public: + CWorker() : m_hWnd(NULL) + { + } + + virtual ~CWorker() + { + } + + static LRESULT CALLBACK + WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + BOOL CreateWorker(HWND hwndParent, DWORD dwExStyle, DWORD dwStyle); + +protected: + HWND m_hWnd; +}; + +struct CChangeNotifyImpl; + +class CChangeNotify : public CWorker +{ +public: + struct ITEM + { + UINT nRegID; + DWORD dwUserPID; + HANDLE hShare; + HWND hwndOldWorker; + }; + + CChangeNotify(); + virtual ~CChangeNotify(); + + operator HWND() + { + return m_hWnd; + } + + BOOL AddItem(UINT nRegID, DWORD dwUserPID, HANDLE hShare, HWND hwndOldWorker); + BOOL RemoveItem(UINT nRegID, DWORD dwOwnerPID); + void RemoveItemsByProcess(DWORD dwOwnerPID, DWORD dwUserPID); + + UINT GetNextRegID(); + BOOL DoDelivery(HANDLE hTicket, DWORD dwOwnerPID); + + BOOL ShouldNotify(LPDELITICKET pTicket, LPNOTIFSHARE pShared); + BOOL DoNotify(LPHANDBAG pHandbag, LPDELITICKET pTicket, LPNOTIFSHARE pShared); + + LRESULT OnReg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnUnReg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnDelivery(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnSuspendResume(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnRemoveByPID(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + + BEGIN_MSG_MAP(CChangeNotify) + MESSAGE_HANDLER(WM_NOTIF_REG, OnReg) + MESSAGE_HANDLER(WM_NOTIF_UNREG, OnUnReg) + MESSAGE_HANDLER(WM_NOTIF_DELIVERY, OnDelivery) + MESSAGE_HANDLER(WM_NOTIF_SUSPEND, OnSuspendResume) + MESSAGE_HANDLER(WM_NOTIF_REMOVEBYPID, OnRemoveByPID); + END_MSG_MAP() + +protected: + UINT m_nNextRegID; + CChangeNotifyImpl *m_pimpl; +}; +#endif diff --git a/dll/win32/shell32/shelldesktop/CDesktopBrowser.cpp b/dll/win32/shell32/shelldesktop/CDesktopBrowser.cpp index 4ef2ab112e7..a444a9b3c97 100644 --- a/dll/win32/shell32/shelldesktop/CDesktopBrowser.cpp +++ b/dll/win32/shell32/shelldesktop/CDesktopBrowser.cpp @@ -2,6 +2,7 @@ * Shell Desktop * * Copyright 2008 Thomas Bluemel + * Copyright 2020 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,8 +26,6 @@ #include #endif - - WINE_DEFAULT_DEBUG_CHANNEL(desktop); static const WCHAR szProgmanClassName[] = L"Progman"; @@ -41,6 +40,7 @@ class CDesktopBrowser : private: HACCEL m_hAccel; HWND m_hWndShellView; + CChangeNotify m_hwndDeliWorker; CComPtr m_Tray; CComPtr m_ShellView; @@ -82,6 +82,7 @@ public: LRESULT OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + LRESULT OnGetDeliveryWorkerWnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); DECLARE_WND_CLASS_EX(szProgmanClassName, CS_DBLCLKS, COLOR_DESKTOP) @@ -94,6 +95,7 @@ BEGIN_MSG_MAP(CBaseBar) MESSAGE_HANDLER(WM_EXPLORER_OPEN_NEW_WINDOW, OnOpenNewWindow) MESSAGE_HANDLER(WM_COMMAND, OnCommand) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) + MESSAGE_HANDLER(WM_GETDELIWORKERWND, OnGetDeliveryWorkerWnd) END_MSG_MAP() BEGIN_COM_MAP(CDesktopBrowser) @@ -217,7 +219,6 @@ HRESULT CDesktopBrowser::Initialize(IShellDesktopTray *ShellDesk) _Resize(); HWND hwndListView = FindWindowExW(m_hWndShellView, NULL, WC_LISTVIEW, NULL); - SetShellWindowEx(m_hWnd, hwndListView); m_hAccel = LoadAcceleratorsW(shell32_hInstance, MAKEINTRESOURCEW(IDA_DESKBROWSER)); @@ -429,6 +430,17 @@ LRESULT CDesktopBrowser::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOO return 0; } +LRESULT CDesktopBrowser::OnGetDeliveryWorkerWnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + if (!::IsWindow(m_hwndDeliWorker)) + { + DWORD exstyle = WS_EX_TOOLWINDOW; + DWORD style = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + m_hwndDeliWorker.CreateWorker(NULL, exstyle, style); + } + return (LRESULT)(HWND)m_hwndDeliWorker; +} + HRESULT CDesktopBrowser_CreateInstance(IShellDesktopTray *Tray, REFIID riid, void **ppv) { return ShellObjectCreatorInit(Tray, riid, ppv); diff --git a/dll/win32/shell32/shelldesktop/CMakeLists.txt b/dll/win32/shell32/shelldesktop/CMakeLists.txt index df1fc738425..90afe178d8f 100644 --- a/dll/win32/shell32/shelldesktop/CMakeLists.txt +++ b/dll/win32/shell32/shelldesktop/CMakeLists.txt @@ -10,6 +10,7 @@ add_definitions( include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/atl) list(APPEND SOURCE + CChangeNotify.cpp CDesktopBrowser.cpp dde.cpp) diff --git a/dll/win32/shell32/shelldesktop/shelldesktop.h b/dll/win32/shell32/shelldesktop/shelldesktop.h index 9b675e715d2..406af9dabf1 100644 --- a/dll/win32/shell32/shelldesktop/shelldesktop.h +++ b/dll/win32/shell32/shelldesktop/shelldesktop.h @@ -29,6 +29,7 @@ #include #include +#include "CChangeNotify.h" #include "../shresdef.h" #include diff --git a/dll/win32/shell32/wine/changenotify.c b/dll/win32/shell32/wine/changenotify.c index e5775470392..0946089ba38 100644 --- a/dll/win32/shell32/wine/changenotify.c +++ b/dll/win32/shell32/wine/changenotify.c @@ -39,6 +39,10 @@ #include "pidl.h" WINE_DEFAULT_DEBUG_CHANNEL(shell); +#ifdef __REACTOS__ + #include "../shelldesktop/CChangeNotify.h" + DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen); +#endif static CRITICAL_SECTION SHELL32_ChangenotifyCS; static CRITICAL_SECTION_DEBUG critsect_debug = @@ -49,20 +53,7 @@ static CRITICAL_SECTION_DEBUG critsect_debug = }; static CRITICAL_SECTION SHELL32_ChangenotifyCS = { &critsect_debug, -1, 0, 0, 0, 0 }; -#ifdef __REACTOS__ -typedef struct { - PCIDLIST_ABSOLUTE pidl; - BOOL fRecursive; - /* File system notification items */ - HANDLE hDirectory; /* Directory handle */ - WCHAR wstrDirectory[MAX_PATH]; /* Directory name */ - OVERLAPPED overlapped; /* Overlapped structure */ - BYTE *buffer; /* Async buffer to fill */ - BYTE *backBuffer; /* Back buffer to swap buffer into */ -} SHChangeNotifyEntryInternal, *LPNOTIFYREGISTER; -#else typedef SHChangeNotifyEntry *LPNOTIFYREGISTER; -#endif /* internal list of notification clients (internal) */ typedef struct _NOTIFICATIONLIST @@ -77,24 +68,10 @@ typedef struct _NOTIFICATIONLIST ULONG id; } NOTIFICATIONLIST, *LPNOTIFICATIONLIST; -#ifdef __REACTOS__ -VOID _ProcessNotification(LPNOTIFYREGISTER item); -BOOL _OpenDirectory(LPNOTIFYREGISTER item); -static void CALLBACK _RequestTermination(ULONG_PTR arg); -static void CALLBACK _RequestAllTermination(ULONG_PTR arg); -static void CALLBACK _AddDirectoryProc(ULONG_PTR arg); -static VOID _BeginRead(LPNOTIFYREGISTER item); -static unsigned int WINAPI _RunAsyncThreadProc(LPVOID arg); -#endif - +#ifndef __REACTOS__ static struct list notifications = LIST_INIT( notifications ); static LONG next_id; - -#ifdef __REACTOS__ -HANDLE m_hThread; -UINT m_dwThreadId; -BOOL m_bTerminate; -#endif +#endif /* ndef __REACTOS__ */ #define SHCNE_NOITEMEVENTS ( \ SHCNE_ASSOCCHANGED ) @@ -142,6 +119,7 @@ static const char * DumpEvent( LONG event ) #undef DUMPEV } +#ifndef __REACTOS__ static const char * NodeName(const NOTIFICATIONLIST *item) { const char *str; @@ -165,30 +143,24 @@ static void DeleteNode(LPNOTIFICATIONLIST item) /* free the item */ for (i=0; icidl; i++) -#ifdef __REACTOS__ - { - QueueUserAPC(_RequestTermination, m_hThread, (ULONG_PTR) &item->apidl[i] ); - WaitForSingleObjectEx(m_hThread, 100, FALSE); -#endif SHFree((LPITEMIDLIST)item->apidl[i].pidl); -#ifdef __REACTOS__ - SHFree(item->apidl[i].buffer); - SHFree(item->apidl[i].backBuffer); - } -#endif SHFree(item->apidl); SHFree(item); } +#endif /* ndef __REACTOS__ */ void InitChangeNotifications(void) { -#ifdef __REACTOS__ - m_hThread = NULL; -#endif } void FreeChangeNotifications(void) { +#ifdef __REACTOS__ + HWND hwndWorker; + hwndWorker = DoGetNewDeliveryWorker(); + SendMessageW(hwndWorker, WM_NOTIF_REMOVEBYPID, GetCurrentProcessId(), 0); + DeleteCriticalSection(&SHELL32_ChangenotifyCS); +#else LPNOTIFICATIONLIST ptr, next; TRACE("\n"); @@ -200,11 +172,8 @@ void FreeChangeNotifications(void) LeaveCriticalSection(&SHELL32_ChangenotifyCS); -#ifdef __REACTOS__ - QueueUserAPC(_RequestAllTermination, m_hThread, (ULONG_PTR) NULL ); -#endif - DeleteCriticalSection(&SHELL32_ChangenotifyCS); +#endif } /************************************************************************* @@ -220,6 +189,72 @@ SHChangeNotifyRegister( int cItems, SHChangeNotifyEntry *lpItems) { +#ifdef __REACTOS__ + HWND hwndNotif, hwndOldWorker = NULL; + HANDLE hShared; + INT iItem; + ULONG nRegID = INVALID_REG_ID; + DWORD dwOwnerPID; + LPNOTIFSHARE pShare; + + TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p)\n", + hwnd, fSources, wEventMask, uMsg, cItems, lpItems); + + if (wEventMask == 0 || cItems <= 0 || cItems > 0x7FFF || lpItems == NULL || + !hwnd || !IsWindow(hwnd)) + { + return INVALID_REG_ID; + } + + hwndNotif = DoGetNewDeliveryWorker(); + if (hwndNotif == NULL) + return INVALID_REG_ID; + + if ((fSources & SHCNRF_NewDelivery) == 0) + { + hwndOldWorker = hwnd = DoHireOldDeliveryWorker(hwnd, uMsg); + uMsg = WM_OLDDELI_HANDOVER; + } + + if ((fSources & SHCNRF_RecursiveInterrupt) != 0 && + (fSources & SHCNRF_InterruptLevel) == 0) + { + fSources &= ~SHCNRF_NewDelivery; + } + + GetWindowThreadProcessId(hwndNotif, &dwOwnerPID); + + EnterCriticalSection(&SHELL32_ChangenotifyCS); + for (iItem = 0; iItem < cItems; ++iItem) + { + hShared = DoCreateNotifShare(nRegID, hwnd, uMsg, fSources, wEventMask, + lpItems[iItem].fRecursive, lpItems[iItem].pidl, + dwOwnerPID, hwndOldWorker); + if (hShared) + { + TRACE("WM_NOTIF_REG: hwnd:%p, hShared:%p, pid:0x%lx\n", hwndNotif, hShared, dwOwnerPID); + SendMessageW(hwndNotif, WM_NOTIF_REG, (WPARAM)hShared, dwOwnerPID); + + pShare = (LPNOTIFSHARE)SHLockSharedEx(hShared, dwOwnerPID, FALSE); + if (pShare) + { + nRegID = pShare->nRegID; + SHUnlockShared(pShare); + } + SHFreeShared(hShared, dwOwnerPID); + } + + if (nRegID == INVALID_REG_ID && (fSources & SHCNRF_NewDelivery) == 0) + { + ERR("Old Delivery is failed\n"); + DestroyWindow(hwnd); + LeaveCriticalSection(&SHELL32_ChangenotifyCS); + return INVALID_REG_ID; + } + } + LeaveCriticalSection(&SHELL32_ChangenotifyCS); + return nRegID; +#else LPNOTIFICATIONLIST item; int i; @@ -228,37 +263,12 @@ SHChangeNotifyRegister( TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p) item=%p\n", hwnd, fSources, wEventMask, uMsg, cItems, lpItems, item); -#ifdef __REACTOS__ - if (!m_hThread) - m_hThread = (HANDLE) _beginthreadex(NULL, 0, _RunAsyncThreadProc, NULL, 0, &m_dwThreadId); -#endif - item->cidl = cItems; -#ifdef __REACTOS__ - item->apidl = SHAlloc(sizeof(SHChangeNotifyEntryInternal) * cItems); -#else item->apidl = SHAlloc(sizeof(SHChangeNotifyEntry) * cItems); -#endif for(i=0;iapidl[i], sizeof(SHChangeNotifyEntryInternal)); -#endif item->apidl[i].pidl = ILClone(lpItems[i].pidl); item->apidl[i].fRecursive = lpItems[i].fRecursive; -#ifdef __REACTOS__ - item->apidl[i].buffer = SHAlloc(BUFFER_SIZE); - item->apidl[i].backBuffer = SHAlloc(BUFFER_SIZE); - item->apidl[i].overlapped.hEvent = &item->apidl[i]; - - if (fSources & SHCNRF_InterruptLevel) - { - if (_OpenDirectory( &item->apidl[i] )) - { - QueueUserAPC( _AddDirectoryProc, m_hThread, (ULONG_PTR) &item->apidl[i] ); - } - } -#endif } item->hwnd = hwnd; item->uMsg = uMsg; @@ -275,6 +285,7 @@ SHChangeNotifyRegister( LeaveCriticalSection(&SHELL32_ChangenotifyCS); return item->id; +#endif } /************************************************************************* @@ -282,6 +293,22 @@ SHChangeNotifyRegister( */ BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify) { +#ifdef __REACTOS__ + HWND hwndNotif; + LRESULT ret = 0; + TRACE("(0x%08x)\n", hNotify); + + hwndNotif = DoGetNewDeliveryWorker(); + if (hwndNotif) + { + ret = SendMessageW(hwndNotif, WM_NOTIF_UNREG, hNotify, 0); + if (!ret) + { + ERR("WM_NOTIF_UNREG failed\n"); + } + } + return ret; +#else LPNOTIFICATIONLIST node; TRACE("(0x%08x)\n", hNotify); @@ -299,6 +326,7 @@ BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify) } LeaveCriticalSection(&SHELL32_ChangenotifyCS); return FALSE; +#endif } /************************************************************************* @@ -322,6 +350,7 @@ struct new_delivery_notification BYTE data[1]; }; +#ifndef __REACTOS__ static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL sub ) { TRACE("%p %p %d\n", changed, watched, sub ); @@ -333,12 +362,87 @@ static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL su return TRUE; return FALSE; } +#endif /************************************************************************* * SHChangeNotify [SHELL32.@] */ void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2) { +#ifdef __REACTOS__ + LPITEMIDLIST pidl1 = NULL, pidl2 = NULL, pidlTemp1 = NULL, pidlTemp2 = NULL; + DWORD dwTick = GetTickCount(); + WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; + LPWSTR psz1, psz2; + TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2); + + switch (uFlags & SHCNF_TYPE) + { + case SHCNF_IDLIST: + if (wEventId == SHCNE_FREESPACE) + { + DoNotifyFreeSpace(dwItem1, dwItem2); + goto Quit; + } + pidl1 = (LPITEMIDLIST)dwItem1; + pidl2 = (LPITEMIDLIST)dwItem2; + break; + + case SHCNF_PATHA: + psz1 = psz2 = NULL; + if (dwItem1) + { + SHAnsiToUnicode(dwItem1, szPath1, ARRAYSIZE(szPath1)); + psz1 = szPath1; + } + if (dwItem2) + { + SHAnsiToUnicode(dwItem2, szPath2, ARRAYSIZE(szPath2)); + psz2 = szPath2; + } + uFlags &= ~SHCNF_TYPE; + uFlags |= SHCNF_PATHW; + SHChangeNotify(wEventId, uFlags, psz1, psz2); + return; + + case SHCNF_PATHW: + if (wEventId == SHCNE_FREESPACE) + { + /* FIXME */ + goto Quit; + } + if (dwItem1) + { + pidl1 = pidlTemp1 = SHSimpleIDListFromPathW(dwItem1); + } + if (dwItem2) + { + pidl2 = pidlTemp2 = SHSimpleIDListFromPathW(dwItem2); + } + break; + + case SHCNF_PRINTERA: + case SHCNF_PRINTERW: + FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n"); + return; + + default: + FIXME("unknown type %08x\n", uFlags & SHCNF_TYPE); + return; + } + + if (wEventId == 0 || (wEventId & SHCNE_ASSOCCHANGED) || pidl1 != NULL) + { + TRACE("notifying event %s(%x)\n", DumpEvent(wEventId), wEventId); + DoTransportChange(wEventId, uFlags, pidl1, pidl2, dwTick); + } + +Quit: + if (pidlTemp1) + ILFree(pidlTemp1); + if (pidlTemp2) + ILFree(pidlTemp2); +#else struct notification_recipients { struct list entry; HWND hwnd; @@ -370,9 +474,7 @@ void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID { TRACE("dwItem2 is not zero, but should be\n"); dwItem2 = 0; -#ifndef __REACTOS__ return; -#endif } if( ( ( wEventId & SHCNE_NOITEMEVENTS ) && @@ -396,43 +498,6 @@ void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID case SHCNF_PATHW: if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW(dwItem1); if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW(dwItem2); -#ifdef __REACTOS__ - if (wEventId & (SHCNE_MKDIR | SHCNE_RMDIR | SHCNE_UPDATEDIR | SHCNE_RENAMEFOLDER)) - { - /* - * The last items in the ID are currently files. So we chop off the last - * entry, and create a new one using a find data struct. - */ - if (dwItem1 && Pidls[0]){ - WIN32_FIND_DATAW wfd; - LPITEMIDLIST oldpidl, newpidl; - LPWSTR p = PathFindFileNameW((LPCWSTR)dwItem1); - ILRemoveLastID(Pidls[0]); - lstrcpynW(&wfd.cFileName[0], p, MAX_PATH); - wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; - newpidl = _ILCreateFromFindDataW(&wfd); - oldpidl = ILClone(Pidls[0]); - ILFree(Pidls[0]); - Pidls[0] = ILCombine(oldpidl, newpidl); - ILFree(newpidl); - ILFree(oldpidl); - } - if (dwItem2 && Pidls[1]){ - WIN32_FIND_DATAW wfd; - LPITEMIDLIST oldpidl, newpidl; - LPWSTR p = PathFindFileNameW((LPCWSTR)dwItem2); - ILRemoveLastID(Pidls[1]); - lstrcpynW(&wfd.cFileName[0], p, MAX_PATH); - wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; - newpidl = _ILCreateFromFindDataW(&wfd); - oldpidl = ILClone(Pidls[0]); - ILFree(Pidls[1]); - Pidls[1] = ILCombine(oldpidl, newpidl); - ILFree(newpidl); - ILFree(oldpidl); - } - } -#endif break; case SHCNF_IDLIST: Pidls[0] = ILClone(dwItem1); @@ -534,7 +599,6 @@ void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID SHFree(Pidls[0]); SHFree(Pidls[1]); -#ifndef __REACTOS__ if (wEventId & SHCNE_ASSOCCHANGED) { static const WCHAR args[] = {' ','-','a',0 }; @@ -572,6 +636,25 @@ HANDLE WINAPI SHChangeNotification_Lock( LPITEMIDLIST **lppidls, LPLONG lpwEventId) { +#ifdef __REACTOS__ + LPHANDBAG pHandbag; + TRACE("%p %08x %p %p\n", hChange, dwProcessId, lppidls, lpwEventId); + + pHandbag = DoGetHandbagFromTicket(hChange, dwProcessId); + if (!pHandbag || pHandbag->dwMagic != HANDBAG_MAGIC) + { + ERR("pHandbag is invalid\n"); + return NULL; + } + + if (lppidls) + *lppidls = pHandbag->pidls; + + if (lpwEventId) + *lpwEventId = pHandbag->pTicket->wEventId; + + return pHandbag; +#else struct new_delivery_notification *ndn; UINT offset; @@ -594,6 +677,7 @@ HANDLE WINAPI SHChangeNotification_Lock( *lpwEventId = ndn->event; return ndn; +#endif } /************************************************************************* @@ -601,8 +685,24 @@ HANDLE WINAPI SHChangeNotification_Lock( */ BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock) { +#ifdef __REACTOS__ + LPHANDBAG pHandbag = (LPHANDBAG)hLock; + BOOL ret; + TRACE("%p\n", hLock); + + if (!pHandbag || pHandbag->dwMagic != HANDBAG_MAGIC) + { + ERR("pHandbag is invalid\n"); + return FALSE; + } + + ret = SHUnlockShared(pHandbag->pTicket); + LocalFree(hLock); + return ret; +#else TRACE("%p\n", hLock); return SHUnlockShared(hLock); +#endif } /************************************************************************* @@ -614,255 +714,3 @@ DWORD WINAPI NTSHChangeNotifyDeregister(ULONG x1) return SHChangeNotifyDeregister( x1 ); } - -#ifdef __REACTOS__ - -static -void -CALLBACK -_AddDirectoryProc(ULONG_PTR arg) -{ - LPNOTIFYREGISTER item = (LPNOTIFYREGISTER)arg; - _BeginRead(item); -} - -BOOL _OpenDirectory(LPNOTIFYREGISTER item) -{ - STRRET strFile; - IShellFolder *psf; - HRESULT hr; - LPCITEMIDLIST child; - ULONG ulAttrs; - - // Makes function idempotent - if (item->hDirectory && !(item->hDirectory == INVALID_HANDLE_VALUE)) - return TRUE; - - hr = SHBindToParent(item->pidl, &IID_IShellFolder, (LPVOID*)&psf, &child); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - ulAttrs = SFGAO_FILESYSTEM | SFGAO_FOLDER; - hr = IShellFolder_GetAttributesOf(psf, 1, (LPCITEMIDLIST*)&child, &ulAttrs); - if (SUCCEEDED(hr)) - hr = IShellFolder_GetDisplayNameOf(psf, child, SHGDN_FORPARSING, &strFile); - - IShellFolder_Release(psf); - if (FAILED_UNEXPECTEDLY(hr)) - return FALSE; - - hr = StrRetToBufW(&strFile, NULL, item->wstrDirectory, _countof(item->wstrDirectory)); - if (FAILED_UNEXPECTEDLY(hr)) - return FALSE; - - if ((ulAttrs & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) != (SFGAO_FILESYSTEM | SFGAO_FOLDER)) - { - TRACE("_OpenDirectory ignoring %s\n", debugstr_w(item->wstrDirectory)); - item->hDirectory = INVALID_HANDLE_VALUE; - return FALSE; - } - - TRACE("_OpenDirectory %s\n", debugstr_w(item->wstrDirectory)); - - item->hDirectory = CreateFileW(item->wstrDirectory, // pointer to the file name - GENERIC_READ | FILE_LIST_DIRECTORY, // access (read/write) mode - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode - NULL, // security descriptor - OPEN_EXISTING, // how to create - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // file attributes - NULL); // file with attributes to copy - - if (item->hDirectory == INVALID_HANDLE_VALUE) - { - ERR("_OpenDirectory failed for %s\n", debugstr_w(item->wstrDirectory)); - return FALSE; - } - return TRUE; -} - -static void CALLBACK _RequestTermination(ULONG_PTR arg) -{ - LPNOTIFYREGISTER item = (LPNOTIFYREGISTER) arg; - TRACE("_RequestTermination %p %p \n", item, item->hDirectory); - if (!item->hDirectory || item->hDirectory == INVALID_HANDLE_VALUE) return; - - CancelIo(item->hDirectory); - CloseHandle(item->hDirectory); - item->hDirectory = NULL; -} - -static -void -CALLBACK -_NotificationCompletion(DWORD dwErrorCode, // completion code - DWORD dwNumberOfBytesTransfered, // number of bytes transferred - LPOVERLAPPED lpOverlapped) // I/O information buffer -{ - /* MSDN: The hEvent member of the OVERLAPPED structure is not used by the - system, so you can use it yourself. We do just this, storing a pointer - to the working struct in the overlapped structure. */ - LPNOTIFYREGISTER item = (LPNOTIFYREGISTER) lpOverlapped->hEvent; - TRACE("_NotificationCompletion\n"); - -#if 0 - if (dwErrorCode == ERROR_OPERATION_ABORTED) - { - /* Command was induced by CancelIo in the shutdown procedure. */ - TRACE("_NotificationCompletion ended.\n"); - return; - } -#endif - -#ifdef __REACTOS__ - /* If the FSD doesn't support directory change notifications, there's no - * no need to retry and requeue notification - */ - if (dwErrorCode == ERROR_INVALID_FUNCTION) - { - WARN("Directory watching not supported\n"); - return; - } - - /* Also, if the notify operation was canceled (like, user moved to another - * directory), then, don't requeue notification - */ - if (dwErrorCode == ERROR_OPERATION_ABORTED) - { - TRACE("Notification aborted\n"); - return; - } - - if (!item) - { - ERR("item == NULL\n"); - return; - } - -#endif - - /* This likely means overflow, so force whole directory refresh. */ - if (!dwNumberOfBytesTransfered) - { - ERR("_NotificationCompletion overflow\n"); - - ZeroMemory(item->buffer, BUFFER_SIZE); - _BeginRead(item); - - SHChangeNotify(SHCNE_UPDATEITEM | SHCNE_INTERRUPT, - SHCNF_IDLIST, - item->pidl, - NULL); - - return; - } - - /* - * Get the new read issued as fast as possible (before we do the - * processing and message posting). All of the file notification - * occur on one thread so the buffers should not collide with one another. - * The extra zero mems are because the FNI size isn't written correctly. - */ - - ZeroMemory(item->backBuffer, BUFFER_SIZE); - memcpy(item->backBuffer, item->buffer, dwNumberOfBytesTransfered); - ZeroMemory(item->buffer, BUFFER_SIZE); - - _BeginRead(item); - - _ProcessNotification(item); -} - -static VOID _BeginRead(LPNOTIFYREGISTER item ) -{ - TRACE("_BeginRead %p \n", item->hDirectory); - - /* This call needs to be reissued after every APC. */ - if (!ReadDirectoryChangesW(item->hDirectory, // handle to directory - item->buffer, // read results buffer - BUFFER_SIZE, // length of buffer - FALSE, // monitoring option (recursion) - FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions - NULL, // bytes returned - &item->overlapped, // overlapped buffer - _NotificationCompletion)) // completion routine - ERR("ReadDirectoryChangesW failed. (%p, %p, %p, %p, %p) Code: %u \n", - item, - item->hDirectory, - item->buffer, - &item->overlapped, - _NotificationCompletion, - GetLastError()); -} - -DWORD _MapAction(DWORD dwAction, BOOL isDir) -{ - switch (dwAction) - { - case FILE_ACTION_ADDED : return isDir ? SHCNE_MKDIR : SHCNE_CREATE; - case FILE_ACTION_REMOVED : return isDir ? SHCNE_RMDIR : SHCNE_DELETE; - case FILE_ACTION_MODIFIED : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM; - case FILE_ACTION_RENAMED_OLD_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM; - case FILE_ACTION_RENAMED_NEW_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM; - default: return SHCNE_UPDATEITEM; - } -} - -VOID _ProcessNotification(LPNOTIFYREGISTER item) -{ - BYTE* pBase = item->backBuffer; - TRACE("_ProcessNotification\n"); - - for (;;) - { - FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)pBase; - LPWSTR wszFilename; - INT len = 0; - WCHAR wstrFilename[MAX_PATH]; - WCHAR tmp[MAX_PATH]; - StringCchCopy(tmp, fni->FileNameLength, fni->FileName); - - PathCombine(wstrFilename, item->wstrDirectory, tmp); - - /* If it could be a short filename, expand it. */ - wszFilename = PathFindFileNameW(wstrFilename); - - len = lstrlenW(wszFilename); - /* The maximum length of an 8.3 filename is twelve, including the dot. */ - if (len <= 12 && wcschr(wszFilename, L'~')) - { - /* Convert to the long filename form. Unfortunately, this - does not work for deletions, so it's an imperfect fix. */ - wchar_t wbuf[MAX_PATH]; - if (GetLongPathName(wstrFilename, wbuf, _countof (wbuf)) > 0) - StringCchCopyW(wstrFilename, MAX_PATH, wbuf); - } - - /* On deletion of a folder PathIsDirectory will return false even if - it *was* a directory, so, again, imperfect. */ - SHChangeNotify(_MapAction(fni->Action, PathIsDirectory(wstrFilename)) | SHCNE_INTERRUPT, - SHCNF_PATHW, - wstrFilename, - NULL); - - if (!fni->NextEntryOffset) - break; - pBase += fni->NextEntryOffset; - } -} - -static void CALLBACK _RequestAllTermination(ULONG_PTR arg) -{ - m_bTerminate = TRUE; -} - -static unsigned int WINAPI _RunAsyncThreadProc(LPVOID arg) -{ - m_bTerminate = FALSE; - while (!m_bTerminate) - { - SleepEx(INFINITE, TRUE); - } - return 0; -} - -#endif /* __REACTOS__ */ diff --git a/dll/win32/shell32/wine/pidl.c b/dll/win32/shell32/wine/pidl.c index cfe204aa5e8..a813f9293cd 100644 --- a/dll/win32/shell32/wine/pidl.c +++ b/dll/win32/shell32/wine/pidl.c @@ -1100,6 +1100,10 @@ LPITEMIDLIST WINAPI SHSimpleIDListFromPathA(LPCSTR lpszPath) wPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, lpszPath, -1, wPath, len); } +#ifdef __REACTOS__ + if (PathFileExistsW(wPath)) + return ILCreateFromPathW(wPath); +#endif _ILParsePathW(wPath, NULL, TRUE, &pidl, NULL); @@ -1113,6 +1117,10 @@ LPITEMIDLIST WINAPI SHSimpleIDListFromPathW(LPCWSTR lpszPath) LPITEMIDLIST pidl = NULL; TRACE("%s\n", debugstr_w(lpszPath)); +#ifdef __REACTOS__ + if (PathFileExistsW(lpszPath)) + return ILCreateFromPathW(lpszPath); +#endif _ILParsePathW(lpszPath, NULL, TRUE, &pidl, NULL); TRACE("%s %p\n", debugstr_w(lpszPath), pidl); diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h index 3b7c5d6463c..859ff6ee32a 100644 --- a/sdk/include/reactos/undocshell.h +++ b/sdk/include/reactos/undocshell.h @@ -702,6 +702,13 @@ IStream* WINAPI SHGetViewStream(LPCITEMIDLIST, DWORD, LPCTSTR, LPCTSTR, LPCTSTR) EXTERN_C HRESULT WINAPI SHCreateSessionKey(REGSAM samDesired, PHKEY phKey); +PVOID +WINAPI +SHLockSharedEx( + _In_ HANDLE hData, + _In_ DWORD dwProcessId, + _In_ BOOL bWriteAccess); + /***************************************************************************** * INVALID_FILETITLE_CHARACTERS */