// private declarations for scnotify.cpp typedef struct { DWORD dwSig; UINT uCmd; ULONG ulID; ULONG ulHwnd; UINT uMsg; DWORD fSources; LONG lEvents; BOOL fRecursive; UINT uidlRegister; } CHANGEREGISTER; typedef struct { DWORD dwSig; DWORD cbSize; LONG lEvent; UINT uFlags; DWORD dwEventTime; UINT uidlMain; UINT uidlExtra; } CHANGEEVENT; typedef struct { DWORD dwSig; LPITEMIDLIST pidlMain; LPITEMIDLIST pidlExtra; CHANGEEVENT *pce; } CHANGELOCK; HANDLE SHChangeNotification_Create(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidlMain, LPCITEMIDLIST pidlExtra, DWORD dwProcId, DWORD dwEventTime); class CNotifyEvent; class CCollapsingClient; class CRegisteredClient; class CInterruptSource; class CAnyAlias; // // this is the global object g_pscn // its lifetime is tied to the SCNotify thread and window. // if the thread or window dies, then the object is destroyed // class CChangeNotify { public: // methods CNotifyEvent *GetEvent(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags); BOOL AddClient(IDLDATAF flags, LPCITEMIDLIST pidl, BOOL *pfInterrupt, BOOL fRecursive, CCollapsingClient *pclient); HRESULT RemoveClient(LPCITEMIDLIST pidl, BOOL fInterrupt, CCollapsingClient *pclient); BOOL AddInterruptSource(LPCITEMIDLIST pidlClient, BOOL fRecursive); void ReleaseInterruptSource(LPCITEMIDLIST pidlClient); void AddAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias, DWORD dwEventTime); void AddSpecialAlias(int csidlReal, int csidlAlias); void UpdateSpecialAlias(int csidlAlias); void NotifyEvent(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime); void PendingCallbacks(BOOL fAdd); void SetFlush(int idt); static DWORD WINAPI ThreadProc(void *pv); static DWORD WINAPI ThreadStartUp(void *pv); static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); protected: // methods BOOL _OnChangeRegistration(HANDLE hChangeRegistration, DWORD dwProcId); LRESULT _OnNotifyEvent(HANDLE hChange, DWORD dwProcId); LRESULT _OnSuspendResume(HANDLE hChange, DWORD dwProcId); void _OnDeviceBroadcast(ULONG_PTR code, DEV_BROADCAST_HANDLE *pbhnd); ULONG _RegisterClient(HWND hwnd, int fSources, LONG fEvents, UINT wMsg, SHChangeNotifyEntry *pfsne); BOOL _DeregisterClient(CRegisteredClient *pclient); BOOL _DeregisterClientByID(ULONG ulID); BOOL _DeregisterClientsByWindow(HWND hwnd); void _FreshenClients(void); BOOL _InitTree(CIDLTree **pptree); BOOL _AddToClients(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags); void _MatchAndNotify(LPCITEMIDLIST pidl, CNotifyEvent *pne, BOOL fFromExtra); void _AddGlobalEvent(CNotifyEvent *pne); CInterruptSource *_InsertInterruptSource(LPCITEMIDLIST pidl, BOOL fRecursive); DWORD _GetInterruptEvents(HANDLE *ahEvents, DWORD cEvents); void _ResetRelatedInterrupts(LPCITEMIDLIST pidl); void _FlushInterrupts(); void _Flush(BOOL fShouldWait); void _WaitForCallbacks(void); BOOL _SuspendResume(BOOL fSuspend, BOOL fRecursive, LPCITEMIDLIST pidl); BOOL _HandleMessages(void); void _MessagePump(void); void _SignalInterrupt(HANDLE hEvent); void _FreshenUp(void); CAnyAlias *_FindAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias); CAnyAlias *_FindSpecialAlias(int csidlReal, int csidlAlias); void _CheckAliasRollover(void); void _FreshenAliases(void); BOOL _InsertAlias(CLinkedNode *p); void _ActivateAliases(LPCITEMIDLIST pidl, BOOL fActivate); protected: // members CIDLTree *_ptreeClients; CIDLTree *_ptreeInterrupts; CIDLTree *_ptreeAliases; CLinkedList _listInterrupts; CLinkedList _listClients; CLinkedList _listAliases; LONG _cFlushing; LONG _cCallbacks; HANDLE _hCallbackEvent; }; typedef struct _MSGEVENT { HANDLE hChange; DWORD dwProcId; } MSGEVENT; // // LIFETIME - based on clients holding references // Each event can have multiple references // each CRegisteredClient has a DPA that points to a list // of events that the client will want to know. // the first time an event is used, it is added // to the ptreeEvents, so that it may be reused. // when the last client stops using the event, then // it is removed from the tree. // class CNotifyEvent { public: STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); MSGEVENT *GetNotification(DWORD dwProcId) { MSGEVENT *pme = new MSGEVENT; if (pme) { pme->dwProcId = dwProcId; pme->hChange = SHChangeNotification_Create((lEvent & ~SHCNE_INTERRUPT), // clients should never see the SHCNE_INTERRUPT flag 0, pidl, pidlExtra, dwProcId, dwEventTime); if (!pme->hChange) { delete pme; pme = NULL; } } return pme; } BOOL Init(LPCITEMIDLIST pidlIn, LPCITEMIDLIST pidlExtraIn); LONG lEvent; LPITEMIDLIST pidl; LPITEMIDLIST pidlExtra; DWORD dwEventTime; UINT uEventFlags; protected: LONG _cRef; static CNotifyEvent *Create(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags); CNotifyEvent(LONG lEventIn, DWORD dwEventTimeIn, UINT uEventFlagsIn) : lEvent(lEventIn), dwEventTime(dwEventTimeIn), uEventFlags(uEventFlagsIn), _cRef(1) {} ~CNotifyEvent() { ILFree(pidl); ILFree(pidlExtra); } // so CSCN can set fUsed; friend class CChangeNotify; friend class CRegisteredClient; }; class CCollapsingClient { public: // methods void Notify(CNotifyEvent *pne, BOOL fFromExtra); BOOL Flush(BOOL fNeedsCallbackEvent); BOOL Init(LPCITEMIDLIST pidl, BOOL fRecursive); CCollapsingClient(); protected: virtual ~CCollapsingClient(); virtual BOOL _WantsEvent(LONG lEvent) = 0; virtual void _SendNotification(CNotifyEvent *pne, BOOL fNeedsCallbackEvent, SENDASYNCPROC pfncb) = 0; virtual BOOL _IsValidClient() = 0; virtual BOOL _CheckUpdatingSelf() = 0; LPITEMIDLIST _pidl; LONG _fEvents; HWND _hwnd; BOOL _fUpdatingSelf; BOOL _fRecursive; private: BOOL _Flush(BOOL fNeedsCallbackEvent); BOOL _CanCollapse(LONG lEvent); BOOL _IsDupe(CNotifyEvent *pne); BOOL _AddEvent(CNotifyEvent *pne, BOOL fFromExtra); CDPA _dpaPendingEvents; int _iUpdatingSelfIndex; int _cEvents; }; // // LIFETIME - based on client registration // when an SCN client calls Register, we create // a corresponding object that lives // as long as the client window is valid // or until Deregister is called. // references are kept in ptreeClients and in // the pclientFirst list. when a client is // removed from the list, it is also removed // from the tree. // class CRegisteredClient : public CCollapsingClient { public: // methods CRegisteredClient(); ~CRegisteredClient(); BOOL Init(HWND hwnd, int fSources, LONG fEvents, UINT wMsg, SHChangeNotifyEntry *pfsne); protected: // methods void _SendNotification(CNotifyEvent *pne, BOOL fNeedsCallbackEvent, SENDASYNCPROC pfncb); BOOL _WantsEvent(LONG lEvent); BOOL _IsValidClient() { return (!_fDeadClient); } BOOL _CheckUpdatingSelf() { return _fUpdatingSelf; } protected: // members ULONG _ulID; BOOL _fDeadClient; BOOL _fInterrupt; private: // members DWORD _dwProcId; int _fSources; UINT _wMsg; friend class CChangeNotify; }; class CAnyAlias : public CCollapsingClient { public: // methods void Activate(BOOL fActivate); BOOL Remove(); BOOL Init(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias); BOOL InitSpecial(int csidlReal, int csidlAlias); BOOL IsAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias); BOOL IsSpecial(int csidlReal, int csidlAlias); ~CAnyAlias(); protected: BOOL _CustomTranslate(); void _SendNotification(CNotifyEvent *pne, BOOL fNeedsCallbackEvent, SENDASYNCPROC pfncb); BOOL _WantsEvent(LONG lEvent); BOOL _IsValidClient() { return TRUE; } BOOL _CheckUpdatingSelf() { return FALSE; } BOOL _OkayToNotifyTranslatedEvent(CNotifyEvent *pne, LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra); private: LONG _cActivated; LPITEMIDLIST _pidlAlias; ITranslateShellChangeNotify *_ptscn; DWORD _dwTime; BOOL _fRemove; BOOL _fInterrupt; BOOL _fCheckedCustom; BOOL _fSpecial; int _csidlReal; int _csidlAlias; friend class CChangeNotify; }; // // LIFETIME - based on client registration // when an SCN client calls Register, we may create // a corresponding object that lives // as long as the client window is valid // or until Deregister is called. // references are kept in ptreeClients and in // the pclientFirst list. when a client is // removed from the list, it is also removed // from the tree. // class CInterruptSource { public: // methods BOOL Init(LPCITEMIDLIST pidl, BOOL fRecursive); void Reset(BOOL fSignal); BOOL GetEvent(HANDLE *phEvent); void Suspend(BOOL fSuspend); BOOL SuspendDevice(BOOL fSuspend, HDEVNOTIFY hPNP); BOOL Flush(void); ~CInterruptSource(); protected: // methods void _Reset(BOOL fDeviceNotify); protected: // members LPITEMIDLIST pidl; // this is SHARED with the fs registered client structure. DWORD cClients; // how many clients are interested in this. (ref counts) private: typedef enum { NO_SIGNAL = 0, SH_SIGNAL, FS_SIGNAL } SIGNAL_STATE; BOOL _fRecursive; // is this a recursive interrupt client? HANDLE _hEvent; // cRecursive clients LONG _cSuspend; // suspended for extended fileops SIGNAL_STATE _ssSignal; // FS has signaled us with an event on this directory HDEVNOTIFY _hPNP; // PnP handle to warn us about drives coming and going HDEVNOTIFY _hSuspended; // suspended PnP handle friend class CChangeNotify; };