2182 lines
64 KiB
C++
2182 lines
64 KiB
C++
//----------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|