windows-nt/Source/XPSP1/NT/enduser/windows.com/iuengine/cdmp.cpp
2020-09-26 16:20:57 +08:00

2010 lines
59 KiB
C++

//=======================================================================
//
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
//
// File: cdmp.cpp
//
// Description:
//
// CDM auxiliary functions
//
// called by DownloadUpdatedFiles()
// GetDownloadPath
//
// called by InternalLogDriverNotFound()
// OpenUniqueFileName
//
//=======================================================================
#include <iuengine.h>
#include <shlwapi.h>
#include <ras.h>
#include <tchar.h>
#include <winver.h>
#include <download.h>
#include <wininet.h>
#include <fileutil.h>
#include "iuxml.h"
#include <wuiutest.h>
#include <StringUtil.h>
#include <cdm.h>
#include "cdmp.h"
#include "schemamisc.h"
#include <safefile.h>
const DWORD MAX_INF_STRING = 512; // From DDK docs "General Syntax Rules for INF Files" section
const OLECHAR szXmlClientInfo[] = L"<clientInfo xmlns=\"x-schema:http://schemas.windowsupdate.com/iu/clientInfo.xml\" clientName=\"CDM\" />";
const OLECHAR szXmlPrinterCatalogQuery[] = L"<query><dObjQueryV1 procedure=\"printercatalog\"></dObjQueryV1></query>";
const OLECHAR szXmlDriverDownloadQuery[] = L"<query><dObjQueryV1 procedure=\"driverupdates\"></dObjQueryV1></query>";
/////////////////////////////////////////////////////////////////////////////
// CXmlDownloadResult
class CXmlDownloadResult : public CIUXml
{
public:
CXmlDownloadResult();
~CXmlDownloadResult();
HRESULT LoadXMLDocumentItemStatusList(BSTR bstrXml);
//
// Expose m_pItemNodeList so it can be used directly
//
IXMLDOMNodeList* m_pItemStatusNodeList;
private:
IXMLDOMDocument* m_pDocResultItems;
};
/////////////////////////////////////////////////////////////////////////////
// CXmlDownloadResult
/////////////////////////////////////////////////////////////////////////////
CXmlDownloadResult::CXmlDownloadResult()
: m_pDocResultItems(NULL), m_pItemStatusNodeList(NULL)
{
}
CXmlDownloadResult::~CXmlDownloadResult()
{
SafeReleaseNULL(m_pDocResultItems);
SafeReleaseNULL(m_pItemStatusNodeList);
}
/////////////////////////////////////////////////////////////////////////////
// LoadXMLDocumentItemStatusList()
//
// Load an XML Document from string and create the list of items
//
// Calls to Download produce return status in the following (example) XML format:
//
// <?xml version="1.0"?>
// <items xmlns="x-schema:http://schemas.windowsupdate.com/iu/resultschema.xml">
// <itemStatus xmlns="">
// <identity name="nvidia.569">nvidia.569
// <publisherName>nvidia</publisherName>
// </identity>
// <downloadStatus value="COMPLETE" errorCode="100"/>
// </itemStatus>
// </items>
//
// We expose m_pItemNodeList so it can be used directly to retrieve the value
// attribute of the <downloadStatus /> item.
//
// NOTE: for CDM there will only be one item downloaded at a time, so the list
// will only contain a single <itemStatus/> element.
//
/////////////////////////////////////////////////////////////////////////////
HRESULT CXmlDownloadResult::LoadXMLDocumentItemStatusList(BSTR bstrXml)
{
LOG_Block("CXmlDownloadResult::LoadXMLDocumentItemStatusList");
HRESULT hr = S_OK;
BSTR bstrAllDocumentItems = NULL;
if (NULL == bstrXml || m_pDocResultItems || m_pItemStatusNodeList)
{
CleanUpIfFailedAndSetHrMsg(E_INVALIDARG);
}
CleanUpIfFailedAndSetHr(LoadXMLDoc(bstrXml, &m_pDocResultItems));
//
// Get a list of all <itemStatus/> elements anywhere in the document
//
if (NULL == (bstrAllDocumentItems = SysAllocString(L"//itemStatus")))
{
CleanUpIfFailedAndSetHrMsg(E_OUTOFMEMORY);
}
if (NULL == (m_pItemStatusNodeList = FindDOMNodeList(m_pDocResultItems, bstrAllDocumentItems)))
{
CleanUpIfFailedAndSetHr(E_FAIL);
}
CleanUp:
SysFreeString(bstrAllDocumentItems);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// CXmlPrinterCatalogList
class CXmlPrinterCatalogList : public CIUXml
{
public:
CXmlPrinterCatalogList();
~CXmlPrinterCatalogList();
HRESULT LoadXMLDocumentAndGetCompHWList(BSTR bstrXml);
//
// Expose m_pCompHWNodeList so it can be used directly
//
IXMLDOMNodeList* m_pCompHWNodeList;
private:
IXMLDOMDocument* m_pDocCatalogItems;
};
/////////////////////////////////////////////////////////////////////////////
// CXmlPrinterCatalogList
/////////////////////////////////////////////////////////////////////////////
CXmlPrinterCatalogList::CXmlPrinterCatalogList()
: m_pDocCatalogItems(NULL), m_pCompHWNodeList(NULL)
{
}
CXmlPrinterCatalogList::~CXmlPrinterCatalogList()
{
SafeReleaseNULL(m_pDocCatalogItems);
SafeReleaseNULL(m_pCompHWNodeList);
}
/////////////////////////////////////////////////////////////////////////////
// LoadXMLDocumentAndGetCompHWList()
//
// Load an XML Document from string and create the list of
// <compatibleHardware/> elements.
//
// "printercatalog" SOAP queries sent via GetManifest return a list of
// all printers for the given platform in the following format (validates against
// http://schemas.windowsupdate.com/iu/catalogschema.xml) having the following
// characteristics:
//
// * <catalog clientType="CONSUMER">
// * Only a single <provider> with <identity name="printerCatalog">printerCatalog</identity>
// * returned <platform/> is not used by CDM
// * <item/> identity and <platform> are likewise ignored by CDM
// * Under item/detection/compatibleHardware/device the driverName, driverProvider, mfgName,
// and driverVer attributes, as well as hwid string are extracted and used to build
// printer INF files.
// * Algorithms in this class take advantage of the fact that driverProvider attributes
// are serialized (e.g. grouped in order by driverProvider), however this is not a requirement.
// * Note that <item/> elements can contain more than one <compatibleHardware/> element,
// but the complete list of <compatibleHardware/> elements provides all printers in
// the given catalog
//
// Sample start of a "printerCatalog" catalog:
// ------------------------------------------
// <?xml version="1.0" ?>
// - <catalog clientType="CONSUMER">
// - <provider>
// <identity name="printerCatalog">printerCatalog</identity>
// <platform>ver_platform_win32_nt.5.0.x86.en</platform>
// + <item installable="1">
// <identity name="hp.3">hp.3</identity>
// - <detection>
// - <compatibleHardware>
// - <device isPrinter="1">
// <printerInfo driverName="HP PSC 500" driverProvider="Hewlett-Packard Co." mfgName="HP" />
// <hwid rank="0" driverVer="1999-12-14">DOT4PRT\HEWLETT-PACKARDPSC_59784</hwid>
// </device>
// </compatibleHardware>
// - <compatibleHardware>
// ... etc.
// </detection>
// </item>
// + <item installable="1">
// ... etc.
//
//
// Likewise, driver information for requested PnP drivers is returned in catalog
// items.
//
/////////////////////////////////////////////////////////////////////////////
HRESULT CXmlPrinterCatalogList::LoadXMLDocumentAndGetCompHWList(BSTR bstrXml)
{
LOG_Block("CXmlPrinterCatalogList::LoadXMLDocumentAndGetCompHWList");
HRESULT hr = S_OK;
BSTR bstrAllDocumentItems = NULL;
if (NULL == bstrXml || m_pDocCatalogItems || m_pCompHWNodeList)
{
CleanUpIfFailedAndSetHrMsg(E_INVALIDARG);
}
CleanUpIfFailedAndSetHr(LoadXMLDoc(bstrXml, &m_pDocCatalogItems));
//
// Get a list of all <item/> elements anywhere in the document
//
if (NULL == (bstrAllDocumentItems = SysAllocString(L"//compatibleHardware")))
{
CleanUpIfFailedAndSetHrMsg(E_OUTOFMEMORY);
}
if (NULL == (m_pCompHWNodeList = FindDOMNodeList(m_pDocCatalogItems, bstrAllDocumentItems)))
{
CleanUpIfFailedAndSetHr(E_FAIL);
}
CleanUp:
SysFreeString(bstrAllDocumentItems);
return hr;
}
///////////////////////////////////////////////////////////////////
//
// Locally defined LPTSTR array - dynamically expands
//
///////////////////////////////////////////////////////////////////
#define NUM_DIIDPTR_ALLOC 10
CDeviceInstanceIdArray::CDeviceInstanceIdArray()
: m_ppszDIID(NULL), m_nCount(0), m_nPointers(0)
{
}
CDeviceInstanceIdArray::~CDeviceInstanceIdArray()
{
LOG_Block("CDeviceInstanceIdArray::~CDeviceInstanceIdArray");
FreeAll();
//
// Free the array of LPTSTRs
//
SafeHeapFree(m_ppszDIID);
m_nPointers = 0;
}
void CDeviceInstanceIdArray::FreeAll()
{
LOG_Block("CDeviceInstanceIdArray::Free");
if (NULL != m_ppszDIID && 0 < m_nCount)
{
//
// Free the strings
//
for (int i = 0; i < m_nCount; i++)
{
SafeHeapFree(*(m_ppszDIID+i));
}
m_nCount = 0;
}
}
int CDeviceInstanceIdArray::Add(LPCWSTR pszDIID)
{
HRESULT hr;
LPWSTR pszIDtoAdd = NULL;
DWORD cch;
LOG_Block("CDeviceInstanceIdArray::Add");
if (NULL == pszDIID)
{
LOG_ErrorMsg(E_INVALIDARG);
return -1;
}
//
// Allocate or realloc space for NUM_DIIDPTR_ALLOC LPSTRs
//
if (NULL == m_ppszDIID)
{
m_ppszDIID = (LPWSTR*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LPWSTR) * NUM_DIIDPTR_ALLOC);
if(NULL == m_ppszDIID)
{
LOG_ErrorMsg(E_OUTOFMEMORY);
return -1;
}
m_nPointers = NUM_DIIDPTR_ALLOC;
}
else if (m_nCount == m_nPointers)
{
//
// We've used all our allocated pointers, realloc more
//
LPWSTR* ppTempDIID;
//
// Increase number of pointers currently allocated by NUM_DIIDPTR_ALLOC
//
ppTempDIID = (LPWSTR*) HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, m_ppszDIID,
(sizeof(LPWSTR) * (m_nPointers + NUM_DIIDPTR_ALLOC)) );
if(NULL == ppTempDIID)
{
LOG_ErrorMsg(E_OUTOFMEMORY);
return -1;
}
m_ppszDIID = ppTempDIID;
m_nPointers += NUM_DIIDPTR_ALLOC;
}
//
// Alloc memory for to hold the DIID and copy it
//
cch = (lstrlenW(pszDIID) + 1);
if (NULL == (pszIDtoAdd = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, cch * sizeof(WCHAR))))
{
LOG_ErrorMsg(E_OUTOFMEMORY);
goto CleanUp;
}
hr = StringCchCopyExW(pszIDtoAdd, cch, pszDIID, NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
HeapFree(GetProcessHeap(), 0, pszIDtoAdd);
pszIDtoAdd = NULL;
goto CleanUp;
}
*(m_ppszDIID+m_nCount) = pszIDtoAdd;
m_nCount++;
CleanUp:
if (NULL == pszIDtoAdd)
{
return -1;
}
else
{
#if defined(_UNICODE) || defined(UNICODE)
LOG_Driver(_T("%s added to list"), pszIDtoAdd);
#else
LOG_Driver(_T("%S added to list"), pszIDtoAdd);
#endif
return m_nCount - 1;
}
}
LPWSTR CDeviceInstanceIdArray::operator[](int index)
{
LOG_Block("CDeviceInstanceIdArray::operator[]");
if (0 > index || m_nCount < index + 1)
{
LOG_ErrorMsg(E_INVALIDARG);
return NULL;
}
return *(m_ppszDIID+index);
}
///////////////////////////////////////////////////////////////////
// Gets a path to the directory that cdm.dll has copied the install cabs to
// and returns the length of the path.
// Note: The input buffer must be at least MAX_PATH size.
HRESULT GetDownloadPath(
IN BSTR bstrXmlCatalog, // Catalog we passed to Download (only contains one item)
IN BSTR bstrXmlDownloadedItems,
IN OUT LPTSTR lpDownloadPath, // Local directory where extracted files were placed.
IN OUT DWORD cchDownloadPath
)
{
USES_IU_CONVERSION;
LOG_Block("GetDownloadPath");
HRESULT hr = S_OK;
BSTR bstrDownloadPath = NULL;
BSTR bstrItem = NULL;
CXmlItems* pxmlDownloadedItems = NULL;
CXmlCatalog catalog;
HANDLE_NODE hCatalogItem;
HANDLE_NODE hProvider;
HANDLE_NODELIST hItemList;
HANDLE_NODELIST hProviderList;
if (NULL == bstrXmlCatalog || NULL == lpDownloadPath || NULL == bstrXmlDownloadedItems || 0 == cchDownloadPath)
{
CleanUpIfFailedAndSetHr(E_INVALIDARG);
}
lpDownloadPath[0] = _T('\0');
//
// Load the XML and get the <item/> list and node of first item (only one in CDM case)
//
CleanUpIfFailedAndSetHr(catalog.LoadXMLDocument(bstrXmlCatalog, g_pCDMEngUpdate->m_fOfflineMode));
hProviderList = catalog.GetFirstProvider(&hProvider);
if (HANDLE_NODELIST_INVALID == hProviderList || HANDLE_NODE_INVALID == hProvider)
{
CleanUpIfFailedAndSetHr(E_INVALIDARG);
}
hItemList = catalog.GetFirstItem(hProvider, &hCatalogItem);
if (HANDLE_NODELIST_INVALID == hItemList || HANDLE_NODE_INVALID == hProvider)
{
CleanUpIfFailedAndSetHr(E_FAIL);
}
//
// Construct CXmlItems for read
//
CleanUpFailedAllocSetHrMsg(pxmlDownloadedItems = new CXmlItems(TRUE));
CleanUpIfFailedAndMsg(pxmlDownloadedItems->LoadXMLDocument(bstrXmlDownloadedItems));
hr = pxmlDownloadedItems->GetItemDownloadPath(&catalog, hCatalogItem, &bstrDownloadPath);
if (NULL == bstrDownloadPath)
{
LOG_Driver(_T("Failed to get Item Download Path from ReturnSchema"));
if (SUCCEEDED(hr))
hr = E_FAIL;
goto CleanUp;
}
hr = StringCchCopyEx(lpDownloadPath, cchDownloadPath, OLE2T(bstrDownloadPath),
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto CleanUp;
CleanUp:
if (pxmlDownloadedItems)
{
delete pxmlDownloadedItems;
}
SysFreeString(bstrDownloadPath);
SysFreeString(bstrItem);
return hr;
}
// called by InternalDriverNotFound(...)
// Find a file name not used so far into which hardware xml information will be inserted
// The file name will be in format hardware_xxx.xml where xxx is in range [1..MAX_INDEX_TO_SEARCH]
// The position file found last time is remembered and new search will start from the next position
// Caller is supposed to close handle and delete file
// pszFilePath IN OUT : allocated and freed by caller. Buffer to store unique file name found: MUST be MAX_PATH
// hFile OUT : store a handle to the opened file
//return S_OK if Unique File Name found
//return E_INVALIDARG if buffer pointer is NULL (must be called with MAX_PATH length buffer)
//return E_FAIL if all qualified file names already taken
HRESULT OpenUniqueFileName(
IN OUT LPTSTR pszFilePath,
IN DWORD cchFilePath,
OUT HANDLE &hFile
)
{
LOG_Block("OpenUniqueFileName");
static DWORD dwFileIndex = 1;
int nCount = 0;
const TCHAR FILENAME[] = _T("Hardware_");
const TCHAR FILEEXT[] = _T("xml");
TCHAR szDirPath[MAX_PATH + 1];
HRESULT hr;
if (NULL == pszFilePath || 0 == cchFilePath)
{
LOG_ErrorMsg(E_INVALIDARG);
return E_INVALIDARG;
}
pszFilePath[0] = _T('\0');
GetIndustryUpdateDirectory(szDirPath);
LOG_Out(_T("Directory to search unique file names: %s"), szDirPath);
hFile = INVALID_HANDLE_VALUE;
do
{
hr = StringCchPrintfEx(pszFilePath, cchFilePath, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("%s%s%d.%s"), szDirPath, FILENAME, dwFileIndex, FILEEXT);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
return hr;
}
hFile = CreateFile(pszFilePath, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
//
// Could test for ERROR_FILE_EXISTS == dwErr (expected) and bail on other errors indicating
// a more serious problem, however this return isn't doc'ed in the JAN 2001 SDK, and the
// documented ERROR_ALREADY_EXISTS applies instead to CREATE_ALWAYS or OPEN_ALWAYS.
//
LOG_Out(_T("%s already exists"), pszFilePath);
dwFileIndex ++;
nCount ++;
if (dwFileIndex > MAX_INDEX_TO_SEARCH)
{
dwFileIndex = 1;
}
}
else
{
break; //first available file name found
}
}while(nCount < MAX_INDEX_TO_SEARCH );
if (nCount == MAX_INDEX_TO_SEARCH )
{
LOG_Out(_T("All %d file names have been taken"), nCount);
LOG_ErrorMsg(E_FAIL);
return E_FAIL;
}
LOG_Out(_T("Unique file name %s opened for GENERIC_WRITE using CreateFile"), pszFilePath);
dwFileIndex++; //next time skip file name found this time
if (dwFileIndex > MAX_INDEX_TO_SEARCH)
{
//
// Start again at the beginning - maybe one of earlier files has been deleted by HelpCenter...
//
dwFileIndex = 1;
}
return S_OK;
}
HRESULT WriteInfHeader(LPCTSTR pszProvider, HANDLE& hFile)
{
LOG_Block("WriteInfHeader");
const TCHAR HEADER_START[] =
_T("[Version]\r\n")
_T("Signature=\"$Windows NT$\"\r\n")
_T("Provider=%PRTPROV%\r\n")
_T("ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318}\r\n")
_T("Class=Printer\r\nCatalogFile=webntprn.cat\r\n")
_T("\r\n")
_T("[ClassInstall32.NT]\r\n")
_T("AddReg=printer_class_addreg\r\n")
_T("\r\n")
_T("[printer_class_addreg]\r\n")
_T("HKR,,,,%%PrinterClassName%%\r\n")
_T("HKR,,Icon,,\"-4\"\r\n")
_T("HKR,,Installer32,,\"ntprint.dll,ClassInstall32\"\r\n")
_T("HKR,,NoDisplayClass,,1\r\n")
_T("HKR,,EnumPropPages32,,\"printui.dll,PrinterPropPageProvider\"\r\n")
_T("\r\n")
_T("[Strings]\r\n")
_T("PRTPROV=\"");
const TCHAR HEADER_END[] =
_T("\"\r\n")
_T("PrinterClassName=\"Printer\"\r\n")
_T("\r\n");
HRESULT hr = S_OK;
DWORD dwWritten;
if (NULL == pszProvider || hFile == INVALID_HANDLE_VALUE)
{
LOG_ErrorMsg(E_INVALIDARG);
return E_INVALIDARG;
}
#if defined(_UNICODE) || defined(UNICODE)
//
// Write Unicode Header
//
if (0 == WriteFile(hFile, (LPCVOID) &UNICODEHDR, sizeof(UNICODEHDR), &dwWritten, NULL))
{
SetHrMsgAndGotoCleanUp(GetLastError());
}
#endif
//
// Write the first part of INF header
//
if (0 == WriteFile(hFile, HEADER_START, sizeof(HEADER_START) - sizeof(TCHAR), &dwWritten, NULL))
{
SetHrMsgAndGotoCleanUp(GetLastError());
}
//
// Write the provider string
//
if (0 == WriteFile(hFile, (LPCVOID) pszProvider, lstrlen(pszProvider) * sizeof(TCHAR), &dwWritten, NULL))
{
SetHrMsgAndGotoCleanUp(GetLastError());
}
//
// Write the remainder of the INF header
//
if (0 == WriteFile(hFile, HEADER_END, sizeof(HEADER_END) - sizeof(TCHAR), &dwWritten, NULL))
{
SetHrMsgAndGotoCleanUp(GetLastError());
}
CleanUp:
if (FAILED(hr) && INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
return hr;
}
//
// pszFilePath must be >= MAX_PATH characters
//
HRESULT OpenUniqueProviderInfName(
IN LPCTSTR szDirPath,
IN LPCTSTR pszProvider,
IN OUT LPTSTR pszFilePath,
IN DWORD cchFilePath,
OUT HANDLE &hFile
)
{
LOG_Block("OpenUniqueProviderInfName");
const TCHAR FILEROOT[] = _T("PList_");
const TCHAR FILEEXT[] = _T("inf");
DWORD dwErr;
HRESULT hr;
hFile = INVALID_HANDLE_VALUE;
if (NULL == pszFilePath || NULL == pszProvider || NULL == szDirPath || 0 == cchFilePath)
{
LOG_ErrorMsg(E_INVALIDARG);
return E_INVALIDARG;
}
pszFilePath[0] = _T('\0');
hr = StringCchPrintfEx(pszFilePath, cchFilePath, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("%s%s%s.%s"), szDirPath, FILEROOT, pszProvider, FILEEXT);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
pszFilePath[0] = _T('\0');
return hr;
}
//
// Try to open an existing INF of this name. If this fails try to create then init the file.
//
hFile = CreateFile(pszFilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
hFile = CreateFile(pszFilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
dwErr = GetLastError();
LOG_ErrorMsg(dwErr);
pszFilePath[0] = _T('\0');
return HRESULT_FROM_WIN32(dwErr);
}
//
// Write the INF "Header" information to the new file
//
if (FAILED( hr = WriteInfHeader(pszProvider, hFile)))
{
pszFilePath[0] = _T('\0');
return hr;
}
}
return S_OK;
}
HRESULT OfferThisPrinterDriver(
DRIVER_INFO_6* paDriverInfo6, // array of DRIVER_INFO_6 structs for installed printer drivers
DWORD dwDriverInfoCount, // count of structs in paDriverInfo6 array
IXMLDOMNode* pCompHWNode, // <compatibleHardware> node from catalog
BOOL* pfOfferDriver, // [OUT] If TRUE offer this driver - remainder of outputs are valid
VARIANT& vDriverName, // [OUT]
VARIANT& vDriverVer, // [OUT]
VARIANT& vDriverProvider, // [OUT]
VARIANT& vMfgName, // [OUT]
BSTR* pbstrHwidText) // [OUT]
{
USES_IU_CONVERSION;
LOG_Block("OfferThisPrinterDriver");
HRESULT hr = S_OK;
IXMLDOMNode* pDriverNameNode = NULL;
IXMLDOMNode* pDriverProviderNode = NULL;
IXMLDOMNode* pMfgNameNode = NULL;
IXMLDOMNode* pPInfoNode = NULL;
IXMLDOMNode* pHwidNode = NULL;
IXMLDOMNode* pDriverVerNode = NULL;
IXMLDOMNamedNodeMap* pAttribMap = NULL;
LPCTSTR pszCompareHwid = NULL;
#if !(defined(_UNICODE) || defined(UNICODE))
//
// We need to special-case ANSI since we can't use pointers into pbstrHwidText, which is wide
//
TCHAR szHwid[MAX_INF_STRING + 1];
#endif
if (
NULL == pCompHWNode ||
NULL == pfOfferDriver ||
NULL == pbstrHwidText)
{
CleanUpIfFailedAndSetHrMsg(E_INVALIDARG);
}
VariantInit(&vDriverName);
VariantInit(&vDriverVer);
VariantInit(&vDriverProvider);
VariantInit(&vMfgName);
*pfOfferDriver = TRUE;
*pbstrHwidText = NULL;
//
// Get the first <printerInfo/> node of the item (we expect at least one else fail)
//
CleanUpIfFailedAndSetHrMsg(pCompHWNode->selectSingleNode(KEY_CDM_PINFO, &pPInfoNode));
//
// 517297 Ignore non-printer HWIDs in OfferThisPrinterDriver
//
// We may get device nodes that are not marked isPrinter="1" and do not have the <printerInfo/>
// element. We don't offer these device nodes, but it is not an error.
//
if (NULL == pPInfoNode)
{
//
// Change S_FALSE back to S_OK, but don't offer this device
//
hr = S_OK;
*pfOfferDriver = FALSE;
goto CleanUp;
}
CleanUpIfFailedAndSetHrMsg(pPInfoNode->get_attributes(&pAttribMap));
if (NULL == pAttribMap) CleanUpIfFailedAndSetHrMsg(E_FAIL);
//
// suck out the printerInfo attributes
//
CleanUpIfFailedAndSetHrMsg(pAttribMap->getNamedItem(KEY_DRIVERNAME, &pDriverNameNode));
if (NULL == pDriverNameNode) CleanUpIfFailedAndSetHrMsg(E_FAIL);
CleanUpIfFailedAndSetHrMsg(pAttribMap->getNamedItem(KEY_DRIVERPROVIDER, &pDriverProviderNode));
if (NULL == pDriverProviderNode) CleanUpIfFailedAndSetHrMsg(E_FAIL);
CleanUpIfFailedAndSetHrMsg(pAttribMap->getNamedItem(KEY_MFGNAME, &pMfgNameNode));
if (NULL == pMfgNameNode) CleanUpIfFailedAndSetHrMsg(E_FAIL);
//
// pAttribMap will be reused later, free it here
//
SafeReleaseNULL(pAttribMap);
CleanUpIfFailedAndSetHrMsg(pDriverNameNode->get_nodeValue(&vDriverName));
CleanUpIfFailedAndSetHrMsg(pDriverProviderNode->get_nodeValue(&vDriverProvider));
CleanUpIfFailedAndSetHrMsg(pMfgNameNode->get_nodeValue(&vMfgName));
if (VT_BSTR != vDriverName.vt || VT_BSTR != vDriverProvider.vt || VT_BSTR != vMfgName.vt)
{
CleanUpIfFailedAndSetHrMsg(E_FAIL);
}
//
// Get the first <hwid/> node of the item (we expect at least one else fail)
//
CleanUpIfFailedAndSetHrMsg(pCompHWNode->selectSingleNode(KEY_CDM_HWIDPATH, &pHwidNode));
if (NULL == pHwidNode) CleanUpIfFailedAndSetHrMsg(E_FAIL);
CleanUpIfFailedAndSetHrMsg(pHwidNode->get_attributes(&pAttribMap));
if (NULL == pAttribMap) CleanUpIfFailedAndSetHrMsg(E_FAIL);
//
// suck out the DriverVer attribute
//
CleanUpIfFailedAndSetHrMsg(pAttribMap->getNamedItem(KEY_DRIVERVER, &pDriverVerNode));
if (NULL == pDriverVerNode) CleanUpIfFailedAndSetHrMsg(E_FAIL);
CleanUpIfFailedAndSetHrMsg(pDriverVerNode->get_nodeValue(&vDriverVer));
if (VT_BSTR != vDriverVer.vt)
{
CleanUpIfFailedAndSetHrMsg(E_FAIL);
}
//
// Get the <hwid/> text
//
// NOTE: Each item is restricted to a single <hwid/> element due to INF syntax,
// however our catalog schema doesn't make similar restrictions and currently our
// backend doesn't distinguish between <hwid/> and <compid/> values, so it is
// possible we could get more than one <hwid/> returned. For the purpose of
// generating INFs for Add Printer Wizard, any <hwid/> from the CAB will do.
//
CleanUpIfFailedAndSetHrMsg(pHwidNode->get_text(pbstrHwidText));
#if !(defined(_UNICODE) || defined(UNICODE))
hr = StringCchCopyEx(szHwid, ARRAYSIZE(szHwid), OLE2T(*pbstrHwidText),
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
LOG_Driver(_T("Got \"%s\" from XML for compare to DRIVER_INFO_6."), szHwid);
#else
LOG_Driver(_T("Got \"%s\" from XML for compare to DRIVER_INFO_6."), *pbstrHwidText);
#endif
if (NULL == paDriverInfo6 || 0 == dwDriverInfoCount)
{
LOG_Driver(_T("WARNING: We're missing information (maybe no installed printer drivers), so we won't prune"));
goto CleanUp;
}
#if !(defined(_UNICODE) || defined(UNICODE))
pszCompareHwid = szHwid;
#else
pszCompareHwid = (LPCTSTR) *pbstrHwidText;
#endif
for (DWORD dwCount = 0; dwCount < dwDriverInfoCount; dwCount++)
{
if (NULL == (paDriverInfo6 + dwCount)->pszHardwareID)
{
continue;
}
//
// Use case-insensitive compares (paDriverInfo6 is different case from pszCompareHwid)
//
if (0 != lstrcmpi(pszCompareHwid, (paDriverInfo6 + dwCount)->pszHardwareID))
{
continue;
}
//
// Else we have a hardware match - check the other attributes for exact match
//
if (0 != lstrcmpi(OLE2T(vDriverName.bstrVal), (paDriverInfo6 + dwCount)->pName) ||
0 != lstrcmpi(OLE2T(vDriverProvider.bstrVal), (paDriverInfo6 + dwCount)->pszProvider) ||
0 != lstrcmpi(OLE2T(vMfgName.bstrVal), (paDriverInfo6 + dwCount)->pszMfgName))
{
//
LOG_Driver(_T("Prune this driver: it doesn't match all the attributes of the installed driver"));
*pfOfferDriver = FALSE;
goto CleanUp;
}
//
// The driver matches, but make sure it has a newer DriverVer than the installed driver
//
LOG_Driver(_T("Driver item in catalog is compatible with installed driver"));
SYSTEMTIME systemTime;
if (0 == FileTimeToSystemTime((CONST FILETIME*) &((paDriverInfo6 + dwCount)->ftDriverDate), &systemTime))
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
//
// Convert to ISO ISO 8601 prefered format (yyyy-mm-dd) so we can string compare with catalog BSTR
//
WCHAR wszDriverVer[11];
hr = StringCchPrintfExW(wszDriverVer, ARRAYSIZE(wszDriverVer), NULL, NULL, MISTSAFE_STRING_FLAGS,
L"%04d-%02d-%02d", systemTime.wYear, systemTime.wMonth, systemTime.wDay);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
if (0 < lstrcmpW(vDriverVer.bstrVal, wszDriverVer))
{
LOG_Driver(_T("WU DriverVer (%s) is > installed (%s)"), vDriverVer.bstrVal, wszDriverVer);
*pfOfferDriver = TRUE;
goto CleanUp;
}
else
{
LOG_Driver(_T("Prune this driver: WU DriverVer (%s) is <= installed (%s)"), vDriverVer.bstrVal, wszDriverVer);
*pfOfferDriver = FALSE;
#if defined(__WUIUTEST)
// DriverVer Override for ==
HKEY hKey;
int error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WUIUTEST, 0, KEY_READ, &hKey);
if (ERROR_SUCCESS == error)
{
DWORD dwSize = sizeof(DWORD);
DWORD dwValue;
error = RegQueryValueEx(hKey, REGVAL_ALLOW_EQUAL_DRIVERVER, 0, 0, (LPBYTE) &dwValue, &dwSize);
if (ERROR_SUCCESS == error && 1 == dwValue)
{
//
// If DriverVers are equal (we already installed a driver from WU, allow it anyway
//
if (0 == lstrcmpW(vDriverVer.bstrVal, wszDriverVer))
{
*pfOfferDriver = TRUE;
LOG_Driver(_T("WU DriverVer (%s) is = installed (%s), WUIUTEST override and offer"), vDriverVer.bstrVal, wszDriverVer);
}
}
RegCloseKey(hKey);
}
#endif
goto CleanUp;
}
}
CleanUp:
if (FAILED(hr))
{
if (NULL != pfOfferDriver)
{
*pfOfferDriver = FALSE;
}
if (NULL != pbstrHwidText)
{
SafeSysFreeString(*pbstrHwidText);
}
VariantClear(&vDriverName);
VariantClear(&vDriverVer);
VariantClear(&vDriverProvider);
VariantClear(&vMfgName);
}
SafeReleaseNULL(pDriverNameNode);
SafeReleaseNULL(pDriverProviderNode);
SafeReleaseNULL(pMfgNameNode);
SafeReleaseNULL(pPInfoNode);
SafeReleaseNULL(pHwidNode);
SafeReleaseNULL(pDriverVerNode);
SafeReleaseNULL(pAttribMap);
return hr;
}
HRESULT GetInstalledPrinterDriverInfo(const OSVERSIONINFO* pOsVersionInfo, DRIVER_INFO_6** ppaDriverInfo6, DWORD* pdwDriverInfoCount)
{
LOG_Block("GetInstalledPrinterDriverInfo");
HRESULT hr = S_OK;
DWORD dwBytesNeeded;
if (NULL == pOsVersionInfo || NULL == ppaDriverInfo6 || NULL == pdwDriverInfoCount)
{
LOG_ErrorMsg(E_INVALIDARG);
return E_INVALIDARG;
}
*pdwDriverInfoCount = 0;
*ppaDriverInfo6 = NULL;
LPTSTR pszEnvironment;
if (VER_PLATFORM_WIN32_WINDOWS == pOsVersionInfo->dwPlatformId)
{
//
// Don't pass an environment string for Win9x
//
pszEnvironment = NULL;
}
else if (5 <= pOsVersionInfo->dwMajorVersion && 1 <= pOsVersionInfo->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, pdwDriverInfoCount))
{
if (ERROR_INSUFFICIENT_BUFFER != GetLastError() || (0 == dwBytesNeeded))
{
LOG_Driver(_T("No printer drivers enumerated"));
}
else
{
//
// Allocate the requested buffer
//
CleanUpFailedAllocSetHrMsg(*ppaDriverInfo6 = (DRIVER_INFO_6*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytesNeeded));
//
// Fill in DRIVER_INFO_6 array
//
if (!EnumPrinterDrivers(NULL, pszEnvironment, 6, (LPBYTE) *ppaDriverInfo6, dwBytesNeeded, &dwBytesNeeded, pdwDriverInfoCount))
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
LOG_Driver(_T("%d printer drivers found"), *pdwDriverInfoCount);
//
// Validate the driver elements for each printer driver.
//
for (DWORD dwCount = 0; dwCount < *pdwDriverInfoCount; dwCount++)
{
if ( NULL == (*ppaDriverInfo6 + dwCount)->pszHardwareID
|| NULL == (*ppaDriverInfo6 + dwCount)->pszProvider
|| NULL == (*ppaDriverInfo6 + dwCount)->pszMfgName
|| NULL == (*ppaDriverInfo6 + dwCount)->pName )
{
LOG_Driver(_T("Skiping driver with incomplete ID info: set pszHardwareID = NULL"));
//
// We use pszHardwareID == NULL to invalidate incomplete entry
//
(*ppaDriverInfo6 + dwCount)->pszHardwareID = NULL;
continue;
}
}
}
}
CleanUp:
if (FAILED(hr))
{
SafeHeapFree(*ppaDriverInfo6);
*ppaDriverInfo6 = NULL;
*pdwDriverInfoCount = 0;
}
return hr;
}
//
// Build and write to disk Printer INFs constructed from printer items available
// on this platform. Also prunes printer drivers that would conflict with installed
// drivers (e.g. Unidriver vs. Monolithic, etc.).
//
HRESULT PruneAndBuildPrinterINFs(BSTR bstrXmlPrinterCatalog, LPTSTR lpDownloadPath, DWORD cchDownloadPath, DRIVER_INFO_6* paDriverInfo6, DWORD dwDriverInfoCount)
{
USES_IU_CONVERSION;
LOG_Block("PruneAndBuildPrinterINFs");
HRESULT hr;
HANDLE hFile = INVALID_HANDLE_VALUE;
const TCHAR SZ_PLISTDIR[] = _T("CDMPlist\\");
DWORD dwWritten;
LONG lLength;
VARIANT vDriverName;
VARIANT vDriverVer;
VARIANT vDriverProvider;
VARIANT vMfgName;
VariantInit(&vDriverName);
VariantInit(&vDriverVer);
VariantInit(&vDriverProvider);
VariantInit(&vMfgName);
BOOL fOfferDriver = FALSE;
BSTR bstrHwidText = NULL;
IXMLDOMNode* pCompHWNode = NULL;
LPTSTR pszInfDirPath = NULL;
LPTSTR pszInfFilePath = NULL;
LPOLESTR pwszDriverProvider = NULL;
LPTSTR pszMfgName = NULL;
LPTSTR pszDriverName = NULL;
LPTSTR pszInstallSection = NULL;
CXmlPrinterCatalogList xmlItemList;
if (NULL == bstrXmlPrinterCatalog || NULL == lpDownloadPath || 0 == cchDownloadPath)
{
LOG_ErrorMsg(E_INVALIDARG);
return E_INVALIDARG;
}
lpDownloadPath[0] = _T('\0');
//
// Dynamically allocate buffers (PreFast warning 831: This function uses 5884 bytes of stack,
// consider moving some data to heap.)
//
pszInfDirPath = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH * sizeof(TCHAR));
pszInfFilePath = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH * sizeof(TCHAR));
pwszDriverProvider = (LPOLESTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_INF_STRING * sizeof(OLECHAR));
pszMfgName = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_INF_STRING * sizeof(TCHAR));
pszDriverName = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_INF_STRING * sizeof(TCHAR));
pszInstallSection = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_INF_STRING * sizeof(TCHAR));
if (NULL == pszInfDirPath ||
NULL == pszInfFilePath ||
NULL == pwszDriverProvider ||
NULL == pszMfgName ||
NULL == pszDriverName ||
NULL == pszInstallSection )
{
SetHrMsgAndGotoCleanUp(E_OUTOFMEMORY);
}
//
// Create the directory for the INFs after deleting any existing directory
//
GetIndustryUpdateDirectory((LPTSTR) pszInfDirPath);
if ((MAX_PATH) < (lstrlen(pszInfDirPath) + ARRAYSIZE(SZ_PLISTDIR) + 1))
{
CleanUpIfFailedAndSetHrMsg(E_OUTOFMEMORY);
}
// pszInfDirPath was alloced to be MAX_PATH above
hr = PathCchAppend(pszInfDirPath, MAX_PATH, SZ_PLISTDIR);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
//
// Delete any existing INFs and recreate the directory - we'll get fresh content
//
LOG_Driver(_T("SafeDeleteFolderAndContents: %s"), pszInfDirPath);
(void) SafeDeleteFolderAndContents(pszInfDirPath, SDF_DELETE_READONLY_FILES | SDF_CONTINUE_IF_ERROR);
hr = CreateDirectoryAndSetACLs(pszInfDirPath, TRUE);
CleanUpIfFailedAndMsg(hr);
//
// Load the XML and get the <compatibleHardware/> list and number of items
//
// NOTE: each <compatibleHardware/> element contains a single unique driver.
// In the event we get duplicates with different driverVer's we really don't care
// as the last one will overright the previous instances and Add Printer Wizard
// doesn't look at driverVer (we prune if it's too old).
//
CleanUpIfFailedAndSetHr(xmlItemList.LoadXMLDocumentAndGetCompHWList(bstrXmlPrinterCatalog));
CleanUpIfFailedAndSetHrMsg(xmlItemList.m_pCompHWNodeList->get_length(&lLength));
for (LONG l = 0; l < lLength; l++)
{
//
// Get the next <item/> node from list
//
CleanUpIfFailedAndSetHrMsg(xmlItemList.m_pCompHWNodeList->nextNode(&pCompHWNode));
if (NULL == pCompHWNode) CleanUpIfFailedAndSetHrMsg(E_FAIL);
//
// Check the driver against installed printer drivers for compatibility and prune if
// incompatible or DriverVer <= installed DriverVer.
//
CleanUpIfFailedAndSetHr(OfferThisPrinterDriver(paDriverInfo6, dwDriverInfoCount, pCompHWNode, &fOfferDriver, \
vDriverName, vDriverVer, vDriverProvider, vMfgName, &bstrHwidText));
SafeReleaseNULL(pCompHWNode);
if (!fOfferDriver)
{
LOG_Driver(_T("Pruning hwid = %s, driverVer = %s, driverName = %s, driverProvider = %s, driverMfgr = %s"),\
OLE2T(bstrHwidText), OLE2T(vDriverVer.bstrVal), \
OLE2T(vDriverName.bstrVal), OLE2T(vDriverProvider.bstrVal), OLE2T(vMfgName.bstrVal) );
VariantClear(&vDriverName);
VariantClear(&vDriverVer);
VariantClear(&vDriverProvider);
VariantClear(&vMfgName);
SafeSysFreeString(bstrHwidText);
continue;
}
LOG_Driver(_T("Adding hwid = %s, driverVer = %s, driverName = %s, driverProvider = %s, driverMfgr = %s to INF"),\
OLE2T(bstrHwidText), OLE2T(vDriverVer.bstrVal), \
OLE2T(vDriverName.bstrVal), OLE2T(vDriverProvider.bstrVal), OLE2T(vMfgName.bstrVal) );
if (0 != lstrcmpiW(pwszDriverProvider, vDriverProvider.bstrVal))
{
// pwszDriverProvider was alloced to be MAX_INF_STRING * sizeof(OLECHAR) above.
hr = StringCchCopyExW(pwszDriverProvider, MAX_INF_STRING, vDriverProvider.bstrVal,
NULL, NULL, MISTSAFE_STRING_FLAGS);
CleanUpIfFailedAndSetHr(hr);
//
// Open pszInfFilePath and initialize with "header" an INF file based on pwszDriverProvider.
// If it already exists, just open it (and return existing pszInfFilePath)
//
// pszInfFilePath is allocated to be MAX_PATH above.
CleanUpIfFailedAndSetHr(OpenUniqueProviderInfName(pszInfDirPath, OLE2T(pwszDriverProvider), pszInfFilePath, MAX_PATH, hFile));
//
// Once the file is initialized, we don't need to keep it open
//
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
//
// Write the mfgName in the [Manufacturer] section, for example
// [Manufacturer]
// "Ricoh"="Ricoh"
//
// ISSUE-2001/02/05-waltw Could optimize by caching last known name like provider above...
// pszMfgName is alloced to be MAX_INF_STRING characters above
hr = StringCchPrintfEx(pszMfgName, MAX_INF_STRING, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("\"%s\""), (LPCTSTR) OLE2T(vMfgName.bstrVal));
CleanUpIfFailedAndSetHr(hr);
if (0 == WritePrivateProfileString(_T("Manufacturer"), pszMfgName, pszMfgName, pszInfFilePath))
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
// pszDriverName is alloced to be MAX_INF_STRING characters above
hr = StringCchPrintfEx(pszDriverName, MAX_INF_STRING, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("\"%s\""), OLE2T(vDriverName.bstrVal));
CleanUpIfFailedAndSetHr(hr);
// pszInstallSection is alloced to be MAX_INF_STRING characters above
hr = StringCchPrintfEx(pszInstallSection, MAX_INF_STRING, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("InstallSection,\"%s\""), OLE2T(bstrHwidText));
CleanUpIfFailedAndSetHr(hr);
//
// Write printer item in [mfgName] section, for example:
// [RICOH]
// "RICOH Aficio 850 PCL 6"=InstallSection,"LPTENUM\RICOHAFICIO_850F1B7"
if (0 == WritePrivateProfileString(OLE2T(vMfgName.bstrVal), pszDriverName, pszInstallSection, pszInfFilePath))
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
VariantClear(&vDriverName);
VariantClear(&vDriverVer);
VariantClear(&vDriverProvider);
VariantClear(&vMfgName);
SafeSysFreeString(bstrHwidText);
}
CleanUp:
if(SUCCEEDED(hr))
{
hr = StringCchCopyEx(lpDownloadPath, cchDownloadPath, pszInfDirPath,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
lpDownloadPath[0] = _T('\0');
LOG_ErrorMsg(hr);
}
}
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
VariantClear(&vDriverName);
VariantClear(&vDriverVer);
VariantClear(&vDriverProvider);
VariantClear(&vMfgName);
SafeHeapFree(pszInfDirPath);
SafeHeapFree(pszInfFilePath);
SafeHeapFree(pwszDriverProvider);
SafeHeapFree(pszMfgName);
SafeHeapFree(pszDriverName);
SafeHeapFree(pszInstallSection);
SafeSysFreeString(bstrHwidText);
SafeReleaseNULL(pCompHWNode);
return hr;
}
BOOL HwidMatchesDeviceInfo(HDEVINFO hDevInfoSet, SP_DEVINFO_DATA deviceInfoData, LPCTSTR pszHardwareID)
{
LOG_Block("HwidMatchesDeviceInfo");
HRESULT hr = S_OK;
LPTSTR pszMultiHwid = NULL;
LPTSTR pszMultiCompid = NULL;
LPTSTR pszTemp;
//
// Get the Hardware and Compatible Multi-SZ strings so we can prune printer devices before commiting to XML.
//
CleanUpIfFailedAndSetHr(GetMultiSzDevRegProp(hDevInfoSet, &deviceInfoData, SPDRP_HARDWAREID, &pszMultiHwid));
CleanUpIfFailedAndSetHr(GetMultiSzDevRegProp(hDevInfoSet, &deviceInfoData, SPDRP_COMPATIBLEIDS, &pszMultiCompid));
//
// Do we have a match with an enumerated device HWID or Compatible ID?
//
if (NULL != pszMultiHwid)
{
for(pszTemp = pszMultiHwid; *pszTemp; pszTemp += (lstrlen(pszTemp) + 1))
{
if (0 == lstrcmpi(pszTemp, pszHardwareID))
{
LOG_Driver(_T("This deviceInfoData matches HWID %s"), pszHardwareID);
goto CleanUp;
}
}
}
if (NULL != pszMultiCompid)
{
for(pszTemp = pszMultiCompid; *pszTemp; pszTemp += (lstrlen(pszTemp) + 1))
{
if (0 == lstrcmpi(pszTemp, pszHardwareID))
{
LOG_Driver(_T("This deviceInfoData matches HWID %s"), pszHardwareID);
goto CleanUp;
}
}
}
//
// We didn't find a match
//
LOG_Driver(_T("Failed to find a matching HWID or Printer ID for %s"), pszHardwareID);
hr = E_FAIL;
CleanUp:
SafeHeapFree(pszMultiHwid);
SafeHeapFree(pszMultiCompid);
return (SUCCEEDED(hr));
}
// This function is called to download the actual package.
//
// If this function is successfull then it returns S_OK. If the case of a
// failure this function returns an error code.
HRESULT GetPackage(
IN ENUM_GETPKG eFunction, // Function to be performed by GetPackage
IN PDOWNLOADINFO pDownloadInfo, // DownloadInformation structure describing package to be read from server
OUT LPTSTR lpDownloadPath, // Pointer to local directory on the client computer system
// where the downloaded files are to be stored. NOTE: OK to pass NULL if
// GET_CATALOG_XML == eFunction.
IN DWORD cchDownloadPath,
OUT BSTR* pbstrXmlCatalog // On SUCCESS, catalog is always allocated - caller must call SysFreeString()
)
{
USES_IU_CONVERSION;
LOG_Block("GetPackage");
HRESULT hr;
BSTR bstrXmlSystemSpec = NULL;
BSTR bstrXmlClientInfo = NULL;
BSTR bstrXmlQuery = NULL;
BSTR bstrXmlDownloadedItems = NULL;
BSTR bstrDownloadStatus = NULL;
BSTR bstrStatusValue = NULL;
BSTR bstrProvider = NULL;
BSTR bstrMfgName = NULL;
BSTR bstrName = NULL;
BSTR bstrHardwareID = NULL;
BSTR bstrDriverVer = NULL;
IU_PLATFORM_INFO iuPlatformInfo;
HDEVINFO hDevInfoSet = INVALID_HANDLE_VALUE;
SP_DEVINFO_DATA devInfoData;
DRIVER_INFO_6* paDriverInfo6 = NULL;
DWORD dwDriverInfoCount = 0;
LPCTSTR pszHardwareID = NULL; // pDownloadInfo LPCWSTR converted to ANSI (automatically freed by IU_CONVERSION)
// OR just points to LPCWSTR pDownloadInfo->lpHardwareIDs or ->lpDeviceInstanceID
DWORD dwDeviceIndex;
BOOL fHwidMatchesInstalledPrinter = FALSE;
BOOL fAPWNewPrinter = FALSE;
HANDLE_NODE hPrinterDevNode = HANDLE_NODE_INVALID;
HANDLE_NODE hDevices = HANDLE_NODE_INVALID;
DWORD dwCount;
CXmlSystemSpec xmlSpec;
CXmlDownloadResult xmlItemStatusList;
IXMLDOMNode* pItemStatus = NULL;
IXMLDOMNode* pStatusNode = NULL;
IXMLDOMNode* pValueNode = NULL;
IXMLDOMNamedNodeMap* pAttribMap = NULL;
VARIANT vStatusValue;
LPTSTR pszMatchingID = NULL;
LPTSTR pszDriverVer= NULL;
//
// Initialize variant before any possible jump to CleanUp (BUG: 467098)
//
VariantInit(&vStatusValue);
if (NULL == pDownloadInfo ||
(NULL == lpDownloadPath && GET_CATALOG_XML != eFunction) ||
NULL == pbstrXmlCatalog ||
NULL == g_pCDMEngUpdate)
{
hr = E_INVALIDARG;
return E_INVALIDARG;
}
if (NULL != lpDownloadPath && cchDownloadPath > 0)
{
lpDownloadPath[0] = _T('\0');
}
*pbstrXmlCatalog = NULL;
//
// Get iuPlatformInfo, but remember to clean up BSTRs on function exit
//
CleanUpIfFailedAndSetHr(DetectClientIUPlatform(&iuPlatformInfo));
//
// Get array of DRIVER_INFO_6 holding info on installed printer drivers. Only allocates and returns
// memory for appropriate platforms that have printer drivers already installed.
//
CleanUpIfFailedAndSetHr(GetInstalledPrinterDriverInfo((OSVERSIONINFO*) &iuPlatformInfo.osVersionInfoEx, &paDriverInfo6, &dwDriverInfoCount));
//
// Build common bstrXmlClientInfo and parts of bstrXmlSystemSpec
//
if (NULL == (bstrXmlClientInfo = SysAllocString((OLECHAR*) &szXmlClientInfo)))
{
CleanUpIfFailedAndSetHrMsg(E_OUTOFMEMORY);
}
//
// Add Computer System
//
CleanUpIfFailedAndSetHr(AddComputerSystemClass(xmlSpec));
//
// Add Platform
//
CleanUpIfFailedAndSetHr(AddPlatformClass(xmlSpec, iuPlatformInfo));
//
// Add OS & USER Locale information
//
CleanUpIfFailedAndSetHr(AddLocaleClass(xmlSpec, FALSE));
CleanUpIfFailedAndSetHr(AddLocaleClass(xmlSpec, TRUE));
//
// If GET_PRINTER_INFS, we are retrieving a list of supported printers (V3 PLIST format) rather than a driver
//
switch (eFunction)
{
case GET_PRINTER_INFS:
{
CleanUpFailedAllocSetHrMsg(bstrXmlQuery = SysAllocString(szXmlPrinterCatalogQuery));
CleanUpIfFailedAndSetHr(xmlSpec.GetSystemSpecBSTR(&bstrXmlSystemSpec));
//
// GetManifest will ResetEvent, so check before calling
//
if (WaitForSingleObject(g_pCDMEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0)
{
CleanUpIfFailedAndSetHrMsg(E_ABORT);
}
CleanUpIfFailedAndSetHrMsg(g_pCDMEngUpdate->GetManifest(bstrXmlClientInfo, bstrXmlSystemSpec, bstrXmlQuery, FLAG_USE_COMPRESSION, pbstrXmlCatalog));
LOG_XmlBSTR(*pbstrXmlCatalog);
//
// Now, convert the returned pbstrXmlCatalog to an inf file per provider and write to a temporary location
//
CleanUpIfFailedAndSetHr(PruneAndBuildPrinterINFs(*pbstrXmlCatalog, lpDownloadPath, cchDownloadPath, paDriverInfo6, dwDriverInfoCount));
break;
}
case DOWNLOAD_DRIVER:
case GET_CATALOG_XML:
{
//
// Put either the Hardware & Compatible ID from the DeviceInstanceID or printer info from DRIVER_INFO_6
// or <hwid> passed by APW into a systemspec to pass to server with driver query.
//
if (NULL != pDownloadInfo->lpDeviceInstanceID)
{
if (INVALID_HANDLE_VALUE == (hDevInfoSet = (HDEVINFO)SetupDiCreateDeviceInfoList(NULL, NULL)))
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
//
// This is the Device Instance ID for an installed hardware device
//
ZeroMemory(&devInfoData, sizeof(SP_DEVINFO_DATA));
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
#if !(defined(_UNICODE) || defined(UNICODE))
// OK to cast away const-ness since OLE2T copies string for ANSI
pszHardwareID = OLE2T(const_cast<LPWSTR>(pDownloadInfo->lpDeviceInstanceID));
CleanUpFailedAllocSetHrMsg(pszHardwareID);
#else
pszHardwareID = pDownloadInfo->lpDeviceInstanceID;
#endif
if (!SetupDiOpenDeviceInfo(hDevInfoSet, pszHardwareID, 0, 0, &devInfoData))
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
}
else if (NULL != pDownloadInfo->lpHardwareIDs)
{
// one hardware id for a package - either printers or w9x if we cannot find device instance ID
// if architecture is not the same as current archtecture we need to prefix it
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
if (pDownloadInfo->dwArchitecture != (DWORD) sysInfo.wProcessorArchitecture)
{
// Supporting PRINT_ENVIRONMENT_INTEL and PRINT_ENVIRONMENT_ALPHA prefixes
// was V3 legacy functionality that was never used (originally intended to
// support installation of non-native architecture drivers on print servers).
// Since this feature isn't required or expected by the print team for
// Windows Update functionality, we simply retain the compare as a sanity
// check in case one of our clients forgets this.
SetHrMsgAndGotoCleanUp(E_NOTIMPL);
}
#if !(defined(_UNICODE) || defined(UNICODE))
// OK to cast away const-ness since OLE2T copies string for ANSI
pszHardwareID = OLE2T(const_cast<LPWSTR>(pDownloadInfo->lpHardwareIDs));
CleanUpFailedAllocSetHrMsg(pszHardwareID);
#else
pszHardwareID = pDownloadInfo->lpHardwareIDs;
#endif
//
// First see if we can match an installed printer driver HWID to the pszHardwareID
//
for (dwCount = 0; dwCount < dwDriverInfoCount; dwCount++)
{
if (NULL == (paDriverInfo6 + dwCount)->pszHardwareID)
{
LOG_Driver(_T("Skipping NULL printer driver index %d"), dwCount);
continue;
}
//
// Use case-insensitive compares (paDriverInfo6 is different case from pszHardwareID)
//
if (0 != lstrcmpi(pszHardwareID, (paDriverInfo6 + dwCount)->pszHardwareID))
{
continue;
}
LOG_Driver(_T("Found match with an installed printer driver dwCount = %d"), dwCount);
fHwidMatchesInstalledPrinter = TRUE;
break;
}
if (!fHwidMatchesInstalledPrinter)
{
LOG_Driver(_T("Didn't find an installed printer driver with a matching HWID, enumerating the PnP IDs..."));
//
// We couldn't find a matching installed printer, so now
// enumerate all the PnP IDs and try to find a matching node to
// add to the system spec
//
if (INVALID_HANDLE_VALUE == (hDevInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES)))
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
ZeroMemory(&devInfoData, sizeof(SP_DEVINFO_DATA));
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
BOOL fRet;
dwDeviceIndex = 0;
while (fRet = SetupDiEnumDeviceInfo(hDevInfoSet, dwDeviceIndex++, &devInfoData))
{
//
// Find a matching ID (could spit out Device Instance ID using SetupDiGetDeviceInstanceId for debug)
//
if (HwidMatchesDeviceInfo(hDevInfoSet, devInfoData, pszHardwareID))
{
break;
}
}
if (!fRet)
{
//
// We hit the end of the list without finding a match
//
if (ERROR_NO_MORE_ITEMS == GetLastError())
{
LOG_Driver(_T("Couldn't find a matching device instance enumerating the PnP devices - must be APW request for new printer"));
fAPWNewPrinter = TRUE;
}
else
{
Win32MsgSetHrGotoCleanup(GetLastError());
}
}
}
}
else
{
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
}
//
// We either found a matching printer driver or PnP device instance - add it to the system spec.
// if DriverVer > installed DriverVer - for printer we have additional requirements
//
//
if (fHwidMatchesInstalledPrinter)
{
//
// Open a <device> element to write the printer info
//
bstrProvider = T2BSTR((paDriverInfo6 + dwCount)->pszProvider);
bstrMfgName = T2BSTR((paDriverInfo6 + dwCount)->pszMfgName);
bstrName = T2BSTR((paDriverInfo6 + dwCount)->pName);
CleanUpIfFailedAndSetHr(xmlSpec.AddDevice(NULL, 1, bstrProvider, \
bstrMfgName, bstrName, &hPrinterDevNode));
SafeSysFreeString(bstrProvider);
SafeSysFreeString(bstrMfgName);
SafeSysFreeString(bstrName);
//
// Convert ftDriverDate to ISO 8601 prefered format (yyyy-mm-dd)
//
SYSTEMTIME systemTime;
if (0 == FileTimeToSystemTime((CONST FILETIME*) &((paDriverInfo6 + dwCount)->ftDriverDate), &systemTime))
{
LOG_Error(_T("FileTimeToSystemTime failed:"));
LOG_ErrorMsg(GetLastError());
SetHrAndGotoCleanUp(HRESULT_FROM_WIN32(GetLastError()));
}
TCHAR szDriverVer[11];
hr = StringCchPrintfEx(szDriverVer, ARRAYSIZE(szDriverVer), NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("%04d-%02d-%02d"), systemTime.wYear, systemTime.wMonth, systemTime.wDay);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
// Always rank 0 and never fIsCompatible
bstrHardwareID = T2BSTR((paDriverInfo6 + dwCount)->pszHardwareID);
bstrDriverVer = T2BSTR(szDriverVer);
CleanUpIfFailedAndSetHr(xmlSpec.AddHWID(hPrinterDevNode, FALSE, 0, \
bstrHardwareID, bstrDriverVer));
SafeSysFreeString(bstrHardwareID);
SafeSysFreeString(bstrDriverVer);
xmlSpec.SafeCloseHandleNode(hPrinterDevNode);
#if defined(DBG)
//
// Need to copy strings to non-const for OLE2T conversion (ANSI)
//
TCHAR szbufHardwareID[MAX_PATH];
TCHAR szbufDriverName[MAX_PATH];
TCHAR szbufDriverProvider[MAX_PATH];
TCHAR szbufMfgName[MAX_PATH];
hr = StringCchCopyEx(szbufHardwareID, ARRAYSIZE(szbufHardwareID),
(paDriverInfo6 + dwCount)->pszHardwareID,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
hr = StringCchCopyEx(szbufDriverName, ARRAYSIZE(szbufDriverName),
(paDriverInfo6 + dwCount)->pName,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
hr = StringCchCopyEx(szbufDriverProvider, ARRAYSIZE(szbufDriverProvider),
(paDriverInfo6 + dwCount)->pszProvider,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
hr = StringCchCopyEx(szbufMfgName, ARRAYSIZE(szbufMfgName),
(paDriverInfo6 + dwCount)->pszMfgName,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
LOG_Driver(_T("Offering Printer hwid = %s, driverVer = %s, driverName = %s, driverProvider = %s, driverMfgr = %s"),\
(LPTSTR) szbufHardwareID, (LPTSTR) szDriverVer, \
(LPTSTR) szbufDriverName, (LPCWSTR) szbufDriverProvider, \
(LPTSTR) szbufMfgName );
#endif
}
else if (fAPWNewPrinter)
{
if (NULL == pDownloadInfo->lpFile)
{
//
// We need to "fake" a <hwid/> element with the ID passed in. User is trying to install
// a printer driver picked in the Add Printer Wizard that doesn't already exist on the
// system.
//
DWORD dwRank;
dwRank = 0;
CleanUpIfFailedAndSetHr(AddIDToXml(pszHardwareID, xmlSpec, SPDRP_HARDWAREID, dwRank, hDevices, NULL, NULL));
if (HANDLE_NODE_INVALID != hDevices)
{
xmlSpec.SafeCloseHandleNode(hDevices);
}
}
else
{
//
// 516376 Changes required in CDM to fix the APW <-> CDM bug found for multi-Provider scenario
//
// APW will pass a MULTI-SZ string containing (in this order) the following strings in the
// previously unused lpFile member of DOWNLOADINFO if it wishes to specify provider.
//
// This string is not required (for convenience and backwards compatibility) but
// if the lpFile member passed to CDM is non-NULL, it must contain all three strings
// as follows:
// String 1: driver provider
// String 2: manufacturer name
// String 3: driver name
#if !(defined(_UNICODE) || defined(UNICODE))
//
// We will never support this functionality on Win9x
//
CleanUpIfFailedAndSetHr(E_NOTIMPL);
#else
LPCWSTR pszProvider = pDownloadInfo->lpFile;
int nLenProvider = lstrlen(pszProvider);
if (NULL == pszProvider + nLenProvider + 1)
{
CleanUpIfFailedAndSetHr(E_INVALIDARG);
}
LPCWSTR pszMfgName = pszProvider + nLenProvider + 1;
int nLenMfgName = lstrlen(pszMfgName);
if (NULL == pszMfgName + nLenMfgName + 1)
{
CleanUpIfFailedAndSetHr(E_INVALIDARG);
}
LPCWSTR pszDriverName = pszMfgName + nLenMfgName + 1;
//
// Open a <device> element to write the printer info
//
bstrProvider = SysAllocString(pszProvider);
bstrMfgName = SysAllocString(pszMfgName);
bstrName = SysAllocString(pszDriverName);
if (NULL == bstrProvider || NULL == bstrMfgName || NULL == bstrName)
{
CleanUpIfFailedAndSetHr(E_OUTOFMEMORY);
}
CleanUpIfFailedAndSetHr(xmlSpec.AddDevice(NULL, 1, bstrProvider, \
bstrMfgName, bstrName, &hPrinterDevNode));
SafeSysFreeString(bstrProvider);
SafeSysFreeString(bstrMfgName);
SafeSysFreeString(bstrName);
// Always rank 0 and never fIsCompatible and no driverVer
CleanUpFailedAllocSetHrMsg(bstrHardwareID = SysAllocString(pszHardwareID));
CleanUpIfFailedAndSetHr(xmlSpec.AddHWID(hPrinterDevNode, FALSE, 0, \
bstrHardwareID));
SafeSysFreeString(bstrHardwareID);
xmlSpec.SafeCloseHandleNode(hPrinterDevNode);
#endif
}
}
else
{
//
// Get DriverVer for the PnP ID and check that offered driver is greater using the
// hDevInfoSet and devInfoData matched above while enumerating
//
CleanUpIfFailedAndSetHr(GetMatchingDeviceID(hDevInfoSet, &devInfoData, &pszMatchingID, &pszDriverVer));
//
// Add <device/> we matched and want to download to XML
//
CleanUpIfFailedAndSetHr(AddPrunedDevRegProps(hDevInfoSet, &devInfoData, xmlSpec, \
pszMatchingID, pszDriverVer, paDriverInfo6, dwDriverInfoCount, FALSE));
}
//
// Get the query string
//
CleanUpFailedAllocSetHrMsg(bstrXmlQuery = SysAllocString(szXmlDriverDownloadQuery));
//
// Get the bstrXmlSystemSpec
//
CleanUpIfFailedAndSetHr(xmlSpec.GetSystemSpecBSTR(&bstrXmlSystemSpec));
LOG_XmlBSTR(bstrXmlSystemSpec);
//
// GetManifest will ResetEvent, so check before calling
//
if (WaitForSingleObject(g_pCDMEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0)
{
CleanUpIfFailedAndSetHrMsg(E_ABORT);
}
//
// Call GetManifest to see if the server has anything that matches our system spec
//
CleanUpIfFailedAndSetHrMsg(g_pCDMEngUpdate->GetManifest(bstrXmlClientInfo, bstrXmlSystemSpec, bstrXmlQuery, \
FLAG_USE_COMPRESSION, pbstrXmlCatalog));
LOG_XmlBSTR( *pbstrXmlCatalog);
//
// If we are just getting the catalog we're done
//
if (GET_CATALOG_XML == eFunction)
{
break;
}
//
// Download will ResetEvent, so check before calling
//
if (WaitForSingleObject(g_pCDMEngUpdate->m_evtNeedToQuit, 0) == WAIT_OBJECT_0)
{
CleanUpIfFailedAndSetHrMsg(E_ABORT);
}
//
// Call Download passing it the catalog we got from GetManifest
//
CleanUpIfFailedAndSetHrMsg(g_pCDMEngUpdate->Download(bstrXmlClientInfo, *pbstrXmlCatalog, NULL, 0, NULL, NULL, &bstrXmlDownloadedItems));
LOG_XmlBSTR(bstrXmlDownloadedItems);
//
// Verify that the package was downloaded
//
CleanUpIfFailedAndSetHr(xmlItemStatusList.LoadXMLDocumentItemStatusList(bstrXmlDownloadedItems));
//
// Get the first [only] item in the list
//
CleanUpIfFailedAndSetHr(xmlItemStatusList.m_pItemStatusNodeList->nextNode(&pItemStatus));
if (NULL == pItemStatus) CleanUpIfFailedAndSetHrMsg(E_FAIL);
//
// Get the first <downloadStatus/> node of the statusItem (we expect at least one else fail)
//
if (NULL == (bstrDownloadStatus = SysAllocString(L"downloadStatus")))
{
CleanUpIfFailedAndSetHrMsg(E_OUTOFMEMORY);
}
CleanUpIfFailedAndSetHrMsg(pItemStatus->selectSingleNode(bstrDownloadStatus, &pStatusNode));
if (NULL == pStatusNode) CleanUpIfFailedAndSetHrMsg(E_FAIL);
CleanUpIfFailedAndSetHr(pStatusNode->get_attributes(&pAttribMap));
if (NULL == pAttribMap) CleanUpIfFailedAndSetHrMsg(E_FAIL);
//
// suck out the <downloadStatus/> value attributes
//
if (NULL == (bstrStatusValue = SysAllocString((OLECHAR*) L"value")))
{
CleanUpIfFailedAndSetHrMsg(E_OUTOFMEMORY);
}
CleanUpIfFailedAndSetHrMsg(pAttribMap->getNamedItem(bstrStatusValue, &pValueNode));
if (NULL == pValueNode) CleanUpIfFailedAndSetHrMsg(E_FAIL);
CleanUpIfFailedAndSetHrMsg(pValueNode->get_nodeValue(&vStatusValue));
if (VT_BSTR != vStatusValue.vt)
{
CleanUpIfFailedAndSetHrMsg(E_FAIL);
}
if (0 != lstrcmpW((LPWSTR) vStatusValue.bstrVal, L"COMPLETE"))
{
CleanUpIfFailedAndSetHrMsg(E_FAIL);
}
//
// Now copy the path to the buffer we were passed
//
CleanUpIfFailedAndSetHr(GetDownloadPath(*pbstrXmlCatalog, bstrXmlDownloadedItems, lpDownloadPath, cchDownloadPath));
//
// DecompressFolderCabs may return S_FALSE if it didn't find a cab to decompress...
//
hr = DecompressFolderCabs(lpDownloadPath);
if (S_OK != hr)
{
CleanUpIfFailedAndSetHr(E_FAIL);
}
break;
}
default:
{
CleanUpIfFailedAndSetHr(E_INVALIDARG);
break;
}
} // switch (eFunction)
CleanUp:
if (INVALID_HANDLE_VALUE != hDevInfoSet)
{
SetupDiDestroyDeviceInfoList(hDevInfoSet);
}
if (HANDLE_NODE_INVALID != hPrinterDevNode)
{
xmlSpec.SafeCloseHandleNode(hPrinterDevNode);
}
if (HANDLE_NODE_INVALID != hDevices)
{
xmlSpec.SafeCloseHandleNode(hDevices);
}
SafeReleaseNULL(pItemStatus);
SafeReleaseNULL(pStatusNode);
SafeReleaseNULL(pValueNode);
SafeReleaseNULL(pAttribMap);
SafeHeapFree(paDriverInfo6);
SafeHeapFree(pszMatchingID);
SafeHeapFree(pszDriverVer);
VariantClear(&vStatusValue);
SysFreeString(bstrXmlSystemSpec);
SysFreeString(bstrXmlClientInfo);
SysFreeString(bstrXmlQuery);
SysFreeString(bstrXmlDownloadedItems);
SysFreeString(bstrDownloadStatus);
SysFreeString(bstrStatusValue);
SysFreeString(bstrProvider);
SysFreeString(bstrMfgName);
SysFreeString(bstrName);
SysFreeString(bstrHardwareID);
SysFreeString(bstrDriverVer);
SysFreeString(iuPlatformInfo.bstrOEMManufacturer);
SysFreeString(iuPlatformInfo.bstrOEMModel);
SysFreeString(iuPlatformInfo.bstrOEMSupportURL);
if (FAILED(hr))
{
SafeSysFreeString(*pbstrXmlCatalog);
}
return hr;
}