//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 2000 // // File: wuaueng.cpp // //-------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop //Handle for Download Events ENGINE_EVENTS EngineEvents; AUCatalog *gpAUcatalog; inline BOOL FServiceDisabled(void) { return (AUSTATE_DISABLED == gpState->GetState()); } //cancel download if any void CancelDownload(void) { if (NULL != gpAUcatalog && gpAUcatalog->m_audownloader.getID() != GUID_NULL) { gpAUcatalog->m_audownloader.DrizzleOperation(DRIZZLEOPS_CANCEL); } gpState->SetDisconnected(FALSE); //In case the disabled happened during TRANSIENT_ERROR we don't want to keep the flag set to disconnected } DWORD RandomWaitTimeBeforeDetect() { return (ULONGLONG)AU_TWENTY_TWO_HOURS - ((ULONGLONG) AU_TWENTY_TWO_HOURS * rand() * AU_RANDOMIZATION_WINDOW ) /( (ULONGLONG) RAND_MAX * 100); //precision to hundredth } //void setAuStateDisconnected(BOOL fDisconnected); BOOL FDisabledDuringDownload(void) { BOOL fRet = FALSE; if (FServiceDisabled()) { DEBUGMSG("WUAUENG Detected Disabled State during download"); CancelDownload(); fRet = TRUE; } return fRet; } #define ISERVICE_FINISHED 0 #define ISERVICE_DISABLED 1 #define WAIT_SERVICE_FINISHED 0 #define WAIT_SERVICE_DISABLED 1 #define WAIT_NOT_NEEDED 2 #define WAIT_CONNECTION_FOUND 3 #define WAIT_DONE 4 //fixcode: don't always need to persist in registry DWORD MWFMO(DWORD dwTimeout, DWORD dwMinTimeout = 1) { HANDLE hEvents[2]; hEvents[ISERVICE_FINISHED] = ghServiceFinished; hEvents[ISERVICE_DISABLED] = ghServiceDisabled; if (FAILED(setLastWaitTimeout(dwTimeout))) { DEBUGMSG("WUAUENG setLastWaitTimeout failed with error %d", GetLastError()); } return MsgWaitForMultipleObjectsEx(2, hEvents, dwTimeToWait(dwTimeout, dwMinTimeout), QS_POSTMESSAGE, MWMO_INPUTAVAILABLE ); } DWORD _MyMWFMO(DWORD dwTimeout, DWORD dwMinTimeout = 1); //dwTimeout in seconds #define MyMWFMO(dwTimeout) _MyMWFMO(dwTimeout) // wait until a timeout happens or we get a service finished event DWORD _MyMWFMO(DWORD dwTimeout, DWORD dwMinTimeout) { DWORD dwRet = WAIT_TIMEOUT; while (1) { dwRet = MWFMO(dwTimeout, dwMinTimeout); if (WAIT_TIMEOUT == dwRet) { DEBUGMSG("WUAUENG MWFMO timed out"); // dwRet = WAIT_TIMEOUT; goto Done; } else if (WAIT_OBJECT_0 + ISERVICE_FINISHED == dwRet) { DEBUGMSG("WUAUENG MWFMO Service Finished"); dwRet = WAIT_SERVICE_FINISHED; goto Done; } else if (WAIT_OBJECT_0 + ISERVICE_DISABLED == dwRet) { DEBUGMSG("WUAUENG MWFMO Engine Changed to Disabled\n"); dwRet = WAIT_SERVICE_DISABLED; goto Done; } else { //we expect possible meaningful message here //leave it untouch in the queue MSG msg; if (0 == PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { DEBUGMSG("MyMWFMO got no message. Possible error!"); continue; //no message got } if ( ((AUMSG_ENG_START <= msg.message) && (AUMSG_ENG_END >= msg.message)) || (WM_QUIT == msg.message)) { DEBUGMSG("MyMWFMO got user msg %#lx, repost it to the queue", msg.message); PostThreadMessage(gdwWorkerThreadId, msg.message, msg.wParam, msg.lParam); dwRet = msg.message; goto Done; } else { DEBUGMSG("WUAUENG MWFMO got system message %#lx", msg.message); TranslateMessage( &msg ); DispatchMessage( &msg ); } } } Done: if (WAIT_SERVICE_FINISHED != dwRet) { removeLastWaitKey(); } return dwRet; } //** WaitForConnection() will wait for connection or //** will bail out if Service Finished or service disabled or getting a msg if fIgnoreNonDetectMsg is FALSE DWORD WaitForConnection(BOOL fIngoreNonDetectMsg = FALSE) { static BOOL s_fWasConnected = FALSE; DWORD dwRet; DEBUGMSG("WUAUENG : Polling for connection"); do { do { if (AUSTATE_DETECT_PENDING == gpState->GetState() && gpState->IsUnableToConnect()) { CAUEventLog aueventlog(g_hInstance); aueventlog.LogEvent( EVENTLOG_WARNING_TYPE, IDS_MSG_Download, IDS_MSG_UnableToConnect); gpState->SetDetectionStartTime(TRUE); } if (IsConnected(gpState->GetIdentServerURL(), !gpState->fInCorpWU())) { break; } dwRet = MyMWFMO(AU_TEN_MINS); if (WAIT_TIMEOUT != dwRet) { if (!fIngoreNonDetectMsg ||!IsValidAUMsg(dwRet) || AUMSG_DETECT == dwRet ) { goto Done; } } s_fWasConnected = FALSE; } while (1); if (!s_fWasConnected) { DEBUGMSG("Found Connection"); if (AUSTATE_DOWNLOAD_PENDING != gpState->GetState()) { BOOL fWait = TRUE; #ifdef DBG //wait 5 mins by Default if 'ConnectWait' regvalue does not exist // or is set to 1 DWORD dwConnectWait; if (SUCCEEDED(GetRegDWordValue(REG_AUCONNECTWAIT, &dwConnectWait)) && 0 == dwConnectWait) { fWait = FALSE; } #endif if (fWait) { DEBUGMSG("Wait for 5 mins "); dwRet = MyMWFMO(AU_FIVE_MINS); if (WAIT_TIMEOUT != dwRet) { if (!fIngoreNonDetectMsg || !IsValidAUMsg(dwRet) || AUMSG_DETECT == dwRet) { goto Done; } } } if (!IsConnected(gpState->GetIdentServerURL(), !gpState->fInCorpWU())) { continue; } } s_fWasConnected = TRUE; } break; } while (1); DEBUGMSG("WUAUENG : Connection found. Polling end"); dwRet = WAIT_CONNECTION_FOUND; Done: return dwRet; } HRESULT PauseDownload(BOOL fPause) { HRESULT hrRet = E_FAIL; if (NULL != gpAUcatalog) { hrRet = gpAUcatalog->m_audownloader.DrizzleOperation(fPause? DRIZZLEOPS_PAUSE: DRIZZLEOPS_RESUME); } DEBUGMSG("PauseDownload return %#lx", hrRet); return hrRet; } HRESULT GetEvtHandles(AUEVTHANDLES *pAuEvtHandles) { HRESULT hr = E_FAIL; HANDLE hSourceProcess = NULL; //#define IEVT_ENGINESTATE 0 #define IEVT_NOTIFYCLIENT 0 struct { HANDLE hSource; HANDLE hTarget; } rhandles [] = {/*{ghEngineState,0},*/{ghNotifyClient,0}}; if (NULL == ghClientHandles.hClientProcess()) { goto Done; } hSourceProcess = GetCurrentProcess(); for ( int i = 0; i < (ARRAYSIZE(rhandles)); i++) { if (!DuplicateHandle( hSourceProcess, // handle to source process rhandles[i].hSource, // handle to duplicate ghClientHandles.hClientProcess(), // handle to target process &rhandles[i].hTarget, // duplicate handle 0, // requested access FALSE, // handle inheritance option DUPLICATE_SAME_ACCESS // optional actions )) { DEBUGMSG("WUAUENG DuplicateHandle for rhandles[%d] failed with %#lx", i, GetLastError()); //should not close target handle cuz it is in target process goto Done; } } #ifdef _WIN64 pAuEvtHandles->ulNotifyClient = (LONG64) rhandles[IEVT_NOTIFYCLIENT].hTarget; #else pAuEvtHandles->ulNotifyClient = (LONG) rhandles[IEVT_NOTIFYCLIENT].hTarget; #endif hr = S_OK; Done: return hr; } DWORD AvailableSessions(void) { DWORD dwRet = 0; // // For win2K, because we're not deleting sessions as soon as we receive // logoff notifications, the array gAdminSesssion might be out-of-date // in a given point in time. We need to validate the array before // saying to client things about it. // Remove any old sessions from our array if there's any // if (IsWin2K()) { DEBUGMSG("WUAUENG Client is querying the number of sessions available; forcing rebuilt of the session cache (win2k)"); gAdminSessions.ValidateCachedSessions(); } for (int iSession = 0; iSession < gAdminSessions.CSessions(); iSession++) { DWORD dwAdminSession; if (gAdminSessions.m_FGetNextSession(&dwAdminSession) && FSessionActive(dwAdminSession)) { dwRet ++; } } // DEBUGMSG("AvailableSessions return %d", dwRet); return dwRet; } HRESULT HrCreateNewCatalog() { HRESULT hr = E_FAIL; SafeDeleteNULL(gpAUcatalog); gpAUcatalog = new AUCatalog; if (NULL == gpAUcatalog) { goto Done; } if (FAILED(hr = gpAUcatalog->Init())) { SafeDeleteNULL(gpAUcatalog); } Done: return hr; } /* This function will return only in case of selfupdate. Normally it will keep on looping * sleeping on various timeouts when necessary */ DWORD CompleteLastMyMWFMO(void) { DWORD dwRet = WAIT_NOT_NEEDED; DWORD dwTimeout; HRESULT hr; hr = getLastWaitTimeout(&dwTimeout); MyMWFMO(0); //call user api to create the thread queue SetEvent(ghWorkerThreadMsgQueueCreation); if (FAILED(hr)) { DEBUGMSG("WUAUENG no need to complete last wait"); goto Done; } DEBUGMSG("WUAUENG is going to complete last wait %d ", dwTimeout); dwRet = MyMWFMO(dwTimeout); Done: return dwRet; } ////////////////////////////////////////////////////////////////////////////////////////////////// // return S_FALSE if selfupdate happens // return S_OK otherwise // pdwRet points to return code: service finish, service disabled, selfupdate done, or detect msg HRESULT PerformSelfUpdate(DWORD *pdwRet) { HRESULT hr= S_OK; DEBUGMSG("Doing selfupdate"); DEBUGMSG("Selfupdate waiting for connection"); DEBUGMSG("Wait for internet connection..."); while ( WAIT_CONNECTION_FOUND == (*pdwRet = WaitForConnection(TRUE)) && ((hr = SelfUpdate()) != S_OK) ) { switch (hr) { case S_FALSE: //selfupdate happens DEBUGMSG("Telling wuauserv.dll to reload wuaueng.dll"); gPingStatus.PingSelfUpdate(TRUE, URLLOGSTATUS_Pending, 0); *pdwRet = WAIT_DONE; goto Done; default: DEBUGMSG("Error during selfupdate (%#lx), timeout=%d secs", hr, dwTimeToWait(AU_ONE_DAY)); gPingStatus.PingSelfUpdate(TRUE, URLLOGSTATUS_Failed, hr); *pdwRet = MyMWFMO(AU_ONE_DAY); if (WAIT_TIMEOUT != *pdwRet) { DEBUGMSG("WUAUENG need to abort wait during SelfUpdate"); goto Done; } break; } } if (WAIT_CONNECTION_FOUND == *pdwRet) { *pdwRet = WAIT_DONE; } DEBUGMSG("Finished self update cycle"); Done: return hr; } void ResumeDownloadIfNeccesary(void) { if (FDownloadIsPaused()) { PauseDownload(FALSE); DEBUGMSG("WUAUENG Resuming download job"); } } void PingSuccessfulDownloads(void) { UINT uItemCount = gpAUcatalog->m_ItemList.Count(); for (UINT i = 0; i < uItemCount; i++) { AUCatalogItem &item = gpAUcatalog->m_ItemList[i]; if (item.fSelected()) { BSTR bstrItemId = item.bstrID(); if (NULL != bstrItemId) { USES_IU_CONVERSION; gPingStatus.PingDownload( TRUE, URLLOGSTATUS_Success, 0, W2T(bstrItemId)); } #ifdef DBG else { DEBUGMSG("WUAUENG title for item %d is NULL!", i); } #endif } } } void ResetState(void) { gpState->SetState(AUSTATE_DETECT_PENDING); PostThreadMessage(gdwWorkerThreadId, AUMSG_DETECT, 0, 0); } void ResetState(BOOL *pfWaitB4Detect, DWORD *pdwWaitB4Detect, BOOL fError) { AUASSERT(NULL != pfWaitB4Detect); AUASSERT(NULL != pdwWaitB4Detect); *pfWaitB4Detect = TRUE; *pdwWaitB4Detect = fError ? AU_FIVE_HOURS : RandomWaitTimeBeforeDetect(); ResetState(); } HRESULT UpdateProc(WORKER_THREAD_INIT_DATA & initData) { HRESULT hr = S_OK; DWORD dwRet; DWORD dwLastWait ; BOOL fReloadAfterSelfUpdate = FALSE; MSG msg; UINT uFirstMsg; static BOOL s_fWaitBeforeDetect; static DWORD s_dwWaitB4Detect; uFirstMsg = initData.uFirstMsg; s_fWaitBeforeDetect = initData.fWaitB4Detect; s_dwWaitB4Detect = initData.dwWaitB4Detect; dwLastWait = CompleteLastMyMWFMO(); switch (dwLastWait) { case WAIT_SERVICE_FINISHED: goto Done; case WAIT_TIMEOUT: case WAIT_NOT_NEEDED: if (IsValidAUMsg(uFirstMsg)) { DEBUGMSG("Update post first msg %#x", uFirstMsg); PostThreadMessage(gdwWorkerThreadId, uFirstMsg, 0, 0); } break; case WAIT_SERVICE_DISABLED: default: //msg got break; } DWORD dwRet2 ; while(WAIT_OBJECT_0 + 1 == (dwRet2 = MsgWaitForMultipleObjectsEx(1, &ghServiceFinished, INFINITE, QS_POSTMESSAGE, MWMO_INPUTAVAILABLE ))) { if (0 == PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { DEBUGMSG("WUAUENG no message to retrieve. Possible error!"); continue; } if (WM_QUIT == msg.message) { break; } switch(msg.message) { case AUMSG_INIT: { //we wait for the first connection so that we don't prompt users who never //make a connection to the internet. no need to have them config AU if they have no //intention of using the internet dwRet = WaitForConnection(); switch (dwRet) { case WAIT_SERVICE_FINISHED: DEBUGMSG("WUAUENG detected that Service finished during WaitForConnection in AUSTATE_OUTOFBOX"); goto Done; case WAIT_CONNECTION_FOUND: break; case WAIT_SERVICE_DISABLED: default: continue; } dwRet = dwLastWait; if (WAIT_NOT_NEEDED == dwLastWait) { //there was no previous wait for 24 hours DEBUGMSG("WUAUENG Out of box, waiting 24 hours (%d secs)", dwSecsToWait(AU_ONE_DAY)); dwRet = MyMWFMO(AU_ONE_DAY); } switch (dwRet) { case WAIT_TIMEOUT: { #if 0 //commented out for bug 493789 DWORD dwRet4; hr = PerformSelfUpdate(&dwRet4); if (WAIT_SERVICE_FINISHED == dwRet4) { setLastWaitTimeout(0); //no wait next time goto Done; } #endif gpState->SetState(AUSTATE_NOT_CONFIGURED); DEBUGMSG("WUAUENG waiting for user to configure AU"); #if 0 //commented out for bug 493789 if (S_FALSE == hr) { fReloadAfterSelfUpdate = TRUE; goto Done; } #endif break; } case WAIT_SERVICE_FINISHED: goto Done; case WAIT_SERVICE_DISABLED: default: continue; } break; } case AUMSG_EULA_ACCEPTED: DEBUGMSG("WUAUENG Msg:Eula accepted, state -> Detect Pending"); ResetState(); break; case AUMSG_DETECT: if (s_fWaitBeforeDetect) { s_fWaitBeforeDetect = FALSE; DEBUGMSG("WUAUENG Wait %d secs before detection", s_dwWaitB4Detect); DWORD dwRet3 = MyMWFMO(s_dwWaitB4Detect); s_dwWaitB4Detect = 0; if (WAIT_SERVICE_DISABLED == dwRet3) { break; } if (WAIT_SERVICE_FINISHED == dwRet3) { goto Done; } } DEBUGMSG("WUAUENG Msg:Detect"); DEBUGMSG("--------------------------------------------------------"); DEBUGMSG("Set new detection start time"); gpState->SetDetectionStartTime(FALSE); DEBUGMSG("Read in au option"); if (FAILED(hr = gpState->HrInit())) { DEBUGMSG("AU state object fail to init with error %#lx", hr); goto Done; } CancelDownload(); if (FAILED(HrCreateNewCatalog())) { //fixcode: what is the expected behavior here? return E_FAIL; } hr = PerformSelfUpdate(&dwRet); if (S_FALSE == hr) { fReloadAfterSelfUpdate = TRUE; goto Done; } switch (dwRet) { case WAIT_SERVICE_FINISHED: goto Done; case WAIT_SERVICE_DISABLED: gpState->RemoveDetectionStartTime(); continue; case WAIT_DONE: break; default: //detect msg got DEBUGMSG("Detect msg got while detecting"); continue; } hr = gpAUcatalog->DetectItems(); if (FServiceDisabled()) { gpState->RemoveDetectionStartTime(); break; } if (SUCCEEDED(hr)) { //Restart counting period w/ no connection at the beginning of the next cycle. gpState->RemoveDetectionStartTime(); } if (S_OK == hr) { DEBUGMSG("WUAUENG Catalog built"); gpState->SetState(AUSTATE_DETECT_COMPLETE); DEBUGMSG("WUAUENG State->Detect complete"); } else if (S_FALSE == hr) { DEBUGMSG("WUAUENG No items in catalog, sleeping a while before next detection"); ResetState(&s_fWaitBeforeDetect,&s_dwWaitB4Detect, FALSE); //wait normal interval time } else { DEBUGMSG("WUAUENG Couldn't build catalog"); ResetState(&s_fWaitBeforeDetect, &s_dwWaitB4Detect, TRUE); //wait shorter time cuz of error } break; case AUMSG_DOWNLOAD: { DEBUGMSG("WUAUENG Msg:Download"); gfDownloadStarted = FALSE; gpState->SetState(AUSTATE_DOWNLOAD_PENDING); dwRet =WaitForConnection(TRUE); switch (dwRet) { case WAIT_SERVICE_FINISHED: DEBUGMSG("WUAUENG detected that Service finished during WaitForConnection in AUMSG_DOWNLOAD"); goto Done; case WAIT_CONNECTION_FOUND: break; case WAIT_SERVICE_DISABLED: default: continue; } if (S_OK != gpAUcatalog->ValidateItems(TRUE)) { DEBUGMSG("WUAUENG Catalog validation failed or no items, State->Detect Pending"); ResetState(); break; } DEBUGMSG("WUAUENG catalog:validateCatalog finished"); EngineEvents.CreateEvents(); // queue up items for download if (S_OK != (hr = gpAUcatalog->DownloadItems())) { if (S_FALSE == hr) { DEBUGMSG("WUAUENG Catalog download items skipped because no items were selected"); } else { DEBUGMSG("WUAUENG Catalog download items failed"); } EngineEvents.CloseEvents(); ResetState(&s_fWaitBeforeDetect,&s_dwWaitB4Detect, FALSE); break; } gfDownloadStarted = TRUE; ghClientHandles.ClientStateChange(); //notify client again for status change do { dwRet = MsgWaitForMultipleObjectsEx( EngineEvents.cEvents(), EngineEvents.grEventHandles(), INFINITE, QS_POSTMESSAGE, MWMO_INPUTAVAILABLE ); if (WAIT_OBJECT_0 + IDOWNLOAD_COMPLETE_EVT == dwRet) //Download finished { if (FDisabledDuringDownload()) { goto CloseHandle; } DEBUGMSG("WUAUENG file download done"); ghClientHandles.ClientRemoveTrayIcon(); //Validate downloaded cabs before continuing AUASSERT(gpAUcatalog); BSTR bstrErrorItemId = NULL; //should not be freed if(FAILED(hr = gpAUcatalog->ValidateDownloadedCabs(&bstrErrorItemId))) { USES_IU_CONVERSION; DEBUGMSG("ValidateDownloadedCabs: Checksum failed, error: %#lx", hr); //bstrErrorItemId will be NULL if it was some error other than ERROR_CRC if(NULL != bstrErrorItemId) { //Pingback failure with itemId gPingStatus.PingDownload( TRUE, URLLOGSTATUS_Failed, hr, W2T(bstrErrorItemId)); } //Reset to detect pending ResetState(&s_fWaitBeforeDetect,&s_dwWaitB4Detect, TRUE); //wait shorter time cuz of error goto CloseHandle; } if (!gpState->fOptionSchedInstall()) { DEBUGMSG("WUAUENG download complete, ready for UNSCHEDULED install"); LogEvent_ItemList( EVENTLOG_INFORMATION_TYPE, IDS_MSG_Installation, IDS_MSG_InstallReady_Unscheduled); } PingSuccessfulDownloads(); DEBUGMSG("WUAUENG validating items to prune out items already installed"); if ( gpAUcatalog->ValidateItems(FALSE) != S_OK) { DEBUGMSG("WUAUENG Validation failed OR no items left in catalog, State->Detect Pending"); ResetState(); } else { DEBUGMSG(" Items still applicable, State->Download Complete"); gpState->SetState(AUSTATE_DOWNLOAD_COMPLETE); } break; } else if (((WAIT_OBJECT_0 + IDOWNLOAD_TRANSIENT_ERROR_EVT) == dwRet) || //Transient error - connection lost ((WAIT_OBJECT_0 + IDOWNLOAD_DOWNLOAD_IN_PROGRESS) == dwRet)) //Download in progress - connection recuperated { BOOL fCheckDisconnect; fCheckDisconnect = ((WAIT_OBJECT_0 + IDOWNLOAD_DOWNLOAD_IN_PROGRESS) == dwRet); //AuStateAux = GetState(); // setState again (and trigger Engine Change State) only if: // - It is not disconnected and you got Transient Error Event // - It is disconnected and you got a Download in Progress Event if ( fCheckDisconnect == gpState->fDisconnected() ) { gpState->SetDisconnected(!fCheckDisconnect); gpState->SetState(AUSTATE_DOWNLOAD_PENDING); //relaunch client if it is not launched and notify client of new state } } else if ((WAIT_OBJECT_0 + IDOWNLOAD_SERVICE_FINISH) == dwRet) //Service Finished { DEBUGMSG("WUAUENG Detected Service Finished while wating for download to be done"); goto Done; } else if ((WAIT_OBJECT_0 + IDOWNLOAD_SERVICE_DISABLED) == dwRet) //Engine State, should be due to disabled { if (FDisabledDuringDownload()) { goto CloseHandle; } } else if ((WAIT_OBJECT_0 + IDOWNLOAD_DOWNLOAD_CANCELED) == dwRet) { if (JOB_ERROR == gpAUcatalog->m_audownloader.m_FinishReason) { DEBUGMSG("WUAUENG got error during download, wait for sometime b4 redetect"); ResetState(&s_fWaitBeforeDetect,&s_dwWaitB4Detect, TRUE); //wait shorter time cuz of error } else { DEBUGMSG("WUAUENG download job got canceled, State -> Detect Pending"); ResetState(); } goto CloseHandle; } else if ((WAIT_OBJECT_0 + IDOWNLOAD_MESSAGE) == dwRet) //Messages { MSG msg2; PeekMessage(&msg2, NULL, NULL, NULL, PM_REMOVE); //we don't expect meaningful message here TranslateMessage(&msg2);// Translates virtual key codes DispatchMessage(&msg2); // Dispatches message to window if ( msg2.message != WM_USER ) //WM_USER is the one to pump to drizzle for processing { DEBUGMSG("WUAUENG dispatched message %#lx during downloading", msg2.message); } } else if (((WAIT_ABANDONED_0 + IDOWNLOAD_COMPLETE_EVT) == dwRet) || ((WAIT_ABANDONED_0 + IDOWNLOAD_TRANSIENT_ERROR_EVT) == dwRet)|| ((WAIT_ABANDONED_0 + IDOWNLOAD_DOWNLOAD_IN_PROGRESS) == dwRet)|| ((WAIT_ABANDONED_0 + IDOWNLOAD_SERVICE_FINISH) == dwRet)|| (WAIT_FAILED == dwRet)) { //fixcode: when will this actually happen? DEBUGMSG("WUAUENG Error in Download Loop with MsgWaitForMultipleObjectsEx"); ResetState(); goto CloseHandle; } else { DEBUGMSG("WUAUENG Unexpected returned value dwRet = %d in n Download Loop with MsgWaitForMultipleObjectsEx", dwRet); } } while ( 1 ); CloseHandle: EngineEvents.CloseEvents(); } break; case AUMSG_POST_INSTALL: { DEBUGMSG("WUAUENG install done, sleeping a while before next detection"); // fixcode: should use ResetState() instead s_fWaitBeforeDetect = TRUE; s_dwWaitB4Detect = RandomWaitTimeBeforeDetect(); PostThreadMessage(gdwWorkerThreadId, AUMSG_DETECT, 0, 0); break; } case AUMSG_VALIDATE_CATALOG: DEBUGMSG("WUAUENG: validating catalog offline"); if ( gpAUcatalog->ValidateItems(FALSE) != S_OK) { DEBUGMSG("WUAUENG Validation failed OR no items left in catalog, State->Detect Pending"); ResetState(); } SetEvent(ghValidateCatalog); break; case AUMSG_LOG_EVENT: DEBUGMSG("WUAUENG: logging the Ready To Install (Scheduled) event"); if (gpState->fShouldScheduledInstall()) { LogEvent_ScheduledInstall(); } break; default: DEBUGMSG("WUAUENG Received unknown msg %#lx", msg.message); TranslateMessage( &msg ); DispatchMessage( &msg ); break; } } if (WAIT_OBJECT_0 == dwRet2) { DEBUGMSG("Update() exit in response to service finish event"); } Done: DEBUGMSG("WUAUENG Update func returning"); // we may ask wuauserv.dll to reload us. return fReloadAfterSelfUpdate ? S_FALSE : S_OK; } void saveSelection(VARIANT *selection) { // fixcode this return should return an error long n = 0; DEBUGMSG("Start saveSelection"); WaitForSingleObject(ghMutex, INFINITE); if ( FAILED(SafeArrayGetUBound(selection->parray, 1, &n))) { DEBUGMSG("WUAUENG SafeArrayGetUBond failed"); goto done; } if (((n + 1) / 2) != gpAUcatalog->m_ItemList.Count()) { AUASSERT(FALSE); DEBUGMSG("WUAUENG got unmatched number of items from client"); goto done; } for ( long i = 0; i < (n + 1) / 2; i++ ) { long dex = i * 2; VARIANT var; VariantInit(&var); if ( FAILED(SafeArrayGetElement(selection->parray, &dex, &var)) ) { DEBUGMSG("SafeArrayGetElement failed"); continue; } BOOL fMatch = (WUCompareStringI(var.bstrVal, gpAUcatalog->m_ItemList[i].bstrID()) == CSTR_EQUAL); VariantClear(&var); if ( fMatch ) { if ( SUCCEEDED(SafeArrayGetElement(selection->parray, &++dex, &var)) ) { gpAUcatalog->m_ItemList[i].SetStatus(var.lVal); // DEBUGMSG("Status for item %S is now %d", gpAUcatalog->m_ItemList[i].bstrID(), gpAUcatalog->m_ItemList[i].dwStatus()); } } else { DEBUGMSG("item ids did not match for saving selections"); } } // gpAUcatalog->m_ItemList.DbgDump(); gpAUcatalog->Serialize(); done: ReleaseMutex(ghMutex); DEBUGMSG("End saveSelection"); } HRESULT StartDownload(void) { //DEBUGMSG("WUAUENG ::StartDownload called"); if ( AUSTATE_DETECT_COMPLETE != gpState->GetState() ) { DEBUGMSG("WUAUENG ::StartDownload state incorrect"); return E_FAIL; } PostThreadMessage(gdwWorkerThreadId, AUMSG_DOWNLOAD, 0, 0); return S_OK; } HRESULT GetUpdatesList(VARIANT *vList) { HRESULT hr = E_FAIL; DWORD dwWait; dwWait = WaitForSingleObject(ghMutex, INFINITE); if (WAIT_FAILED == dwWait) { DEBUGMSG("WUAUENG GetUpdateList got WAIT_ABANDONED"); } else DEBUGMSG("WUAUENG Getting Updates list dWait=%d",dwWait); if ( (AUSTATE_DETECT_COMPLETE != gpState->GetState()) && (AUSTATE_DOWNLOAD_COMPLETE != gpState->GetState()) ) { goto Done; } hr = gpAUcatalog->getUpdatesList(vList); Done: ReleaseMutex(ghMutex); return hr; } HRESULT GetInstallXML(/*[out]*/ BSTR *pbstrCatalogXML, /*[out]*/ BSTR *pbstrDownloadXML) { // DEBUGMSG("::GetInstallXML"); HRESULT hr = E_FAIL; DWORD dwWait; dwWait = WaitForSingleObject(ghMutex, INFINITE); if (WAIT_FAILED == dwWait) { DEBUGMSG("WUAUENG GetInstallXML got WAIT_ABANDONED"); } else { DEBUGMSG("WUAUENG Getting Updates list dWait=%d",dwWait); } hr = gpAUcatalog->GetInstallXML(pbstrCatalogXML, pbstrDownloadXML); //Done: ReleaseMutex(ghMutex); return hr; } HRESULT GetDownloadStatus(UINT *pPercentage, DWORD *pdwnldStatus, BOOL fCareAboutConnection) { DWORD dwComplete; DWORD dwstatus; HRESULT hr; if ( AUSTATE_DOWNLOAD_PENDING != gpState->GetState() ) { *pPercentage = (AUSTATE_DOWNLOAD_COMPLETE == gpState->GetState()) ? 100 : 0 ; *pdwnldStatus = DWNLDSTATUS_DOWNLOADING; //for trayicon to show 100% //DEBUGMSG("WUAUENG %% complete = %d", *pPercentage); return S_OK; } *pPercentage = 0; if (fCareAboutConnection && !gfDownloadStarted) { DEBUGMSG("WUAUENG Download status is checking for connection"); *pdwnldStatus = DWNLDSTATUS_CHECKING_CONNECTION; return S_OK; } WaitForSingleObject(ghMutex, INFINITE); hr = gpAUcatalog->m_audownloader.getStatus(&dwComplete, &dwstatus); ReleaseMutex(ghMutex); if (FAILED(hr)) { *pdwnldStatus = DWNLDSTATUS_NOT_DOWNLOADING; return S_OK; } *pPercentage = (int)dwComplete; //DEBUGMSG("WUAUENG %% complete = %d", *pPercentage); switch (dwstatus) { case BG_JOB_STATE_TRANSFERRING: case BG_JOB_STATE_TRANSFERRED: //for trayicon to show 100%, as in the beginning of the routine { *pdwnldStatus = DWNLDSTATUS_DOWNLOADING; break; } case BG_JOB_STATE_SUSPENDED: { *pdwnldStatus = DWNLDSTATUS_PAUSED; break; } case BG_JOB_STATE_ERROR: case BG_JOB_STATE_TRANSIENT_ERROR: case BG_JOB_STATE_ACKNOWLEDGED: case BG_JOB_STATE_CANCELLED: case BG_JOB_STATE_QUEUED: case BG_JOB_STATE_CONNECTING: { *pdwnldStatus = DWNLDSTATUS_NOT_DOWNLOADING; break; } default: { DEBUGMSG("WUAUENG GetDownloadStatus got an unexpected BG_JOB_STATE %d", dwstatus); *pdwnldStatus = DWNLDSTATUS_NOT_DOWNLOADING; } } return S_OK; }