#include "priv.h" #include "inetnot.h" //+------------------------------------------------------------------------- // Static initialization //-------------------------------------------------------------------------- HWND CWinInetNotify::s_hwnd = NULL; ULONG CWinInetNotify::s_ulEnabled = 0; CWinInetNotify* CWinInetNotify::s_pWinInetNotify = NULL; //+------------------------------------------------------------------------- // Constructor - Creates invisible top-level window. //-------------------------------------------------------------------------- CWinInetNotify::CWinInetNotify() : _hMutex(NULL), _fEnabled(FALSE) { } //+------------------------------------------------------------------------- // Enables/disables wininet notifications //-------------------------------------------------------------------------- void CWinInetNotify::Enable(BOOL fEnable) { if (fEnable && !_fEnabled) { // // Enable the notifications // ENTERCRITICAL; ++s_ulEnabled; if (NULL == s_hwnd) { // create an invisible top-level window to receive notifications WNDCLASS wc; ZeroMemory(&wc, SIZEOF(wc)); wc.lpfnWndProc = _WndProc; wc.hInstance = HINST_THISDLL; wc.lpszClassName = CWinInetNotify_szWindowClass; SHRegisterClass(&wc); s_hwnd = CreateWindow(CWinInetNotify_szWindowClass, NULL, WS_POPUP, 0, 0, 1, 1, NULL, NULL, HINST_THISDLL, this); } if (s_hwnd) { _fEnabled = TRUE; } LEAVECRITICAL; } else if (!fEnable && _fEnabled) { // // Disable the notifications // ENTERCRITICAL; if (--s_ulEnabled == 0) { // // We use a mutex here because we can have multiple instances of // iexplore. We want to avoid setting up a window to accept wininet // notifications if it is in the process of being destroyed. // _EnterMutex(); // Look for another window to receive wininet notifications if (EnumWindows(EnumWindowsProc, NULL)) { // No one left so turn off notifications RegisterUrlCacheNotification(0, 0, 0, 0, 0); } // // Handle any queued notifications. // // Note that we have a small window in which a notification // can be lost! Something could be posted to us after we are // destroyed! // MSG msg; if (PeekMessage(&msg, s_hwnd, CWM_WININETNOTIFY, CWM_WININETNOTIFY, PM_REMOVE)) { _OnNotify(msg.wParam); } DestroyWindow(s_hwnd); s_hwnd = NULL; // Now that our window is gone, we can allow other processes to // look for windows to receive notifications. _LeaveMutex(); } LEAVECRITICAL; _fEnabled = FALSE; } } //+------------------------------------------------------------------------- // Destructor - Destroys top-level window when last instance is destroyed //-------------------------------------------------------------------------- CWinInetNotify::~CWinInetNotify() { Enable(FALSE); } //+------------------------------------------------------------------------- // Called for each top level window to find another one to accept wininet // notifications. //-------------------------------------------------------------------------- BOOL CALLBACK CWinInetNotify::EnumWindowsProc ( HWND hwnd, // handle to top-level window LPARAM lParam // application-defined value ) { // Ignore our own window if (hwnd == s_hwnd) return TRUE; // See if it's one of our windows TCHAR szWindowClass[30]; if (GetClassName(hwnd, szWindowClass, ARRAYSIZE(szWindowClass)) && StrCmp(CWinInetNotify_szWindowClass, szWindowClass) == 0) { _HookInetNotifications(hwnd); return FALSE; } return TRUE; } //+------------------------------------------------------------------------- // Hooks up wininet notifications. //-------------------------------------------------------------------------- void CWinInetNotify::_HookInetNotifications(HWND hwnd) { // We always want to know when cache items become sticky or unstickey // or transition between online and offline DWORD dwFlags = CACHE_NOTIFY_URL_SET_STICKY | CACHE_NOTIFY_URL_UNSET_STICKY | CACHE_NOTIFY_SET_ONLINE | CACHE_NOTIFY_SET_OFFLINE ; // // We only care about things being added to or removed from the // cache when we are offline. The name-space-control greys unavailable // items when we are offline. // if (SHIsGlobalOffline()) { dwFlags |= CACHE_NOTIFY_ADD_URL | CACHE_NOTIFY_DELETE_URL | CACHE_NOTIFY_DELETE_ALL; } RegisterUrlCacheNotification(hwnd, CWM_WININETNOTIFY, 0, dwFlags, 0); } //+------------------------------------------------------------------------- // Re-broadcasts the notification using SHChangeNotify //-------------------------------------------------------------------------- void CWinInetNotify::_OnNotify(DWORD_PTR dwFlags) { // Remove any other queued notifications MSG msg; while (PeekMessage(&msg, s_hwnd, CWM_WININETNOTIFY, CWM_WININETNOTIFY, PM_REMOVE)) { // Combine the notification bits dwFlags |= msg.wParam; } SHChangeDWORDAsIDList dwidl; // Align for UNIX dwidl.cb = (unsigned short) PtrDiff(& dwidl.cbZero, &dwidl); dwidl.dwItem1 = SHCNEE_WININETCHANGED; dwidl.dwItem2 = (DWORD)dwFlags; dwidl.cbZero = 0; SHChangeNotify(SHCNE_EXTENDED_EVENT, SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, (LPCITEMIDLIST)&dwidl, NULL); // If we are switching between online and offline, we need to update the // events that we are interested in. if (dwFlags & (CACHE_NOTIFY_SET_ONLINE | CACHE_NOTIFY_SET_OFFLINE)) { _HookInetNotifications(s_hwnd); } } //+------------------------------------------------------------------------- // Window procedure for our invisible top-level window. Receives // notifications from wininet. //-------------------------------------------------------------------------- LRESULT CALLBACK CWinInetNotify::_WndProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam) { switch (uMessage) { case WM_CREATE: { // Hook us up to get the notifications _HookInetNotifications(hwnd); break; } case CWM_WININETNOTIFY: { _OnNotify(wParam); return 0; } } return DefWindowProcWrap(hwnd, uMessage, wParam, lParam); } //+------------------------------------------------------------------------- // Protect simultaneous access by multiple processes //-------------------------------------------------------------------------- void CWinInetNotify::_EnterMutex() { ASSERT(_hMutex == NULL); // This gets an existing mutex if one exists _hMutex = CreateMutex(NULL, FALSE, CWinInetNotify_szWindowClass); // Wait for up to 20 seconds if (!_hMutex || WaitForSingleObject(_hMutex, 20000) == WAIT_TIMEOUT) { ASSERT(FALSE); } } void CWinInetNotify::_LeaveMutex() { if (_hMutex) { ReleaseMutex(_hMutex); CloseHandle(_hMutex); _hMutex = NULL; } } //+------------------------------------------------------------------------- // Manages a global CWinInetNotify object //-------------------------------------------------------------------------- void CWinInetNotify::GlobalEnable() { if (s_pWinInetNotify == NULL) { ENTERCRITICAL; if (s_pWinInetNotify == NULL) { s_pWinInetNotify = new CWinInetNotify(); if (s_pWinInetNotify) { s_pWinInetNotify->Enable(); } } LEAVECRITICAL; } } void CWinInetNotify::GlobalDisable() { ENTERCRITICAL; if (s_pWinInetNotify) { delete s_pWinInetNotify; s_pWinInetNotify = NULL; } LEAVECRITICAL; }