Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2293 lines
90 KiB

//=======================================================================
//
// 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>
#include <setupapi.h>
#include <regstr.h>
#include <winspool.h> // for DRIVER_INFO_6
#include <cdmp.h>
#include <cfgmgr32.h>
typedef struct tagDRIVERPREINSTALLINFO
{
DWORD dwPnPMatchCount;
LPTSTR pszDeviceInstance;
LPTSTR pszMostSpecificHWID;
LPTSTR pszPreviousMatchingID;
LPTSTR pszPreviousProvider;
LPTSTR pszPreviousManufacturer;
LPTSTR pszPreviousDriverName;
LPTSTR pszPreviousDriverVerDate;
LPTSTR pszPreviousDriverVerVersion;
LPTSTR pszPreviousDriverRank;
ULONG ulStatus;
ULONG ulProblemNumber;
} DRIVERPREINSTALLINFO, *PDRIVERPREINSTALLINFO;
HRESULT GetMatchingInstallInfo(HDEVINFO hDevInfoSet, PSP_DEVINFO_DATA pDevInfoData, PDRIVERPREINSTALLINFO pDriverInfo, LPCTSTR pszHWID);
HRESULT CachePnPInstalledDriverInfo(PDRIVERPREINSTALLINFO pDriverInfo, LPCTSTR pszHWID);
HRESULT GetPnPInstallStatus(PDRIVERPREINSTALLINFO pDriverInfo);
HRESULT CacheInstalledPrinterDriverInfo(PDRIVERPREINSTALLINFO pDriverInfo, LPCTSTR pszDriverName, LPCTSTR pszHWID, LPCTSTR pszManufacturer, LPCTSTR pszProvider);
#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, PDRIVERPREINSTALLINFO pDriverInfo=NULL);
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 bstrHWID = NULL;
BSTR bstrDisplayName = NULL;
BSTR bstrUniqueIdentity = NULL;
BSTR bstrCodeBase = NULL;
BSTR bstrCRC = NULL;
BSTR bstrFileName = NULL;
BSTR bstrManufacturer = NULL;
BSTR bstrProvider = 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;
BOOL fIsPrinterDriverUpgrade = FALSE;
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);
DRIVERPREINSTALLINFO driverInfo;
ZeroMemory(&driverInfo, sizeof(DRIVERPREINSTALLINFO));
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))
{
m_xmlCatalog.GetPrinterDriverInfo(hItem, &bstrDriverName, &bstrHWID, &bstrManufacturer, &bstrProvider);
// Need to determine if this is a previous installed printer driver and fill out the PREDRIVERINFO structure
if (FAILED(hr = CacheInstalledPrinterDriverInfo(&driverInfo, (LPCTSTR)bstrDriverName, (LPCTSTR)bstrHWID, (LPCTSTR)bstrManufacturer, (LPCTSTR)bstrProvider)))
{
// Problem with the Catalog Data
LOG_ErrorMsg(hr);
goto CleanUp;
}
if (S_OK == hr)
{
fIsPrinterDriverUpgrade = TRUE;
}
SafeSysFreeString(bstrHWID);
SafeSysFreeString(bstrManufacturer);
SafeSysFreeString(bstrProvider);
}
// InstallPrinterDriver can only be called if the driver is a upgrade to a previously installed printer driver
// otherwise the install will fail.
if (fIsPrinterDriverUpgrade)
{
hr = InstallPrinterDriver(OLE2T(bstrDriverName), szItemSourcePath, NULL, &dwStatus);
if (FAILED(hr))
{
LogError(hr, "Installing Printer Driver %ls", bstrDriverName);
}
SafeSysFreeString(bstrDriverName);
}
else
{
// Normal Device Driver or new Driver for a Printer Device
if (SUCCEEDED(hr = m_xmlCatalog.GetDriverInfo(hItem, &bstrHWID, &bstrDisplayName)))
{
(void)CachePnPInstalledDriverInfo(&driverInfo, (LPCTSTR) bstrHWID);
hr = InstallDriver(szItemSourcePath, OLE2T(bstrDisplayName), OLE2T(bstrHWID), &dwStatus);
}
if (FAILED(hr))
{
LogError(hr, "Installing PnP Driver %ls, %ls", bstrHWID, bstrDisplayName);
}
else
{
//
// Attempt to get status of successfully installed driver, but don't fail
//
(void) GetPnPInstallStatus(&driverInfo);
}
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, FALSE, &driverInfo);
}
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(bstrManufacturer);
SafeSysFreeString(bstrProvider);
SafeSysFreeString(bstrCommandType);
SafeSysFreeString(bstrCommand);
SafeSysFreeString(bstrSwitches);
SafeSysFreeString(bstrInfSection);
SafeSysFreeString(bstrHWID);
SafeSysFreeString(bstrDriverName);
SafeSysFreeString(evtData.bstrProgress);
SafeSysFreeString(evtData.bstrUuidOperation);
SafeHeapFree(driverInfo.pszDeviceInstance);
SafeHeapFree(driverInfo.pszMostSpecificHWID);
SafeHeapFree(driverInfo.pszPreviousDriverName);
SafeHeapFree(driverInfo.pszPreviousDriverVerDate);
SafeHeapFree(driverInfo.pszPreviousDriverVerVersion);
SafeHeapFree(driverInfo.pszPreviousDriverRank);
SafeHeapFree(driverInfo.pszPreviousManufacturer);
SafeHeapFree(driverInfo.pszPreviousMatchingID);
SafeHeapFree(driverInfo.pszPreviousProvider);
//
// 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*/, PDRIVERPREINSTALLINFO pDriverInfo /*=NULL*/)
{
LOG_Block("CIUInstall::PingServerForInstall()");
HRESULT hrTemp = S_OK;
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;
}
LPTSTR pszMessage = NULL; // String used to pass Pre Installed Driver Info
LPCTSTR pszNull = _T("");
if (NULL != pDriverInfo)
{
TCHAR szPingVersion[] = _T("1");
DWORD dwLength = 0;
// Initialize version length
dwLength += 1;
dwLength += lstrlen(szPingVersion);
// Initialize Length to 5 (4 characters plus the | delimeter)
dwLength += 5;
// Get the Length of the pszMostSpecificHWID
dwLength++; // add 1 for the | delimeter for each Field, except for the last field
if (NULL != pDriverInfo->pszMostSpecificHWID)
{
dwLength += lstrlen(pDriverInfo->pszMostSpecificHWID); // length
}
// Get the Length of the pszPreviousDriverVerDate
dwLength++;
if (NULL != pDriverInfo->pszPreviousDriverVerDate)
{
dwLength += lstrlen(pDriverInfo->pszPreviousDriverVerDate);
}
// Get the Length of the pszPreviousDriverVerVersion
dwLength++;
if (NULL != pDriverInfo->pszPreviousDriverVerVersion)
{
dwLength += lstrlen(pDriverInfo->pszPreviousDriverVerVersion);
}
// Get the length of the pszPreviousRank
dwLength++;
if (NULL != pDriverInfo->pszPreviousDriverRank)
{
dwLength += lstrlen(pDriverInfo->pszPreviousDriverRank);
}
// Get the length of the ulStatus
dwLength++;
dwLength += 10; // formatted Hex String '0x00000000' = 10 chars
// Get the length of the ulProblemNumber
dwLength++;
dwLength += 10; // formatted Hex String '0x00000000' = 10 chars
// Get the length of the pszPreviousProvider
dwLength++;
if (NULL != pDriverInfo->pszPreviousProvider)
{
dwLength += lstrlen(pDriverInfo->pszPreviousProvider);
}
// Get the length of the pszPreviousManufacturer
dwLength++;
if (NULL != pDriverInfo->pszPreviousManufacturer)
{
dwLength += lstrlen(pDriverInfo->pszPreviousManufacturer);
}
// Get the length of the pszPreviousDriverName
dwLength++;
if (NULL != pDriverInfo->pszPreviousDriverName)
{
dwLength += lstrlen(pDriverInfo->pszPreviousDriverName);
}
// Get the length of the pszPreviousMatchingID
// Last Field, so no | delimeter added, BUT we need to add for the \0 termination
dwLength++;
if (NULL != pDriverInfo->pszPreviousMatchingID)
{
dwLength += lstrlen(pDriverInfo->pszPreviousMatchingID);
}
// Format: <ping version>|[dwPnPMatchCount]|[pszMostSpecificHWID]|[pszPreviousDriverVerDate]|[pszPreviousDriverVerVersion]|
// [pszPreviousDriverRank]|[ulStatus]|[ulProblemNumber]|[pszPreviousProvider]|[pszPreviousManufacturer]|
// [pszPreviousDriverName]|[pszPreviousMatchingID]
//
// NOTE: <ping version> starts with '1' and increments when message format changes
//
pszMessage = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
if (NULL != pszMessage)
{
hrTemp = StringCchPrintfEx(pszMessage, dwLength, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("%s|%d|%s|%s|%s|%s|%#x|%#x|%s|%s|%s|%s"),
szPingVersion,
pDriverInfo->dwPnPMatchCount,
(NULL != pDriverInfo->pszMostSpecificHWID) ? pDriverInfo->pszMostSpecificHWID : pszNull,
(NULL != pDriverInfo->pszPreviousDriverVerDate) ? pDriverInfo->pszPreviousDriverVerDate : pszNull,
(NULL != pDriverInfo->pszPreviousDriverVerVersion) ? pDriverInfo->pszPreviousDriverVerVersion : pszNull,
(NULL != pDriverInfo->pszPreviousDriverRank) ? pDriverInfo->pszPreviousDriverRank : pszNull,
pDriverInfo->ulStatus,
pDriverInfo->ulProblemNumber,
(NULL != pDriverInfo->pszPreviousProvider) ? pDriverInfo->pszPreviousProvider : pszNull,
(NULL != pDriverInfo->pszPreviousManufacturer) ? pDriverInfo->pszPreviousManufacturer : pszNull,
(NULL != pDriverInfo->pszPreviousDriverName) ? pDriverInfo->pszPreviousDriverName : pszNull,
(NULL != pDriverInfo->pszPreviousMatchingID) ? pDriverInfo->pszPreviousMatchingID : pszNull);
if (FAILED(hrTemp))
{
SafeHeapFree(pszMessage); // If the Printf failed NULL the pointer so the Ping doesn't get an Empty String or Garbage
}
}
}
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
OLE2T(bstrIdentity), // itemID
lpszDeviceId, // add'l device data for driver update
pszMessage // add'l device driver status info or NULL
);
SafeHeapFree(pszMessage);
}
SafeSysFreeString(bstrIdentity);
}
inline BOOL DeviceInstanceMatchesHWID(LPTSTR pMultiSZ, LPCTSTR pszHWID)
{
LOG_Block("DeviceInstanceMatchesHWID");
if (NULL == pMultiSZ || NULL == pszHWID || pszHWID[0] == 0)
{
//
// Nothing to match to
//
return FALSE;
}
// Now need to find the passed in HWID in the supplied MultiSZ string
LPCTSTR pszTemp = NULL;
for(pszTemp = pMultiSZ; *pszTemp; pszTemp += (lstrlen(pszTemp) + 1))
{
if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE,
pszTemp, -1, pszHWID, -1))
{
// matched
return TRUE;
}
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// GetMatchingInstallInfo()
//
// Determine if this Device Matches the HWID of the Device we are Installing
// Input:
// hDevInfoSet - SetupDi Device Info Set
// pDevInfoData - SetupDi Device Info Data for the current Device
// pDriverInfo - Driver Pre Install Info Structure will be filled out if the device matches
// pszHWID - HWID of the Device we are Installing
//
// Output:
// HRESULT and pDriverInfo Structure Filled out
// NOTE: Fields in pDriverInfo are allocated and need to be free'd by the caller.
/////////////////////////////////////////////////////////////////////////////
HRESULT GetMatchingInstallInfo(HDEVINFO hDevInfoSet, PSP_DEVINFO_DATA pDevInfoData, PDRIVERPREINSTALLINFO pDriverInfo, LPCTSTR pszHWID)
{
LOG_Block("GetMatchingInstallInfo");
HRESULT hr = S_FALSE;
LPTSTR pszMultiHwid = NULL;
LPTSTR pszMultiCompid = NULL;
DWORD dwLength = 0;
DWORD dwNeeded = 0;
OSVERSIONINFO osvi;
if (INVALID_HANDLE_VALUE == hDevInfoSet || NULL == pDevInfoData || NULL == pszHWID || pszHWID[0] == 0)
{
CleanUpIfFailedAndSetHrMsg(E_INVALIDARG);
}
//
// Get the Hardware and Compatible Multi-SZ strings
//
// Note that GetMultiSzDevRegProp may return S_OK and a NULL *ppMultiSZ if the SRDP doesn't exist.
//
CleanUpIfFailedAndSetHr(GetMultiSzDevRegProp(hDevInfoSet, pDevInfoData, SPDRP_HARDWAREID, &pszMultiHwid));
CleanUpIfFailedAndSetHr(GetMultiSzDevRegProp(hDevInfoSet, pDevInfoData, SPDRP_COMPATIBLEIDS, &pszMultiCompid));
if (DeviceInstanceMatchesHWID(pszMultiHwid, pszHWID) || DeviceInstanceMatchesHWID(pszMultiCompid, pszHWID))
{
//
// This device instance matches the HWID we are going to install, so update pDriverInfo
//
if (0 == pDriverInfo->dwPnPMatchCount)
{
// Update the Count and Fill out the DriverInfo structure
pDriverInfo->dwPnPMatchCount++;
dwLength = lstrlen(pszMultiHwid) + 1;
// Write out the HWID
pDriverInfo->pszMostSpecificHWID = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
CleanUpFailedAllocSetHrMsg(pDriverInfo->pszMostSpecificHWID);
hr = StringCchCopyEx(pDriverInfo->pszMostSpecificHWID, dwLength, pszMultiHwid, NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
SafeHeapFree(pDriverInfo->pszMostSpecificHWID);
}
// Write out the Device Instance Id
dwNeeded = 0;
if (!SetupDiGetDeviceInstanceId(hDevInfoSet, pDevInfoData, NULL, 0, &dwNeeded)
&& 0 != dwNeeded
&& ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
pDriverInfo->pszDeviceInstance = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwNeeded * sizeof(TCHAR));
if (NULL != pDriverInfo->pszDeviceInstance)
{
if (!SetupDiGetDeviceInstanceId(hDevInfoSet, pDevInfoData, pDriverInfo->pszDeviceInstance, dwNeeded, NULL))
{
SafeHeapFree(pDriverInfo->pszDeviceInstance);
}
}
}
// Write out the Matching HWID
hr = GetPropertyFromSetupDiReg(hDevInfoSet, *pDevInfoData, REGSTR_VAL_MATCHINGDEVID, &pDriverInfo->pszPreviousMatchingID);
if (FAILED(hr))
{
//
// No REGSTR_VAL_MATCHINGDEVID so there is no driver installed. This is not an error.
//
SafeHeapFree(pDriverInfo->pszPreviousMatchingID);
hr = S_OK;
}
else
{
//
// Get as much additional data about the installed driver as possible, but don't fail
//
// Write out the Provider
hr = GetPropertyFromSetupDiReg(hDevInfoSet, *pDevInfoData, REGSTR_VAL_PROVIDER_NAME, &pDriverInfo->pszPreviousProvider);
if (FAILED(hr))
{
SafeHeapFree(pDriverInfo->pszPreviousProvider);
}
// Write out the Manufacturer
hr = GetPropertyFromSetupDi(hDevInfoSet, *pDevInfoData, SPDRP_MFG, &pDriverInfo->pszPreviousManufacturer);
if (FAILED(hr))
{
SafeHeapFree(pDriverInfo->pszPreviousManufacturer);
}
// Write out the Driver Date
hr = GetPropertyFromSetupDiReg(hDevInfoSet, *pDevInfoData, REGSTR_VAL_DRIVERDATE, &pDriverInfo->pszPreviousDriverVerDate);
if (FAILED(hr))
{
SafeHeapFree(pDriverInfo->pszPreviousDriverVerDate);
}
// Write out the Driver Version
hr = GetPropertyFromSetupDiReg(hDevInfoSet, *pDevInfoData, REGSTR_VAL_DRIVERVERSION, &pDriverInfo->pszPreviousDriverVerVersion);
if (FAILED(hr))
{
SafeHeapFree(pDriverInfo->pszPreviousDriverVerVersion);
}
// Write out the Rank of the Previous Driver
ZeroMemory(&osvi, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
if (6 <= osvi.dwMajorVersion || (5 <= osvi.dwMajorVersion && 1 <= osvi.dwMinorVersion))
{
SP_DEVINSTALL_PARAMS DeviceInstallParams;
SP_DRVINFO_DATA DriverInfoData;
SP_DRVINSTALL_PARAMS DriverInstallParams;
DeviceInstallParams.cbSize = sizeof(DeviceInstallParams);
if (!SetupDiGetDeviceInstallParams(hDevInfoSet, pDevInfoData, &DeviceInstallParams))
{
goto SkipSignedCheck;
}
//
// Set the following flags:
// - DI_FLAGSEX_INSTALLEDDRIVER - just get the installed driver.
// - DI_FLAGSEX_ALLOWEXCLUDEDRVS - allow excluded drivers.
//
DeviceInstallParams.FlagsEx |= (DI_FLAGSEX_INSTALLEDDRIVER | DI_FLAGSEX_ALLOWEXCLUDEDDRVS);
if (!SetupDiSetDeviceInstallParams(hDevInfoSet, pDevInfoData, &DeviceInstallParams))
{
goto SkipSignedCheck;
}
//
// Build the list of drivers that contains just the installed driver.
//
if (!SetupDiBuildDriverInfoList(hDevInfoSet, pDevInfoData, SPDIT_COMPATDRIVER))
{
goto SkipSignedCheck;
}
//
// There should only be one driver in the list, so select it (index 0).
//
DriverInfoData.cbSize = sizeof(DriverInfoData);
if (!SetupDiEnumDriverInfo(hDevInfoSet, pDevInfoData, SPDIT_COMPATDRIVER, 0, &DriverInfoData))
{
goto SkipSignedCheck;
}
//
// Get the SP_DRVINSTALL_PARAMS structure, which contains the rank.
//
DriverInstallParams.cbSize = sizeof(DriverInstallParams);
if (!SetupDiGetDriverInstallParams(hDevInfoSet, pDevInfoData, &DriverInfoData, &DriverInstallParams))
{
goto SkipSignedCheck;
}
//
// Cache the rank
//
const DWORD MAX_DWORD_HEX_CHARS = 11; // "0x00000000 + NULL"
pDriverInfo->pszPreviousDriverRank = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_DWORD_HEX_CHARS * sizeof(TCHAR));
if (NULL != pDriverInfo->pszPreviousDriverRank)
{
if (FAILED(StringCchPrintfEx(pDriverInfo->pszPreviousDriverRank, MAX_DWORD_HEX_CHARS, NULL, NULL,\
MISTSAFE_STRING_FLAGS, _T("%#x"), DriverInstallParams.Rank)))
{
SafeHeapFree(pDriverInfo->pszPreviousDriverRank);
}
}
SkipSignedCheck:
NULL;
}
}
}
else
{
// just update the count, we already cached info on the first instance
pDriverInfo->dwPnPMatchCount++;
}
}
CleanUp:
SafeHeapFree(pszMultiHwid);
SafeHeapFree(pszMultiCompid);
return hr;
}
HRESULT CacheInstalledPrinterDriverInfo(PDRIVERPREINSTALLINFO pDriverInfo, LPCTSTR pszDriverName, LPCTSTR pszHWID, LPCTSTR pszManufacturer, LPCTSTR pszProvider)
{
LOG_Block("CacheInstalledPrinterDriverInfo");
HRESULT hr = S_FALSE;
HRESULT hrTemp = S_OK;
if (NULL == pDriverInfo || NULL == pszDriverName || NULL == pszHWID || NULL == pszManufacturer || NULL == pszProvider)
{
CleanUpIfFailedAndSetHrMsg(E_INVALIDARG);
}
DWORD dwBytesNeeded, dwDriverCount, dwLength;
DRIVER_INFO_6 *pDriverInfo6 = NULL;
LPTSTR pszEnvironment;
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (!GetVersionEx(&osvi))
{
// can't determine the OS, so can't call Enum Printer Drivers, nothing to do here.
CleanUpIfFailedAndSetHrMsg(HRESULT_FROM_WIN32(GetLastError()));
}
if (VER_PLATFORM_WIN32_WINDOWS == osvi.dwPlatformId)
{
//
// Don't pass an environment string for Win9x
//
pszEnvironment = NULL;
}
else if (5 <= osvi.dwMajorVersion && 1 <= osvi.dwMinorVersion)
{
//
// Use EPD_ALL_LOCAL_AND_CLUSTER only on Whistler and up
//
pszEnvironment = EPD_ALL_LOCAL_AND_CLUSTER;
}
else
{
//
// From V3 sources (hard-coded for NT)
//
pszEnvironment = _T("all");
}
if (!EnumPrinterDrivers(NULL, pszEnvironment, 6, NULL, 0, &dwBytesNeeded, &dwDriverCount))
{
if (ERROR_INSUFFICIENT_BUFFER != GetLastError() || (0 == dwBytesNeeded))
{
LOG_Driver(_T("No printer drivers enumerated"));
}
else
{
//
// Allocate the requested buffer
//
CleanUpFailedAllocSetHrMsg(pDriverInfo6 = (DRIVER_INFO_6*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytesNeeded));
// Fill out the DriverInfo6 Array
if (!EnumPrinterDrivers(NULL, pszEnvironment, 6, (LPBYTE)pDriverInfo6, dwBytesNeeded, &dwBytesNeeded, &dwDriverCount))
{
CleanUpIfFailedAndSetHrMsg(HRESULT_FROM_WIN32(GetLastError()));
}
hr = S_FALSE; // init to no printer found
// Find the specified Printer Driver Name, HWID, Manufacturer and Provider in the Device Info 6 Array
for (DWORD dwCount = 0; dwCount < dwDriverCount; dwCount++)
{
if (CSTR_EQUAL != CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE,
pszDriverName, -1, pDriverInfo6[dwCount].pName, -1))
{
// does not match, skip to next Printer
continue;
}
// does match, now try to match the rest, any of them doesn't match means not the same printer driver
if (CSTR_EQUAL != CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE,
pszHWID, -1, pDriverInfo6[dwCount].pszHardwareID, -1))
{
continue;
}
if (CSTR_EQUAL != CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE,
pszManufacturer, -1, pDriverInfo6[dwCount].pszMfgName, -1))
{
continue;
}
if (CSTR_EQUAL != CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE,
pszProvider, -1, pDriverInfo6[dwCount].pszProvider, -1))
{
continue;
}
// if we get here, we found a matching printer driver
hr = S_OK;
// save the information in the DriverInfo structure
if (0 == pDriverInfo->dwPnPMatchCount)
{
pDriverInfo->dwPnPMatchCount++;
// Save the DriverName(Model)
dwLength = lstrlen(pszDriverName) + 1;
pDriverInfo->pszPreviousDriverName = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
if (NULL != pDriverInfo->pszPreviousDriverName)
{
hrTemp = StringCchCopyEx(pDriverInfo->pszPreviousDriverName, dwLength, pszDriverName, NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hrTemp))
{
SafeHeapFree(pDriverInfo->pszPreviousDriverName);
}
}
// Save the HWID
dwLength = lstrlen(pszHWID) + 1;
pDriverInfo->pszMostSpecificHWID = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
if (NULL != pDriverInfo->pszMostSpecificHWID)
{
hrTemp = StringCchCopyEx(pDriverInfo->pszMostSpecificHWID, dwLength, pszHWID, NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hrTemp))
{
SafeHeapFree(pDriverInfo->pszMostSpecificHWID);
}
}
// Save the Provider
dwLength = lstrlen(pszProvider) + 1;
pDriverInfo->pszPreviousProvider = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
if (NULL != pDriverInfo->pszPreviousProvider)
{
hrTemp = StringCchCopyEx(pDriverInfo->pszPreviousProvider, dwLength, pszProvider, NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hrTemp))
{
SafeHeapFree(pDriverInfo->pszPreviousProvider);
}
}
// Save the Manufacturer
dwLength = lstrlen(pszManufacturer) + 1;
pDriverInfo->pszPreviousManufacturer = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
if (NULL != pDriverInfo->pszPreviousManufacturer)
{
hrTemp = StringCchCopyEx(pDriverInfo->pszPreviousManufacturer, dwLength, pszManufacturer, NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hrTemp))
{
SafeHeapFree(pDriverInfo->pszPreviousManufacturer);
}
}
// Save the Driver Date
SYSTEMTIME st;
ZeroMemory(&st, sizeof(SYSTEMTIME));
dwLength = 32; // Maximum Length for a Date String (00-00-0000)
pDriverInfo->pszPreviousDriverVerDate = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
if (NULL != pDriverInfo->pszPreviousDriverVerDate)
{
if (!FileTimeToSystemTime(&pDriverInfo6[dwCount].ftDriverDate, &st))
{
// use 00-00-0000 as the date
hrTemp = StringCchCopyEx(pDriverInfo->pszPreviousDriverVerDate, dwLength, _T("00-00-0000"), NULL, NULL, MISTSAFE_STRING_FLAGS);
}
else
{
hrTemp = StringCchPrintfEx(pDriverInfo->pszPreviousDriverVerDate, dwLength, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("%02d-%02d-%d"), st.wMonth, st.wDay, st.wYear);
}
if (FAILED(hrTemp))
{
SafeHeapFree(pDriverInfo->pszPreviousDriverVerDate);
}
}
// Save the Driver Version
dwLength = 32; // Maximum Length for a Driver Version String (xxxx.xxxx.xxxx.xxxx)
pDriverInfo->pszPreviousDriverVerVersion = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
if (NULL != pDriverInfo->pszPreviousDriverVerVersion)
{
// Cast the DWORDLONG to a WORD * so we can access it as a WORD array
WORD *pwVersion = (WORD *)&pDriverInfo6[dwCount].dwlDriverVersion;
hrTemp = StringCchPrintfEx(pDriverInfo->pszPreviousDriverVerVersion, dwLength, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("%d.%d.%d.%d"),
pwVersion[3],
pwVersion[2],
pwVersion[1],
pwVersion[0]);
if (FAILED(hrTemp))
{
SafeHeapFree(pDriverInfo->pszPreviousDriverVerVersion);
}
}
}
else
{
pDriverInfo->dwPnPMatchCount++;
}
}
}
}
CleanUp:
SafeHeapFree(pDriverInfo6);
return hr;
}
HRESULT CachePnPInstalledDriverInfo(PDRIVERPREINSTALLINFO pDriverInfo, LPCTSTR pszHWID)
{
LOG_Block("CachePnPInstalledDriverInfo");
DWORD dwDeviceIndex = 0;
HDEVINFO hDevInfoSet = INVALID_HANDLE_VALUE;
SP_DEVINFO_DATA devInfoData;
HRESULT hr = S_FALSE;
if (NULL == pDriverInfo || NULL == pszHWID || pszHWID[0] == 0)
{
CleanUpIfFailedAndSetHrMsg(E_INVALIDARG);
}
if (INVALID_HANDLE_VALUE == (hDevInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES)))
{
LOG_Error(_T("SetupDiGetClassDevs failed: 0x%08x"), GetLastError());
return HRESULT_FROM_WIN32(GetLastError());
}
ZeroMemory(&devInfoData, sizeof(SP_DEVINFO_DATA));
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
while (SetupDiEnumDeviceInfo(hDevInfoSet, dwDeviceIndex++, &devInfoData))
{
CleanUpIfFailedAndSetHr(GetMatchingInstallInfo(hDevInfoSet, &devInfoData, pDriverInfo, pszHWID));
}
CleanUp:
if (INVALID_HANDLE_VALUE != hDevInfoSet)
{
if (0 == SetupDiDestroyDeviceInfoList(hDevInfoSet))
{
LOG_Driver(_T("Warning: SetupDiDestroyDeviceInfoList failed: 0x%08x"), GetLastError());
}
}
return hr;
}
HRESULT GetPnPInstallStatus(PDRIVERPREINSTALLINFO pDriverInfo)
{
LOG_Block("GetPnPInstallStatus");
if (NULL == pDriverInfo || NULL == pDriverInfo->pszDeviceInstance)
{
return E_INVALIDARG;
}
DEVINST hDevInst;
if (CR_SUCCESS == CM_Locate_DevNode(&hDevInst, pDriverInfo->pszDeviceInstance, CM_LOCATE_DEVNODE_NORMAL))
{
(void)CM_Get_DevNode_Status(&pDriverInfo->ulStatus, &pDriverInfo->ulProblemNumber, hDevInst, 0);
}
return S_OK;
}