|
|
//----------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997
//
// File: update.cpp
//
// Contents: update subscriptions agent
//
// Classes:
//
// Functions:
//
// History: 01-14-1997 rayen (Raymond Endres) Created
//
//----------------------------------------------------------------------------
//xnotfmgr - darremi owns this
#include "private.h"
#include "offline.h"
#include "offl_cpp.h"
#undef TF_THISMODULE
#define TF_THISMODULE TF_UPDATEAGENT
const IDMSG_NOTHING = 100 + IDCANCEL; const IDMSG_INITFAILED = 101 + IDCANCEL; const IDMSG_SESSIONEND = 102 + IDCANCEL; const IDMSG_UPDATEBEGIN = 103 + IDCANCEL; const IDMSG_UPDATEPROGRESS = 104 + IDCANCEL; const IDMSG_ADJUSTPROBAR = 105 + IDCANCEL;
const TID_UPDATE = 7405; // TimerID
const TID_STATISTICS = 1243; // TimerID for statistics update
#define SHRESTARTDIALOG_ORDINAL 59 // restart only exported by ordinal
typedef BOOL (WINAPI *SHRESTARTDIALOG)( HWND, LPTSTR, DWORD );
CUpdateAgent * g_pUpdate = NULL; BOOL CUpdateDialog::m_bDetail = FALSE;
CDialHelper::CDialHelper() : m_cRef(1), m_iDialerStatus(DIALER_OFFLINE) { ASSERT(0 == m_cConnection); ASSERT(NULL == m_pController); }
STDMETHODIMP_(ULONG) CDialHelper::AddRef(void) { // DBG("CDialHelper::AddRef");
return ++m_cRef; }
STDMETHODIMP_(ULONG) CDialHelper::Release(void) { // DBG("CDialHelper::Release");
if( 0L != --m_cRef ) return m_cRef;
DBG("CDialHelper::Release, ref count down to 0"); ASSERT(!m_cConnection); ASSERT(m_iDialerStatus == DIALER_OFFLINE); if (m_iDialerStatus != DIALER_OFFLINE) { DBG("CDialHelper::Release, send disconnect message(abnormal)"); NotifyAutoDialer(NOTIFICATIONTYPE_TASKS_COMPLETED); } SAFERELEASE(m_pNotMgr);
PostThreadMessage(m_ThreadID, UM_DECREASE, 0, 0); delete this; return 0L; }
STDMETHODIMP CDialHelper::QueryInterface(REFIID riid, void ** ppv) { // DBG("CDialHelper::QueryInterface");
*ppv = NULL;
if ((IID_IUnknown == riid) || (IID_INotificationSink == riid)) { *ppv = (INotificationSink *)this; }
if( NULL != *ppv ) { // DBG("CDialHelper::QueryInterface/AddRef");
((LPUNKNOWN)*ppv)->AddRef(); return NOERROR; }
return ResultFromScode(E_NOINTERFACE); }
STDMETHODIMP CDialHelper::OnNotification( LPNOTIFICATION pNotification, LPNOTIFICATIONREPORT pNotificationReport, DWORD dwReserved) { // DBG("CDialHelper::OnNotification called");
ASSERT(pNotification); // Extract Notification Type
HRESULT hr; NOTIFICATIONTYPE notfType; hr = pNotification->GetNotificationInfo(¬fType, NULL, NULL, NULL, 0); ASSERT(SUCCEEDED(hr));
if (NOTIFICATIONTYPE_PROGRESS_REPORT == notfType) { // get hresult from notification
HRESULT hrConnect; ReadSCODE(pNotification, NULL, c_szPropStatusCode, &hrConnect); if(SUCCEEDED(hrConnect)) return OnInetOnline(pNotification); else return OnInetOffline(pNotification); } else { DBG("CDialHelper::OnNotification - Unknown notification"); return S_OK; } }
STDMETHODIMP CDialHelper::Init(CUpdateController * pController) { ASSERT(pController); m_pController = pController; ASSERT(pController->m_pNotMgr); m_pNotMgr = pController->m_pNotMgr; m_ThreadID = pController->m_ThreadID; m_pNotMgr->AddRef();
return S_OK; }
STDMETHODIMP CDialHelper::DialOut(void) { HRESULT hr = S_OK;
if (m_iDialerStatus == DIALER_OFFLINE) { DBG("CDialHelper::DialOut - Dialing Out"); hr = NotifyAutoDialer(NOTIFICATIONTYPE_AGENT_START); if (SUCCEEDED(hr)) { m_iDialerStatus = DIALER_CONNECTING; m_cConnection ++; } }
return hr; }
STDMETHODIMP CDialHelper::HangUp(void) { m_cConnection --;
if (!m_cConnection) { if (m_iDialerStatus != DIALER_OFFLINE) { DBG("CDialHelper::HangUp - Hanging up"); m_iDialerStatus = DIALER_OFFLINE; NotifyAutoDialer(NOTIFICATIONTYPE_TASKS_COMPLETED); } }
return S_OK; }
STDMETHODIMP CDialHelper::CleanUp() { m_pController = NULL; return S_OK; }
STDMETHODIMP CDialHelper::OnInetOnline( INotification *pNotification) { HRESULT hr=S_OK;
if (m_iDialerStatus == DIALER_CONNECTING) { DBG("Dial Helper: CONNECTION SUCCESSFUL, BEGINNING DOWNLOAD");
m_iDialerStatus = DIALER_ONLINE; if (m_pController) m_pController->StartService(); else HangUp(); }
return hr; }
STDMETHODIMP CDialHelper::OnInetOffline( INotification *pNotification) { DBG("Dial Helper: received InetOffline, aborting"); SCODE eCode = S_OK; TCHAR szCaption[128]; TCHAR szString[1024]; CONNECT_ERROR error;
if (m_iDialerStatus == DIALER_CONNECTING) { error = E_ATTEMPT_FAILED; } else { error = E_CONNECTION_LOST; }
if (SUCCEEDED(ReadSCODE(pNotification, NULL, c_szPropStatusCode, & eCode))) { UINT uID; if (eCode == E_INVALIDARG) { uID = IDS_STRING_E_CONFIG; } else if (E_ABORT == eCode) { uID = IDS_STRING_E_SECURITYCHECK; } else { uID = IDS_STRING_E_FAILURE; } MLLoadString(uID, szString , ARRAYSIZE(szString)); MLLoadString(IDS_CAPTION_ERROR_CONNECTING, szCaption, ARRAYSIZE(szCaption)); MessageBox(NULL, szString, szCaption, MB_ICONWARNING | MB_SYSTEMMODAL); }
if (m_pController) m_pController->StopService(error); else HangUp();
m_iDialerStatus = DIALER_OFFLINE;
return S_OK; }
STDMETHODIMP CDialHelper::NotifyAutoDialer(NOTIFICATIONTYPE pType) { HRESULT hr; INotification * pNot = NULL; ASSERT(m_pNotMgr);
hr = m_pNotMgr->CreateNotification( pType, (NOTIFICATIONFLAGS) 0, NULL, &pNot, 0);
if (pNot) { INotificationSink *pSink = NULL;
if (pType == NOTIFICATIONTYPE_AGENT_START) { DBG("CDialHelper::NotifyAutoDialer AGENT_START"); pSink = (INotificationSink *)this;
// HACK HACK [darrenmi] Until DZhang yanks out the umbrella code
// we need something here - tell conn agent to let this connection
// slide
WriteAnsiSTR(pNot, NULL, c_szPropURL, TEXT("<override>"));
// have not mgr deliver for us
hr = m_pNotMgr->DeliverNotification( pNot, CLSID_ConnectionAgent, DM_NEED_COMPLETIONREPORT | DM_DELIVER_DEFAULT_PROCESS, pSink, &m_pConnAgentReport, 0);
} else { DBG("CDialHelper::NotifyAutoDialer TASKS_COMPLETED"); if(m_pConnAgentReport) { // deliver using the sink we've already got
hr = m_pConnAgentReport->DeliverUpdate(pNot, 0, 0); TraceMsg(TF_THISMODULE, "CDialHelper::NotifyAutoDialer releasing report pointer"); SAFERELEASE(m_pConnAgentReport); } }
SAFERELEASE(pNot); }
return hr; }
DWORD WINAPI DialogThreadProc(LPVOID pData) { ASSERT(pData); CUpdateDialog * pDialog = (CUpdateDialog *)pData; MSG msg;
pDialog->m_ThreadID = GetCurrentThreadId();
while (GetMessage(&msg, NULL, 0, 0)) { switch (msg.message) { case UM_READY: { pDialog->Init(NULL, (CUpdateController *)msg.lParam); pDialog->Show(TRUE); } break; default: IsDialogMessage(pDialog->m_hDlg, &msg); break; } } #ifdef DEBUG
if(g_fInitTable) LeakDetFunctionTable.pfnDebugMemLeak(DML_TYPE_THREAD | DML_END, TEXT(__FILE__), __LINE__); #endif
DBG("DialogThreadProc returning"); return 0; }
// application subscription channels can force a reboot
void DoReboot() { HRESULT hrReboot = S_OK; HINSTANCE hShell32Lib;
DBG("UpdateThreadProc returning - attempting reboot"); SHRESTARTDIALOG pfSHRESTARTDIALOG = NULL;
if ((hShell32Lib = LoadLibrary("shell32.dll")) != NULL) {
if (!(pfSHRESTARTDIALOG = (SHRESTARTDIALOG) GetProcAddress( hShell32Lib, MAKEINTRESOURCE(SHRESTARTDIALOG_ORDINAL)))) {
hrReboot = HRESULT_FROM_WIN32(GetLastError());
} else { // FEATURE: What hwnd to use?
pfSHRESTARTDIALOG(NULL, NULL, EWX_REBOOT); }
} else { hrReboot = HRESULT_FROM_WIN32(GetLastError()); }
if (hShell32Lib) { FreeLibrary(hShell32Lib); } }
DWORD WINAPI UpdateThreadProc(LPVOID pData) { ASSERT(pData); CUpdateController * pController = (CUpdateController *) pData; INotification * pNotification = NULL; MSG msg; int l_cObj; BOOL bNeedReboot = FALSE;
HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) { DBG("UpdateThreadProc exiting, failed to CoInitialize."); return hr; }
while (GetMessage(&msg, NULL, 0, 0)) { switch (msg.message) { case UM_ONREQUEST: if (!pController->m_fInit) break; pNotification = (INotification *)msg.lParam; // WARNING. There is a chance we fail in OnRequest (Failed to
// send out Notification to dialer agent).
hr = pController->OnRequest(pNotification); if (FAILED(hr)) { ASSERT(0); if ((!pController->m_count) && pController->m_pDialog) { PostMessage(pController->m_pDialog->m_hDlg, WM_COMMAND, IDMSG_SESSIONEND, 0); } } SAFERELEASE(pNotification); break; case UM_BACKGROUND: if (!pController->m_fInit) break; break; case UM_ONABORT: if (!pController->m_fInit) break; #ifdef DEBUG
hr = #endif
pController->Abort(); #ifdef DEBUG
ASSERT(SUCCEEDED(hr)); #endif
break; case UM_ONSKIP: if (!pController->m_fInit) break; #ifdef DEBUG
hr = #endif
pController->Skip(); #ifdef DEBUG
ASSERT(SUCCEEDED(hr)); #endif
break; case UM_ONADDSINGLE: if (!pController->m_fInit) break; MemFree((HLOCAL)msg.lParam); break; case UM_ONSKIPSINGLE: if (!pController->m_fInit) break; #ifdef DEBUG
hr = #endif
pController->SkipSingle((CLSID *)msg.lParam); MemFree((HLOCAL)msg.lParam); #ifdef DEBUG
ASSERT(SUCCEEDED(hr)); #endif
break; case UM_CLEANUP: pController->CleanUp(); pController->Release(); break; case UM_READY: pController->AddRef(); if (FAILED(pController->Init((CUpdateDialog *)msg.lParam))) { DBG("UpdateThreadProc - failed to init controller"); CUpdateDialog * pDlg = (CUpdateDialog *)msg.lParam; PostMessage(pDlg->m_hDlg, WM_COMMAND, IDMSG_INITFAILED, 0); } else { l_cObj = 2; } break; case UM_DECREASE: l_cObj --; if (!l_cObj) goto QUIT; break; case UM_NEEDREBOOT: bNeedReboot = TRUE; break;
default: TranslateMessage(&msg); DispatchMessage(&msg); break; } }
QUIT: ; #ifdef DEBUG
if(g_fInitTable) LeakDetFunctionTable.pfnDebugMemLeak(DML_TYPE_THREAD | DML_END, TEXT(__FILE__), __LINE__); #endif
CoUninitialize();
// This may need to be moved to a more appropriate location
if (bNeedReboot) DoReboot();
DBG("UpdateThreadProc returning"); return 0; }
STDMETHODIMP CUpdateController::ResyncData() { return S_OK; }
STDMETHODIMP CUpdateController::StartPending(void) { HRESULT hr;
DBG("CUpdateController::StartPending - entered"); ASSERT(GetCurrentThreadId() == m_ThreadID);
ASSERT(m_pDialer); ASSERT(m_pDialer->m_iDialerStatus == DIALER_ONLINE); for ( UINT ui = 0; ui < m_cReportCount; ui ++) { if (m_aReport[ui].status == ITEM_STAT_PENDING) { hr = DispatchRequest(&(m_aReport[ui])); ASSERT(SUCCEEDED(hr)); } }
return S_OK; }
STDMETHODIMP CUpdateController::StartService(void) { HRESULT hr = S_OK;
DBG("CUpdateController: Start Service"); ASSERT(GetCurrentThreadId() == m_ThreadID); ASSERT(!m_cFinished);
hr = StartPending();
if (!m_count) { m_fSessionEnded = TRUE; m_pDialer->HangUp(); if (m_pDialog) PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_SESSIONEND, 0); } else if (m_pDialog) PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_UPDATEBEGIN, 0); return S_OK; }
STDMETHODIMP CUpdateController::StopService(CONNECT_ERROR err) { DBG("Update Controller: Stop Service, aborting"); ASSERT(GetCurrentThreadId() == m_ThreadID); HRESULT hr;
if (!m_count && (err == E_ATTEMPT_FAILED)) { if (m_pDialer) m_pDialer->HangUp(); }
for ( UINT ui = 0; ui < m_cReportCount; ui ++) { switch (m_aReport[ui].status) { case ITEM_STAT_UPDATING: case ITEM_STAT_QUEUED: hr = CancelRequest(&(m_aReport[ui])); if (FAILED(hr)) break; else ; // Fall through.
case ITEM_STAT_PENDING: m_aReport[ui].status = ITEM_STAT_ABORTED; if (m_pDialog) m_pDialog->RefreshStatus(&(m_aReport[ui].startCookie), NULL, m_aReport[ui].status); break; default: break; } }
if (!m_count) { m_fSessionEnded = TRUE; PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_SESSIONEND, 0); }
return S_OK; }
STDMETHODIMP CUpdateController::IncreaseCount() { ASSERT(m_count >= 0); InterlockedIncrement(&m_count);
return S_OK; }
STDMETHODIMP CUpdateController::DecreaseCount(CLSID * pCookie) { InterlockedDecrement(&m_count); ASSERT(m_count >= 0);
m_cFinished ++; // check for growing subscriptions (this could be better)
if (m_cFinished > m_cTotal) m_cTotal = m_cFinished;
if (m_count == 0) { m_pDialer->HangUp(); }
if (m_pDialog) { PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_UPDATEPROGRESS, 0); if (!m_count) { m_fSessionEnded = TRUE; PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_SESSIONEND, 0); } } return S_OK; }
STDMETHODIMP CUpdateController::GetItemList(UINT * pNewItem) { DBG("CUpdateController::GetItemList - entered");
NOTIFICATIONITEM item; item.cbSize = sizeof(NOTIFICATIONITEM); ASSERT(m_pNotMgr);
IEnumNotification * pEnumNot = NULL; HRESULT hr; ULONG cItems = 0; UINT count = 0;
hr = m_pNotMgr->GetEnumNotification(0, &pEnumNot); RETURN_ON_FAILURE(hr);
ASSERT(pEnumNot);
hr = pEnumNot->Next(1, &item, &cItems); while (SUCCEEDED(hr) && cItems) { ASSERT(item.pNotification);
if ((NOTIFICATIONTYPE_AGENT_START == item.NotificationType) && (item.pNotification) && (TASK_FLAG_HIDDEN & ~item.TaskData.dwTaskFlags)) { SCODE scodeLast; STATUS statusLast;
hr = ReadSCODE(item.pNotification, NULL, c_szPropStatusCode, &scodeLast); if (FAILED(scodeLast)) { statusLast = ITEM_STAT_FAILED; } else { statusLast = ITEM_STAT_SUCCEEDED; }
hr = AddEntry(&item, statusLast); if (SUCCEEDED(hr)) { count ++; #ifdef DEBUG
} else { DBGIID("CUpdateController::GetItemList - Failed to add entry", item.NotificationCookie); #endif
} } SAFERELEASE(item.pNotification); item.cbSize = sizeof(NOTIFICATIONITEM); hr = pEnumNot->Next(1, &item, &cItems); }
if (pNewItem) *pNewItem = count;
SAFERELEASE(pEnumNot); return hr; }
STDMETHODIMP CUpdateController::Abort(void) { DBG("CUpdateController::Abort - entered");
ASSERT(GetCurrentThreadId() == m_ThreadID); ASSERT(m_pDialer);
HRESULT hr = StopService(ITEM_STAT_ABORTED);
return hr; }
STDMETHODIMP CUpdateController::SkipSingle(CLSID * pCookie) { ASSERT(pCookie); if (!pCookie) return E_INVALIDARG;
HRESULT hr = S_OK; PReportMap pEntry = FindReportEntry(pCookie); if (pEntry) { switch (pEntry->status) { case ITEM_STAT_UPDATING: case ITEM_STAT_QUEUED: hr = CancelRequest(pEntry); if (FAILED(hr)) break; else ; // Fall through.
case ITEM_STAT_PENDING: pEntry->status = ITEM_STAT_SKIPPED; if (m_pDialog) { m_pDialog->RefreshStatus(pCookie, NULL, pEntry->status); //select first updating item in list, which should be skippable
m_pDialog->SelectFirstUpdatingSubscription(); } break; default: break; } }
return hr; }
STDMETHODIMP CUpdateController::Skip(void) { DBG("CUpdateController::Skip - entered"); HRESULT hr = S_OK; ASSERT(GetCurrentThreadId() == m_ThreadID); ASSERT(m_pDialog); UINT selCount = 0;
hr = m_pDialog->GetSelectionCount(&selCount); if (FAILED(hr)) return hr;
if (!selCount) return S_OK;
CLSID * pSelCookies = (CLSID *)MemAlloc(LPTR, sizeof(CLSID)*selCount); if (!pSelCookies) return E_OUTOFMEMORY;
hr = m_pDialog->GetSelectedCookies(pSelCookies, &selCount); if (FAILED(hr)) { MemFree(pSelCookies); pSelCookies = NULL; return hr; } for (UINT ui = 0; ui < selCount; ui ++) { SkipSingle(pSelCookies + ui); }
MemFree(pSelCookies); pSelCookies = NULL; return S_OK; }
const GUIDSTR_LEN = GUIDSTR_MAX - 1;
STDMETHODIMP CUpdateController::AddSingle(CLSID * pCookie) { ASSERT(pCookie); if (!pCookie) return E_INVALIDARG;
HRESULT hr = E_FAIL; PReportMap pEntry = FindReportEntry(pCookie);
if (!pEntry) { NOTIFICATIONITEM item; item.cbSize = sizeof(NOTIFICATIONITEM); ASSERT(m_pNotMgr);
hr = m_pNotMgr->FindNotification(pCookie, &item, 0); if (FAILED(hr)) return hr;
ASSERT(item.pNotification); hr = E_FAIL; if ((NOTIFICATIONTYPE_AGENT_START == item.NotificationType) && (item.pNotification) && (TASK_FLAG_HIDDEN & ~item.TaskData.dwTaskFlags)) { SCODE scodeLast; STATUS statusLast;
hr = ReadSCODE(item.pNotification, NULL, c_szPropStatusCode, &scodeLast); if (FAILED(scodeLast)) { statusLast = ITEM_STAT_FAILED; } else { statusLast = ITEM_STAT_SUCCEEDED; }
hr = AddEntry(&item, statusLast); #ifdef DEBUG
if (FAILED(hr)) { DBGIID("CUpdateController::AddSingle - Failed to add new entry", item.NotificationCookie); } #endif
} SAFERELEASE(item.pNotification); item.cbSize = sizeof(NOTIFICATIONITEM); // Why is this line here?
if (FAILED(hr)) return hr;
pEntry = FindReportEntry(pCookie); ASSERT(pEntry); }
if (pEntry) { switch (pEntry->status) { case ITEM_STAT_QUEUED: case ITEM_STAT_UPDATING: hr = S_FALSE; break; #ifdef DEBUG
case ITEM_STAT_IDLE: ASSERT(0); break; #endif
default: pEntry->status = ITEM_STAT_PENDING; if (m_pDialog) m_pDialog->RefreshStatus(pCookie,pEntry->name,pEntry->status); hr = S_OK; break; } } return hr; }
STDMETHODIMP CUpdateController::Restart(UINT count) { if (!count) return S_OK;
HRESULT hr = S_OK; m_cTotal = m_cTotal + count; if (m_pDialog) PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_ADJUSTPROBAR, 0);
ASSERT(m_pDialer); if (m_pDialer->IsOffline()) { hr = m_pDialer->DialOut(); } else if (m_pDialer->IsConnecting()) { ; // Nothing to do;
} else { hr = StartPending(); }
return hr; }
STDMETHODIMP CUpdateController::OnRequest(INotification * pNotification) { BOOL bUpdateAll = TRUE; HRESULT hr; UINT count = 0;
DBG("CUpdateController::OnRequest - entered"); ASSERT(GetCurrentThreadId() == m_ThreadID);
// There is a chance that we are still receiving request even through
// we have already ended the session.
if (m_fSessionEnded) return S_FALSE; // We don't accept any more requests.
if (pNotification) { VARIANT var;
VariantInit(&var);
// Right now (02/21/97) urlmon can't handle the SAFEARRAY.
// We assembly this array of GUIDs as a BSTR in SendUpdateRequest
// and disassembly it here.
hr = pNotification->Read(c_szPropGuidsArr, &var); if (var.vt == VT_BSTR) { UINT bstrLen = 0; BSTR bstr = var.bstrVal; int guidCount, i; CLSID cookie;
ASSERT(bstr); DBG("CUpdateController::OnRequest - found cookie list"); bstrLen = lstrlenW(bstr); guidCount = bstrLen / GUIDSTR_LEN;
SYSTEMTIME stNow; DATE dtNow; NOTIFICATIONITEM item;
GetSystemTime(&stNow); SystemTimeToVariantTime(&stNow, &dtNow); item.cbSize = sizeof(NOTIFICATIONITEM); for (i = 0; i < guidCount; i ++) { BSTR bstrCookie = NULL;
bstrCookie = SysAllocStringLen(bstr+i*GUIDSTR_LEN, GUIDSTR_LEN); hr = CLSIDFromString(bstrCookie, &cookie); #ifdef DEBUG
DBGIID(TEXT("On request to update "), cookie); #endif
SysFreeString(bstrCookie); if (FAILED(hr)) continue; if (S_OK == AddSingle(&cookie)) count ++; }
hr = Restart(count); VariantClear(&var); return hr; } else { VariantClear(&var); } }
DBG("CUpdateController::OnRequest - Update all");
for ( UINT ui = 0; ui < m_cReportCount; ui ++) { switch (m_aReport[ui].status) { #ifdef DEBUG
case ITEM_STAT_IDLE: ASSERT(0); break; #endif
case ITEM_STAT_UPDATING: case ITEM_STAT_QUEUED: break; default: m_aReport[ui].status = ITEM_STAT_PENDING; if (m_pDialog) m_pDialog->RefreshStatus(&(m_aReport[ui].startCookie), m_aReport[ui].name, m_aReport[ui].status); count ++; break; } }
hr = Restart(count); return hr; }
CUpdateController::CUpdateController() : m_fInit(FALSE), m_fSessionEnded(FALSE) { m_pNotMgr = NULL; m_pDialog = NULL; m_pDialer = NULL; m_cRef = 1; }
CUpdateController::~CUpdateController() { ASSERT(0L == m_cRef); }
STDMETHODIMP_(ULONG) CUpdateController::AddRef(void) { // DBG("CUpdateController::AddRef");
return ++m_cRef; }
STDMETHODIMP_(ULONG) CUpdateController::Release(void) { // DBG("CUpdateController::Release");
if( 0L != --m_cRef ) return m_cRef;
DBG("Destroying Controller object"); m_fInit = FALSE; PostThreadMessage(m_ThreadID, UM_DECREASE, 0, 0); SAFERELEASE(m_pNotMgr);
if (m_pDialer) { m_pDialer->CleanUp(); SAFERELEASE(m_pDialer); }
m_pDialog = NULL; for (UINT ui = 0; ui < m_cReportCount; ui ++) { SAFELOCALFREE(m_aReport[ui].name); SAFELOCALFREE(m_aReport[ui].url); } SAFELOCALFREE(m_aReport); delete this; return 0L; }
STDMETHODIMP CUpdateController::QueryInterface(REFIID riid, void ** ppv) { // DBG("CUpdateController::QueryInterface");
*ppv = NULL;
if ((IID_IUnknown == riid) || (IID_INotificationSink == riid)) { *ppv = (INotificationSink *)this; }
if( NULL != *ppv ) { ((LPUNKNOWN)*ppv)->AddRef(); return S_OK; }
return E_NOINTERFACE; }
STDMETHODIMP CUpdateController::Init(CUpdateDialog * pDialog) { DBG("CUpdateController::Init"); ASSERT( !m_pNotMgr );
ASSERT(pDialog); ASSERT(!m_fInit); m_pDialog = pDialog; m_cReportCount = m_cReportCapacity = 0;
m_ThreadID = GetCurrentThreadId(); ASSERT (!m_aReport);
m_aReport = (PReportMap)MemAlloc(LPTR, sizeof(ReportMapEntry) * CUC_ENTRY_INCRE); if (!m_aReport) { DBG("CUpdateController::Init - Out of mem"); return E_OUTOFMEMORY; }
m_cReportCapacity = CUC_ENTRY_INCRE;
HRESULT hr = CoCreateInstance(CLSID_StdNotificationMgr, NULL, CLSCTX_INPROC_SERVER, IID_INotificationMgr,(void **)&m_pNotMgr);
if (SUCCEEDED(hr)) { m_pDialer = new CDialHelper; if (!m_pDialer) { DBG("CUpdateController::Init - Failed to dialer"); hr = E_OUTOFMEMORY; } else { m_pDialer->AddRef(); hr = m_pDialer->Init(this); } }
if (SUCCEEDED(hr)) { m_cFinished = m_cTotal = 0; m_count = 0; m_fInit = TRUE; GetItemList(NULL); if ((!m_cReportCount) && (m_pDialog)) { // Didn't find nothing.
PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_NOTHING, 0); } } else { SAFERELEASE(m_pNotMgr); SAFERELEASE(m_pDialer); }
return hr; }
// REARCHITECT: Copied from NotificationMgr code (notifctn.hxx)
#define WZ_COOKIE L"Notification_COOKIE"
//
// INotificationSink member(s)
//
STDMETHODIMP CUpdateController::OnNotification( LPNOTIFICATION pNotification, LPNOTIFICATIONREPORT pNotificationReport, DWORD dwReserved) { DBG("CUpdateController::OnNotification called"); ASSERT(pNotification); HRESULT hr; NOTIFICATIONTYPE notfType; ASSERT(GetCurrentThreadId() == m_ThreadID);
// Extract Notification Type
hr = pNotification->GetNotificationInfo(¬fType, NULL, NULL, NULL, 0); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return E_INVALIDARG; }
if (notfType == NOTIFICATIONTYPE_END_REPORT) { //
// Except those we fail to DeliverNotification in the first place for
// each request we will get an End Report when we finished the current
// update or aborted/skipped the current update.
//
DBG("CUpdateController::OnNotification - END REPORT"); CLSID cookie; PReportMap pEntry = NULL;
if (SUCCEEDED(ReadGUID(pNotification, NULL, c_szStartCookie, &cookie))) { pEntry = FindReportEntry(&cookie); }
if (!pEntry) { DBGIID("CUpdateController::OnNotification(END_REPORT) - invalid cookie", cookie); return E_FAIL; }
//update count of total kbytes downloaded with size of this site from end report
DWORD dwCrawlKBytes; if (SUCCEEDED (ReadDWORD (pNotification, NULL, c_szPropCrawlActualSize, &dwCrawlKBytes))) { if (m_pDialog) { DWORD dwKBytesPrevious = m_pDialog->SetSiteDownloadSize (&cookie, dwCrawlKBytes);
m_pDialog->m_cDlKBytes += dwCrawlKBytes - dwKBytesPrevious; SendMessage (m_pDialog->m_hDlg, WM_TIMER, TID_STATISTICS, 0); //force update
} }
switch (pEntry->status) { case ITEM_STAT_UPDATING : { SCODE eCode = S_OK;
hr = ReadSCODE(pNotification, NULL, c_szPropStatusCode, & eCode); ASSERT(SUCCEEDED(hr)); if (FAILED(eCode)) { pEntry->status = ITEM_STAT_FAILED; } else { pEntry->status = ITEM_STAT_SUCCEEDED; }
if (m_pDialog) { m_pDialog->RefreshStatus(&cookie, NULL, pEntry->status); PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_UPDATEPROGRESS, 100 - pEntry->progress); } pEntry->progress = 0; break; } case ITEM_STAT_SKIPPED: case ITEM_STAT_ABORTED: ASSERT(!(pEntry->progress)); break; default: ASSERT(0); break; } return S_OK; } else if (notfType == NOTIFICATIONTYPE_TASKS_COMPLETED || notfType == NOTIFICATIONTYPE_TASKS_ERROR) { DBG("CUpdateController::OnNotification - TASKS_ENDED"); CLSID cookie; PReportMap pEntry = NULL;
if (SUCCEEDED(ReadGUID(pNotification, NULL, WZ_COOKIE, &cookie))) { pEntry = FindReportEntry(&cookie); }
if (!pEntry) { DBGIID("\t(TASKS_ENDED) - invalid cookie ", cookie); return E_FAIL; } else { DBGIID("\t(TASKS_ENDED) - cookie ", cookie); }
DecreaseCount(&cookie); switch (pEntry->status) { case ITEM_STAT_UPDATING : { if (notfType == NOTIFICATIONTYPE_TASKS_ERROR) { pEntry->status = ITEM_STAT_FAILED; } else { pEntry->status = ITEM_STAT_SUCCEEDED; }
if (m_pDialog) { m_pDialog->RefreshStatus(&cookie, NULL, pEntry->status); PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_UPDATEPROGRESS, 100 - pEntry->progress); } pEntry->progress = 0; break; } case ITEM_STAT_SKIPPED: case ITEM_STAT_ABORTED: ASSERT(!(pEntry->progress)); break; case ITEM_STAT_QUEUED: pEntry->status = ITEM_STAT_ABORTED; if (m_pDialog) { m_pDialog->RefreshStatus(&cookie, NULL, pEntry->status); PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_UPDATEPROGRESS, 100 - pEntry->progress); } pEntry->progress = 0; break; default: ASSERT(!(pEntry->progress)); break; } return S_OK; } else if (notfType == NOTIFICATIONTYPE_PROGRESS_REPORT) { DBG("CUpdateController::OnNotification - progress report"); CLSID cookie; PReportMap pEntry = NULL;
if (SUCCEEDED(ReadGUID(pNotification, NULL, c_szStartCookie, &cookie))) { pEntry = FindReportEntry(&cookie); }
if (!pEntry) { DBGIID("CUpdateController::OnNotification(PROGRESS_REPORT) - invalid cookie", cookie); return E_FAIL; }
//start a document dl -- update count and status indicators
if (m_pDialog) { BSTR bCurrentUrl; TCHAR szCurrentUrl[MAX_URL + 1];
if (SUCCEEDED(ReadBSTR(pNotification, NULL, c_szPropCurrentURL, &bCurrentUrl))) { //does not appear to be a real BSTR (with length byte) -- just an OLESTR
MyOleStrToStrN (szCurrentUrl, MAX_URL, bCurrentUrl); SAFEFREEBSTR(bCurrentUrl); m_pDialog->RefreshStatus(&cookie, NULL, pEntry->status, szCurrentUrl);
//update size of download
DWORD dwKBytesCurrent; if (SUCCEEDED (ReadDWORD (pNotification, NULL, c_szPropCurrentSize, &dwKBytesCurrent)) && (dwKBytesCurrent != -1)) { DWORD dwKBytesPrevious = m_pDialog->SetSiteDownloadSize (&cookie, dwKBytesCurrent);
m_pDialog->m_cDlKBytes += dwKBytesCurrent - dwKBytesPrevious; }
++m_pDialog->m_cDlDocs; //increase number of docs downloaded
SendMessage (m_pDialog->m_hDlg, WM_TIMER, TID_STATISTICS, 0); //force update
} }
DWORD dwProgress; DWORD dwProgressMax; if (SUCCEEDED(ReadDWORD(pNotification, NULL, c_szPropProgressMax, &dwProgressMax)) && SUCCEEDED(ReadDWORD(pNotification, NULL, c_szPropProgress, &dwProgress))) { // (INT)dwProgressMax could be -1!
if ((((INT)dwProgress) >= 0) && (((INT)dwProgressMax) >= 0)) { ASSERT(dwProgress <= dwProgressMax);
// The progress report is sent at the beginning of current
// download. We should substrat Progress by 1.
UINT cProgress, cProgressMax, newPercentage; cProgress = (dwProgress)?dwProgress - 1:0; cProgressMax = dwProgressMax;
newPercentage = MulDiv(cProgress, 100, cProgressMax); if ((newPercentage > pEntry->progress) && m_pDialog) { PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_UPDATEPROGRESS, (LPARAM)(newPercentage - pEntry->progress)); } pEntry->progress = newPercentage; } } return S_OK; } else if (notfType == NOTIFICATIONTYPE_BEGIN_REPORT) { DBG("CUpdateController::OnNotification - begin report"); CLSID cookie; PReportMap pEntry = NULL;
if (SUCCEEDED(ReadGUID(pNotification, NULL, c_szStartCookie, &cookie))) { pEntry = FindReportEntry(&cookie); }
if (!pEntry) { DBGIID("CUpdateController::OnNotification(BEGIN_REPORT) - invalid cookie", cookie); return E_FAIL; }
if (pEntry->status == ITEM_STAT_UPDATING) return S_OK;
// Note there is a case that we send out the 'Abort' notification to
// the agent, and the agent sends 'begin report' at almost the same
// time. In that case we can get begin-report when we think we already
// cancelled the update.
if (pEntry->status != ITEM_STAT_QUEUED) { ASSERT((pEntry->status == ITEM_STAT_SKIPPED) || (pEntry->status == ITEM_STAT_ABORTED)); return S_OK; // We bail out.
} pEntry->status = ITEM_STAT_UPDATING; if (m_pDialog) m_pDialog->RefreshStatus(&cookie, NULL, ITEM_STAT_UPDATING);
return S_OK; } else if (notfType == NOTIFICATIONTYPE_TASKS_STARTED) { DBG("CUpdateController::OnNotification - TASKS_STARTED"); CLSID cookie; PReportMap pEntry = NULL;
if (SUCCEEDED(ReadGUID(pNotification, NULL, WZ_COOKIE, &cookie))) { pEntry = FindReportEntry(&cookie); }
if (!pEntry) { DBGIID("\t(TASKS_STARTED) - invalid cookie ", cookie); return E_FAIL; } else { DBGIID("\t(TASKS_STARTED) - cookie ", cookie); } ASSERT(pEntry->status == ITEM_STAT_QUEUED);
if (pEntry->status != ITEM_STAT_QUEUED) { ASSERT((pEntry->status == ITEM_STAT_SKIPPED) || (pEntry->status == ITEM_STAT_ABORTED)); return S_OK; // We bail out.
} pEntry->status = ITEM_STAT_UPDATING; if (m_pDialog) m_pDialog->RefreshStatus(&cookie, NULL, ITEM_STAT_UPDATING);
return S_OK; } else { DBG("CUpdateController::OnNotification - unknown notification"); return S_OK; } }
STDMETHODIMP CUpdateController::DispatchRequest(PReportMap pEntry) { DBG("CUpdateController::Dispatch - entered"); ASSERT(pEntry); ASSERT(m_pNotMgr); ASSERT(m_pDialog); ASSERT(ITEM_STAT_PENDING == pEntry->status); HRESULT hr; NOTIFICATIONITEM item; item.cbSize = sizeof(item);
hr = m_pNotMgr->FindNotification(&(pEntry->startCookie), &item, 0); ASSERT(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { hr = m_pNotMgr->DeliverNotification(item.pNotification, item.clsidDest, DM_DELIVER_DEFAULT_PROCESS | DM_NEED_COMPLETIONREPORT | DM_NEED_PROGRESSREPORT | DM_THROTTLE_MODE,
(INotificationSink *)this, NULL, 0); SAFERELEASE(item.pNotification); if (FAILED(hr)) { DBG("CUpdateController::Dispatch - failed to DeliverNotification"); m_pDialog->RefreshStatus(&(pEntry->startCookie), NULL, ITEM_STAT_FAILED); } else { DBG("Increase Count"); pEntry->status = ITEM_STAT_QUEUED; pEntry->progress = 0; IncreaseCount(); } } return hr; }
// In CancelRequest() we only attempt to send out the notification of
// abort. The count on agent side will be decreased when we get end report.
// So matched number of request and end report is crucial.
STDMETHODIMP CUpdateController::CancelRequest(PReportMap pEntry) { ASSERT(pEntry);
if ((ITEM_STAT_UPDATING != pEntry->status) && (ITEM_STAT_QUEUED != pEntry->status)) return S_OK;
ASSERT(m_pNotMgr);
HRESULT hr = S_OK; INotification *pNot = NULL; hr = m_pNotMgr->CreateNotification(NOTIFICATIONTYPE_TASKS_ABORT, (NOTIFICATIONFLAGS)0, NULL, &pNot, 0); ASSERT(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) { hr = m_pNotMgr->DeliverReport(pNot, &(pEntry->startCookie), 0); if (SUCCEEDED(hr)) { if (m_pDialog) { PostMessage(m_pDialog->m_hDlg, WM_COMMAND, IDMSG_UPDATEPROGRESS, 100 - pEntry->progress); } pEntry->progress = 0; // This is the default status afterwards.
pEntry->status = ITEM_STAT_ABORTED; } else { TraceMsg(TF_THISMODULE, TEXT("CancelRequest:Error:%x"), hr); } } else { DBG("CUpdateController::Stop failed"); } SAFERELEASE(pNot); }
return hr; }
PReportMap CUpdateController::FindReportEntry(CLSID * pCookie) { ASSERT(pCookie);
for (UINT ui = 0; ui < m_cReportCount; ui ++) { if (m_aReport[ui].startCookie == * pCookie) { return &(m_aReport[ui]); } }
return NULL; }
STDMETHODIMP CUpdateController::GetLocationOf(CLSID * pCookie, LPTSTR pszText, UINT cchTextMax) { ASSERT(pCookie && pszText);
PReportMap pEntry = FindReportEntry(pCookie);
if (pEntry) { lstrcpyn(pszText, pEntry->url, cchTextMax); } else { lstrcpyn(pszText, c_szStrEmpty, cchTextMax); }
return S_OK; }
SUBSCRIPTIONTYPE CUpdateController::GetSubscriptionType(CLSID * pCookie) { ASSERT(pCookie);
SUBSCRIPTIONTYPE subType = SUBSTYPE_EXTERNAL; PReportMap pEntry = FindReportEntry(pCookie);
if (pEntry) { subType = pEntry->subType; }
return subType; }
BOOL CUpdateController::IsSkippable(CLSID * pCookie) { ASSERT(pCookie);
PReportMap pEntry = FindReportEntry(pCookie);
if (pEntry) if (pEntry->status == ITEM_STAT_PENDING || pEntry->status == ITEM_STAT_QUEUED || pEntry->status == ITEM_STAT_UPDATING) { return TRUE; } return FALSE; }
STDMETHODIMP CUpdateController::AddEntry(NOTIFICATIONITEM *pItem, STATUS status) { ASSERT(pItem); ASSERT(m_cReportCount <= m_cReportCapacity);
if (m_cReportCount == m_cReportCapacity) { UINT newSize = m_cReportCapacity + CUC_ENTRY_INCRE; ASSERT(newSize <= CUC_MAX_ENTRY); HLOCAL newBuf = MemReAlloc(m_aReport, newSize * sizeof(ReportMapEntry), LHND); if (!newBuf) { DBG("CUpdateController::AddEntry - Failed to realloc"); return E_OUTOFMEMORY; }
m_aReport = (PReportMap)(newBuf); m_cReportCapacity = newSize; }
m_aReport[m_cReportCount].startCookie = pItem->NotificationCookie; m_aReport[m_cReportCount].progress = 0; m_aReport[m_cReportCount].status = status;
OOEBuf ooeBuf; DWORD dwSize = 0;
ZeroMemory((void *)&ooeBuf, sizeof(ooeBuf)); HRESULT hr = LoadOOEntryInfo(&ooeBuf, pItem, &dwSize); if (S_OK != hr) { if (FAILED(hr)) return hr; else return E_FAIL; }
LPTSTR nameStr = NULL, urlStr = NULL;
nameStr = (LPTSTR)MemAlloc(LPTR, (lstrlen(ooeBuf.m_Name) + 1) * sizeof(TCHAR)); urlStr = (LPTSTR)MemAlloc(LPTR, (lstrlen(ooeBuf.m_URL) + 1) * sizeof(TCHAR)); if (!(nameStr && urlStr)) { SAFELOCALFREE(nameStr); SAFELOCALFREE(urlStr);
return E_OUTOFMEMORY; }
lstrcpy(nameStr, ooeBuf.m_Name); lstrcpy(urlStr, ooeBuf.m_URL);
m_aReport[m_cReportCount].name = nameStr; m_aReport[m_cReportCount].url = urlStr; m_aReport[m_cReportCount].subType = GetItemCategory(&ooeBuf);
m_cReportCount ++; return S_OK; }
STDMETHODIMP CUpdateController::CleanUp() { m_pDialog = NULL; return S_OK; }
//////////////////////////////////////////////////////////////////////////
//
// CUpdateAgent
//
// The only reason we need this class is that so we can create the
// dialog in the different thread.
CUpdateAgent::CUpdateAgent() { DBG("Creating CUpdateAgent object"); ASSERT(!(m_pDialog || m_pController)); }
CUpdateAgent::~CUpdateAgent() { DBG("Destroying CUpdateAgent object");
if (m_pController) { PostThreadMessage(m_ThreadID, UM_CLEANUP, 0,0); m_pController = NULL; }
if (m_pDialog) { // delete m_pDialog;
// m_pDialog will be destroyed by m_pController in CleanUp.
m_pDialog = NULL; }
g_pUpdate = NULL; }
STDMETHODIMP CUpdateAgent::Init(void) { DBG("CUpdateAgent::Init"); HRESULT hr = S_OK;
ASSERT(!(m_pDialog || m_pController));
if (SUCCEEDED(hr)) { m_pDialog = new CUpdateDialog; if (!m_pDialog) { DBG("CUpdateAgent::Init - Failed to create dialog"); hr = E_OUTOFMEMORY; } }
if (SUCCEEDED(hr)) { m_pController = new CUpdateController; if (!m_pController) { DBG("CUpdateAgent::Init - Failed to create download controller"); hr = E_OUTOFMEMORY; } }
if (SUCCEEDED(hr)) { HANDLE hThread;
hThread = CreateThread(NULL, 0, DialogThreadProc, (LPVOID)m_pDialog, 0, &m_DialogThreadID); if (!hThread) { DBG("CUpdateAgent::Init - Failed to create dialog thread"); hr = E_FAIL; } else { int i = 0; while ( i < 3) { if (PostThreadMessage(m_DialogThreadID, UM_READY, 0, (LPARAM)m_pController)) break; i ++; Sleep(1000); } // Is there a safer way to do this?
if (i >= 3) { ASSERT(0); hr = E_FAIL; } CloseHandle(hThread); } }
if (SUCCEEDED(hr)) { HANDLE hThread;
hThread = CreateThread(NULL, 0, UpdateThreadProc, (LPVOID)m_pController, 0, &m_ThreadID); if (!hThread) { DBG("CUpdateAgent::Init - Failed to create thread"); hr = E_FAIL; } else { int i = 0; while ( i < 3) { if (PostThreadMessage(m_ThreadID, UM_READY, 0, (LPARAM)m_pDialog)) break; i ++; Sleep(1000); } // FEATURE: Is there a safer way to do this?
if (i >= 3) { ASSERT(0); hr = E_FAIL; } SetThreadPriority(hThread, THREAD_PRIORITY_IDLE); CloseHandle(hThread); } if (FAILED(hr)) { PostThreadMessage(m_DialogThreadID, WM_QUIT, 0, 0); } }
if (FAILED(hr)) { if (m_pController) { delete m_pController; m_pController = NULL; } if (m_pDialog) { delete m_pDialog; m_pDialog = NULL; } } return hr; }
BOOL ListView_OnNotify(HWND hDlg, NM_LISTVIEW* plvn, CUpdateController * pController) { ASSERT(plvn && pController); CUpdateDialog * pDialog = pController->m_pDialog;
if (!pDialog) return FALSE; // If m_pDialog is NULL, we haven't call Init
// for pController on second thread yet.
HRESULT hr;
switch (plvn->hdr.code) { case LVN_ITEMCHANGED: { if (!(plvn->uChanged & LVIF_STATE)) break;
UINT uOldState = plvn->uOldState & LVIS_SELECTED; UINT uNewState = plvn->uNewState & LVIS_SELECTED;
UINT count = 0; hr = pDialog->GetSelectionCount(&count); if (FAILED(hr)) break;
HWND hButton = GetDlgItem(hDlg, IDCMD_SKIP); BOOL fEnable = FALSE; if (count) {
CLSID cookie; int iItem = plvn->iItem; hr = pDialog->IItem2Cookie(iItem, &cookie); if (SUCCEEDED(hr)) { fEnable = pController->IsSkippable(&cookie); } }
Button_Enable(hButton, fEnable); return TRUE; } default: break; }
return FALSE; }
void ResizeDialog(HWND hDlg, BOOL bShowDetail) { ASSERT(hDlg); RECT rcDlg, rcChild; HWND hSplitter, hLV; TCHAR szButton[32];
//calculate margin (upper-left position of IDC_SIZENODETAILS)
GetWindowRect(GetDlgItem (hDlg, IDC_SIZENODETAILS), &rcChild); MapWindowPoints(NULL, hDlg, (LPPOINT)&rcChild, 2); int iMargin = rcChild.left;
GetWindowRect(hDlg, &rcDlg);
if (bShowDetail) { hLV = GetDlgItem(hDlg, IDL_SUBSCRIPTION); ASSERT(hLV); GetWindowRect(hLV, &rcChild); rcDlg.bottom = rcChild.bottom + iMargin + GetSystemMetrics (SM_CXSIZEFRAME); rcDlg.right = rcChild.right + iMargin + GetSystemMetrics (SM_CYSIZEFRAME);
LONG dwStyle = GetWindowLong (hDlg, GWL_STYLE); dwStyle = dwStyle | WS_MAXIMIZEBOX | WS_THICKFRAME; SetWindowLong (hDlg, GWL_STYLE, dwStyle);
rcDlg.left -= (GetSystemMetrics (SM_CXSIZEFRAME) - GetSystemMetrics (SM_CXFIXEDFRAME)); rcDlg.top -= (GetSystemMetrics (SM_CYSIZEFRAME) - GetSystemMetrics (SM_CYFIXEDFRAME)); MoveWindow(hDlg, rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top, TRUE); MLLoadString(IDS_NODETAILS, szButton, ARRAYSIZE(szButton)); } else { hSplitter = GetDlgItem(hDlg, IDC_SIZENODETAILS); ASSERT(hSplitter); GetWindowRect(hSplitter, &rcChild);
LONG dwStyle = GetWindowLong (hDlg, GWL_STYLE); dwStyle = dwStyle & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME; SetWindowLong (hDlg, GWL_STYLE, dwStyle);
MoveWindow(hDlg, rcDlg.left + (GetSystemMetrics (SM_CXSIZEFRAME) - GetSystemMetrics (SM_CXFIXEDFRAME)), rcDlg.top + (GetSystemMetrics (SM_CYSIZEFRAME) - GetSystemMetrics (SM_CYFIXEDFRAME)), rcChild.right - rcDlg.left, rcChild.bottom - rcDlg.top, TRUE); MLLoadString(IDS_DETAILS, szButton, ARRAYSIZE(szButton)); } SetDlgItemText(hDlg, IDCMD_DETAILS, szButton); }
void UpdateStatistics (HWND hDlg, int nFiles, int nKBytes, int nSeconds) { TCHAR szStats[128], szFormat[64];
MLLoadString (IDS_STATISTICS, szFormat, ARRAYSIZE(szFormat)); wnsprintf (szStats, ARRAYSIZE(szStats), szFormat, nFiles, nKBytes, nSeconds/60, nSeconds%60); SetDlgItemText (hDlg, IDC_STATISTICS, szStats); }
void DrawResizeWidget (HWND hDlg) //copied from athena's CGroupListDlg::OnPaint
{ PAINTSTRUCT ps; RECT rc;
GetClientRect(hDlg, &rc); rc.left = rc.right - GetSystemMetrics(SM_CXSMICON); rc.top = rc.bottom - GetSystemMetrics(SM_CYSMICON); BeginPaint(hDlg, &ps);
if (CUpdateDialog::m_bDetail && !IsZoomed(hDlg)) DrawFrameControl(ps.hdc, &rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
CUpdateDialog * pDialog = (CUpdateDialog *)GetWindowLong(hDlg, DWL_USER); if (pDialog != NULL) { pDialog->m_cxWidget = rc.left; pDialog->m_cyWidget = rc.top; }
EndPaint(hDlg, &ps); }
void EraseResizeWidget (HWND hDlg) { CUpdateDialog * pDialog = (CUpdateDialog *)GetWindowLong(hDlg, DWL_USER); RECT rWidget;
if (pDialog != NULL) { //invalidate resize widget
rWidget.left = pDialog->m_cxWidget; rWidget.top = pDialog->m_cyWidget; rWidget.right = pDialog->m_cxWidget + GetSystemMetrics(SM_CXSMICON); rWidget.bottom = pDialog->m_cyWidget + GetSystemMetrics(SM_CYSMICON); InvalidateRect(hDlg, &rWidget, FALSE);
// pDialog->m_cxWidget = rWidget.left;
// pDialog->m_cxWidget = rWidget.top;
} }
extern BOOL GetSubscriptionFolderPath(LPTSTR); //----------------------------------------------------------------------------
// UpdateDlgProc function
//----------------------------------------------------------------------------
BOOL CALLBACK UpdateDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static DWORD dwStartTicks; CUpdateDialog * pDialog = (CUpdateDialog *)GetWindowLong(hDlg, DWL_USER); CUpdateController * pController = (pDialog)?pDialog->m_pController:NULL; switch (uMsg) { case WM_INITDIALOG : { DBG("DLGBOX: Creating dialog box."); ASSERT(lParam); ASSERT(GetWindowLong(hDlg, DWL_USER) == 0); SetWindowLong(hDlg, DWL_USER, lParam); SetForegroundWindow(hDlg); TCHAR szString[1024]; MLLoadString(IDS_CONNECTING, szString, ARRAYSIZE(szString)); SetDlgItemText(hDlg, IDC_AGENTSTATUS, szString); ResizeDialog(hDlg, CUpdateDialog::m_bDetail);
SetTimer (hDlg, TID_STATISTICS, 1000, NULL); dwStartTicks = GetTickCount();
// Check current keyboard layout is IME or not
if (((DWORD)GetKeyboardLayout(0) & 0xF0000000L) == 0xE0000000L) { HWND hwndIME = ImmGetDefaultIMEWnd(hDlg); if (hwndIME) SendMessage(hwndIME, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0); }
return FALSE; // keep the focus on the dialog
} case WM_ACTIVATE: if (LOWORD(wParam) != WA_INACTIVE) { // Check current keyboard layout is IME or not
if (((DWORD)GetKeyboardLayout(0) & 0xF0000000L) == 0xE0000000L) { HWND hwndIME = ImmGetDefaultIMEWnd(hDlg); if (hwndIME) SendMessage(hwndIME, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0); } } break; case WM_NOTIFY : switch (LOWORD (wParam)) { case IDL_SUBSCRIPTION: if (!pController) break; return ListView_OnNotify(hDlg, (NM_LISTVIEW *)lParam, pController); default: return FALSE; } break; case WM_TIMER: switch (wParam) { case TID_UPDATE: KillTimer(hDlg, wParam); pDialog->CleanUp(); SetWindowLong(hDlg, DWL_USER, 0); delete pDialog; return TRUE; case TID_STATISTICS: UpdateStatistics (hDlg, pDialog->m_cDlDocs, pDialog->m_cDlKBytes, (GetTickCount() - dwStartTicks)/1000); return TRUE; } break; case WM_GETMINMAXINFO : if (CUpdateDialog::m_bDetail) { LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam; DWORD style = GetWindowLong (hDlg, GWL_STYLE); RECT smallest; GetWindowRect (GetDlgItem (hDlg, IDC_MINBORDER), &smallest); AdjustWindowRect (&smallest, style, FALSE); lpmmi->ptMinTrackSize.x = smallest.right - smallest.left; lpmmi->ptMinTrackSize.y = smallest.bottom - smallest.top; return 0; } break; case WM_PAINT: DrawResizeWidget (hDlg); break; case WM_SIZE: if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED) { const int CHILDREN[] = { IDCMD_HIDE, IDCMD_ABORT, IDCMD_DETAILS, IDCMD_SKIP }; int width = LOWORD(lParam), height = HIWORD(lParam); RECT rChild; int child;
//calculate margin (upper-left position of IDC_SIZENODETAILS)
GetWindowRect(GetDlgItem (hDlg, IDC_SIZENODETAILS), &rChild); // Use MapWindowPints for mirroring
MapWindowPoints(NULL, hDlg, (LPPOINT)&rChild, 2); int iMargin = rChild.left;
for (child = 0; child < sizeof(CHILDREN)/sizeof(CHILDREN[0]); child++) { GetWindowRect (GetDlgItem (hDlg, CHILDREN[child]), &rChild); MapWindowPoints(NULL, hDlg, (LPPOINT)&rChild, 2); SetWindowPos (GetDlgItem (hDlg, CHILDREN[child]), 0, width - iMargin - (rChild.right - rChild.left), rChild.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER); }
if (CUpdateDialog::m_bDetail) //only apply to bottom half
{ EraseResizeWidget (hDlg);
GetWindowRect (GetDlgItem (hDlg, IDD_SPLITTER), &rChild); MapWindowPoints(NULL, hDlg, (LPPOINT)&rChild, 2); SetWindowPos (GetDlgItem (hDlg, IDD_SPLITTER), 0, 0, 0, width - 2 * rChild.left, rChild.bottom - rChild.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); //for some reason there's a weird redraw bug on this control -- NOCOPYBITS fixes it
GetWindowRect (GetDlgItem (hDlg, IDL_SUBSCRIPTION), &rChild); MapWindowPoints(NULL, hDlg, (LPPOINT)&rChild, 2); SetWindowPos (GetDlgItem (hDlg, IDL_SUBSCRIPTION), 0, 0, 0, width - rChild.left - iMargin, height - rChild.top - iMargin, SWP_NOMOVE | SWP_NOZORDER); } return 0; } break;
case WM_CLOSE: KillTimer (hDlg, TID_STATISTICS); break;
case WM_COMMAND : { if (!pController) { if (IDCANCEL == LOWORD (wParam)) { KillTimer(hDlg, TID_UPDATE); pDialog->CleanUp(); SetWindowLong(hDlg, DWL_USER, 0); delete pDialog; return TRUE; } else { break; } }
HWND hProgress = GetDlgItem(hDlg, IDD_PROBAR); HWND hAnimate = GetDlgItem(hDlg, IDD_ANIMATE); TCHAR szString[1024];
switch (LOWORD (wParam)) { case IDMSG_SESSIONEND: // Stop dialog, session concludes.
DBG("UpdateDlgProc - all updates are complete"); pController->m_cTotal = pController->m_cFinished = 0; pDialog->m_pController = pController = NULL; delete g_pUpdate; MessageBeep(0xFFFFFFFF); Animate_Close(hAnimate); ShowWindow(hAnimate, SW_HIDE); MLLoadString(IDS_SESSIONEND, szString, ARRAYSIZE(szString)); SetDlgItemText(hDlg, IDC_AGENTSTATUS, szString); ShowWindow (GetDlgItem (hDlg, IDC_AGENTSTATUS), SW_SHOW); Button_Enable(GetDlgItem(hDlg, IDCMD_ABORT), FALSE); Button_Enable(GetDlgItem(hDlg, IDCMD_DETAILS), FALSE); SetTimer(hDlg, TID_UPDATE, 3000, NULL); KillTimer (hDlg, TID_STATISTICS); return TRUE;
case IDMSG_UPDATEBEGIN: { DBG("UpdateDlgProc - Start updating"); SetForegroundWindow(hDlg); ShowWindow (hAnimate, SW_SHOW); ShowWindow (GetDlgItem (hDlg, IDC_AGENTSTATUS), SW_HIDE); Animate_Open(hAnimate, IDA_DOWNLOAD); Animate_Play(hAnimate, 0, -1, -1); return TRUE; }
case IDMSG_ADJUSTPROBAR: { ASSERT(pController->m_cTotal); ASSERT(pController->m_cFinished <= pController->m_cTotal); SendMessage(hProgress, PBM_SETRANGE32, 0, pController->m_cTotal * 100); SendMessage(hProgress, PBM_SETPOS, pController->m_cFinished * 100, 0); return TRUE; }
case IDMSG_NOTHING: // Nothing to show yet.
{ DBG("UpdateDlgProc - No item found"); MLLoadString(IDS_STRING_NOTHING_TO_UPDATE, szString , ARRAYSIZE(szString)); SetDlgItemText(hDlg, IDC_AGENTSTATUS, szString); pController->m_cTotal = pController->m_cFinished = 0; pDialog->m_pController = pController = NULL; delete g_pUpdate; MessageBeep(0xFFFFFFFF); Button_Enable(GetDlgItem(hDlg, IDCMD_ABORT), FALSE); Button_Enable(GetDlgItem(hDlg, IDCMD_DETAILS), FALSE); SetTimer(hDlg, TID_UPDATE, 3000, NULL); KillTimer (hDlg, TID_STATISTICS); return TRUE; }
case IDMSG_UPDATEPROGRESS: SendMessage(hProgress, PBM_DELTAPOS, lParam, 0); return TRUE;
case IDMSG_INITFAILED: DBG("UpdateDlgProc - Controller Init Failed."); SetWindowLong(hDlg, DWL_USER, 0); pDialog->m_pController = pController = NULL; delete g_pUpdate; MessageBeep(0xFFFFFFFF); Button_Enable(GetDlgItem(hDlg, IDCMD_ABORT), FALSE); Button_Enable(GetDlgItem(hDlg, IDCMD_DETAILS), FALSE); pDialog->CleanUp(); SetWindowLong(hDlg, DWL_USER, 0); delete pDialog; return TRUE;
case IDCANCEL: { int mbRet = SGMessageBox(hDlg, IDS_DOWNLOAD_ABORT_WARNING, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_APPLMODAL); if (mbRet == IDNO) { return TRUE; } else { if (!pDialog->m_pController) return TRUE; ; // Fall through.
} }
// Following messages are from the UI and need to be
// forward to update thread.
case IDCMD_ABORT: // Abort all updates
DBG("UpdateDlgProc - closing, all downloads aborted"); ASSERT(pController->m_ThreadID); PostThreadMessage(pController->m_ThreadID, UM_ONABORT, 0,0); MLLoadString(IDS_ABORTING, szString, ARRAYSIZE(szString)); SetDlgItemText(hDlg, IDC_AGENTSTATUS, szString); Animate_Close(hAnimate); ShowWindow(hAnimate, SW_HIDE); ShowWindow (GetDlgItem (hDlg, IDC_AGENTSTATUS), SW_SHOW); return TRUE;
case IDCMD_SKIP: PostThreadMessage(pController->m_ThreadID, UM_ONSKIP, 0,0); return TRUE;
case IDCMD_DETAILS: { CUpdateDialog::m_bDetail = !CUpdateDialog::m_bDetail; ResizeDialog(hDlg, CUpdateDialog::m_bDetail); return TRUE; }
case IDCMD_HIDE: ShowWindow(hDlg, SW_SHOWMINIMIZED); return TRUE;
default: break; } break; } } return FALSE; }
HRESULT GetActiveUpdateAgent(CUpdateAgent ** ppUpdate) { ASSERT (ppUpdate); // We are assuming the Update agent is free threaded.
*ppUpdate = NULL;
if (g_pUpdate == NULL) { DBG("GetActiveUpdateAgent - Creating new agent"); g_pUpdate = new CUpdateAgent(); if (!g_pUpdate) { DBG("GetActiveUpdateAgent - Failed to create new agent"); return E_OUTOFMEMORY; }
HRESULT hr = g_pUpdate->Init(); if (FAILED(hr)) { DBG("GetActiveUpdateAgent - Failed to init new agent"); return hr; } } *ppUpdate = g_pUpdate; return NOERROR; }
DWORD WINAPI BackgroundUpdate(void) { DBG("BackgroundUpdate entered");
HRESULT hr; CUpdateAgent * pAgent = NULL;
hr = GetActiveUpdateAgent(&pAgent); if (SUCCEEDED(hr)) { ASSERT(pAgent); ASSERT(pAgent->m_ThreadID); // APPCOMPAT: Even when we succeed here, there are chances that we won't
// get updated because CONTROLLER CAN FAIL TO INITIALIZE AND WE DON'T
// KNOW IT!
if (!PostThreadMessage(pAgent->m_ThreadID, UM_BACKGROUND,0,0)) { hr = E_FAIL; DBG("Failed to post ONREQUEST message."); } pAgent = NULL; } DBG("BackgroundUpdate ended"); return (DWORD)hr; }
DWORD WINAPI UpdateRequest(UINT idCmd, INotification *pNot) { // DBG("UpdateRequest entered");
if (idCmd != UM_ONREQUEST) return E_FAIL;
HRESULT hr; CUpdateAgent * pAgent = NULL;
hr = GetActiveUpdateAgent(&pAgent); if (SUCCEEDED(hr)) { ASSERT(pAgent); ASSERT(pAgent->m_ThreadID); if (pNot) pNot->AddRef(); // APPCOMPAT: Even when we succeed here, there are chances that we won't
// get updated because CONTROLLER CAN FAIL TO INITIALIZE AND WE DON'T
// KNOW IT!
if (!PostThreadMessage(pAgent->m_ThreadID, UM_ONREQUEST,0,(LPARAM)pNot)) { hr = E_FAIL; SAFERELEASE(pNot); DBG("Failed to post ONREQUEST message."); } pAgent = NULL; } // DBG("UpdateRequest ended");
return (DWORD)hr; }
HRESULT UpdateNotifyReboot(void) { DBG("UpdateNotifyReboot entered");
HRESULT hr; CUpdateAgent * pAgent = NULL;
hr = GetActiveUpdateAgent(&pAgent); if (SUCCEEDED(hr)) { ASSERT(pAgent); ASSERT(pAgent->m_ThreadID);
hr = PostThreadMessage(pAgent->m_ThreadID, UM_NEEDREBOOT, 0, 0); }
DBG("UpdateNotifyReboot ended"); return hr; }
|