Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899/* * 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 "precomp.h"
WINE_DEFAULT_DEBUG_CHANNEL(shcn);
CRITICAL_SECTION SHELL32_ChangenotifyCS;
// This function requests creation of the server window if it doesn't exist yetstatic HWNDGetNotificationServer(BOOL bCreate){ static HWND s_hwndServer = NULL;
// use cache if any if (s_hwndServer && IsWindow(s_hwndServer)) return s_hwndServer;
// get the shell window HWND hwndShell = GetShellWindow(); if (hwndShell == NULL) { TRACE("GetShellWindow() returned NULL\n"); return NULL; }
// Get the window of the notification server that runs in explorer HWND hwndServer = (HWND)SendMessageW(hwndShell, WM_DESKTOP_GET_CNOTIFY_SERVER, bCreate, 0); if (!IsWindow(hwndServer)) { ERR("Unable to get server window\n"); hwndServer = NULL; }
// save and return s_hwndServer = hwndServer; return hwndServer;}
// This function will be called from DllMain.DLL_PROCESS_ATTACH.EXTERN_C void InitChangeNotifications(void){ InitializeCriticalSection(&SHELL32_ChangenotifyCS);}
// This function will be called from DllMain.DLL_PROCESS_DETACH.EXTERN_C void FreeChangeNotifications(void){ HWND hwndServer = GetNotificationServer(FALSE); if (hwndServer) SendMessageW(hwndServer, CN_UNREGISTER_PROCESS, GetCurrentProcessId(), 0); DeleteCriticalSection(&SHELL32_ChangenotifyCS);}
static HRESULTShell_ParsePrinterName( _In_ LPCWSTR pszName, _Out_ LPITEMIDLIST *ppidl, _In_ IBindCtx *pBindCtx){ *ppidl = NULL;
CComHeapPtr<ITEMIDLIST> pidlPrinters; HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_PRINTERS, &pidlPrinters); if (FAILED_UNEXPECTEDLY(hr)) return hr;
CComPtr<IShellFolder> pFolder; hr = SHBindToObject(NULL, pidlPrinters, IID_PPV_ARG(IShellFolder, &pFolder)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
CComHeapPtr<ITEMIDLIST> pidlTemp; hr = pFolder->ParseDisplayName(NULL, pBindCtx, (LPWSTR)pszName, NULL, &pidlTemp, NULL); if (FAILED_UNEXPECTEDLY(hr)) return hr;
return SHILCombine(pidlPrinters, pidlTemp, ppidl);}
//////////////////////////////////////////////////////////////////////////////////////// There are two delivery methods: "old delivery method" and "new delivery method".//// The old delivery method creates a broker window in the caller process// for message trampoline. The old delivery method is slow and deprecated.//// The new delivery method is enabled by SHCNRF_NewDelivery flag.// With the new delivery method the server directly sends the delivery message.
// This class brokers all notifications that don't have the SHCNRF_NewDelivery flagclass CChangeNotifyBroker : public CWindowImpl<CChangeNotifyBroker, CWindow, CWorkerTraits>{public: CChangeNotifyBroker(HWND hwndClient, UINT uMsg) : m_hwndClient(hwndClient), m_uMsg(uMsg) { }
// Message handlers LRESULT OnBrokerNotification(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return BrokerNotification((HANDLE)wParam, (DWORD)lParam); }
void OnFinalMessage(HWND) { // The server will destroy this window. // After the window gets destroyed we can delete this broker here. delete this; }
DECLARE_WND_CLASS_EX(L"WorkerW", 0, 0)
BEGIN_MSG_MAP(CChangeNotifyBroker) MESSAGE_HANDLER(WM_BROKER_NOTIFICATION, OnBrokerNotification) END_MSG_MAP()
private: HWND m_hwndClient; UINT m_uMsg;
BOOL BrokerNotification(HANDLE hTicket, DWORD dwOwnerPID) { // lock the ticket PIDLIST_ABSOLUTE *ppidl = NULL; LONG lEvent; HANDLE hLock = SHChangeNotification_Lock(hTicket, dwOwnerPID, &ppidl, &lEvent); if (hLock == NULL) { ERR("hLock is NULL\n"); return FALSE; }
// perform the delivery TRACE("broker notifying: %p, 0x%x, %p, 0x%lx\n", m_hwndClient, m_uMsg, ppidl, lEvent); SendMessageW(m_hwndClient, m_uMsg, (WPARAM)ppidl, lEvent);
// unlock the ticket SHChangeNotification_Unlock(hLock); return TRUE; }};
// This function creates a notification broker for old method. Used in SHChangeNotifyRegister.static HWNDCreateNotificationBroker(HWND hwnd, UINT wMsg){ // Create a new broker. It will be freed when the window gets destroyed CChangeNotifyBroker* pBroker = new CChangeNotifyBroker(hwnd, wMsg); if (pBroker == NULL) { ERR("Out of memory\n"); return NULL; }
HWND hwndBroker = SHCreateDefaultWorkerWindow(); if (hwndBroker == NULL) { ERR("hwndBroker == NULL\n"); delete pBroker; return NULL; }
pBroker->SubclassWindow(hwndBroker); return hwndBroker;}
// This function creates a delivery ticket for shell change nofitication.// Used in SHChangeNotify.static HANDLECreateNotificationParam(LONG wEventId, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwOwnerPID, DWORD dwTick){ // pidl1 and pidl2 have variable length. To store them into the delivery ticket, // we have to consider the offsets and the sizes of pidl1 and pidl2. DWORD cbPidl1 = 0, cbPidl2 = 0, ibOffset1 = 0, ibOffset2 = 0; if (pidl1) { cbPidl1 = ILGetSize(pidl1); ibOffset1 = DWORD_ALIGNMENT(sizeof(DELITICKET)); } if (pidl2) { cbPidl2 = ILGetSize(pidl2); ibOffset2 = DWORD_ALIGNMENT(ibOffset1 + cbPidl1); }
// allocate the delivery ticket DWORD cbSize = ibOffset2 + cbPidl2; HANDLE hTicket = SHAllocShared(NULL, cbSize, dwOwnerPID); if (hTicket == NULL) { ERR("Out of memory\n"); return NULL; }
// lock the ticket LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, TRUE); if (pTicket == NULL) { ERR("SHLockSharedEx failed\n"); SHFreeShared(hTicket, dwOwnerPID); return NULL; }
// populate the ticket 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);
// unlock the ticket and return SHUnlockShared(pTicket); return hTicket;}
// This function creates a "handbag" by using a delivery ticket.// The handbag is created in SHChangeNotification_Lock and used in OnBrokerNotification.// hTicket is a ticket handle of a shared memory block and dwOwnerPID is// the owner PID of the ticket.static LPHANDBAGDoGetHandbagFromTicket(HANDLE hTicket, DWORD dwOwnerPID){ // lock and validate the delivery ticket LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, FALSE); if (pTicket == NULL || pTicket->dwMagic != DELITICKET_MAGIC) { ERR("pTicket is invalid\n"); SHUnlockShared(pTicket); return NULL; }
// allocate the handbag LPHANDBAG pHandbag = (LPHANDBAG)LocalAlloc(LMEM_FIXED, sizeof(HANDBAG)); if (pHandbag == NULL) { ERR("Out of memory\n"); SHUnlockShared(pTicket); return NULL; }
// populate the handbag 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;}
// This function creates a registration entry in SHChangeNotifyRegister function.static HANDLECreateRegistrationParam(ULONG nRegID, HWND hwnd, UINT wMsg, INT fSources, LONG fEvents, LONG fRecursive, LPCITEMIDLIST pidl, DWORD dwOwnerPID, HWND hwndBroker){ // pidl has variable length. To store it into the registration entry, // we have to consider the length of pidl. DWORD cbPidl = ILGetSize(pidl); DWORD ibPidl = DWORD_ALIGNMENT(sizeof(REGENTRY)); DWORD cbSize = ibPidl + cbPidl;
// create the registration entry and lock it HANDLE hRegEntry = SHAllocShared(NULL, cbSize, dwOwnerPID); if (hRegEntry == NULL) { ERR("Out of memory\n"); return NULL; } LPREGENTRY pRegEntry = (LPREGENTRY)SHLockSharedEx(hRegEntry, dwOwnerPID, TRUE); if (pRegEntry == NULL) { ERR("SHLockSharedEx failed\n"); SHFreeShared(hRegEntry, dwOwnerPID); return NULL; }
// populate the registration entry pRegEntry->dwMagic = REGENTRY_MAGIC; pRegEntry->cbSize = cbSize; pRegEntry->nRegID = nRegID; pRegEntry->hwnd = hwnd; pRegEntry->uMsg = wMsg; pRegEntry->fSources = fSources; pRegEntry->fEvents = fEvents; pRegEntry->fRecursive = fRecursive; pRegEntry->hwndBroker = hwndBroker; pRegEntry->ibPidl = 0; if (pidl) { pRegEntry->ibPidl = ibPidl; memcpy((LPBYTE)pRegEntry + ibPidl, pidl, cbPidl); }
// unlock and return SHUnlockShared(pRegEntry); return hRegEntry;}
// This function is the body of SHChangeNotify function.// It creates a delivery ticket and send CN_DELIVER_NOTIFICATION message to// transport the change.static voidCreateNotificationParamAndSend(LONG wEventId, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwTick){ // get server window HWND hwndServer = GetNotificationServer(FALSE); if (hwndServer == NULL) return;
// the ticket owner is the process of the notification server DWORD pid; GetWindowThreadProcessId(hwndServer, &pid);
// create a delivery ticket HANDLE hTicket = CreateNotificationParam(wEventId, uFlags, pidl1, pidl2, pid, dwTick); if (hTicket == NULL) return;
// Create alias PIDLs CComHeapPtr<ITEMIDLIST> pidl1Alias, pidl2Alias; if (pidl1) SHELL32_AliasTranslatePidl(pidl1, &pidl1Alias, ALIAS_ANY); if (pidl2) SHELL32_AliasTranslatePidl(pidl2, &pidl2Alias, ALIAS_ANY);
HANDLE hTicket2 = NULL; if (((pidl1 && pidl1Alias && !ILIsEqual(pidl1, pidl1Alias)) || (pidl2 && pidl2Alias && !ILIsEqual(pidl2, pidl2Alias)))) { hTicket2 = CreateNotificationParam(wEventId, uFlags, pidl1Alias, pidl2Alias, pid, dwTick); if (!hTicket2) { SHFreeShared(hTicket, pid); return; } }
TRACE("hTicket:%p, hTicket2:%p, pid:0x%lx\n", hTicket, hTicket2, pid);
// send the ticket by using CN_DELIVER_NOTIFICATION if (pid != GetCurrentProcessId() || (uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH) { SendMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket, pid); if (hTicket2) SendMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket2, pid); } else { SendNotifyMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket, pid); if (hTicket2) SendNotifyMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket2, 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;}
/************************************************************************* * SHChangeNotifyRegister [SHELL32.2] */EXTERN_C ULONG WINAPISHChangeNotifyRegister(HWND hwnd, INT fSources, LONG wEventMask, UINT uMsg, INT cItems, SHChangeNotifyEntry *lpItems){ HWND hwndServer, hwndBroker = NULL; HANDLE hRegEntry; INT iItem; ULONG nRegID = INVALID_REG_ID; DWORD dwOwnerPID; LPREGENTRY pRegEntry;
TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p)\n", hwnd, fSources, wEventMask, uMsg, cItems, lpItems);
// sanity check if (wEventMask == 0 || cItems <= 0 || cItems > 0x7FFF || lpItems == NULL || hwnd == NULL || !IsWindow(hwnd)) { return INVALID_REG_ID; }
// request the window of the server hwndServer = GetNotificationServer(TRUE); if (hwndServer == NULL) return INVALID_REG_ID;
// disable recursive interrupt in specific condition if ((fSources & SHCNRF_RecursiveInterrupt) && !(fSources & SHCNRF_InterruptLevel)) { fSources &= ~SHCNRF_RecursiveInterrupt; }
// if it is old delivery method, then create a broker window if ((fSources & SHCNRF_NewDelivery) == 0) { hwndBroker = hwnd = CreateNotificationBroker(hwnd, uMsg); uMsg = WM_BROKER_NOTIFICATION; }
// The owner PID is the process ID of the server GetWindowThreadProcessId(hwndServer, &dwOwnerPID);
EnterCriticalSection(&SHELL32_ChangenotifyCS); for (iItem = 0; iItem < cItems; ++iItem) { // create a registration entry hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask, lpItems[iItem].fRecursive, lpItems[iItem].pidl, 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);
// update nRegID pRegEntry = (LPREGENTRY)SHLockSharedEx(hRegEntry, dwOwnerPID, FALSE); if (pRegEntry) { nRegID = pRegEntry->nRegID; SHUnlockShared(pRegEntry); }
// free registration entry SHFreeShared(hRegEntry, dwOwnerPID); }
if (nRegID == INVALID_REG_ID) { ERR("Delivery failed\n");
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);
return nRegID;}
/************************************************************************* * SHChangeNotifyDeregister [SHELL32.4] */EXTERN_C BOOL WINAPISHChangeNotifyDeregister(ULONG hNotify){ TRACE("(0x%08x)\n", hNotify);
// get the server window HWND hwndServer = GetNotificationServer(FALSE); if (hwndServer == NULL) return FALSE;
// send CN_UNREGISTER message and try to unregister BOOL ret = (BOOL)SendMessageW(hwndServer, CN_UNREGISTER, hNotify, 0); if (!ret) ERR("CN_UNREGISTER failed\n");
return ret;}
/************************************************************************* * SHChangeNotifyUpdateEntryList [SHELL32.5] */EXTERN_C BOOL WINAPISHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2, DWORD unknown3, DWORD unknown4){ FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", unknown1, unknown2, unknown3, unknown4); return TRUE;}
/* for dumping events */static LPCSTR DumpEvent(LONG event){ if (event == SHCNE_ALLEVENTS) return "SHCNE_ALLEVENTS";#define DUMPEV(x) ,( event & SHCNE_##x )? #x " " : "" return wine_dbg_sprintf( "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" DUMPEV(RENAMEITEM) DUMPEV(CREATE) DUMPEV(DELETE) DUMPEV(MKDIR) DUMPEV(RMDIR) DUMPEV(MEDIAINSERTED) DUMPEV(MEDIAREMOVED) DUMPEV(DRIVEREMOVED) DUMPEV(DRIVEADD) DUMPEV(NETSHARE) DUMPEV(NETUNSHARE) DUMPEV(ATTRIBUTES) DUMPEV(UPDATEDIR) DUMPEV(UPDATEITEM) DUMPEV(SERVERDISCONNECT) DUMPEV(UPDATEIMAGE) DUMPEV(DRIVEADDGUI) DUMPEV(RENAMEFOLDER) DUMPEV(FREESPACE) DUMPEV(EXTENDED_EVENT) DUMPEV(ASSOCCHANGED) DUMPEV(INTERRUPT) );#undef DUMPEV}
/************************************************************************* * SHChangeRegistrationReceive [SHELL32.646] */EXTERN_C BOOL WINAPISHChangeRegistrationReceive(LPVOID lpUnknown1, DWORD dwUnknown2){ TRACE("\n"); return FALSE; /* Just return FALSE */}
EXTERN_C VOID WINAPISHChangeNotifyReceiveEx(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 WINAPISHChangeNotifyReceive(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2){ SHChangeNotifyReceiveEx(lEvent, uFlags, pidl1, pidl2, GetTickCount());}
EXTERN_C VOID WINAPISHChangeNotifyTransmit(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwTick){ SHChangeNotifyReceiveEx(lEvent, uFlags, pidl1, pidl2, dwTick);}
/************************************************************************* * SHChangeNotify [SHELL32.@] */EXTERN_C void WINAPISHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2){ TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2);
if (!GetNotificationServer(FALSE)) return;
DWORD dwTick = GetTickCount();
WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; LPITEMIDLIST pidl1 = NULL, pidl2 = NULL; LPWSTR psz1, psz2; SHChangeDWORDAsIDList dwidl; DWORD dwType;
Retry: dwType = (uFlags & SHCNF_TYPE); switch (dwType) { case SHCNF_IDLIST: { if (wEventId == SHCNE_FREESPACE) { szPath1[0] = szPath2[0] = UNICODE_NULL; if (dwItem1) SHGetPathFromIDList((LPCITEMIDLIST)dwItem1, szPath1); if (dwItem2) SHGetPathFromIDList((LPCITEMIDLIST)dwItem2, szPath2);
uFlags = SHCNF_PATHW; dwItem1 = (szPath1[0] ? szPath1 : NULL); dwItem2 = (szPath2[0] ? szPath2 : NULL); goto Retry; }
pidl1 = (LPITEMIDLIST)dwItem1; pidl2 = (LPITEMIDLIST)dwItem2; break; } case SHCNF_PATHA: case SHCNF_PRINTERA: { psz1 = psz2 = NULL; szPath1[0] = szPath2[0] = UNICODE_NULL; if (dwItem1) { SHAnsiToUnicode((LPCSTR)dwItem1, szPath1, _countof(szPath1)); psz1 = szPath1; } if (dwItem2) { SHAnsiToUnicode((LPCSTR)dwItem2, szPath2, _countof(szPath2)); psz2 = szPath2; }
uFlags = ((dwType == SHCNF_PRINTERA) ? SHCNF_PRINTERW : SHCNF_PATHW); dwItem1 = psz1; dwItem2 = psz2; goto Retry; } case SHCNF_PATHW: { if (wEventId == SHCNE_FREESPACE) { INT iDrive1 = -1, iDrive2 = -1; if (dwItem1) iDrive1 = PathGetDriveNumberW((LPCWSTR)dwItem1); if (dwItem2) iDrive2 = PathGetDriveNumberW((LPCWSTR)dwItem2);
DWORD dwValue = 0; if (iDrive1 >= 0) dwValue |= (1 << iDrive1); if (iDrive2 >= 0) dwValue |= (1 << iDrive2);
if (!dwValue) return;
uFlags = SHCNF_DWORD; dwItem1 = UlongToPtr(dwValue); dwItem2 = NULL; goto Retry; }
if (dwItem1) pidl1 = SHSimpleIDListFromPathW((LPCWSTR)dwItem1); if (dwItem2) pidl2 = SHSimpleIDListFromPathW((LPCWSTR)dwItem2); break; } case SHCNF_PRINTERW: { if (dwItem1) { HRESULT hr = Shell_ParsePrinterName((LPCWSTR)dwItem1, &pidl1, NULL); if (FAILED_UNEXPECTEDLY(hr)) return; }
if (dwItem2) { HRESULT hr = Shell_ParsePrinterName((LPCWSTR)dwItem2, &pidl2, NULL); if (FAILED_UNEXPECTEDLY(hr)) { ILFree(pidl1); return; } } break; } case SHCNF_DWORD: { dwidl.cb = offsetof(SHChangeDWORDAsIDList, cbZero); dwidl.dwItem1 = PtrToUlong(dwItem1); dwidl.dwItem2 = PtrToUlong(dwItem2); dwidl.cbZero = 0; pidl1 = (LPITEMIDLIST)&dwidl; break; } default: { FIXME("Unknown type: 0x%X\n", dwType); return; } }
if (pidl1 || !wEventId || (wEventId & SHCNE_ASSOCCHANGED)) { TRACE("notifying event %s(%x)\n", DumpEvent(wEventId), wEventId); SHChangeNotifyTransmit(wEventId, uFlags, pidl1, pidl2, dwTick); }
if ((dwType == SHCNF_PATHW) || (dwType == SHCNF_PRINTERW)) { ILFree(pidl1); ILFree(pidl2); }}
/************************************************************************* * NTSHChangeNotifyRegister [SHELL32.640] */EXTERN_C ULONG WINAPINTSHChangeNotifyRegister(HWND hwnd, INT fSources, LONG fEvents, UINT msg, INT count, SHChangeNotifyEntry *idlist){ return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery, fEvents, msg, count, idlist);}
/************************************************************************* * SHChangeNotification_Lock [SHELL32.644] */EXTERN_C HANDLE WINAPISHChangeNotification_Lock(HANDLE hTicket, DWORD dwOwnerPID, LPITEMIDLIST **lppidls, LPLONG lpwEventId){ TRACE("%p %08x %p %p\n", hTicket, dwOwnerPID, lppidls, lpwEventId);
// create a handbag from the ticket LPHANDBAG pHandbag = DoGetHandbagFromTicket(hTicket, dwOwnerPID); if (pHandbag == NULL || pHandbag->dwMagic != HANDBAG_MAGIC) { ERR("pHandbag is invalid\n"); return NULL; }
// populate parameters from the handbag if (lppidls) *lppidls = pHandbag->pidls; if (lpwEventId) *lpwEventId = (pHandbag->pTicket->wEventId & ~SHCNE_INTERRUPT);
// return the handbag return pHandbag;}
/************************************************************************* * SHChangeNotification_Unlock [SHELL32.645] */EXTERN_C BOOL WINAPISHChangeNotification_Unlock(HANDLE hLock){ TRACE("%p\n", hLock);
// validate the handbag LPHANDBAG pHandbag = (LPHANDBAG)hLock; if (pHandbag == NULL || pHandbag->dwMagic != HANDBAG_MAGIC) { ERR("pHandbag is invalid\n"); return FALSE; }
// free the handbag BOOL ret = SHUnlockShared(pHandbag->pTicket); LocalFree(hLock); return ret;}
/************************************************************************* * NTSHChangeNotifyDeregister [SHELL32.641] */EXTERN_C DWORD WINAPINTSHChangeNotifyDeregister(ULONG hNotify){ FIXME("(0x%08x):semi stub.\n", hNotify); return SHChangeNotifyDeregister(hNotify);}
/************************************************************************* * SHChangeNotifySuspendResume [SHELL32.277] */EXTERN_C BOOLWINAPISHChangeNotifySuspendResume(BOOL bSuspend, LPITEMIDLIST pidl, BOOL bRecursive, DWORD dwReserved){ FIXME("SHChangeNotifySuspendResume() stub\n"); return FALSE;}