WindowsXP-SP1/enduser/windows.com/iuengine/install.cpp
2020-09-30 16:53:49 +02:00

1656 lines
60 KiB
C++

//=======================================================================
//
// 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 <iu.h>
#include <iucommon.h>
#include <trust.h>
#include <install.h>
#include <fileutil.h>
#include <shlwapi.h>
#include <srrestoreptapi.h>
#include <iuprogress.h>
#include "history.h"
#include "iuxml.h"
//#include <serverPing.h>
#include <logging.h>
#include <UrlLogging.h>
#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.
// <id guid="2560AD4D-3ED3-49C6-A937-4368C0B0E06D" installed="1"/>
/////////////////////////////////////////////////////////////////////////////
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);
}