//======================================================================= // // Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. // // File: install.cpp // // Description: // // Implementation for the Install() function // //======================================================================= #include "iuengine.h" // PCH - must include first #include #include #include #include #include #include #include #include #include "history.h" #include "iuxml.h" //#include #include #include #define AVERAGE_IDENTITY_SIZE_PER_ITEM 200 #define SafeFreeLibrary(x) if (NULL != x) { FreeLibrary(x); x = NULL; } const TCHAR SFCDLL[] = _T("sfc.dll"); const TCHAR SYSTEMRESTOREDESCRIPTION[] = _T("Windows Update V4"); const CHAR SZ_INSTALL_FINISHED[] = "Install finished"; const CHAR SZ_INSTALLASYNC_FAILED[] = "Asynchronous Install failed during startup"; typedef BOOL (WINAPI * PFN_SRSetRestorePoint)(PRESTOREPOINTINFO pRestorePtSpec, PSTATEMGRSTATUS pSMgrStatus); typedef struct IUINSTALLSTARTUPINFO { BSTR bstrXmlClientInfo; BSTR bstrXmlCatalog; BSTR bstrXmlDownloadedItems; BSTR bstrOperationUUID; LONG lMode; IUnknown *punkProgressListener; HWND hwnd; CEngUpdate* pEngUpdate; } IUINSTALLSTARTUPINFO, *PIUINSTALLSTARTUPINFO; DWORD WINAPI InstallThreadProc(LPVOID lpv); class CIUInstall { public: CIUInstall(BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrXmlDownloadedItems, BSTR bstrOperationUUID, LONG lMode, IUnknown *punkProgressListener, HWND hWnd); ~CIUInstall(); public: HRESULT ProcessInstallCatalog(CEngUpdate* pEngUpdate); HRESULT GetXmlItemsBSTR(BSTR *pbstrXmlItems); private: HRESULT RecursiveInstallDependencies(HANDLE_NODE hItem, CEngUpdate* pEngUpdate); HRESULT DoInstall(HANDLE_NODE hItem, CEngUpdate* pEngUpdate); void RemoveDownloadTemporaryFolders(LPCTSTR pszComponentPath); void PingServerForInstall(HRESULT hr, HANDLE_NODE hItem, PHANDLE phEvtNeedToQuit, LPCTSTR lpszDeviceId=NULL, BOOL fExclusive=FALSE); private: BSTR m_bstrXmlClientInfo; BSTR m_bstrClientName; BSTR m_bstrXmlCatalog; BSTR m_bstrOperationUUID; BSTR m_bstrXmlResult; LONG m_lMode; IProgressListener* m_pProgressListener; HWND m_hWnd; CXmlCatalog m_xmlCatalog; CXmlItems m_xmlItems; CXmlItems *m_pxmlDownloadedItems; CXmlClientInfo m_xmlClientInfo; CIUHistory m_history; CUrlLog m_pingSvr; DWORD m_dwStatus; LPTSTR m_pszInstalledItemsList; LPTSTR m_pszItemDownloadPathListForDelete; LONG m_lInstalledItemsListAllocatedLength; LONG m_lItemDownloadPathListForDeleteLength; LONG m_lItemCount; LONG m_lItemsCompleted; BOOL m_fAbort; BOOL m_fSomeItemsSuccessful; }; CIUInstall::CIUInstall(BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrXmlDownloadedItems, BSTR bstrOperationUUID, LONG lMode, IUnknown *punkProgressListener, HWND hWnd) : m_pProgressListener(NULL), m_bstrXmlClientInfo(NULL), m_bstrClientName(NULL), m_bstrXmlCatalog(NULL), m_bstrOperationUUID(NULL), m_bstrXmlResult(NULL), m_lMode(lMode), m_hWnd(hWnd), m_dwStatus(0), m_pszInstalledItemsList(NULL), m_pszItemDownloadPathListForDelete(NULL), m_lInstalledItemsListAllocatedLength(0), m_lItemDownloadPathListForDeleteLength(0), m_lItemCount(0), m_lItemsCompleted(0), m_fAbort(FALSE), m_fSomeItemsSuccessful(FALSE), m_pxmlDownloadedItems(NULL) { USES_IU_CONVERSION; m_bstrXmlClientInfo = SysAllocString(bstrXmlClientInfo); m_bstrXmlCatalog = SysAllocString(bstrXmlCatalog); m_bstrOperationUUID = SysAllocString(bstrOperationUUID); if (NULL != punkProgressListener) { punkProgressListener->QueryInterface(IID_IProgressListener, (void**)&m_pProgressListener); } m_pxmlDownloadedItems = new CXmlItems(TRUE); if (NULL != m_pxmlDownloadedItems) { m_pxmlDownloadedItems->LoadXMLDocument(bstrXmlDownloadedItems); } } CIUInstall::~CIUInstall() { SysFreeString(m_bstrXmlClientInfo); SysFreeString(m_bstrClientName); SysFreeString(m_bstrXmlCatalog); SysFreeString(m_bstrOperationUUID); SafeReleaseNULL(m_pProgressListener); SafeHeapFree(m_pszInstalledItemsList); SafeHeapFree(m_pszItemDownloadPathListForDelete); SysFreeString(m_bstrXmlResult); if (NULL != m_pxmlDownloadedItems) { delete m_pxmlDownloadedItems; } } HRESULT CIUInstall::GetXmlItemsBSTR(BSTR *pbstrXmlItems) { if (NULL != m_bstrXmlResult) *pbstrXmlItems = SysAllocString(m_bstrXmlResult); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // Install() // // Do synchronous installation. // Input: // bstrXmlCatalog - the xml catalog portion containing items to be installed // bstrXmlDownloadedItems - the xml of downloaded items and their respective download // result as described in the result schema. Install uses this // to know whether the items were downloaded and if so where they // were downloaded to so that it can install the items // punkProgressListener - the callback function pointer for reporting install progress // hWnd - the event msg window handler passed from the stub // Output: // pbstrXmlItems - the items with installation status in xml format // e.g. // ///////////////////////////////////////////////////////////////////////////// HRESULT WINAPI CEngUpdate::Install(BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrXmlDownloadedItems, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR *pbstrXmlItems) { HRESULT hr; if ((DWORD) lMode & (DWORD) UPDATE_OFFLINE_MODE) { m_fOfflineMode = TRUE; } else { m_fOfflineMode = FALSE; } LogMessage("Install started"); CIUInstall iuInstall(bstrXmlClientInfo, bstrXmlCatalog, bstrXmlDownloadedItems, NULL, lMode, punkProgressListener, hWnd); hr = iuInstall.ProcessInstallCatalog(this); iuInstall.GetXmlItemsBSTR(pbstrXmlItems); return hr; } HRESULT CIUInstall::ProcessInstallCatalog(CEngUpdate* pEngUpdate) { LOG_Block("ProcessInstallCatalog()"); // clear any previous cancel event ResetEvent(pEngUpdate->m_evtNeedToQuit); HRESULT hr = S_OK, hrString = S_OK; HANDLE_NODE hCatalogItemList = HANDLE_NODELIST_INVALID; HANDLE_NODE hProviderList = HANDLE_NODELIST_INVALID; HANDLE_NODE hDependentItemList = HANDLE_NODELIST_INVALID; HANDLE_NODE hItem = HANDLE_NODE_INVALID; HANDLE_NODE hDependentItem = HANDLE_NODE_INVALID; HANDLE_NODE hProvider = HANDLE_NODE_INVALID; BSTR bstrPlatform = NULL; BSTR bstrUniqueIdentity = NULL; BSTR bstrProviderName = NULL; BSTR bstrProviderPublisher = NULL; BSTR bstrProviderUUID = NULL; TCHAR szUniqueIdentitySearch[MAX_PATH]; HINSTANCE hSystemRestoreDLL = NULL; PFN_SRSetRestorePoint fpnSRSetRestorePoint = NULL; RESTOREPOINTINFO restoreInfo; STATEMGRSTATUS restoreStatus; BOOL fContinue = TRUE; LPTSTR ptszLivePingServerUrl = NULL; LPTSTR ptszCorpPingServerUrl = NULL; BOOL fPostWaitSuccess = TRUE; DWORD dwStatus = 0; USES_IU_CONVERSION; EventData evtData; ZeroMemory((LPVOID) &evtData, sizeof(evtData)); if (NULL == m_pxmlDownloadedItems) { // Error occured during object initialization, no Return Schema Available // Cannot continue hr = E_INVALIDARG; LOG_ErrorMsg(hr); goto CleanUp; } hr = m_xmlCatalog.LoadXMLDocument(m_bstrXmlCatalog, pEngUpdate->m_fOfflineMode); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } hr = m_xmlClientInfo.LoadXMLDocument(m_bstrXmlClientInfo, pEngUpdate->m_fOfflineMode); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } m_xmlClientInfo.GetClientName(&m_bstrClientName); if (NULL == m_bstrClientName) { hr = E_INVALIDARG; LOG_ErrorMsg(hr); goto CleanUp; } m_pingSvr.SetDefaultClientName(OLE2T(m_bstrClientName)); if (NULL != (ptszLivePingServerUrl = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR)))) { if (SUCCEEDED(g_pUrlAgent->GetLivePingServer(ptszLivePingServerUrl, INTERNET_MAX_URL_LENGTH))) { m_pingSvr.SetLiveServerUrl(ptszLivePingServerUrl); } else { LOG_Out(_T("failed to get live ping server URL")); } SafeHeapFree(ptszLivePingServerUrl); } else { LOG_Out(_T("failed to allocate memory for ptszLivePingServerUrl")); } if (NULL != (ptszCorpPingServerUrl = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR)))) { if (SUCCEEDED(g_pUrlAgent->GetCorpPingServer(ptszCorpPingServerUrl, INTERNET_MAX_URL_LENGTH))) { m_pingSvr.SetCorpServerUrl(ptszCorpPingServerUrl); } else { LOG_Out(_T("failed to get corp WU ping server URL")); } SafeHeapFree(ptszCorpPingServerUrl); } else { LOG_Out(_T("failed to allocate memory for ptszCorpPingServerUrl")); } m_xmlCatalog.GetItemCount(&m_lItemCount); SafeHeapFree(m_pszInstalledItemsList); m_lInstalledItemsListAllocatedLength = m_lItemCount * (AVERAGE_IDENTITY_SIZE_PER_ITEM * sizeof(TCHAR)); m_pszInstalledItemsList = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, m_lInstalledItemsListAllocatedLength); if (NULL == m_pszInstalledItemsList) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto CleanUp; } SafeHeapFree(m_pszItemDownloadPathListForDelete); m_lItemDownloadPathListForDeleteLength = m_lItemCount * (MAX_PATH * sizeof(TCHAR)); m_pszItemDownloadPathListForDelete = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, m_lItemDownloadPathListForDeleteLength); if (NULL == m_pszItemDownloadPathListForDelete) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto CleanUp; } // try to load the System Restore DLL (sfc.dll). hSystemRestoreDLL = LoadLibraryFromSystemDir(SFCDLL); if (NULL != hSystemRestoreDLL) { #ifdef UNICODE fpnSRSetRestorePoint = (PFN_SRSetRestorePoint)GetProcAddress(hSystemRestoreDLL, "SRSetRestorePointW"); #else fpnSRSetRestorePoint = (PFN_SRSetRestorePoint)GetProcAddress(hSystemRestoreDLL, "SRSetRestorePointA"); #endif if (NULL != fpnSRSetRestorePoint) { // Set the Restore Point ZeroMemory(&restoreInfo, sizeof(restoreInfo)); ZeroMemory(&restoreStatus, sizeof(restoreStatus)); restoreInfo.dwEventType = BEGIN_SYSTEM_CHANGE; restoreInfo.dwRestorePtType = APPLICATION_INSTALL; restoreInfo.llSequenceNumber = 0; hr = StringCchCopyEx(restoreInfo.szDescription, ARRAYSIZE(restoreInfo.szDescription), SYSTEMRESTOREDESCRIPTION, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } if (!fpnSRSetRestorePoint(&restoreInfo, &restoreStatus)) { // this will return FALSE if there is an error 'or' if its called from an OS without SystemRestore // support. SR is only supported on Professional and Personal SKU's of Whistler. if (ERROR_SUCCESS != restoreStatus.nStatus) { LOG_Software(_T("Failed SRSetRestorePoint Call, Error was: 0x%x"), restoreStatus.nStatus); LogError(restoreStatus.nStatus, "Install Set Restore Point"); } } } } // // added by JHou for bug#433 in IU db: send 0:N OnProgress event before the install begins // TCHAR szProgress[64]; hr = StringCchPrintfEx(szProgress, ARRAYSIZE(szProgress), NULL, NULL, MISTSAFE_STRING_FLAGS, _T("%lu:0"), (ULONG)m_lItemCount); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } evtData.bstrProgress = SysAllocString(T2OLE(szProgress)); if (NULL != m_pProgressListener) { m_pProgressListener->OnProgress(m_bstrOperationUUID, VARIANT_FALSE, evtData.bstrProgress, &evtData.lCommandRequest); } else { if (NULL != m_hWnd) { evtData.fItemCompleted = FALSE; evtData.bstrUuidOperation = SysAllocString(m_bstrOperationUUID); SendMessage(m_hWnd, UM_EVENT_PROGRESS, 0, LPARAM(&evtData)); } } // // Need to check for a cancel command returned from OnProgress // if (UPDATE_COMMAND_CANCEL == evtData.lCommandRequest) { LOG_Out(_T("OnProgress received UPDATE_COMMAND_CANCEL")); SetEvent(pEngUpdate->m_evtNeedToQuit); // asked to quit hr = E_ABORT; fContinue = FALSE; } // Install has a complexity in how we loop through to install each item. Basically // we have to handle any dependent Item installs before installing core Item // Since Detection will already have been done at this point we rely on the caller // to only give us the list of items that really need to be installed. What we'll do // is go through each item and before actually installing it we'll look for any // dependent items that are also in the catalog. If they are in the Catalog then it is // assumed it needs to be installed. This check is done recursively for each item. // start the base item loop. hProviderList = m_xmlCatalog.GetFirstProvider(&hProvider); while (HANDLE_NODE_INVALID != hProvider && fContinue) { m_xmlCatalog.GetIdentity(hProvider, &bstrProviderName, &bstrProviderPublisher, &bstrProviderUUID); SafeSysFreeString(bstrProviderName); SafeSysFreeString(bstrProviderPublisher); SafeSysFreeString(bstrProviderUUID); // Get the Enumerator List of Items in this Catalog, and get the first item hCatalogItemList = m_xmlCatalog.GetFirstItem(hProvider, &hItem); if ((HANDLE_NODELIST_INVALID == hCatalogItemList) || (HANDLE_NODE_INVALID == hItem)) { hr = E_FAIL; LOG_ErrorMsg(hr); goto CleanUp; } // // loop through each item in the catalog, calling the installer for each one // while (HANDLE_NODE_INVALID != hItem && fContinue) { BSTR bstrXmlItemForCallback = NULL; if (SUCCEEDED(m_xmlCatalog.GetBSTRItemForCallback(hItem, &bstrXmlItemForCallback))) { if (NULL != m_pProgressListener) { m_pProgressListener->OnItemStart(m_bstrOperationUUID, bstrXmlItemForCallback, &evtData.lCommandRequest); } else { if (NULL != m_hWnd) { evtData.bstrXmlData = bstrXmlItemForCallback; SendMessage(m_hWnd, UM_EVENT_ITEMSTART, 0, LPARAM(&evtData)); evtData.bstrXmlData = NULL; } } SysFreeString(bstrXmlItemForCallback); bstrXmlItemForCallback = NULL; if (UPDATE_COMMAND_CANCEL == evtData.lCommandRequest) { LOG_Out(_T("OnItemStart received UPDATE_COMMAND_CANCEL")); SetEvent(pEngUpdate->m_evtNeedToQuit); // asked to quit hr = E_ABORT; fContinue = FALSE; } else { // // check the global quit event. If quit, then server ping treat it as a cancel. // fContinue = (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0); } if (!fContinue) { continue; // or break, same effect. } } else { // // something wrong with this item, so we should skip it // // get the next item. hItem will be HANDLE_NODE_INVALID when there are no // remaining items. m_xmlCatalog.CloseItem(hItem); m_xmlCatalog.GetNextItem(hCatalogItemList, &hItem); continue; } // We have an Item in the Catalog to be Installed. First Look for any Top // level dependencies hDependentItemList = m_xmlCatalog.GetFirstItemDependency(hItem, &hDependentItem); if (HANDLE_NODELIST_INVALID != hDependentItemList) { hr = S_OK; while (S_OK == hr) { // walk each dependent Item and call the Recursive Installer if (HANDLE_NODE_INVALID != hDependentItem) { // Check if we have installed this item already in this session m_xmlCatalog.GetIdentityStr(hDependentItem, &bstrUniqueIdentity); hrString = StringCchPrintfEx(szUniqueIdentitySearch, ARRAYSIZE(szUniqueIdentitySearch), NULL, NULL, MISTSAFE_STRING_FLAGS, _T("%ls|"), bstrUniqueIdentity); SafeSysFreeString(bstrUniqueIdentity); if (FAILED(hrString)) { // The string check for the unique identity is an optimization to prevent installing the same item more // than once in an install operation. If we cannot do this optimization, we will just go ahead and install // the item. There is no real problem with installing the same item more than once, its just ineffecient. LOG_ErrorMsg(hrString); } else { if (NULL != StrStrI(m_pszInstalledItemsList, szUniqueIdentitySearch)) { // we have already installed this item, skip to the next one. m_xmlCatalog.CloseItem(hDependentItem); hr = m_xmlCatalog.GetNextItemDependency(hDependentItemList, &hDependentItem); continue; } } // There is a Item in the Catalog that is a dependency of the Item we // are installing and we haven't installed it yet, so Call the // RecursiveInstaller to handle this one first hr = RecursiveInstallDependencies(hDependentItem, pEngUpdate); } // Get the next Dependent Item - will Return S_FALSE when there are // no more items. m_xmlCatalog.CloseItem(hDependentItem); hr = m_xmlCatalog.GetNextItemDependency(hDependentItemList, &hDependentItem); } m_xmlCatalog.CloseItemList(hDependentItemList); } if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0) { m_xmlCatalog.CloseItem(hItem); goto CleanUp; } // Check if we have installed this item already in this session m_xmlCatalog.GetIdentityStr(hItem, &bstrUniqueIdentity); hrString = StringCchPrintfEx(szUniqueIdentitySearch, ARRAYSIZE(szUniqueIdentitySearch), NULL, NULL, MISTSAFE_STRING_FLAGS, _T("%ls|"), bstrUniqueIdentity); SafeSysFreeString(bstrUniqueIdentity); if (FAILED(hrString)) { // The string check for the unique identity is an optimization to prevent installing the same item more // than once in an install operation. If we cannot do this optimization, we will just go ahead and install // the item. There is no real problem with installing the same item more than once, its just ineffecient. LOG_ErrorMsg(hrString); } else { if (NULL == StrStrI(m_pszInstalledItemsList, szUniqueIdentitySearch)) { // we have NOT installed this item in this session hr = DoInstall(hItem, pEngUpdate); } } if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0) { m_xmlCatalog.CloseItem(hItem); goto CleanUp; } // get the next item. hItem will be HANDLE_NODE_INVALID when there are no // remaining items. m_xmlCatalog.CloseItem(hItem); m_xmlCatalog.GetNextItem(hCatalogItemList, &hItem); } m_xmlCatalog.CloseItem(hProvider); m_xmlCatalog.GetNextProvider(hProviderList, &hProvider); } CleanUp: // BUG: 441316: Earlier in the install process we left the downloaded files on the machine to // support this bug which involves installing Multi Function Device Drivers, Now we want to enumerate // the list of download source paths and all folders/files from them. if (NULL != m_pszItemDownloadPathListForDelete) { LPTSTR pszWalk = m_pszItemDownloadPathListForDelete; LPTSTR pszChr = NULL; while (_T('\0') != *pszWalk) { pszChr = StrChr(pszWalk, _T('|')); if (NULL != pszChr) { *pszChr = _T('\0'); // Call RemoveDownloadTemporaryFolders to delete this folder path RemoveDownloadTemporaryFolders(pszWalk); *pszChr = _T('|'); pszWalk = pszChr + 1; // skip to next character } } } // // add HRESULT in case the install failed before the install loop // if (S_OK != hr) { m_xmlItems.AddGlobalErrorCodeIfNoItems(hr); } m_xmlItems.GetItemsBSTR(&m_bstrXmlResult); // get result for Caller and to Send OnOperationComplete if (NULL != m_pProgressListener) { m_pProgressListener->OnOperationComplete(m_bstrOperationUUID, m_bstrXmlResult); } else { if (NULL != m_hWnd) { if (NULL == evtData.bstrUuidOperation) { evtData.bstrUuidOperation = SysAllocString(m_bstrOperationUUID); } evtData.bstrXmlData = SysAllocString(m_bstrXmlResult); evtData.fItemCompleted = TRUE; fPostWaitSuccess = WUPostEventAndBlock(m_hWnd, UM_EVENT_COMPLETE, &evtData); } } if ((NULL != fpnSRSetRestorePoint) && (ERROR_SUCCESS == restoreStatus.nStatus)) { if (!m_fSomeItemsSuccessful) { // need to revert our systemrestore point, no successful installs were done. restoreInfo.dwEventType = END_SYSTEM_CHANGE; restoreInfo.dwRestorePtType = CANCELLED_OPERATION; restoreInfo.llSequenceNumber = restoreStatus.llSequenceNumber; fpnSRSetRestorePoint(&restoreInfo, &restoreStatus); } else { // signal the end of the restore point change. restoreInfo.dwEventType = END_SYSTEM_CHANGE; restoreInfo.llSequenceNumber = restoreStatus.llSequenceNumber; fpnSRSetRestorePoint(&restoreInfo, &restoreStatus); m_fSomeItemsSuccessful = FALSE; } } if (SUCCEEDED(hr)) { LogMessage("%s %s", SZ_SEE_IUHIST, SZ_INSTALL_FINISHED); } else { LogError(hr, "%s %s", SZ_SEE_IUHIST, SZ_INSTALL_FINISHED); } fpnSRSetRestorePoint = NULL; SafeFreeLibrary(hSystemRestoreDLL); // don't free up the strings below unless the wait succeeded in // WUPostEventAndBlock. If we do free the strings up and the wait didn't // succeed, then we run the risk of AVing ourselves. Note that fPostWaitSuccess // is initialized to TRUE so if we will free these BSTRs if WUPostEventAndBlock // is not called. if (fPostWaitSuccess) { SafeSysFreeString(evtData.bstrProgress); SafeSysFreeString(evtData.bstrUuidOperation); SafeSysFreeString(evtData.bstrXmlData); } return hr; } ///////////////////////////////////////////////////////////////////////////// // InstallAsync() // // Install Asynchronously. // Input: // bstrXmlCatalog - the xml catalog portion containing items to be installed // bstrXmlDownloadedItems - the xml of downloaded items and their respective download // result as described in the result schema. Install uses this // to know whether the items were downloaded and if so where they // were downloaded to so that it can install the items // punkProgressListener - the callback function pointer for reporting install progress // hWnd - the event msg window handler passed from the stub // bstrUuidOperation - an id provided by the client to provide further // identification to the operation as indexes may be reused. // Output: // pbstrUuidOperation - the operation ID. If it is not provided by the in bstrUuidOperation // parameter (an empty string is passed), it will generate a new UUID. // Otherwise, it allocates and copies the value passed by bstrUuidOperation. // The caller is responsible for freeing the memory returned in // pbstrUuidOperation using SysFreeString(). ///////////////////////////////////////////////////////////////////////////// HRESULT WINAPI CEngUpdate::InstallAsync(BSTR bstrXmlClientInfo, BSTR bstrXmlCatalog, BSTR bstrXmlDownloadedItems, LONG lMode, IUnknown *punkProgressListener, HWND hWnd, BSTR bstrUuidOperation, BSTR *pbstrUuidOperation) { HRESULT hr = S_OK; DWORD dwThreadId; DWORD dwErr; HANDLE hThread = NULL; GUID guid; LPOLESTR pwszUuidOperation = NULL; PIUINSTALLSTARTUPINFO pStartupInfo; LOG_Block("InstallAsync()"); LogMessage("Asynchronous Install started"); if ((NULL == bstrXmlCatalog) || (NULL == pbstrUuidOperation)) { hr = E_INVALIDARG; LOG_ErrorMsg(hr); LogError(hr, SZ_INSTALLASYNC_FAILED); return hr; } *pbstrUuidOperation = NULL; if (NULL == (pStartupInfo = (PIUINSTALLSTARTUPINFO) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IUINSTALLSTARTUPINFO)))) { hr = E_OUTOFMEMORY; LOG_ErrorMsg(hr); LogError(hr, SZ_INSTALLASYNC_FAILED); return hr; } if ((DWORD) lMode & (DWORD) UPDATE_OFFLINE_MODE) { m_fOfflineMode = TRUE; } else { m_fOfflineMode = FALSE; } // // 481020 IU - Getting a blank for bstrUuidOperation in the // oIUControl_OnItemStart/OnProgress/OnOperationComplete events // when calling InstallAsync // // Also found that BSTRs were leaking (now freed), and that if bstrUuidOperation // was NULL or zero length, we need to generate a GUID, so this is done // prior to allocating (again) for pStartupInfo->bstrOperationUUID // if (NULL != bstrUuidOperation && SysStringLen(bstrUuidOperation) > 0) { *pbstrUuidOperation = SysAllocString(bstrUuidOperation); } else { hr = CoCreateGuid(&guid); if (FAILED(hr)) { LOG_ErrorMsg(hr); LogError(hr, SZ_INSTALLASYNC_FAILED); return hr; } hr = StringFromCLSID(guid, &pwszUuidOperation); if (FAILED(hr)) { LOG_ErrorMsg(hr); LogError(hr, SZ_INSTALLASYNC_FAILED); return hr; } *pbstrUuidOperation = SysAllocString(pwszUuidOperation); CoTaskMemFree(pwszUuidOperation); } pStartupInfo->lMode = lMode; pStartupInfo->hwnd = hWnd; pStartupInfo->punkProgressListener = punkProgressListener; pStartupInfo->pEngUpdate = this; pStartupInfo->bstrXmlClientInfo = SysAllocString(bstrXmlClientInfo); pStartupInfo->bstrXmlCatalog = SysAllocString(bstrXmlCatalog); pStartupInfo->bstrXmlDownloadedItems = SysAllocString(bstrXmlDownloadedItems); pStartupInfo->bstrOperationUUID = SysAllocString(*pbstrUuidOperation); LOG_XmlBSTR(pStartupInfo->bstrXmlClientInfo); LOG_XmlBSTR(pStartupInfo->bstrXmlCatalog); LOG_XmlBSTR(pStartupInfo->bstrXmlDownloadedItems); InterlockedIncrement(&m_lThreadCounter); if (NULL != pStartupInfo->punkProgressListener) { pStartupInfo->punkProgressListener->AddRef(); } hThread = CreateThread(NULL, 0, InstallThreadProc, (LPVOID)pStartupInfo, 0, &dwThreadId); if (NULL == hThread) { dwErr = GetLastError(); hr = HRESULT_FROM_WIN32(dwErr); LOG_ErrorMsg(hr); SysFreeString(pStartupInfo->bstrXmlClientInfo); SysFreeString(pStartupInfo->bstrXmlCatalog); SysFreeString(pStartupInfo->bstrXmlDownloadedItems); SysFreeString(pStartupInfo->bstrOperationUUID); SafeRelease(pStartupInfo->punkProgressListener); SafeHeapFree(pStartupInfo); SysFreeString(*pbstrUuidOperation); *pbstrUuidOperation = NULL; InterlockedDecrement(&m_lThreadCounter); LogError(hr, SZ_INSTALLASYNC_FAILED); return hr; } if (SUCCEEDED(hr)) { LogMessage("Asynchronous Install completed startup"); } else { LogError(hr, SZ_INSTALLASYNC_FAILED); } return hr; } DWORD WINAPI InstallThreadProc(LPVOID lpv) { USES_IU_CONVERSION; LOG_Block("InstallThreadProc"); PIUINSTALLSTARTUPINFO pStartupInfo = (PIUINSTALLSTARTUPINFO)lpv; HRESULT hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { LOG_Out(_T("CoInitialize called successfully")); } { // we need to scope this object so it destructs before we decrement our thread counter // If we didn't do this and the control was unloading while the thread closed we would fault // when the engine unloaded and this class was destructing. CIUInstall iuInstall(pStartupInfo->bstrXmlClientInfo, pStartupInfo->bstrXmlCatalog, pStartupInfo->bstrXmlDownloadedItems, pStartupInfo->bstrOperationUUID, pStartupInfo->lMode, pStartupInfo->punkProgressListener, pStartupInfo->hwnd); iuInstall.ProcessInstallCatalog(pStartupInfo->pEngUpdate); } SysFreeString(pStartupInfo->bstrXmlClientInfo); SysFreeString(pStartupInfo->bstrXmlCatalog); SysFreeString(pStartupInfo->bstrXmlDownloadedItems); SysFreeString(pStartupInfo->bstrOperationUUID); SafeRelease(pStartupInfo->punkProgressListener); if (SUCCEEDED(hr)) { CoUninitialize(); LOG_Out(_T("CoUninitialize called")); } InterlockedDecrement(&pStartupInfo->pEngUpdate->m_lThreadCounter); SafeHeapFree(pStartupInfo); return 0; } HRESULT CIUInstall::RecursiveInstallDependencies(HANDLE_NODE hItem, CEngUpdate* pEngUpdate) { LOG_Block("RecursiveInstallDependencies()"); BOOL fRet = FALSE; HRESULT hr = S_FALSE, hrString; HANDLE_NODE hDependentItemList = HANDLE_NODELIST_INVALID; HANDLE_NODE hDependentItem = HANDLE_NODE_INVALID; BSTR bstrUniqueIdentity = NULL; TCHAR szUniqueIdentitySearch[MAX_PATH]; // Check to see if this item has dependencies hDependentItemList = m_xmlCatalog.GetFirstItemDependency(hItem, &hDependentItem); if (HANDLE_NODELIST_INVALID != hDependentItemList) { hr = S_OK; while (S_OK == hr) { // walk each dependent Item and call the Recursive Installer if (HANDLE_NODE_INVALID != hDependentItem) { // Check if we have installed this item already in this session m_xmlCatalog.GetIdentityStr(hDependentItem, &bstrUniqueIdentity); hrString = StringCchPrintfEx(szUniqueIdentitySearch, ARRAYSIZE(szUniqueIdentitySearch), NULL, NULL, MISTSAFE_STRING_FLAGS, _T("%ls|"), bstrUniqueIdentity); SafeSysFreeString(bstrUniqueIdentity); if (FAILED(hrString)) { // The string check for the unique identity is an optimization to prevent installing the same item more // than once in an install operation. If we cannot do this optimization, we will just go ahead and install // the item. There is no real problem with installing the same item more than once, its just ineffecient. LOG_ErrorMsg(hrString); } else { if (NULL != StrStrI(m_pszInstalledItemsList, szUniqueIdentitySearch)) { // we have already installed this item, skip to the next one. m_xmlCatalog.CloseItem(hDependentItem); hr = m_xmlCatalog.GetNextItemDependency(hDependentItemList, &hDependentItem); continue; } } // There is a Item in the Catalog that is a dependency of the Item we // are installing and we haven't installed it yet, so Call the // RecursiveInstaller to handle this one first hr = RecursiveInstallDependencies(hDependentItem, pEngUpdate); } // Get the next Dependent Item - will Return S_FALSE when there are // no more items. m_xmlCatalog.CloseItem(hDependentItem); hr = m_xmlCatalog.GetNextItemDependency(hDependentItemList, &hDependentItem); } m_xmlCatalog.CloseItemList(hDependentItemList); } // if all installs have succeeded up to this point (hr should be S_FALSE when all // nested installs have completed) if (SUCCEEDED(hr)) { // No More Recursive Dependencies, Install This Item - Recursive Functions will unwind // Installing each nested item as it goes. // Check if we have installed this item already in this session m_xmlCatalog.GetIdentityStr(hItem, &bstrUniqueIdentity); hrString = StringCchPrintfEx(szUniqueIdentitySearch, ARRAYSIZE(szUniqueIdentitySearch), NULL, NULL, MISTSAFE_STRING_FLAGS, _T("%ls|"), bstrUniqueIdentity); SafeSysFreeString(bstrUniqueIdentity); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); // The string check for the unique identity is an optimization to prevent installing the same item more // than once in an install operation. If we cannot do this optimization, we will just go ahead and install // the item. There is no real problem with installing the same item more than once, its just ineffecient. hr = DoInstall(hItem, pEngUpdate); } else { if (NULL == StrStrI(m_pszInstalledItemsList, szUniqueIdentitySearch)) { // we have NOT installed this item in this session hr = DoInstall(hItem, pEngUpdate); } } } return hr; } HRESULT CIUInstall::DoInstall(HANDLE_NODE hItem, CEngUpdate* pEngUpdate) { LOG_Block("DoInstall()"); HRESULT hr, hrString; USES_IU_CONVERSION; BSTR bstrName = NULL; BSTR bstrPublisherName = NULL; BSTR bstrItemUUID = NULL; BSTR bstrInstallerType = NULL; BSTR bstrCommand = NULL; BSTR bstrSwitches = NULL; BSTR bstrCommandType = NULL; BSTR bstrInfSection = NULL; BSTR bstrItemDownloadPath = NULL; BSTR bstrDriverName = NULL; BSTR bstrArchitecture = NULL; BSTR bstrHWID = NULL; BSTR bstrDisplayName = NULL; BSTR bstrUniqueIdentity = NULL; BSTR bstrCodeBase = NULL; BSTR bstrCRC = NULL; BSTR bstrFileName = NULL; TCHAR szProgress[64]; TCHAR szCommandType[64]; TCHAR szInstallerType[256]; TCHAR szItemSourcePath[MAX_PATH]; TCHAR szCabFilePath[MAX_PATH]; LPTSTR pszCabUrl = NULL; LPTSTR pszAllocatedFileName = NULL; LPTSTR pszLocalFileName = NULL; LONG lItemCommandCount = 0; LONG lListNeededLength = 0; LONG lSize; BOOL fInstallerNeedsReboot = FALSE; BOOL fExclusive = FALSE; BOOL fPatch; BOOL fContinue = TRUE; PINSTALLCOMMANDINFO pCommandInfoArray = NULL; DWORD dwStatus = ITEM_STATUS_FAILED; HANDLE_NODE hXmlItem = HANDLE_NODE_INVALID; HANDLE_NODELIST hItemCodeBaseList = HANDLE_NODELIST_INVALID; EventData evtData; ZeroMemory((LPVOID) &evtData, sizeof(evtData)); LPTSTR pszClientName = OLE2T(m_bstrClientName); m_dwStatus = ITEM_STATUS_FAILED; // default install status to failure hr = m_xmlCatalog.GetIdentity(hItem, &bstrName, &bstrPublisherName, &bstrItemUUID); if (FAILED(hr)) { LOG_Software(_T("Failed to get an Identity for an Item (invalid document??)")); LogError(hr, "Install failed to get an Item Identity"); goto CleanUp; } hr = m_pxmlDownloadedItems->GetItemDownloadPath(&m_xmlCatalog, hItem, &bstrItemDownloadPath); if (NULL == bstrItemDownloadPath) { LOG_Software(_T("Failed to get Item Download Path")); if (SUCCEEDED(hr)) hr = E_FAIL; LogError(hr, "Install couldn't get Item %ls Download Path", bstrName); goto CleanUp; } hr = StringCchCopyEx(szItemSourcePath, ARRAYSIZE(szItemSourcePath), OLE2T(bstrItemDownloadPath), NULL, NULL, MISTSAFE_STRING_FLAGS); SafeSysFreeString(bstrItemDownloadPath); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } hr = m_xmlCatalog.GetItemInstallInfo(hItem, &bstrInstallerType, &fExclusive, &fInstallerNeedsReboot, &lItemCommandCount); if (FAILED(hr)) { LOG_Software(_T("Failed to get ItemInstallInfo for Item %ls"), bstrName); LogError(hr, "Failed to get Item %ls Install Information", bstrName); goto CleanUp; } LogMessage("Installing %ls item from publisher %ls", bstrInstallerType, bstrPublisherName); if (lItemCommandCount > 0) { // Allocate INSTALLCOMMANDINFO array and fill out with command info pCommandInfoArray = (PINSTALLCOMMANDINFO) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(INSTALLCOMMANDINFO) * lItemCommandCount); if (NULL == pCommandInfoArray) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); LogError(hr, "Install Command processing"); goto CleanUp; } } for (LONG lCnt = 0; lCnt < lItemCommandCount; lCnt++) { // Get Install Command Information for each Command m_xmlCatalog.GetItemInstallCommand(hItem, lCnt, &bstrCommandType, &bstrCommand, &bstrSwitches, &bstrInfSection); if (NULL == bstrCommandType || NULL == bstrCommand) { hr = E_INVALIDARG; LOG_ErrorMsg(hr); goto CleanUp; } LogMessage("Installer Command Type: %ls", bstrCommandType); // Copy the Command to Execute hr = StringCchCopyEx(pCommandInfoArray[lCnt].szCommandLine, ARRAYSIZE(pCommandInfoArray[lCnt].szCommandLine), OLE2T(bstrCommand), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } hr = StringCchCopyEx(szCommandType, ARRAYSIZE(szCommandType), OLE2T(bstrCommandType), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szCommandType, -1, _T("INF"), -1)) { pCommandInfoArray[lCnt].iCommandType = COMMANDTYPE_INF; } else if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szCommandType, -1, _T("ADVANCED_INF"), -1)) { pCommandInfoArray[lCnt].iCommandType = COMMANDTYPE_ADVANCEDINF; } else if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szCommandType, -1, _T("EXE"), -1)) { pCommandInfoArray[lCnt].iCommandType = COMMANDTYPE_EXE; } else if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szCommandType, -1, _T("WI"), -1)) { pCommandInfoArray[lCnt].iCommandType = COMMANDTYPE_MSI; } else if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szCommandType, -1, _T("CUSTOM"), -1)) { pCommandInfoArray[lCnt].iCommandType = COMMANDTYPE_CUSTOM; } else { LOG_Software(_T("Unable to determine Installer Type %s"), szCommandType); pCommandInfoArray[lCnt].iCommandType = 0; // unknown } // Copy the Command Line Parameters (if any) if (NULL != bstrSwitches) { hr = StringCchCopyEx(pCommandInfoArray[lCnt].szCommandParameters, ARRAYSIZE(pCommandInfoArray[lCnt].szCommandParameters), OLE2T(bstrSwitches), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } } if (NULL != bstrInfSection) { hr = StringCchCopyEx(pCommandInfoArray[lCnt].szInfSection, ARRAYSIZE(pCommandInfoArray[lCnt].szInfSection), OLE2T(bstrInfSection), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } } SafeSysFreeString(bstrCommandType); SafeSysFreeString(bstrCommand); SafeSysFreeString(bstrSwitches); SafeSysFreeString(bstrInfSection); } // Before we start the install we need to verify the signature on all cabs of this item. This is to verify that they // haven't been tampered with between download and install (especially during non-consumer scenarios) hItemCodeBaseList = m_xmlCatalog.GetItemFirstCodeBase(hItem, &bstrCodeBase, &bstrFileName, &bstrCRC, &fPatch, &lSize); if ((HANDLE_NODELIST_INVALID == hItemCodeBaseList) || (NULL == bstrCodeBase)) { LOG_Software(_T("Item %s has no Cabs, cannot verify signature"), bstrName); hr = E_INVALIDARG; goto CleanUp; } while (fContinue && NULL != bstrCodeBase) { if (NULL != bstrFileName && SysStringLen(bstrFileName) > 0) { if (NULL != pszAllocatedFileName) { MemFree(pszAllocatedFileName); } pszAllocatedFileName = OLE2T(bstrFileName); } else // no special filename specified, use filename from URL { if (NULL != pszCabUrl) { MemFree(pszCabUrl); } pszCabUrl = OLE2T(bstrCodeBase); // search for the last forward slash (will separate the URL from the filename) LPTSTR pszLastSlash = StrRChr(pszCabUrl, NULL, _T('/')); // if the last slash was found, skip to next character (will be the beginning of the filename) if (NULL != pszLastSlash) pszLastSlash++; pszLocalFileName = pszLastSlash; } hr = PathCchCombine(szCabFilePath, ARRAYSIZE(szCabFilePath), szItemSourcePath, (NULL != pszAllocatedFileName) ? pszAllocatedFileName : pszLocalFileName); if (FAILED(hr)) { LOG_ErrorMsg(hr); m_xmlCatalog.CloseItemList(hItemCodeBaseList); goto CleanUp; } hr = VerifyFileTrust(szCabFilePath, NULL, ReadWUPolicyShowTrustUI() ); if (FAILED(hr)) { // Cab File Failed Trust Validation LOG_ErrorMsg(hr); m_xmlCatalog.CloseItemList(hItemCodeBaseList); goto CleanUp; } SafeSysFreeString(bstrCodeBase); SafeSysFreeString(bstrFileName); SafeSysFreeString(bstrCRC); fContinue = SUCCEEDED(m_xmlCatalog.GetItemNextCodeBase(hItemCodeBaseList, &bstrCodeBase, &bstrFileName, &bstrCRC, &fPatch, &lSize)) && (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) != WAIT_OBJECT_0); } // If this item is Exclusive we need to write something to the history to indicate that the install has started // Normally an exclusive item won't return control back to the installer so no other history information will be // available if (fExclusive) { m_history.AddHistoryItemInstallStatus(&m_xmlCatalog, hItem, HISTORY_STATUS_IN_PROGRESS, pszClientName, fInstallerNeedsReboot, S_OK); m_history.SaveHistoryToDisk(); PingServerForInstall(hr, hItem, &(pEngUpdate->m_evtNeedToQuit), NULL, TRUE); // ping exclusive item now } // Call Install Library with Item Information hr = StringCchCopyEx(szInstallerType, ARRAYSIZE(szInstallerType), OLE2T(bstrInstallerType), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto CleanUp; } if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szInstallerType, -1, _T("SOFTWARE"), -1)) { if (lItemCommandCount == 0) { LOG_Software(_T("Item %s has no Commands.. cannot install"), bstrName); hr = E_INVALIDARG; goto CleanUp; } hr = InstallSoftwareItem(szItemSourcePath, fInstallerNeedsReboot, lItemCommandCount, pCommandInfoArray, &dwStatus); } else if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szInstallerType, -1, _T("CDM"), -1)) { if (m_xmlCatalog.IsPrinterDriver(hItem)) { // Printer Driver m_xmlCatalog.GetPrinterDriverInfo(hItem, &bstrDriverName, &bstrArchitecture); SafeSysFreeString(bstrArchitecture); // not used yet, should be NULL from xmlCatalog.GetPrinterDriverInfo() hr = InstallPrinterDriver(OLE2T(bstrDriverName), szItemSourcePath, NULL, &dwStatus); if (FAILED(hr)) { LogError(hr, "Installing Printer Driver %ls", bstrDriverName); } } else { // Normal Device Driver m_xmlCatalog.GetDriverInfo(hItem, &bstrHWID, &bstrDisplayName); hr = InstallDriver(szItemSourcePath, OLE2T(bstrDisplayName), OLE2T(bstrHWID), &dwStatus); if (FAILED(hr)) { LogError(hr, "Installing PnP Driver %ls, %ls", bstrHWID, bstrDisplayName); } SafeSysFreeString(bstrDisplayName); } } // Bug 441336: Deleting all files on a successful install of an item causes some problems with // Multi Function Device Drivers, these show up as multiple instances of the same 'item', share // the same downloaded driver package, but have different HWID's. The Item's all have different // identities because the calling application is making them unique when they recognize a MFD // driver package. We need to purposely leave the downloaded files installed until the 'very' end // of the installation process, so all items have a chance to be installed. // So, we will no longer perform this deletion step here, but will instead enumerate the InstalledItemsList // and remove the folders for each one. // Regardless of Success or Failure, update the Count of Items Installed m_lItemsCompleted++; // Add this Item to the InstalledItemsList m_xmlCatalog.GetIdentityStr(hItem, &bstrUniqueIdentity); lListNeededLength = (lstrlen(m_pszInstalledItemsList) + lstrlen(OLE2T(bstrUniqueIdentity)) + 2) * sizeof(TCHAR); if (lListNeededLength > m_lInstalledItemsListAllocatedLength) { // need to reallocate the installeditemlist LPTSTR pszNew = (LPTSTR) HeapReAlloc(GetProcessHeap(), 0, m_pszInstalledItemsList, m_lInstalledItemsListAllocatedLength * 2); if (NULL != pszNew) { m_pszInstalledItemsList = pszNew; m_lInstalledItemsListAllocatedLength *= 2; // only do this if the realloc worked, if it didn't we won't be adding more items hrString = StringCbCatEx(m_pszInstalledItemsList, m_lInstalledItemsListAllocatedLength, OLE2T(bstrUniqueIdentity), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); } hrString = StringCbCatEx(m_pszInstalledItemsList, m_lInstalledItemsListAllocatedLength, _T("|"), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); } } } else { // only do this if the realloc worked, if it didn't we won't be adding more items hrString = StringCbCatEx(m_pszInstalledItemsList, m_lInstalledItemsListAllocatedLength, OLE2T(bstrUniqueIdentity), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); } hrString = StringCbCatEx(m_pszInstalledItemsList, m_lInstalledItemsListAllocatedLength, _T("|"), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); } } if (SUCCEEDED(hr)) { lListNeededLength = (lstrlen(m_pszItemDownloadPathListForDelete) + lstrlen(szItemSourcePath) + 2) * sizeof(TCHAR); if (lListNeededLength > m_lItemDownloadPathListForDeleteLength) { // need to reallocate the downloadpathitemlist LPTSTR pszNew = (LPTSTR) HeapReAlloc(GetProcessHeap(), 0, m_pszItemDownloadPathListForDelete, m_lItemDownloadPathListForDeleteLength * 2); if (NULL != pszNew) { m_pszItemDownloadPathListForDelete = pszNew; m_lItemDownloadPathListForDeleteLength *= 2; // only do this if the realloc worked, if it didn't we won't be adding more items hrString = StringCbCatEx(m_pszItemDownloadPathListForDelete, m_lItemDownloadPathListForDeleteLength, szItemSourcePath, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); } hrString = StringCbCatEx(m_pszItemDownloadPathListForDelete, m_lItemDownloadPathListForDeleteLength, _T("|"), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); } } } else { hrString = StringCbCatEx(m_pszItemDownloadPathListForDelete, m_lItemDownloadPathListForDeleteLength, szItemSourcePath, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); } hr = StringCbCatEx(m_pszItemDownloadPathListForDelete, m_lItemDownloadPathListForDeleteLength, _T("|"), NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hrString)) { LOG_ErrorMsg(hrString); } } } CleanUp: // // Could have gotten here with stale hr if client called SetOperationMode // if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0) { hr = E_ABORT; } m_xmlItems.AddItem(&m_xmlCatalog, hItem, &hXmlItem); if (ITEM_STATUS_SUCCESS_REBOOT_REQUIRED == dwStatus) { fInstallerNeedsReboot = TRUE; } if (ITEM_STATUS_FAILED == dwStatus) { if (fExclusive) { m_history.UpdateHistoryItemInstallStatus(&m_xmlCatalog, hItem, HISTORY_STATUS_FAILED, fInstallerNeedsReboot, hr); } else { m_history.AddHistoryItemInstallStatus(&m_xmlCatalog, hItem, HISTORY_STATUS_FAILED, pszClientName, fInstallerNeedsReboot, hr); } m_xmlItems.AddInstallStatus(hXmlItem, KEY_STATUS_FAILED, 0, hr); } else { // install completed successfully if (fExclusive) { m_history.UpdateHistoryItemInstallStatus(&m_xmlCatalog, hItem, HISTORY_STATUS_COMPLETE, fInstallerNeedsReboot, 0); } else { m_history.AddHistoryItemInstallStatus(&m_xmlCatalog, hItem, HISTORY_STATUS_COMPLETE, pszClientName, fInstallerNeedsReboot, 0); } if (ITEM_STATUS_INSTALLED_ERROR == dwStatus) { LOG_Software(_T("Item Installed However there were Minor Errors")); } m_xmlItems.AddInstallStatus(hXmlItem, KEY_STATUS_COMPLETE, fInstallerNeedsReboot, 0); m_fSomeItemsSuccessful = TRUE; // any success in the install operation should set to true } // // ping server to report the download status for this item // if (!fExclusive) { // // if we haven't done so, ping server now // LPCTSTR pDeviceId = NULL; if (NULL != bstrDriverName) { pDeviceId = OLE2T(bstrDriverName); } else if (NULL != bstrHWID) { pDeviceId = OLE2T(bstrHWID); } PingServerForInstall(hr, hItem, &(pEngUpdate->m_evtNeedToQuit), pDeviceId); } if ((DWORD) m_lMode & (DWORD) UPDATE_NOTIFICATION_COMPLETEONLY) { // Only Send OnOperationComplete, we won't send any progress messages. } else { // Send all Progress Messages hrString = StringCchPrintfEx(szProgress, ARRAYSIZE(szProgress), NULL, NULL, MISTSAFE_STRING_FLAGS, _T("%lu:%lu"), (ULONG)m_lItemCount, (ULONG)m_lItemsCompleted); if (SUCCEEDED(hrString)) { evtData.bstrProgress = SysAllocString(T2OLE(szProgress)); if (NULL != m_pProgressListener) { m_pProgressListener->OnProgress(m_bstrOperationUUID, VARIANT_TRUE, evtData.bstrProgress, &evtData.lCommandRequest); } else { if (NULL != m_hWnd) { evtData.fItemCompleted = TRUE; evtData.bstrUuidOperation = SysAllocString(m_bstrOperationUUID); SendMessage(m_hWnd, UM_EVENT_PROGRESS, 0, LPARAM(&evtData)); } } } else { LOG_ErrorMsg(hrString); } // // Need to check for a cancel command returned from OnProgress // if (UPDATE_COMMAND_CANCEL == evtData.lCommandRequest) { LOG_Out(_T("OnProgress received UPDATE_COMMAND_CANCEL")); SetEvent(pEngUpdate->m_evtNeedToQuit); // asked to quit, we'll handle it in WaitForSingleObject } } m_dwStatus = dwStatus; // return the status up the chain SafeHeapFree(pCommandInfoArray); SysFreeString(bstrName); SysFreeString(bstrPublisherName); SysFreeString(bstrItemUUID); SysFreeString(bstrInstallerType); SafeSysFreeString(bstrCommandType); SafeSysFreeString(bstrCommand); SafeSysFreeString(bstrSwitches); SafeSysFreeString(bstrInfSection); SafeSysFreeString(bstrHWID); SafeSysFreeString(bstrDriverName); SafeSysFreeString(evtData.bstrProgress); SafeSysFreeString(evtData.bstrUuidOperation); // // Could have missed change during lengthy ping or OnProgress // if (WaitForSingleObject(pEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0) { hr = E_ABORT; } return hr; } // // RemoveDownloadTemporaryFolders // // This helper function is called after the install is successfully finished // in DoInstall() to delete the temporary component directory and all files underneath // void CIUInstall::RemoveDownloadTemporaryFolders(LPCTSTR pszComponentPath) { LOG_Block("CIUInstall::RemoveDownloadTemporaryFolders()"); HRESULT hr; TCHAR szBuffer[MAX_PATH], szDeleteFile[MAX_PATH]; WIN32_FIND_DATA fd; HANDLE hFind; hr = PathCchCombine(szBuffer, ARRAYSIZE(szBuffer), pszComponentPath, _T("*.*")); if (FAILED(hr)) { LOG_ErrorMsg(hr); return; } hFind = FindFirstFile(szBuffer, &fd); BOOL fMore = (hFind != INVALID_HANDLE_VALUE); while (fMore) { if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { hr = PathCchCombine(szDeleteFile, ARRAYSIZE(szDeleteFile), pszComponentPath, fd.cFileName); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto doneCurrentEntry; } if (!DeleteFile(szDeleteFile)) { // Try waiting a bit before trying one last time. Sleep(1000); DeleteFile(szDeleteFile); } } else if (_T('.') != fd.cFileName[0] && (_T('\0') != fd.cFileName[1] || (_T('.') != fd.cFileName[1] && _T('\0') != fd.cFileName[2]))) { hr = PathCchCombine(szBuffer, ARRAYSIZE(szDeleteFile), pszComponentPath, fd.cFileName); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto doneCurrentEntry; } RemoveDownloadTemporaryFolders(szBuffer); } doneCurrentEntry: fMore = FindNextFile(hFind, &fd); } if (hFind != INVALID_HANDLE_VALUE) FindClose(hFind); BOOL fSuccess = RemoveDirectory(pszComponentPath); if (!fSuccess) { // Try waiting a bit before trying one last time. Sleep(1000); fSuccess = RemoveDirectory(pszComponentPath); } } // // private utility function to ping server for installation activity. // since there are two places we do this, so wrap this in a function to safe size // void CIUInstall::PingServerForInstall(HRESULT hr, HANDLE_NODE hItem, PHANDLE phEvtNeedToQuit, LPCTSTR lpszDeviceId /*=NULL*/, BOOL fExclusive /*=FALSE*/) { LOG_Block("CIUInstall::PingServerForInstall()"); BSTR bstrIdentity = NULL; USES_IU_CONVERSION; if (SUCCEEDED(m_xmlCatalog.GetIdentityStrForPing(hItem, &bstrIdentity))) { BOOL fOnLine = (0 == ((DWORD) m_lMode & (DWORD) UPDATE_OFFLINE_MODE)); URLLOGSTATUS status = SUCCEEDED(hr) ? URLLOGSTATUS_Success : URLLOGSTATUS_Failed; if (fExclusive) { status = URLLOGSTATUS_Pending; } if (m_fAbort) { // // user/system cancelled the current process // hr = E_ABORT; status = URLLOGSTATUS_Cancelled; } m_pingSvr.Ping( fOnLine, // on-line URLLOGDESTINATION_DEFAULT, // going to live or corp WU server phEvtNeedToQuit, // pt to cancel events 1, // number of events URLLOGACTIVITY_Installation, // activity status, // status code hr, // error code, can be 0 or 1 OLE2T(bstrIdentity), // itemID lpszDeviceId // add'l device data for driver update ); } SafeSysFreeString(bstrIdentity); }