Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3011 lines
88 KiB

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1993
//
// File: fsnotify.c
//
// This file contains the file system notification service.
//
// History:
// 03-05-93 AndrewCo Created
//
//---------------------------------------------------------------------------
// REVIEW WIN32 : A lot of this will need to be re-done for Win32.
#include "shellprv.h"
#pragma hdrstop
#define FSM_WAKEUP (WM_USER + 0x0000)
#define FSM_GOAWAY (WM_USER + 0x0001)
#define MSEC_INTERVAL 1000 // Accumerate changes for one-second.
#define MSEC_MAXWAIT 30000 // Maximum delay.
#define MSEC_MAXINTERVAL INFINITE
#ifdef FSNDEBUG
#define FSNIDEBUG
#endif
#ifdef DEBUG
#define DESKTOP_EVENT 0x1
#define SFP_EVENT 0x2
#define LINK_EVENT 0x4
#define ICON_EVENT 0x8
#define RLFS_EVENT 0x10
UINT g_fPerf = (DESKTOP_EVENT | SFP_EVENT | LINK_EVENT | ICON_EVENT | RLFS_EVENT);
#define PERFTEST(n) if (g_fPerf & n)
#else
#define PERFTEST(n)
#endif
#define FSNENTERCRITICAL ENTERCRITICAL
#define FSNLEAVECRITICAL LEAVECRITICAL
#define FSN_EVENTSPENDING ((int)(s_fsn.dwLastEvent-sp_fsn.dwLastFlush) >= 0)
#define WakeThread(_id) ((_id) ? PostThreadMessage(_id, FSM_WAKEUP, 0, 0) : FALSE)
#define SignalKillThread(_id) ((_id) ? PostThreadMessage(_id, FSM_GOAWAY, 0, 0) : FALSE)
//
// Note: The string pointers are const which is to indicate that freeing
// them is the caller's, not the client's, responsibility.
//
#define FSSF_IN_USE 0x0001
#define FSSF_DELETE_ME 0x0002
typedef struct _FSNCI FSNotifyClientInfo, *PFSNotifyClientInfo;
typedef struct _FSNCI
{
PFSNotifyClientInfo pfsnciNext;
HWND hwnd;
ULONG ulID;
DWORD dwProcID;
int fSources;
int iSerializationFlags;
LONG fEvents;
WORD wMsg;
HDSA hdsaNE;
HDPA hdpaPendingEvents;
} FSNotifyClientInfo, *PFSNotifyClientInfo; // Hungarian: fsnci
typedef struct
{
LPCITEMIDLIST pidl; // this is SHARED with the fs registered client structure.
HANDLE hEvent;
int iCount; // how many clients are interested in this. (ref counts)
} FSIntClient, * LPFSIntClient;
//
// FSNotifyEvent's member pszFile points to a buffer containing the NULL
// terminated name of the file that changed, followed by the new name of
// that file, if the operation was RENAME, or NULL if not.
//
typedef struct
{
// these two must be together and at the front
LPITEMIDLIST pidl;
LPITEMIDLIST pidlExtra;
LONG lEvent;
UINT cRef;
} FSNotifyEvent; // Hungarian: fsnevt
typedef struct
{
HANDLE hThread;
DWORD idThread;
} AWAKETHREAD;
//
// We don't start the event-dispatch timer until an
// event occurs.
//
typedef struct _FSNotify
{
UINT cRefClientList;
DWORD dwLastEvent;
HDSA hdsaThreadAwake;
FSNotifyClientInfo *pfsnciFirst;
HDPA hdpaIntEvents;
ULONG ulNextID;
} FSNotify;
// this data block is attached to the notify window so that we can remember
// enough information without having to change the API's
#define WM_CHANGENOTIFYMSG WM_USER + 1
typedef struct _tag_NotifyProxyData
{
ULONG ulShellRegCode;
HWND hwndParent;
UINT wMsg;
} _NotifyProxyData, * LP_NotifyProxyData;
static FSNotify s_fsn =
{
0,
0,
NULL,
NULL,
NULL,
1,
};
typedef struct _FSNotifyPerProc
{
HANDLE htStarting;
DWORD idtStarting;
HANDLE htRunning;
DWORD idtRunning; // invalid if htRunning is NULL
UINT cclients; // number of registered client
int iCallbackCount;
HDSA hdsaIntEvents;
HDSA hdsaIntClients;
DWORD dwLastFlush;
BOOL fFlushNow : 1;
HANDLE hCallbackEvent; // iCallbackCount == 0 ? SetEvent : ResetClear
} FSNotifyPerProc;
const TCHAR c_szCallbackName[] = TEXT("Shell_NotificationCallbacksOutstanding");
const TCHAR c_szWindowClassName[] = TEXT("Shell32HiddenNotfyWnd");
const TCHAR c_szDummyWindowName[] = TEXT("");
#define CALLBACK_TIMEOUT 30000 // 30 seconds
#pragma data_seg(DATASEG_PERINSTANCE)
static FSNotifyPerProc sp_fsn =
{
NULL,
0,
NULL,
0,
0,
0,
NULL,
NULL,
0,
FALSE
};
#pragma data_seg()
#define MAX_EVENT_COUNT 10
//
// internal event handlers.
//
void CDesktop_FSEvent (LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra);
void SFP_FSEvent (LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra);
void Link_FSEvent (LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra);
void Icon_FSEvent (LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra);
LRESULT CALLBACK HiddenNotifyWindow_WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam );
BOOL _RegisterNotifyProxyWndProc( void );
void _SHChangeNotifyHandleClientEvents(FSNotifyClientInfo * pfsnci);
void FSNPostInterruptEvent(LPCITEMIDLIST pidl);
PFSNotifyClientInfo _SHChangeNotifyNukeClient(FSNotifyClientInfo *pfsnci, BOOL fNukeInterrupts);
void WINAPI _SHChangeNotifyHandleEvents(BOOL);
// void WINAPI _TEMPSHChangeNotifyHandleEvent()
// {
// _SHChangeNotifyHandleEvents(TRUE);
// }
//---------------------------------------------------------------------------
#ifdef FSNDEBUG
void DebugDumpPidl(TCHAR *szOut, LPCITEMIDLIST pidl)
{
TCHAR szPath[MAX_PATH];
LPTSTR lpsz;
if (pidl) {
lpsz = szPath;
SHGetPathFromIDList(pidl, szPath);
} else {
lpsz = TEXT("(NULL)");
}
DebugMsg(DM_TRACE, TEXT("%s : %s"), szOut, lpsz);
}
#else
#define DebugDumpPidl(lpsz, pidl)
#endif
#define INTERRUPT_TIMEOUT 2 * MSEC_INTERVAL
int FSNBuildEventList(LPHANDLE lphe)
{
int i;
int j;
int iMax;
LPFSIntClient lpfsic;
i = 0;
if (sp_fsn.hdsaIntClients) {
FSNENTERCRITICAL;
iMax = DSA_GetItemCount(sp_fsn.hdsaIntClients);
for (j = 0; j < iMax; j++, i++) {
lpfsic = DSA_GetItemPtr(sp_fsn.hdsaIntClients, j);
if (lpfsic->iCount == 0) {
// this one is marked for deletion.
// we know we're not waiting for it here, so nuke now.
if (lpfsic->hEvent)
FindCloseChangeNotification(lpfsic->hEvent);
ILGlobalFree((LPITEMIDLIST)lpfsic->pidl);
DSA_DeleteItem(sp_fsn.hdsaIntClients, j);
j--;
i--;
iMax--;
} else {
// create this here so that it will be owned by our global thread
if (!lpfsic->hEvent) {
TCHAR szPath[MAX_PATH];
if (!SHGetPathFromIDList(lpfsic->pidl, szPath) || !szPath[0])
goto Punt;
lpfsic->hEvent = FindFirstChangeNotification(szPath, FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_ATTRIBUTES);
if (lpfsic->hEvent != INVALID_HANDLE_VALUE) {
FindNextChangeNotification(lpfsic->hEvent);
} else {
#ifdef DEBUG
if (IsLFNDrive(szPath))
Assert(0);
#endif
Punt:
DebugMsg(DM_TRACE, TEXT("FindfirstChangeNotification failed on path %s, %d"), szPath, GetLastError());
lpfsic->iCount = 0;
lpfsic->hEvent = NULL;
}
}
lphe[i] = lpfsic->hEvent;
// Don't allow us to overflow the maximum the system
// supports. Must leave one for MsgWait...
if (i >= (MAXIMUM_WAIT_OBJECTS -1 ))
break;
}
}
FSNLEAVECRITICAL;
}
return i;
}
void _FSN_WaitForCallbacks(void)
{
MSG msg;
HANDLE hCallbackEvent;
DWORD dwWaitResult;
hCallbackEvent = OpenEvent(SYNCHRONIZE, FALSE, c_szCallbackName);
if (!hCallbackEvent)
return; // No event, no callbacks...
do {
dwWaitResult = MsgWaitForMultipleObjects(1, &hCallbackEvent, FALSE,
CALLBACK_TIMEOUT, QS_SENDMESSAGE);
if (dwWaitResult == WAIT_OBJECT_0) break; // Event completed
if (dwWaitResult == WAIT_TIMEOUT) break; // Ran out of time
if (dwWaitResult == WAIT_OBJECT_0+1) {
//
// Some message came in, reset message event, deliver callbacks, etc.
//
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); // we need to do this to flush callbacks
}
} while (TRUE);
CloseHandle(hCallbackEvent);
}
DWORD WINAPI Shell32ThreadProc(LPVOID lpUnused)
{
DWORD idThread = GetCurrentThreadId();
HANDLE ahEvents[MAXIMUM_WAIT_OBJECTS];
int cEvents;
int cIntEvents;
MSG msg;
DWORD dwWaitTime;
DWORD dwWaitResult;
AWAKETHREAD pat;
//
// Call dummy USER API to create a message queue
// NB Don't call PeekMessage() as that will make this the primary
// thread and break WaitForInpuIdle()
GetActiveWindow();
FSNENTERCRITICAL;
if (sp_fsn.htStarting && idThread == sp_fsn.idtStarting)
{
sp_fsn.htRunning = sp_fsn.htStarting;
sp_fsn.idtRunning = idThread;
sp_fsn.htStarting = NULL;
sp_fsn.idtStarting = 0;
}
{
// I want this very tightly scoped
HANDLE APIENTRY ConvertToGlobalHandle( HANDLE hSource );
#ifdef WINNT
// BUGBUG - BobDay - What do we do here?
pat.hThread = OpenProcess(SYNCHRONIZE,FALSE,GetCurrentProcessId());
#else
pat.hThread = ConvertToGlobalHandle(OpenProcess(SYNCHRONIZE,
FALSE, GetCurrentProcessId()));
#endif
pat.idThread = idThread;
}
// BUGBUG: I am asserting this because I don't know what to do
// if it fails
Assert(pat.hThread);
DSA_InsertItem(s_fsn.hdsaThreadAwake, 0xffff, &pat);
FSNLEAVECRITICAL;
for ( ; ; )
{
// check before and after waitfor to help avoid async problems.
if (!sp_fsn.htRunning || idThread!=sp_fsn.idtRunning)
break;
cEvents = FSNBuildEventList(ahEvents);
// if there are events, wait a limited time only
if ((sp_fsn.hdsaIntEvents && (cIntEvents = DSA_GetItemCount(sp_fsn.hdsaIntEvents))) ||
FSN_EVENTSPENDING) {
dwWaitTime = INTERRUPT_TIMEOUT;
} else {
cIntEvents = 0;
dwWaitTime = Binder_Timeout();
if ((long)dwWaitTime < 0)
{
dwWaitTime = MSEC_MAXINTERVAL;
}
}
dwWaitResult = MsgWaitForMultipleObjects(cEvents, ahEvents, FALSE,
dwWaitTime, QS_ALLINPUT);
if ((int)(dwWaitResult-WAIT_OBJECT_0) <= cEvents) {
DebugMsg(DM_TRACE, TEXT("SH:FSNotify wait for multiple objects found %d"), dwWaitResult);
}
if (!sp_fsn.htRunning || idThread != sp_fsn.idtRunning)
break;
if (sp_fsn.fFlushNow) {
// we use a process global instead of a signal so that
// even if multiple threads signal us, we only do this once.
// this is for flushnowait. they want an immediate return,
// but to flushb out any changes (ie, want ui update, but
// don't need to do anything with it.
// need to do it here because their thread might die in which
// case the sendmessages don't go through
DebugMsg(DM_TRACE, TEXT("**GotFlushNowEvent!**"));
_SHChangeNotifyHandleEvents(FALSE);
sp_fsn.fFlushNow = FALSE;
}
if ((int)(dwWaitResult-WAIT_OBJECT_0) == cEvents)
{
// There was some message put in our queue, so we need to dispose
// of it
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
Assert(!msg.hwnd);
switch (msg.message)
{
case FSM_WAKEUP:
// Note that we do not actually do anything now; we check
// that some events are pending next time through the loop
// and wait for the WaitForMultiple to timeout
// Also note that since we are in a PeekMessage loop, all
// WAKEUP message will be removed from the queue
break;
case FSM_GOAWAY:
FSNENTERCRITICAL;
// Set the global hThread to NULL to tell this loop to end
if (sp_fsn.idtRunning == idThread)
{
sp_fsn.htRunning = NULL;
sp_fsn.idtRunning = 0;
}
FSNLEAVECRITICAL;
break;
default:
Assert(FALSE);
DispatchMessage(&msg);
break;
}
}
} else if (dwWaitResult == WAIT_TIMEOUT) {
// if we had an event to
// deal with and the wait timed out (meaning we haven't gotten
// any new events for a while) handle it now
if (FSN_EVENTSPENDING || cIntEvents)
_SHChangeNotifyHandleEvents(FALSE);
if (FSN_EVENTSPENDING)
{
// Apparently they did not all get flushed by the
// HandleEvents, so wake up immediately
WakeThread(idThread);
}
Binder_Timer();
} else if ((int)(dwWaitResult-WAIT_OBJECT_0) < cEvents) {
LPFSIntClient lpfsic;
FSNENTERCRITICAL;
// a FindFirstChangeNotification went off.
lpfsic = DSA_GetItemPtr(sp_fsn.hdsaIntClients, dwWaitResult);
if (lpfsic) {
// post and reset it.
FSNPostInterruptEvent(lpfsic->pidl);
FindNextChangeNotification(lpfsic->hEvent);
}
FSNLEAVECRITICAL;
}
}
DebugMsg(DM_TRACE, TEXT("sh TR - FSNotify is killing itself"));
_FSN_WaitForCallbacks();
return(0);
}
typedef struct {
LPCITEMIDLIST pidl;
int iCount; // how many of these events have we had?
} FSIntEvent, *LPFSIntEvent;
LPFSIntEvent FSNFindInterruptEvent(LPCITEMIDLIST pidl)
{
// assumes caller has checked that sp_fsn.hdsaIntEvents exists
// and has already grabbed the semaphore
int i;
for ( i = DSA_GetItemCount(sp_fsn.hdsaIntEvents) - 1 ; i >= 0 ; i-- ) {
LPFSIntEvent lpfsie;
lpfsie = DSA_GetItemPtr(sp_fsn.hdsaIntEvents, i);
// check for immediate parent. no recursive FindFirstChangeNotify allowed.
if (ILIsEqual(lpfsie->pidl, pidl)) {
return lpfsie;
}
}
return NULL;
}
BOOL _FSN_InitIntEvents(void)
{
if (sp_fsn.hdsaIntEvents)
{
return(TRUE);
}
sp_fsn.hdsaIntEvents = DSA_Create(SIZEOF(FSIntEvent), 4);
if (!sp_fsn.hdsaIntEvents)
{
goto Error0;
}
if (!s_fsn.hdpaIntEvents)
{
s_fsn.hdpaIntEvents = DPA_Create(4);
if (!s_fsn.hdpaIntEvents)
{
goto Error1;
}
}
if (DPA_InsertPtr(s_fsn.hdpaIntEvents, 0xffff, sp_fsn.hdsaIntEvents)
== -1)
{
// When we created the DPA, it should have had room for at least
// 4 items, so we should never fail if the DPA has no items, so
// we do not have to worry about destroying the DPA here
Assert(DPA_GetPtrCount(s_fsn.hdpaIntEvents) != 0);
goto Error1;
}
return(TRUE);
Error1:;
DSA_Destroy(sp_fsn.hdsaIntEvents);
sp_fsn.hdsaIntEvents = NULL;
Error0:;
return(FALSE);
}
void FSNPostInterruptEvent(LPCITEMIDLIST pidl)
{
FSIntEvent fsie;
LPFSIntEvent lpfsie;
int i;
#ifdef FSNIDEBUG
{
TCHAR szPath[MAX_PATH];
SHGetPathFromIDList(pidl, szPath);
DebugMsg(DM_TRACE, TEXT("FSNOTIFY: PostEvent: %s"), szPath);
}
#endif
FSNENTERCRITICAL;
if (!_FSN_InitIntEvents())
{
return;
}
lpfsie = FSNFindInterruptEvent(pidl);
// if we can't find the item, add it.
if ( !lpfsie ) {
fsie.pidl = ILGlobalClone(pidl);
if (fsie.pidl) {
fsie.iCount = 0;
i = DSA_InsertItem(sp_fsn.hdsaIntEvents, 0x7FFF, &fsie);
if (i != -1) {
lpfsie = DSA_GetItemPtr(sp_fsn.hdsaIntEvents, i);
} else {
ILGlobalFree((LPITEMIDLIST)fsie.pidl);
}
}
}
if (lpfsie)
lpfsie->iCount++;
FSNLEAVECRITICAL;
}
// called when we get a GenerateEvent. We want to remove the corresponding
// interrupt event
void FSNRemoveInterruptEvent(LPCITEMIDLIST pidl)
{
LPFSIntEvent lpfsie;
int i, j;
if (!s_fsn.hdpaIntEvents)
return;
#ifdef FSNIDEBUG
{
TCHAR szPath[MAX_PATH];
SHGetPathFromIDList(pidl, szPath);
DebugMsg(DM_TRACE, TEXT("FSNOTIFY: Remove: %s"), szPath);
}
#endif
FSNENTERCRITICAL;
// assumes caller has checked that sp_fsn.hdsaIntEvents exists
// and has already grabbed the semaphore
for ( j = DPA_GetPtrCount(s_fsn.hdpaIntEvents) - 1 ; j >= 0 ; j-- ) {
HDSA hdsaIntEvents;
hdsaIntEvents = DPA_FastGetPtr(s_fsn.hdpaIntEvents, j);
for ( i = DSA_GetItemCount(hdsaIntEvents) - 1 ; i >= 0 ; i-- ) {
lpfsie = DSA_GetItemPtr(hdsaIntEvents, i);
#ifdef FSNIDEBUG
{
TCHAR szPath[MAX_PATH];
SHGetPathFromIDList(lpfsie->pidl, szPath);
DebugMsg(DM_TRACE, TEXT("FSNOTIFY: comparing against: %s"), szPath);
}
#endif
// check for immediate parent. no recursive FindFirstChangeNotify allowed.
if (ILIsParent(lpfsie->pidl, pidl, TRUE)
|| ILIsEqual(lpfsie->pidl, pidl)) {
#ifdef FSNIDEBUG
{
TCHAR szPath[MAX_PATH];
DebugMsg(DM_TRACE, TEXT("FSNOTIFY: RemoveEvent found: %x"), lpfsie);
SHGetPathFromIDList(lpfsie->pidl, szPath);
DebugMsg(DM_TRACE, TEXT("FSNOTIFY: removing: %s %d"), szPath, lpfsie->iCount);
}
#endif
lpfsie->iCount--;
if (lpfsie->iCount == 0) {
ILGlobalFree((LPITEMIDLIST)lpfsie->pidl);
DSA_DeleteItem(hdsaIntEvents, i);
}
}
}
}
FSNLEAVECRITICAL;
}
void FSNFlushInterruptEvents()
{
LPFSIntEvent lpfsie;
int i;
if (!sp_fsn.hdsaIntEvents)
return;
FSNENTERCRITICAL;
for (i = DSA_GetItemCount(sp_fsn.hdsaIntEvents) - 1; i >= 0; i--) {
lpfsie = DSA_GetItemPtr(sp_fsn.hdsaIntEvents, i);
SHChangeNotifyReceive(SHCNE_INTERRUPT | SHCNE_UPDATEDIR, SHCNF_IDLIST, lpfsie->pidl, NULL);
ILGlobalFree((LPITEMIDLIST)lpfsie->pidl);
}
DSA_DeleteAllItems(sp_fsn.hdsaIntEvents);
FSNLEAVECRITICAL;
}
LPFSIntClient FSNFindInterruptClient(LPCITEMIDLIST pidl, LPINT lpi)
{
// assumes caller has checked that sp_fsn.hdsaIntClients exists
// and has already grabbed the semaphore
int i;
// REVIEW: Chee, should we assert or simply return NULL?
if (!pidl) {
Assert(0);
return (NULL);
}
for ( i = DSA_GetItemCount(sp_fsn.hdsaIntClients) - 1 ; i >= 0 ; i-- ) {
LPFSIntClient lpfsic;
lpfsic = DSA_GetItemPtr(sp_fsn.hdsaIntClients, i);
if (ILIsEqual(lpfsic->pidl, pidl)) {
if (lpi)
*lpi = i;
return lpfsic;
}
}
return NULL;
}
void _FSN_RemoveAwakeThread(int i)
{
AWAKETHREAD *pAwake = DSA_GetItemPtr(s_fsn.hdsaThreadAwake, i);
CloseHandle(pAwake->hThread);
DSA_DeleteItem(s_fsn.hdsaThreadAwake, i);
if (DSA_GetItemCount(s_fsn.hdsaThreadAwake) == 0)
{
DSA_Destroy(s_fsn.hdsaThreadAwake);
s_fsn.hdsaThreadAwake = NULL;
}
}
void _FSN_SetEvents(void)
{
int i;
if (!s_fsn.hdsaThreadAwake)
{
return;
}
for (i=DSA_GetItemCount(s_fsn.hdsaThreadAwake)-1; i>=0; --i)
{
AWAKETHREAD *pAwake = DSA_GetItemPtr(s_fsn.hdsaThreadAwake, i);
if (WaitForSingleObject(pAwake->hThread, 0) == WAIT_TIMEOUT)
{
WakeThread(pAwake->idThread);
}
else
{
// The process associated with this thread must have
// died unnaturally
_FSN_RemoveAwakeThread(i);
}
}
}
void FSNAddInterruptClient(LPCITEMIDLIST pidl)
{
FSIntClient fsic;
LPFSIntClient lpfsic;
int i;
FSNENTERCRITICAL;
if (!sp_fsn.hdsaIntClients) {
if (!(sp_fsn.hdsaIntClients = DSA_Create(SIZEOF(FSIntClient), 4)))
goto Punt;
}
lpfsic = FSNFindInterruptClient(pidl, NULL);
// if we can't find the item, add it.
if ( !lpfsic ) {
fsic.pidl = ILGlobalClone(pidl);
fsic.iCount = 0;
// set this to null so that we'll build it on our global thread
fsic.hEvent = NULL;
i = DSA_InsertItem(sp_fsn.hdsaIntClients, 0x7FFF, &fsic);
if (i != -1) {
lpfsic = DSA_GetItemPtr(sp_fsn.hdsaIntClients, i);
}
}
if (lpfsic) {
lpfsic->iCount++;
// We only need to wake up our thread, not all
WakeThread(sp_fsn.idtRunning); // set the event so that we'll redo the waitformultipleobjects
}
Punt:
FSNLEAVECRITICAL;
}
void FSNRemoveInterruptClient(LPCITEMIDLIST pidl)
{
LPFSIntClient lpfsic;
int i;
if (!sp_fsn.hdsaIntClients)
return;
// REVIEW: Chee, should we assert or simply return?
if (!pidl) {
Assert(0);
return;
}
FSNENTERCRITICAL;
lpfsic = FSNFindInterruptClient(pidl, &i);
if (lpfsic) {
lpfsic->iCount--;
// rely on the next BuildEventList to remove it.
// this keeps us from nuking an active event.
// We only need to wake up our thread, not all
WakeThread(sp_fsn.idtRunning);
}
FSNLEAVECRITICAL;
}
void FSEventRelease(FSNotifyEvent *pfsnevt)
{
#ifdef FSNDEBUG
DebugMsg(DM_TRACE, TEXT("****FSEventRelease of %x called with count %d %s"), pfsnevt, pfsnevt->cRef, (pfsnevt->cRef - 1) ? TEXT("") : TEXT("RELEASING!!!!!"));
#endif
Assert(pfsnevt->cRef > 0);
pfsnevt->cRef--;
if (!pfsnevt->cRef) {
Free(pfsnevt);
}
}
// create an FSNotifyEvent.. but do NOT set the cRef
FSNotifyEvent *FSNAllocEvent(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
{
int cbPidlOrig = ILGetSize(pidl);
int cbPidl = (cbPidlOrig + 3) & ~(0x0000003);
int cbPidlExtra = pidlExtra ? ILGetSize(pidlExtra) : 0;
FSNotifyEvent *pfsnevt = Alloc(cbPidl + cbPidlExtra + SIZEOF(FSNotifyEvent));
if (pfsnevt)
{
pfsnevt->cRef = 1;
if (pidl)
{
pfsnevt->pidl = (LPITEMIDLIST)(pfsnevt + 1);
CopyMemory(pfsnevt->pidl, pidl, cbPidlOrig);
if (pidlExtra)
{
pfsnevt->pidlExtra = (LPITEMIDLIST)(((LPBYTE)pfsnevt->pidl) + cbPidl);
CopyMemory(pfsnevt->pidlExtra, pidlExtra, cbPidlExtra);
}
}
pfsnevt->lEvent = lEvent;
#ifdef FSNIDEBUG
if (lEvent & SHCNE_UPDATEDIR) {
Assert(0);
}
#endif
}
return pfsnevt;
}
ULONG SHChangeNotification_Release(HANDLE hChangeNotification, DWORD dwProcId)
{
LPSHChangeNotification pshcn;
ULONG tmp;
pshcn = SHLockShared(hChangeNotification,dwProcId);
if (!pshcn)
return 0;
Assert(pshcn->cRef > 0);
tmp = pshcn->cRef;
if (0 == InterlockedDecrement((LONG*)&pshcn->cRef))
{
SHUnlockShared(pshcn);
SHFreeShared(hChangeNotification,dwProcId);
return 0;
}
else
{
SHUnlockShared(pshcn);
return tmp - 1;
}
}
HANDLE SHChangeNotification_Create(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidlMain, LPCITEMIDLIST pidlExtra, DWORD dwProcId)
{
LPBYTE lpb;
UINT cbPidlOrig;
UINT cbPidl;
UINT cbPidlExtra;
DWORD dwSize;
HANDLE hChangeNotification;
LPSHChangeNotification pshcn;
cbPidlOrig = ILGetSize(pidlMain);
cbPidlExtra = pidlExtra ? ILGetSize(pidlExtra) : 0;
cbPidl = (cbPidlOrig + 3) & ~(0x0000003); // Round up to dword size
dwSize = SIZEOF(SHChangeNotification) + cbPidl + cbPidlExtra;
hChangeNotification = SHAllocShared(NULL,dwSize,dwProcId);
if (!hChangeNotification)
return (HANDLE)NULL;
pshcn = SHLockShared(hChangeNotification,dwProcId);
if (!pshcn)
{
SHFreeShared(hChangeNotification,dwProcId);
return (HANDLE)NULL;
}
pshcn->dwSize = dwSize;
pshcn->lEvent = lEvent;
pshcn->uFlags = uFlags;
pshcn->cRef = 1;
lpb = (LPBYTE)(pshcn+1);
pshcn->uidlMain = lpb - (LPBYTE)pshcn;
CopyMemory(lpb, pidlMain, cbPidlOrig);
lpb += cbPidl;
if (pidlExtra)
{
pshcn->uidlExtra = lpb - (LPBYTE)pshcn;
CopyMemory(lpb, pidlExtra, cbPidlExtra);
}
SHUnlockShared(pshcn);
return hChangeNotification;
}
#define SHCNL_SIG 0xbabababa
LPSHChangeNotificationLock SHChangeNotification_Lock(HANDLE hChangeNotification, DWORD dwProcId, LPITEMIDLIST **pppidl, LONG *plEvent)
{
LPSHChangeNotificationLock pshcnl;
LPSHChangeNotification pshcn;
pshcn = SHLockShared(hChangeNotification,dwProcId);
if (!pshcn)
return NULL;
// BUGBUG - Bobday - We could alloc this structure on the calling functions stack for faster execution
pshcnl = (LPSHChangeNotificationLock)LocalAlloc(LPTR, SIZEOF(SHChangeNotificationLock));
if (!pshcnl)
{
SHUnlockShared(pshcn);
return NULL;
}
pshcnl->pshcn = pshcn;
#ifdef DEBUG
pshcnl->dwSignature = SHCNL_SIG;
#endif
pshcnl->pidlMain = NULL;
pshcnl->pidlExtra = NULL;
if (pshcn->uidlMain)
pshcnl->pidlMain = (LPITEMIDLIST)((LPBYTE)pshcn + pshcn->uidlMain);
if (pshcn->uidlExtra)
pshcnl->pidlExtra = (LPITEMIDLIST)((LPBYTE)pshcn + pshcn->uidlExtra);
//
// Give back some easy values (causes less code to change for now)
//
if (pppidl)
*pppidl = (LPITEMIDLIST *)&(pshcnl->pidlMain);
if (plEvent)
*plEvent = pshcnl->pshcn->lEvent;
return pshcnl;
}
BOOL SHChangeNotification_Unlock(LPSHChangeNotificationLock pshcnl)
{
Assert(pshcnl->dwSignature == SHCNL_SIG);
SHUnlockShared(pshcnl->pshcn);
return (LocalFree(pshcnl) == NULL);
}
//---------------------------------------------------------------------------
//
void _SHChangeNotifyEmptyEventsList(HDPA hdpaEvents)
{
FSNotifyEvent *pfsnevt;
int iCount = DPA_GetPtrCount(hdpaEvents);
while (iCount--) {
pfsnevt= (FSNotifyEvent *)DPA_FastGetPtr(hdpaEvents, iCount);
FSEventRelease(pfsnevt);
}
DPA_DeleteAllPtrs(hdpaEvents);
}
//
// Simple function to try to make the FSNOTIFY code properly handle the SCHCNE_UPDATEDIR
// when some objects in parents or sibling folders are changed also...
//
void _StripPidlToCommonParent(LPITEMIDLIST pidl1, LPITEMIDLIST pidl2, LPCITEMIDLIST pidlRoot)
{
// This function is not foolproof, but it should work in 99 percent
// of the time as the notifications are generated from the same place...
//
if (!pidl1 || !pidl2)
return;
if (pidlRoot)
{
// Make sure we are at least to where the root of the watch was from...
while (!ILIsEmpty(pidlRoot))
{
pidlRoot = _ILNext(pidlRoot);
pidl1 = _ILNext(pidl1);
pidl2 = _ILNext(pidl2);
}
}
for (;!ILIsEmpty(pidl1); pidl1 = _ILNext(pidl1), pidl2 = _ILNext(pidl2))
{
// If they are not equal truncate pidl1 at this point...
if ((pidl1->mkid.cb != pidl2->mkid.cb) ||
(memcmp(pidl1, pidl2, pidl1->mkid.cb) != 0))
{
pidl1->mkid.cb = 0; // Truncate the rest off of the pidl...
DebugMsg(DM_TRACE, TEXT("sh TR - updated pidl for FSCNE_UPDATEDIR"));
return;
}
}
}
//
// returns: TRUE if we are going to do an UPDATEDIR
// FALSE otherwise
//
BOOL _SHChangeNotifyAddEventToHDPA(FSNotifyClientInfo * pfsnci,
FSNotifyEvent *pfsnevt, BOOL fAllowCollapse,
LPCITEMIDLIST pidlRoot)
{
BOOL fUpdateClock = FALSE;
HDPA hdpaEvents;
BOOL fRelease = FALSE;
BOOL bUpdateDir = FALSE;
FSNENTERCRITICAL;
// We need to make sure HandleEvents does not attempt to flush this
// HDSA while we are adding things to it
hdpaEvents = pfsnci->hdpaPendingEvents;
if ((pfsnevt->lEvent & SHCNE_DISKEVENTS) && fAllowCollapse)
{
//
// BUGBUG: Is UpdateDir considered general enough for use by non
// DISK events?!?
//
int iCount = DPA_GetPtrCount(hdpaEvents);
if (iCount > 0) {
FSNotifyEvent *pfsnevtOld;
pfsnevtOld = DPA_FastGetPtr(hdpaEvents, iCount - 1);
if (pfsnevtOld->lEvent == SHCNE_UPDATEDIR) {
//
// If we already have an unprocessed global update message in
// the queue there is no point adding new messages.
//
if (pidlRoot)
{
// We are a recursive type of notify client. We need to deal with
// cases where we have an SHCNE_UPDATEDIR in the queue and we get a new
// update for an entry which is a directory which is in a directory who
// is either higher up or a sibling to the update dir...
_StripPidlToCommonParent((LPITEMIDLIST)pfsnevtOld->pidl,
(LPITEMIDLIST)pfsnevt->pidl, pidlRoot);
}
bUpdateDir = TRUE;
fUpdateClock = TRUE;
} else if (iCount >= MAX_EVENT_COUNT
|| pfsnevt->lEvent==SHCNE_UPDATEDIR)
{
//
// If we get too many messages in the queue at any given time,
// tack on an updatedir to prevent any more from coming in.
//
pfsnevt = FSNAllocEvent(SHCNE_UPDATEDIR, pfsnevt->pidl, NULL);
if (pfsnevt) {
bUpdateDir = TRUE;
fRelease = TRUE;
ILRemoveLastID((LPITEMIDLIST)pfsnevt->pidl);
if (pidlRoot) {
// We should strip this pidl back to the highest level item
// that was changed...
while(iCount){
pfsnevtOld = DPA_FastGetPtr(hdpaEvents, --iCount);
if (pfsnevtOld->lEvent & SHCNE_DISKEVENTS)
_StripPidlToCommonParent((LPITEMIDLIST)pfsnevt->pidl,
(LPITEMIDLIST)pfsnevtOld->pidl, pidlRoot);
}
}
//
// This should be a full recursive upate, so put the
// same path in pidlExtra (which is the flag for
// doing full recursive UPDATEDIRs).
//
pfsnevt->pidlExtra = ILClone( pfsnevt->pidl );
}
}
}
}
//
// If fUpdateClock is already set it means that we had a redundant
// message and don't want to process it.
//
if (!fUpdateClock && pfsnevt)
{
#ifdef FSNDEBUG
DebugMsg(DM_TRACE, TEXT("****FSNotify: DPA_InsertPtr for hwnd %x hdpa = %x, pfsnevt = %x"), pfsnci->hwnd, hdpaEvents, pfsnevt);
#endif
if (DPA_InsertPtr(hdpaEvents, 0x7FFF, pfsnevt) != -1)
{
pfsnevt->cRef++;
fUpdateClock = TRUE;
}
}
if (fRelease) {
FSEventRelease(pfsnevt);
}
if (fUpdateClock)
{
// We always need to set the events in case the client that cares about
// this event is in another process.
s_fsn.dwLastEvent = GetCurrentTime();
_FSN_SetEvents();
}
FSNLEAVECRITICAL;
return(bUpdateDir);
}
//
// returns: TRUE if every (receiving) client does an UPDATEDIR
// FALSE otherwise
//
BOOL _SHChangeNotifyAddEventToClientQueues(LONG lEvent,
LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
{
FSNotifyClientInfo * pfsnci;
FSNotifyEvent *pfsnevt = NULL;
BOOL bOnlyUpdateDirs = TRUE;
if (!s_fsn.pfsnciFirst)
return bOnlyUpdateDirs;
FSNENTERCRITICAL;
// Don't let clients get deleted out of the DSA; we'll just mark them
// as "deletion pending" and delete them when handling events
++s_fsn.cRefClientList;
FSNLEAVECRITICAL;
//
// Add this event to all the relevant queues.
//
for (pfsnci = s_fsn.pfsnciFirst; pfsnci; pfsnci=pfsnci->pfsnciNext)
{
int iEntry;
BOOL fShouldAdd = FALSE;
LPCITEMIDLIST pidlRoot=NULL;
BOOL fAllowCollapse;
if (pfsnci->iSerializationFlags & FSSF_DELETE_ME)
{
// No use adding events to deleted clients
continue;
}
if (lEvent & SHCNE_INTERRUPT)
{
if (!(pfsnci->fSources & SHCNRF_InterruptLevel))
{
//
// This event was generated by an interrupt, and the
// client has interrupt notification turned off, so
// we skip it.
//
continue;
}
}
else if (!(pfsnci->fSources & SHCNRF_ShellLevel))
{
//
// This event was generated by the shell, and the
// client has shell notification turned off, so
// we skip it.
//
continue;
}
//
// If this client is not interested in the event, skip to next client.
//
if (!(pfsnci->fEvents & lEvent))
continue;
for (iEntry = 0; !fShouldAdd && (iEntry < DSA_GetItemCount(pfsnci->hdsaNE)); iEntry++)
{
SHChangeNotifyEntry *pfsne = DSA_GetItemPtr(pfsnci->hdsaNE, iEntry);
// Check if this is a global notify.
if (pfsne->pidl && ((lEvent & SHCNE_GLOBALEVENTS) == 0))
{
fAllowCollapse = TRUE;
// No, we need to filter out unrelated events.
if (pfsne->fRecursive)
{
//
// This case treats directory entries with the recursive
// bit set.
//
if ((ILIsParent(pfsne->pidl, pidl, FALSE) ||
(pidlExtra && ILIsParent(pfsne->pidl, pidlExtra, FALSE))))
{
fShouldAdd = TRUE;
pidlRoot = pfsne->pidl; // Pass through this as the root...
}
}
else
{
//
// This case treats file and non recursive directory
// entries.
//
// We should not send SHCNE_RENAMEFOLDER/UPDATEDIR/SHCNE_RMDIR,
// otherwise it might trigger the re-enumeration on
// invalid folder.
//
if (((ILIsEqual(pfsne->pidl, pidl)
|| (pidlExtra && ILIsEqual(pfsne->pidl, pidlExtra)))
&& !(lEvent&(SHCNE_RENAMEFOLDER|SHCNE_RMDIR)))
|| ILIsParent(pfsne->pidl, pidl, TRUE)
|| (pidlExtra && ILIsParent(pfsne->pidl, pidlExtra, TRUE)))
{
fShouldAdd = TRUE;
}
}
} else {
fAllowCollapse = FALSE;
fShouldAdd = TRUE;
}// End if (pfsne->pidl)
} // End per-entry event loop
// do this after the event loop so that we don't
// add events multiple times (as in the case of a rename)
if (fShouldAdd) {
if (!pfsnevt) {
// make the event struct
pfsnevt = FSNAllocEvent(lEvent, pidl, pidlExtra);
if (!pfsnevt) // out of memory... bail!
return bOnlyUpdateDirs;
pfsnevt->lEvent = lEvent & ~SHCNE_INTERRUPT; //Strip off this flag.
}
if (!_SHChangeNotifyAddEventToHDPA(pfsnci, pfsnevt, fAllowCollapse, pidlRoot))
{
bOnlyUpdateDirs = FALSE;
}
}
} // End client loop
if (pfsnevt)
FSEventRelease(pfsnevt);
// Note no critical section
--s_fsn.cRefClientList;
return bOnlyUpdateDirs;
}
BOOL IsILShared(LPCITEMIDLIST pidl, BOOL fUpdateCache)
{
TCHAR szTemp[MAXPATHLEN];
SHGetPathFromIDList(pidl, szTemp);
return IsShared(szTemp, fUpdateCache);
}
void NotifyShellInternals(LONG lEvent, LPCITEMIDLIST pidl,
LPCITEMIDLIST pidlExtra)
{
PERFTEST(DESKTOP_EVENT) CDesktop_FSEvent(lEvent, pidl, pidlExtra);
PERFTEST(RLFS_EVENT) RLFSChanged(lEvent, (LPITEMIDLIST)pidl, (LPITEMIDLIST)pidlExtra);
PERFTEST(SFP_EVENT) SFP_FSEvent(lEvent, pidl, pidlExtra);
PERFTEST(LINK_EVENT) Link_FSEvent(lEvent, pidl, pidlExtra);
PERFTEST(ICON_EVENT) Icon_FSEvent(lEvent, pidl, pidlExtra);
}
#define MSEC_GUIMAXWAIT 2000 // Maximum wait from GUI thread
#define MSEC_GUIEVTWAIT 20 // dwTimeOut for WaitForMultipleObjects
#define MSEC_GUISLEEP 20 // Time to sleep when a GUI thread got a event.
//
// Wait until the FSThread finish processing all the events. The GUI threads
// need to call this function from within GenerateEvents().
//
void _WaitFSThreadProcessEvents(void)
{
HANDLE hEvents[MAXIMUM_WAIT_OBJECTS];
ULONG cEvents;
DWORD dwTick = GetCurrentTime();
//
// This function must not be called by the FSNotify thread.
//
if (sp_fsn.idtRunning == GetCurrentThreadId())
{
DebugMsg(DM_ERROR, TEXT("sh ERROR - FSNotify threads called FS_GenerateEvents!"));
Assert(0);
return;
}
//
// Get the same list of events the FSNotify thread waits for.
//
FSNENTERCRITICAL;
cEvents = (ULONG)FSNBuildEventList(hEvents);
FSNLEAVECRITICAL;
//
// We continue until either
// (1) all the events are cleared by FSNotify threads, or
// (2) we wait it more than 2 (MSEC_GUIMAXWAIT/1000) second.
//
while(1)
{
DWORD dwWaitResult;
ULONG iEvent;
//
// Wait for same sets of objects as the FSNotify thread waits for.
//
dwWaitResult = WaitForMultipleObjects(cEvents, hEvents, FALSE, 0);
//
// Check if we are signaled or not.
//
iEvent = dwWaitResult-WAIT_OBJECT_0;
if (iEvent<cEvents)
{
//
// Yes, we are signaled (probably by our own previous FS call).
// Sleep (to give the FSNotify thread to process it) and wait.
//
Sleep(MSEC_GUISLEEP);
if (GetCurrentTime()-dwTick > MSEC_GUIMAXWAIT)
{
DebugMsg(DM_WARNING, TEXT("sh TR - _WaitFSTPE timeout (FSNotify thread might be dead/blocked)"));
Assert(0); // FSNotify thread might be dead!
break;
}
}
else
{
//
// No, WaitForMultipleObject failed. We can assume that FSNotify
// thread finished processing all the interrupt events.
//
if (dwWaitResult == 0xffffffff)
{
DebugMsg(DM_WARNING, TEXT("sh TR - _WaitFSTPE failed (%x)"), GetLastError());
}
if (dwWaitResult != WAIT_TIMEOUT)
{
DebugMsg(DM_WARNING, TEXT("sh TR - _WaitFSTPE strange result (%x)"), dwWaitResult);
}
break;
}
}
}
void FreeSpacePidlToPath(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
TCHAR szPath1[MAX_PATH];
TCHAR szPath2[MAX_PATH];
if (SHGetPathFromIDList(pidl1, szPath1)) {
szPath2[0] = 0;
if (pidl2) {
if (!SHGetPathFromIDList(pidl2, szPath2)) {
szPath2[0] = 0;
}
}
SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPath1, szPath2[0] ? szPath2 : NULL);
}
}
void SHChangeNotifyReceive(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
{
BOOL bOnlyUpdateDirs;
/// now do the actual generating of the event
if (lEvent & (SHCNE_NETSHARE | SHCNE_NETUNSHARE))
{
// Update the cache.
IsILShared(pidl, TRUE);
}
#ifdef FSNDEBUG
DebugMsg(DM_TRACE, TEXT("SHChangeNotifyGenreateEvent 0x%X"), lEvent);
DebugDumpPidl(TEXT("SHChangeNotifyGenerateEvent"), pidl);
#endif
if (lEvent)
bOnlyUpdateDirs = _SHChangeNotifyAddEventToClientQueues(lEvent, pidl, pidlExtra);
// remove any shell generated events for the file system
if ((lEvent & SHCNE_DISKEVENTS) &&
!(lEvent & (SHCNE_INTERRUPT | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM))) {
if (!bOnlyUpdateDirs)
{
// No use waiting for events to come through if everybody is just
// doing an updatedir anyway. Note that we will still clear out
// as amny int events as we can so that we do not fill up that
// queue
_WaitFSThreadProcessEvents();
}
FSNRemoveInterruptEvent(pidl);
if (lEvent & (SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM)) {
FSNRemoveInterruptEvent(pidlExtra);
}
}
//
// note make sure the internal events go first.
//
// unless the nonotifyinteranls flag is set meaning that this was created
// by ultroot's desktop relayer (CDesktop_FSEvent), in which case don't bother
// checking everything again
if (lEvent && (!(uFlags & SHCNF_NONOTIFYINTERNALS)))
NotifyShellInternals(lEvent, pidl, pidlExtra);
//
// then the registered events
//
if (uFlags & (SHCNF_FLUSH)) {
if (uFlags & (SHCNF_FLUSHNOWAIT)) {
sp_fsn.fFlushNow = TRUE;
WakeThread(sp_fsn.idtRunning);
} else
_SHChangeNotifyHandleEvents(FALSE);
}
}
void SHChangeNotifyTransmit(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
{
HWND hwndDesktop;
hwndDesktop = GetShellWindow();
if (hwndDesktop)
{
HANDLE hChangeNotification;
DWORD dwProcId;
GetWindowThreadProcessId(hwndDesktop, &dwProcId);
hChangeNotification = SHChangeNotification_Create(lEvent, uFlags, pidl, pidlExtra, dwProcId);
if (!hChangeNotification)
return;
// Flush but not flush no wait
if ((uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH)
{
SendMessage(hwndDesktop, CWM_FSNOTIFY,
(WPARAM)hChangeNotification, (LPARAM)dwProcId);
}
else
{
SendNotifyMessage(hwndDesktop, CWM_FSNOTIFY,
(WPARAM)hChangeNotification, (LPARAM)dwProcId);
}
}
else
{
SHChangeNotifyReceive(lEvent, uFlags, pidl, pidlExtra);
}
}
void WINAPI SHChangeNotify(LONG lEvent, UINT uFlags, const void * dwItem1, const void * dwItem2)
{
LPCITEMIDLIST pidl = NULL;
LPCITEMIDLIST pidlExtra = NULL;
LPITEMIDLIST pidlFree = NULL;
LPITEMIDLIST pidlExtraFree = NULL;
UINT uType = uFlags & SHCNF_TYPE;
SHChangeDWORDAsIDList dwidl;
BOOL fPrinter = FALSE;
BOOL fPrintJob = FALSE;
// first setup anything the flags request
switch (uType)
{
#ifdef UNICODE
case SHCNF_PRINTJOBA:
fPrintJob = TRUE;
case SHCNF_PRINTERA:
fPrinter = TRUE;
case SHCNF_PATHA:
#else
case SHCNF_PRINTJOBW:
fPrintJob = TRUE;
case SHCNF_PRINTERW:
fPrinter = TRUE;
case SHCNF_PATHW:
#endif
{
TCHAR szPath1[MAX_PATH];
TCHAR szPath2[MAX_PATH];
LPCVOID pvItem1 = NULL;
LPCVOID pvItem2 = NULL;
if (dwItem1)
{
#ifdef UNICODE
MultiByteToWideChar(CP_ACP,0,
(LPSTR)dwItem1, -1,
szPath1, ARRAYSIZE(szPath1));
#else
WideCharToMultiByte(CP_ACP, 0,
(LPWSTR)dwItem1, -1,
szPath1, ARRAYSIZE(szPath1),
NULL, NULL);
#endif
pvItem1 = szPath1;
}
if (dwItem2)
{
if (fPrintJob)
pvItem2 = dwItem2; // SHCNF_PRINTJOB_DATA needs no conversion
else
{
#ifdef UNICODE
MultiByteToWideChar(CP_ACP,0,
(LPSTR)dwItem2, -1,
szPath2, ARRAYSIZE(szPath2));
#else
WideCharToMultiByte(CP_ACP, 0,
(LPWSTR)dwItem2, -1,
szPath2, ARRAYSIZE(szPath2),
NULL, NULL);
#endif
pvItem2 = szPath2;
}
}
SHChangeNotify(lEvent,
(fPrintJob ? SHCNF_PRINTJOB :
(fPrinter ? SHCNF_PRINTER : SHCNF_PATH)),
pvItem1, pvItem2);
goto Cleanup; // Let the recursive version do all the work
}
break;
case SHCNF_PATH:
if (lEvent == SHCNE_FREESPACE) {
int idDrive;
DWORD dwItem;
idDrive = PathGetDriveNumber((LPCTSTR)dwItem1);
if (idDrive != -1)
dwItem = (1 << idDrive);
else
dwItem = 0;
if (dwItem2) {
idDrive = PathGetDriveNumber((LPCTSTR) dwItem2);
if (idDrive != -1)
dwItem |= (1 << idDrive);
}
dwItem1 = (LPCVOID)dwItem;
if (dwItem1) {
goto DoDWORD;
} else
goto Cleanup;
} else {
if (dwItem1)
{
pidl = pidlFree = SHSimpleIDListFromPath((LPCTSTR)dwItem1);
if (!pidl)
goto Cleanup;
if (dwItem2) {
pidlExtra = pidlExtraFree = SHSimpleIDListFromPath((LPCTSTR)dwItem2);
if (!pidlExtra)
goto Cleanup;
}
}
}
break;
case SHCNF_PRINTER:
if (dwItem1)
{
DebugMsg(DM_TRACE, TEXT("SHChangeNotify: SHCNF_PRINTER %s"), (LPTSTR)dwItem1);
pidl = pidlFree = Printers_GetPidl(NULL, (LPCTSTR)dwItem1);
if (!pidl)
goto Cleanup;
if (dwItem2)
{
pidlExtra = pidlExtraFree = Printers_GetPidl(NULL, (LPCTSTR)dwItem2);
if (!pidlExtra)
goto Cleanup;
}
}
break;
case SHCNF_PRINTJOB:
if (dwItem1)
{
#if 0
switch (lEvent)
{
case SHCNE_CREATE:
DebugMsg(DM_TRACE, TEXT("SHChangeNotify: SHCNE_CREATE SHCNF_PRINTJOB %s"), (LPTSTR)dwItem1);
break;
case SHCNE_DELETE:
DebugMsg(DM_TRACE, TEXT("SHChangeNotify: SHCNE_DELETE SHCNF_PRINTJOB %s"), (LPTSTR)dwItem1);
break;
case SHCNE_UPDATEITEM:
DebugMsg(DM_TRACE, TEXT("SHChangeNotify: SHCNE_UPDATEITEM SHCNF_PRINTJOB %s"), (LPTSTR)dwItem1);
break;
default:
DebugMsg(DM_TRACE, TEXT("SHChangeNotify: SHCNE_? SHCNF_PRINTJOB %s"), (LPTSTR)dwItem1);
break;
}
#endif
pidl = pidlFree = Printjob_GetPidl((LPCTSTR)dwItem1, (LPSHCNF_PRINTJOB_DATA)dwItem2);
if (!pidl)
goto Cleanup;
}
else
{
// Caller goofed.
goto Cleanup;
}
break;
case SHCNF_DWORD:
DoDWORD:
Assert(lEvent & SHCNE_GLOBALEVENTS);
dwidl.cb = SIZEOF(dwidl) - SIZEOF(dwidl.cbZero);
dwidl.dwItem1 = (DWORD)dwItem1;
dwidl.dwItem2 = (DWORD)dwItem2;
dwidl.cbZero = 0;
pidl = (LPCITEMIDLIST)&dwidl;
pidlExtra= NULL;
break;
case 0:
if (lEvent == SHCNE_FREESPACE) {
// convert this to paths.
FreeSpacePidlToPath((LPCITEMIDLIST)dwItem1, (LPCITEMIDLIST)dwItem2);
goto Cleanup;
}
pidl = (LPCITEMIDLIST)dwItem1;
pidlExtra = (LPCITEMIDLIST)dwItem2;
break;
default:
DebugMsg(DM_TRACE, TEXT("SHChangeNotify: Unrecognized uFlags 0x%X"), uFlags);
Assert(0);
return;
}
SHChangeNotifyTransmit(lEvent,uFlags,pidl,pidlExtra);
// now wait for all the callbacks to empty out
if ((uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH)
{
_FSN_WaitForCallbacks();
}
Cleanup:
if (pidlFree)
ILFree(pidlFree);
if (pidlExtraFree)
ILFree(pidlExtraFree);
}
//---------------------------------------------------------------------------
//
BOOL SHChangeNotifyInit()
{
return(TRUE);
}
//---------------------------------------------------------------------------
//
// REVIEW: We assume that other processes cannot interrupt us
//
void SHChangeNotifyTerminate(BOOL bLastTerm)
{
PFSNotifyClientInfo pfsnci;
int iEvent;
if (bLastTerm && s_fsn.pfsnciFirst)
{
#ifdef FSNotifyTest
// BUGBUG: test code
SHChangeNotifyDeregister(g_ulMyID);
#endif
for (pfsnci = s_fsn.pfsnciFirst; pfsnci; )
{
// Delete the client.
pfsnci = _SHChangeNotifyNukeClient(pfsnci, FALSE);
}
s_fsn.pfsnciFirst = NULL;
}
// free all the interrupt events
if (sp_fsn.hdsaIntEvents) {
LPFSIntEvent lpfsie;
for (iEvent = DSA_GetItemCount(sp_fsn.hdsaIntEvents) - 1; iEvent >= 0; iEvent--) {
lpfsie = DSA_GetItemPtr(sp_fsn.hdsaIntEvents, iEvent);
ILGlobalFree((LPITEMIDLIST)lpfsie->pidl);
}
FSNENTERCRITICAL;
for (iEvent=DPA_GetPtrCount(s_fsn.hdpaIntEvents)-1; iEvent>=0;
--iEvent)
{
if (DPA_FastGetPtr(s_fsn.hdpaIntEvents, iEvent)
== sp_fsn.hdsaIntEvents)
{
DPA_DeletePtr(s_fsn.hdpaIntEvents, iEvent);
}
}
if (DPA_GetPtrCount(s_fsn.hdpaIntEvents) == 0)
{
DPA_Destroy(s_fsn.hdpaIntEvents);
s_fsn.hdpaIntEvents = NULL;
}
FSNLEAVECRITICAL;
DSA_Destroy(sp_fsn.hdsaIntEvents);
sp_fsn.hdsaIntEvents = NULL;
}
// free all the interrupt clients
if (sp_fsn.hdsaIntClients) {
LPFSIntClient lpfsic;
for (iEvent = DSA_GetItemCount(sp_fsn.hdsaIntClients) - 1; iEvent >= 0; iEvent--) {
lpfsic = DSA_GetItemPtr(sp_fsn.hdsaIntClients, iEvent);
ILGlobalFree((LPITEMIDLIST)lpfsic->pidl);
if (lpfsic->hEvent)
FindCloseChangeNotification(lpfsic->hEvent);
}
DSA_Destroy(sp_fsn.hdsaIntClients);
sp_fsn.hdsaIntClients = NULL;
}
#ifdef FSNotifyTest
SHChangeNotifyDeregister(g_ulMyID);
#endif
}
void _Shell32ThreadAddRef(BOOL bEnterCrit)
{
if (bEnterCrit)
{
FSNENTERCRITICAL;
}
// Check if this is the first client from this process.
if (!sp_fsn.cclients)
{
// This thread will only get created once for each process that
// registers
DebugMsg(DM_TRACE, TEXT("SH:FSNotify creating new thread"));
if (!s_fsn.hdsaThreadAwake)
{
s_fsn.hdsaThreadAwake = DSA_Create(SIZEOF(AWAKETHREAD), 4);
// BUGBUG: I'm asserting this mostly because I don't
// know what to do if it fails
Assert(s_fsn.hdsaThreadAwake);
}
// Suspend the thread to start with so that our globals get
// set before they get checked
sp_fsn.htStarting = CreateThread(NULL, 0, Shell32ThreadProc, NULL,
CREATE_SUSPENDED, &sp_fsn.idtStarting);
Assert(sp_fsn.htStarting && !sp_fsn.htRunning);
ResumeThread(sp_fsn.htStarting);
}
else
{
// No, we should have the fsnotify thread already.
Assert(sp_fsn.htRunning || sp_fsn.htStarting);
}
sp_fsn.cclients++;
if (bEnterCrit)
{
FSNLEAVECRITICAL;
}
}
void _Shell32ThreadRelease(UINT nClients)
{
FSNENTERCRITICAL;
sp_fsn.cclients -= nClients;
// If we have no more clients, tell the FSNotify thread to kill itself
if (!sp_fsn.cclients)
{
HANDLE hThread;
DWORD idThread;
int i;
Assert(sp_fsn.htRunning || sp_fsn.htStarting);
DebugMsg(DM_TRACE, TEXT("sh TR - Telling FSNotify thread to kill itself"));
hThread = sp_fsn.htRunning;
idThread = sp_fsn.idtRunning;
if (hThread)
{
Assert(s_fsn.hdsaThreadAwake);
for (i=DSA_GetItemCount(s_fsn.hdsaThreadAwake)-1; i>=0; --i)
{
AWAKETHREAD *pAwake = DSA_GetItemPtr(s_fsn.hdsaThreadAwake, i);
if (pAwake->idThread == idThread)
{
_FSN_RemoveAwakeThread(i);
break;
}
}
// We should release the critical section to allow the thread
// to finish its processing
FSNLEAVECRITICAL;
// Check if the thread still exists
if (WaitForSingleObject(hThread, 0) == WAIT_TIMEOUT)
{
SignalKillThread(idThread);
if (WaitForSingleObject(hThread, 2000) == WAIT_TIMEOUT)
{
DebugMsg(DM_TRACE, TEXT("sh TR - FSNotify has not been killed"));
}
else
{
DebugMsg(DM_TRACE, TEXT("sh TR - FSNotify killed itself"));
}
}
CloseHandle(hThread);
FSNENTERCRITICAL;
// Make sure nobody has started a new thread while we were not
// looking
if (sp_fsn.idtRunning == idThread)
{
sp_fsn.htRunning = NULL; // Notes: no need to touch sp_fsn.idThread
sp_fsn.idtRunning = 0; // Notes: no need to touch sp_fsn.idThread
}
}
else
{
// We must not have initialized yet, and we know we are
// not in the critical section that touches the DSA,
// so kill the thread immediately
TerminateThread(sp_fsn.htStarting, 0);
CloseHandle(sp_fsn.htStarting);
sp_fsn.htStarting = NULL;
sp_fsn.idtStarting = 0;
}
}
FSNLEAVECRITICAL;
}
void _Shell32ThreadAwake(void)
{
if (sp_fsn.htRunning)
{
WakeThread(sp_fsn.idtRunning);
}
}
//--------------------------------------------------------------------------
// We changed the way that the SHChangeNotifyRegister function worked, so
// to prevent people from calling the old function, we stub it out here.
// The change we made would have broken everbody because we changed the
// lparam and wparam for the notification messages which are sent to the
// registered window.
//
ULONG WINAPI NTSHChangeNotifyRegister(HWND hwnd,
int fSources, LONG fEvents,
UINT wMsg, int cEntries,
SHChangeNotifyEntry *pfsne)
{
return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery, fEvents, wMsg, cEntries, pfsne);
}
BOOL WINAPI NTSHChangeNotifyDeregister(ULONG ulID)
{
return SHChangeNotifyDeregister(ulID);
}
//
// REVIEW: BobDay - SHChangeNotifyUpdateEntryList doesn't appear to be
// called by anybody and since we've change the notification message
// structure, anybody who calls it needs to be identified and fixed.
//
BOOL WINAPI SHChangeNotifyUpdateEntryList(ULONG ulID, int iUpdateType,
int cEntries, SHChangeNotifyEntry *pfsne)
{
Assert(FALSE);
return FALSE;
}
ULONG WINAPI SHChangeNotifyRegisterInternal(HWND hwnd, int fSources,
LONG fEvents, UINT wMsg, int cEntries,
SHChangeNotifyEntry *pfsne)
{
int i;
FSNotifyClientInfo fsnci;
PFSNotifyClientInfo pfsnci;
fsnci.hwnd = hwnd;
// GetWindowThreadProcessId(hwnd, &fsnci.dwProcID); BUGBUG BobDay - This is the old way
fsnci.dwProcID = GetCurrentProcessId();
fsnci.fSources = fSources;
fsnci.fEvents = fEvents;
fsnci.wMsg = wMsg;
fsnci.hdsaNE = DSA_Create(SIZEOF(SHChangeNotifyEntry), 4);
fsnci.hdpaPendingEvents = DPA_Create(4);
fsnci.iSerializationFlags = 0;
if (!fsnci.hdsaNE || !fsnci.hdpaPendingEvents)
{
DebugMsg(DM_ERROR, TEXT("Failed to alloc notify data"));
goto ErrorExit;
}
for (i = 0; i < cEntries; i++)
{
SHChangeNotifyEntry fsne;
// Check if this is global notification.
if (pfsne[i].pidl == NULL)
{
// Yes, put NULL in pszNotificatoinPath.
fsne.fRecursive = TRUE;
fsne.pidl = NULL;
}
else
{
// No, copy specified path and fRecursive flag.
fsne.fRecursive = pfsne[i].fRecursive;
fsne.pidl = ILGlobalClone(pfsne[i].pidl);
Assert(fsne.pidl);
if (!fsne.pidl)
goto ErrorExit;
}
if (DSA_InsertItem(fsnci.hdsaNE, i, &fsne) == -1)
{
ILGlobalFree((LPITEMIDLIST)fsne.pidl);
goto ErrorExit;
}
// set up the interrupt events if desired
if (fsne.pidl && (fSources & SHCNRF_InterruptLevel))
FSNAddInterruptClient(fsne.pidl);
}
FSNENTERCRITICAL;
{
//
// Skip ID 0, as this is our error value.
//
fsnci.ulID = s_fsn.ulNextID;
if (!++s_fsn.ulNextID)
s_fsn.ulNextID = 1;
//
// Don't want to party on the client list while I'm using it in
// SHChangeNotifyHandleEvents() because a Realloc() could move the whole
// damn thing in memory.
//
// Must alloc from shared memory pool
pfsnci = Alloc(SIZEOF(fsnci));
if (pfsnci)
{
// Move the inforamion into our newly allocated structure.
// and link to the head of the list.
*pfsnci = fsnci;
pfsnci->pfsnciNext = s_fsn.pfsnciFirst;
s_fsn.pfsnciFirst = pfsnci;
_Shell32ThreadAddRef(FALSE);
}
}
FSNLEAVECRITICAL;
if (pfsnci)
return fsnci.ulID;
ErrorExit:
if (fsnci.hdsaNE)
{
//
// If the memory allocation fails, free up everything
// allocated to date and return error.
//
for (i = DSA_GetItemCount(fsnci.hdsaNE) - 1; i >= 0; i--)
{
SHChangeNotifyEntry * pfsne = DSA_GetItemPtr(fsnci.hdsaNE, i);
ILGlobalFree((LPITEMIDLIST)pfsne->pidl);
}
// Finally destroy the entries.
DSA_Destroy(fsnci.hdsaNE);
}
// And if the pending events then destroy it also.
if (fsnci.hdpaPendingEvents)
DPA_Destroy(fsnci.hdpaPendingEvents);
return 0;
}
//--------------------------------------------------------------------------
//
FSNotifyClientInfo * _GetNotificationClientFromID(ULONG ulID)
{
register FSNotifyClientInfo * pfsnci;
//
// Locate the given client within the list.
//
FSNENTERCRITICAL;
for (pfsnci = s_fsn.pfsnciFirst; pfsnci; pfsnci = pfsnci->pfsnciNext)
{
if (pfsnci->ulID == ulID)
{
break;
}
}
FSNLEAVECRITICAL;
return(pfsnci);
}
//--------------------------------------------------------------------------
//
PFSNotifyClientInfo _SHChangeNotifyNukeClient(PFSNotifyClientInfo pfsnci, BOOL fNukeInterrupts)
{
int iEntry;
PFSNotifyClientInfo pfsnciT;
DebugMsg(DM_TRACE, TEXT("SH:Deleting client %lx"), (ULONG)pfsnci->ulID);
Assert(s_fsn.cRefClientList == 0);
// Unlink this item from the linked list of items...
FSNENTERCRITICAL;
if (s_fsn.pfsnciFirst == pfsnci)
s_fsn.pfsnciFirst = pfsnci->pfsnciNext;
else
{
for (pfsnciT = s_fsn.pfsnciFirst; pfsnciT; pfsnciT = pfsnciT->pfsnciNext)
{
if (pfsnciT->pfsnciNext == pfsnci)
{
// Found the one we are looking for...
pfsnciT->pfsnciNext = pfsnci->pfsnciNext;
break;
}
}
}
FSNLEAVECRITICAL;
// nuke any pending events
if (pfsnci->hdpaPendingEvents)
{
_SHChangeNotifyEmptyEventsList(pfsnci->hdpaPendingEvents);
DPA_Destroy(pfsnci->hdpaPendingEvents);
pfsnci->hdpaPendingEvents = NULL;
}
for (iEntry = DSA_GetItemCount(pfsnci->hdsaNE) - 1; iEntry >= 0; iEntry--)
{
SHChangeNotifyEntry *pfsne = DSA_GetItemPtr(pfsnci->hdsaNE, iEntry);
if (fNukeInterrupts && pfsne->pidl && (pfsnci->fSources & SHCNRF_InterruptLevel))
{
FSNRemoveInterruptClient(pfsne->pidl);
}
ILGlobalFree((LPITEMIDLIST)pfsne->pidl);
// Don't need to delete items; the destory below will take care of it.
}
DSA_Destroy(pfsnci->hdsaNE);
// We unlinked ourself earlier so now just free the memory.
pfsnciT = pfsnci->pfsnciNext;
if (!(pfsnci->fSources & SHCNRF_NewDelivery))
{
PostMessage(pfsnci->hwnd, WM_CLOSE, 0, 0); // Tell other process to destroy window
}
Free(pfsnci);
return pfsnciT;
}
// this deregisters anything that this window might have been registered in
void WINAPI SHChangeNotifyDeregisterWindow(HWND hwnd)
{
int nClients;
PFSNotifyClientInfo pfsnci;
FSNENTERCRITICAL;
nClients = 0;
// This is always a bit tricky as if we delete an item we need to
// start the next one off at the right place.
for (pfsnci = s_fsn.pfsnciFirst; pfsnci; )
{
if (pfsnci->hwnd == hwnd)
{
if (s_fsn.cRefClientList)
{
// Can't delete this yet, let SHChangeNotifyHandleEvents() do it.
pfsnci->iSerializationFlags = FSSF_DELETE_ME;
pfsnci = pfsnci->pfsnciNext;
}
else
{
// Stomp it. - It returns the pointer to the next
// item in the list...
pfsnci = _SHChangeNotifyNukeClient(pfsnci, TRUE);
}
++nClients;
}
else
pfsnci = pfsnci->pfsnciNext;
}
FSNLEAVECRITICAL;
_Shell32ThreadRelease(nClients);
}
//--------------------------------------------------------------------------
//
// Returns TRUE if we found and removed the specified Client, otherwise
// returns FALSE.
//
BOOL WINAPI SHChangeNotifyDeregisterInternal(ULONG ulID)
{
BOOL fRetval = TRUE;
//
// If the client was found, free up its heap data, and then remove
// it from the list.
//
FSNENTERCRITICAL;
{
FSNotifyClientInfo *pfsnci = _GetNotificationClientFromID(ulID);
if (!pfsnci)
{
Assert(FALSE);
fRetval = FALSE;
}
else
{
if (s_fsn.cRefClientList)
{
// Can't delete this yet, let SHChangeNotifyHandleEvents() do it.
pfsnci->iSerializationFlags = FSSF_DELETE_ME;
}
else
{
// Stomp it.
_SHChangeNotifyNukeClient(pfsnci, TRUE);
}
}
}
FSNLEAVECRITICAL;
_Shell32ThreadRelease(1);
return fRetval;
}
BOOL WINAPI SHChangeRegistrationReceive(HANDLE hChangeRegistration, DWORD dwProcId)
{
LPSHChangeRegistration pshcr;
BOOL fResult = FALSE;
pshcr = SHLockShared(hChangeRegistration, dwProcId);
if (pshcr)
{
switch(pshcr->uCmd)
{
case SHCR_CMD_REGISTER:
{
SHChangeNotifyEntry fsne;
fsne.pidl = NULL;
fsne.fRecursive = pshcr->fRecursive;
if (pshcr->uidlRegister)
fsne.pidl = (LPITEMIDLIST)((LPBYTE)pshcr+pshcr->uidlRegister);
pshcr->ulID = SHChangeNotifyRegisterInternal(
pshcr->hwnd, pshcr->fSources,
pshcr->lEvents, pshcr->uMsg,
1, &fsne);
fResult = TRUE;
}
break;
case SHCR_CMD_DEREGISTER:
fResult = SHChangeNotifyDeregisterInternal(pshcr->ulID);
break;
default:
break;
}
SHUnlockShared(pshcr);
}
return fResult;
}
HANDLE SHChangeRegistration_Create( UINT uCmd, ULONG ulID,
HWND hwnd, UINT uMsg,
DWORD fSources, LONG lEvents,
BOOL fRecursive, LPCITEMIDLIST pidl,
DWORD dwProcId)
{
LPSHChangeRegistration pshcr;
HANDLE hChangeRegistration;
UINT uSize = SIZEOF(SHChangeRegistration);
UINT uidlSize = 0;
if (pidl)
uidlSize = ILGetSize(pidl);
hChangeRegistration = SHAllocShared(NULL, uSize+uidlSize, dwProcId);
if (!hChangeRegistration)
{
return (HANDLE)NULL;
}
pshcr = SHLockShared(hChangeRegistration,dwProcId);
if (!pshcr)
{
SHFreeShared(hChangeRegistration,dwProcId);
return (HANDLE)NULL;
}
pshcr->uCmd = uCmd;
pshcr->ulID = ulID;
pshcr->hwnd = hwnd;
pshcr->uMsg = uMsg;
pshcr->fSources = fSources;
pshcr->lEvents = lEvents;
pshcr->fRecursive = fRecursive;
pshcr->uidlRegister = 0;
if (pidl)
{
pshcr->uidlRegister = SIZEOF(SHChangeRegistration);
hmemcpy((LPVOID)(pshcr+1),pidl,uidlSize);
}
SHUnlockShared(pshcr);
return hChangeRegistration;
}
//--------------------------------------------------------------------------
//
// Returns a positive integer registration ID, or 0 if out of memory or if
// invalid parameters were passed in.
//
// If the hwnd is != NULL we do a PostMessage(hwnd, wMsg, ...) when a
// relevant FS event takes place, otherwise if fsncb is != NULL we call it.
//
ULONG WINAPI SHChangeNotifyRegister(HWND hwnd,
int fSources, LONG fEvents,
UINT wMsg, int cEntries,
SHChangeNotifyEntry *pfsne)
{
int i;
HWND hwndDesktop;
ULONG ulID = 0;
DWORD dwProcId;
HWND hwndProxy = NULL;
UINT wMsgIn = wMsg;
HWND hwndIn = hwnd;
Assert(pfsne);
dwProcId = GetCurrentProcessId();
hwndDesktop = GetShellWindow();
for (i = 0; i < cEntries; i++)
{
if (!(fSources & SHCNRF_NewDelivery))
{
// This is an old style notification, we need to create a hidden
// proxy type of window to properly handle the messages...
//
LP_NotifyProxyData pData;
_RegisterNotifyProxyWndProc( );
pData = (LP_NotifyProxyData) LocalAlloc( LPTR, SIZEOF( _NotifyProxyData ) );
if ( pData == NULL )
return 0;
pData->hwndParent = hwndIn;
pData->wMsg = wMsgIn;
hwndProxy = CreateWindow( c_szWindowClassName,
c_szDummyWindowName,
WS_MINIMIZE | WS_CHILD,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
hwnd,
NULL,
HINST_THISDLL,
(CREATESTRUCT *) pData );
if ( hwndProxy == NULL )
{
LocalFree((HLOCAL)pData);
return 0;
}
// Now setup to use the proxy window and message through the rest of thi
// function...
hwnd = hwndProxy;
wMsg = WM_CHANGENOTIFYMSG;
}
if (hwndDesktop)
{
HANDLE hChangeRegistration;
LPSHChangeRegistration pshcr;
hChangeRegistration = SHChangeRegistration_Create(
SHCR_CMD_REGISTER, ulID,
hwnd, wMsg,
fSources, fEvents,
pfsne[i].fRecursive, pfsne[i].pidl,
dwProcId);
//
// Transmit the change regsitration
//
SendMessage(hwndDesktop, CWM_CHANGEREGISTRATION,
(WPARAM)hChangeRegistration, (LPARAM)dwProcId);
//
// Now get back the ulID value, for further registrations and
// for returning to the calling function...
//
pshcr = (LPSHChangeRegistration)SHLockShared(hChangeRegistration,dwProcId);
if (pshcr)
{
ulID = pshcr->ulID;
SHUnlockShared(pshcr);
}
else
{
ulID = 0; // Error condition
}
SHFreeShared(hChangeRegistration,dwProcId);
}
else
{
ulID = SHChangeNotifyRegisterInternal(hwnd, fSources, fEvents,
wMsg, 1, &pfsne[i]);
}
if (ulID == 0)
{
if (hwndProxy)
DestroyWindow(hwndProxy);
break;
}
}
return ulID;
}
//--------------------------------------------------------------------------
//
// Returns TRUE if we found and removed the specified Client, otherwise
// returns FALSE.
//
BOOL WINAPI SHChangeNotifyDeregister(ULONG ulID)
{
int i;
HWND hwndDesktop;
BOOL fResult;
hwndDesktop = GetShellWindow();
if (hwndDesktop)
{
HANDLE hChangeRegistration;
DWORD dwProcId;
dwProcId = GetCurrentProcessId();
hChangeRegistration = SHChangeRegistration_Create(
SHCR_CMD_DEREGISTER, ulID,
(HWND)NULL, 0, 0, 0,
FALSE, NULL,
dwProcId);
//
// Transmit the change registration
//
fResult = SendMessage(hwndDesktop, CWM_CHANGEREGISTRATION,
(WPARAM)hChangeRegistration, (LPARAM)dwProcId);
SHFreeShared(hChangeRegistration,dwProcId);
}
else
{
fResult = SHChangeNotifyDeregisterInternal(ulID);
}
return fResult;
}
#if 0 // Nobody seems to use this anymore...
//--------------------------------------------------------------------------
//
// Returns TRUE if we sucessfully updated the specified client
// else returns FALSE.
//
BOOL WINAPI SHChangeNotifyUpdateEntryList(ULONG ulID, int iUpdateType,
int cEntries, SHChangeNotifyEntry *pfsne)
{
int i;
register int iEntry = 0;
FSNotifyClientInfo * pfsnci;
SHChangeNotifyEntry fsne;
SHChangeNotifyEntry * pfsneT;
//
// If the client was found, free up its heap data, and then remove
// it from the list.
//
if (!(pfsnci = _GetNotificationClientFromID(ulID)))
return(FALSE);
switch (iUpdateType)
{
case SHCNNU_SET:
//
// Simply loop through and destroy all of the old items
// and then fall through to the add function
//
for (iEntry = DSA_GetItemCount(pfsnci->hdsaNE) - 1; iEntry >= 0; iEntry--)
{
pfsneT = DSA_GetItemPtr(pfsnci->hdsaNE, iEntry);
ILGlobalFree((LPITEMIDLIST)pfsneT->pidl);
DSA_DeleteItem(pfsnci->hdsaNE, iEntry);
}
// Fall Through
case SHCNNU_ADD:
for(i = 0; i < cEntries; i++)
{
// REVIEW:: We should add checks to see if there are
// duplciates...
// Also we should be able to share this code with
// SHChangeNotifyRegister
if (pfsne[i].pidl == NULL) {
fsne.fRecursive = TRUE;
fsne.pidl = FALSE;
} else {
fsne.fRecursive = pfsne[i].fRecursive;
fsne.pidl = ILGlobalClone(pfsne[i].pidl);
if (!fsne.pidl) {
Assert(FALSE);
break;
}
}
// Use large index to add at end.
if (DSA_InsertItem(pfsnci->hdsaNE, 32767, &fsne) == -1)
{
// Should also probably set some other error condition...
ILGlobalFree((LPITEMIDLIST)fsne.pidl);
}
}
break;
case SHCNNU_REMOVE:
// Nice N*N algorithm!
for(i = 0; i < cEntries; i++)
{
for (iEntry = DSA_GetItemCount(pfsnci->hdsaNE) - 1; iEntry >= 0; iEntry--)
{
// We might as well remove duplicates...
pfsneT = DSA_GetItemPtr(pfsnci->hdsaNE, iEntry);
if (ILIsEqual(pfsneT->pidl,
pfsne[i].pidl) &&
(pfsneT->fRecursive == pfsne[i].fRecursive))
{
// We found a match!
ILGlobalFree((LPITEMIDLIST)pfsne->pidl);
DSA_DeleteItem(pfsnci->hdsaNE, iEntry);
}
}
}
break;
default:
Assert (FALSE);
}
//
// REVIEW: We may want to scan the list of pending notificiations
// remove entries that may no longer apply...
//
return(TRUE);
}
#endif
void CALLBACK _DispatchCallback(HWND hwnd, UINT uiMsg,
DWORD hChangeNotification, LRESULT result)
{
DWORD dwProcId = GetCurrentProcessId();
SHChangeNotification_Release((HANDLE)hChangeNotification,dwProcId);
sp_fsn.iCallbackCount--;
if (sp_fsn.iCallbackCount == 0)
SetEvent(sp_fsn.hCallbackEvent); // Free up anybody waiting...
}
//--------------------------------------------------------------------------
// Sends out all of the change notification packets for the given client.
void _SHChangeNotifyHandleClientEvents(FSNotifyClientInfo * pfsnci)
{
int iEvent;
int iMax;
DWORD dwProcId = GetCurrentProcessId();
iMax = DPA_GetPtrCount(pfsnci->hdpaPendingEvents);
for (iEvent = 0; iEvent < iMax; iEvent++)
{
FSNotifyEvent *pfsnevt;
pfsnevt = DPA_GetPtr(pfsnci->hdpaPendingEvents, iEvent);
#ifdef FSNDEBUG
if (pfsnevt) {
DebugMsg(DM_TRACE, TEXT("Dispatching message hwnd = %x (%x) hdpa = %x with ref %d"), pfsnci->hwnd, pfsnevt, pfsnci->hdpaPendingEvents, pfsnevt->cRef);
}
#endif
// send it off!
if (pfsnevt) {
HANDLE hChangeNotification;
sp_fsn.iCallbackCount++;
//
// callback count must be non-zero, we just incremented it.
// Put the event into the reset/false state.
//
if (!sp_fsn.hCallbackEvent)
sp_fsn.hCallbackEvent = CreateEvent(NULL, TRUE, FALSE, c_szCallbackName);
else
ResetEvent(sp_fsn.hCallbackEvent);
hChangeNotification = SHChangeNotification_Create(
pfsnevt->lEvent,
0,
pfsnevt->pidl,
pfsnevt->pidlExtra,
dwProcId);
if (!SendMessageCallback(pfsnci->hwnd, pfsnci->wMsg,
(WPARAM)hChangeNotification,
(LPARAM)dwProcId,
(SENDASYNCPROC)_DispatchCallback,
(DWORD)hChangeNotification))
{
SHChangeNotification_Release(hChangeNotification,dwProcId);
sp_fsn.iCallbackCount--;
if (sp_fsn.iCallbackCount == 0)
SetEvent(sp_fsn.hCallbackEvent); // Free up anybody waiting...
}
FSEventRelease(pfsnevt);
}
}
DPA_Destroy(pfsnci->hdpaPendingEvents);
}
//--------------------------------------------------------------------------
//
// Note that we allow Clients to deregister while this function is executing.
// Each time through our main loop we recalculate the Client count to take
// into account any shrinkange due to deregistrations.
//
// We also allow new Clients to register while we're processing the list.
//
// We skip "InUse" clients, because they are either in the process of
// deregistering or of having their messages dispatched by another thread.
//
void WINAPI _SHChangeNotifyHandleEvents(BOOL fShouldWait)
{
static BOOL s_bAlreadyEntered = FALSE;
BOOL fEventsAlreadyBeingHandled = FALSE;
DWORD dwProcID = GetCurrentProcessId();
FSNotifyClientInfo *pfsnci;
//
// Kill the timer now that we're handling the events. It'll get turned
// back on the next time an event is queued up.
//
FSNENTERCRITICAL;
{
if (s_bAlreadyEntered)
{
fEventsAlreadyBeingHandled = TRUE;
}
else
{
s_bAlreadyEntered = TRUE;
++s_fsn.cRefClientList;
}
}
FSNLEAVECRITICAL;
if (fEventsAlreadyBeingHandled)
goto WaitForFlush;
sp_fsn.dwLastFlush = GetCurrentTime();
// flush any pending interrupt events
FSNFlushInterruptEvents();
// Note that when a client de-registers, it is not removed from the client
// queue. Instead, it is marked as DELETE_ME, and we delete it at the
// end of handling events. Therefore, we should not need to worry about
// the DSA getting smaller or any clients shuffling position.
// We also do not really need to worry about new clients being added, since
// they would only have events in their queues that came after we set
// the dwLastFlush, so the Notify thread should wake up and call
// HandleEvents again for those new events.
for (pfsnci = s_fsn.pfsnciFirst; pfsnci; pfsnci = pfsnci->pfsnciNext)
{
FSNotifyClientInfo fsnciT;
int nEvents = 0;
FSNENTERCRITICAL;
{
if (!(pfsnci->iSerializationFlags & FSSF_DELETE_ME))
{
nEvents = DPA_GetPtrCount(pfsnci->hdpaPendingEvents);
if (nEvents)
{
if (pfsnci->dwProcID == dwProcID)
{
// this hands off the hdpaPendingEvents by creating a
// temporary client, and setting the old one to have a new
// empty hdpaPendingEvents.
//
// Copy the entry so we're safe from reallocs in the DSA.
fsnciT = *pfsnci;
pfsnci->hdpaPendingEvents = DPA_Create(4);
}
else
{
// Don't handle events for other processes
nEvents = 0;
}
}
}
}
FSNLEAVECRITICAL;
if (nEvents)
_SHChangeNotifyHandleClientEvents(&fsnciT);
}
FSNENTERCRITICAL;
--s_fsn.cRefClientList;
// Well, there is the possibility that we could leave some "deletion-
// pending" clients around, but it shouldn't be a big deal, since we will
// just delete them at some later time
if (s_fsn.cRefClientList == 0)
{
for (pfsnci = s_fsn.pfsnciFirst; pfsnci;)
{
if (pfsnci->iSerializationFlags & FSSF_DELETE_ME)
{
pfsnci = _SHChangeNotifyNukeClient(pfsnci, TRUE);
}
else
pfsnci = pfsnci->pfsnciNext;
}
}
s_bAlreadyEntered = FALSE;
FSNLEAVECRITICAL;
WaitForFlush:
/// now wait for all the callbacks to empty out
if (fShouldWait)
_FSN_WaitForCallbacks();
}
//==========================================================================
// This part is psuedo bogus. Basically we have problems at times doing a
// translation from things like \\pyrex\user to the appropriate PIDL,
// especially if you want to avoid the overhead of hitting the network and
// also problems of knowing if the server is in the "HOOD"
typedef struct _NPTItem
{
struct _NPTItem *pnptNext; // Pointer to next item;
LPCITEMIDLIST pidl; // The pidl
USHORT cchName; // size of the name in characters.
TCHAR szName[1]; // The name to translate from
} NPTItem, *PNPTItem;
#pragma data_seg(DATASEG_PERINSTANCE)
// Each process will maintain their own list.
PNPTItem g_pnptHead = NULL;
#pragma data_seg()
//--------------------------------------------------------------------------
//
// Function to register translations from Path to IDList translations.
//
void NPTRegisterNameToPidlTranslation(LPCTSTR pszPath, LPCITEMIDLIST pidl)
{
PNPTItem pnpt;
int cItemsRemoved = 0;
TCHAR szPath[MAX_PATH];
// We currently are only interested in UNC Roots
// If the table becomes large we can reduce this to only servers...
if (!PathIsUNC(pszPath))
return; // Not interested.
//
// If this item is not a root we need to count how many items to remove
//
lstrcpy(szPath, pszPath);
while (!PathIsRoot(szPath))
{
cItemsRemoved++;
if (!PathRemoveFileSpec(szPath))
return; // Did not get back to a valid root
}
FSNENTERCRITICAL;
// We don't want to add duplicates
for (pnpt = g_pnptHead; pnpt != NULL ; pnpt = pnpt->pnptNext)
{
if (lstrcmpi(szPath, pnpt->szName) == 0)
break;
}
if (pnpt == NULL)
{
SHORT cch = lstrlen(szPath);
DebugMsg(DM_TRACE, TEXT("NPT Register: %s"), szPath);
pnpt = (PNPTItem)LocalAlloc(LPTR, SIZEOF(NPTItem) + cch*SIZEOF(TCHAR));
if (pnpt)
{
pnpt->pidl = ILClone(pidl);
if (pnpt->pidl)
{
while(cItemsRemoved--)
{
ILRemoveLastID((LPITEMIDLIST)pnpt->pidl);
}
pnpt->pnptNext = g_pnptHead;
g_pnptHead = pnpt;
pnpt->cchName = cch;
lstrcpy(pnpt->szName, szPath);
}
else
{
LocalFree((HLOCAL)pnpt);
}
}
}
FSNLEAVECRITICAL;
}
//--------------------------------------------------------------------------
// The main function to attemp to map a portion of the name into an idlist
// Right now limit it to UNC roots
//
LPCTSTR NPTMapNameToPidl(LPCTSTR pszPath, LPCITEMIDLIST *ppidl)
{
PNPTItem pnpt;
FSNENTERCRITICAL;
// See if we can find the item in the list.
for (pnpt = g_pnptHead; pnpt != NULL ; pnpt = pnpt->pnptNext)
{
if ((IntlStrEqNI(pszPath, pnpt->szName, pnpt->cchName))
&& (*(pszPath+pnpt->cchName) == TEXT('\\')))
break;
}
FSNLEAVECRITICAL;
// See if we found a match
if (pnpt == NULL)
return(NULL);
// Found a match
*ppidl = pnpt->pidl;
return(pszPath+pnpt->cchName);
}
// register the hidden window class
BOOL _RegisterNotifyProxyWndProc( void )
{
// register a hidden window class
WNDCLASS wc;
if ( !GetClassInfo(HINST_THISDLL, c_szWindowClassName, &wc ))
{
wc.style = CS_PARENTDC;
wc.lpfnWndProc = (WNDPROC) HiddenNotifyWindow_WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(LP_NotifyProxyData);
wc.hInstance = HINST_THISDLL;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = c_szWindowClassName;
return RegisterClass(&wc);
}
else
{
return TRUE;
}
}
LRESULT CALLBACK HiddenNotifyWindow_WndProc( HWND hWnd,
UINT iMessage,
WPARAM wParam,
LPARAM lParam )
{
LRESULT lRes = FALSE;
LP_NotifyProxyData pData = ( LP_NotifyProxyData ) GetWindowLong( hWnd, 0 );
switch( iMessage )
{
case WM_CREATE:
{
// cast the create struct pointer to the object (as that is what we passed )
LPCREATESTRUCT pCS = (LPCREATESTRUCT) lParam;
pData = ( LP_NotifyProxyData ) pCS->lpCreateParams;
Assert(pData != NULL );
SetWindowLong( hWnd, 0, (LONG) pData );
}
break;
case WM_NCDESTROY:
Assert(pData != NULL );
// clear it so it won't be in use....
SetWindowLong( hWnd, 0, (LONG)NULL );
// free the memory ...
LocalFree( pData );
break;
case WM_CHANGENOTIFYMSG :
if ( pData != NULL )
{
LPSHChangeNotificationLock pshcnl;
LPITEMIDLIST *ppidl;
LONG lEvent;
// lock and break the info structure ....
pshcnl = SHChangeNotification_Lock( (HANDLE)wParam,
(DWORD)lParam,
&ppidl,
&lEvent );
// pass on to the old style client. ...
lRes = SendMessage( pData->hwndParent, pData->wMsg, (WPARAM) ppidl, (LPARAM) lEvent );
// new notifications ......
SHChangeNotification_Unlock(pshcnl);
}
break;
default:
lRes = DefWindowProc( hWnd, iMessage, wParam, lParam );
break;
}
return lRes;
}