275 lines
8.2 KiB
C++
275 lines
8.2 KiB
C++
#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;
|
|
}
|