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.
2111 lines
66 KiB
2111 lines
66 KiB
//=======================================================================
|
|
//
|
|
// 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,
|
|
IN LPTSTR** ppszUniqueProviderNameArray,
|
|
IN OUT PDWORD pdwProviderArrayLength,
|
|
OUT HANDLE& hFile
|
|
)
|
|
{
|
|
LOG_Block("OpenUniqueProviderInfName");
|
|
|
|
const TCHAR FILEROOT[] = _T("PList_");
|
|
const TCHAR FILEEXT[] = _T("inf");
|
|
DWORD dwErr;
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
if (NULL == pszFilePath || NULL == pszProvider || NULL == szDirPath || 0 == cchFilePath ||
|
|
NULL == ppszUniqueProviderNameArray || NULL == *ppszUniqueProviderNameArray || NULL == pdwProviderArrayLength)
|
|
{
|
|
LOG_ErrorMsg(E_INVALIDARG);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
pszFilePath[0] = _T('\0');
|
|
|
|
// The Unique Provider Inf Names are based on the Index of the ProviderMapArray that the Provider name matches
|
|
// The Array is Pre-Allocated to 20 Providers (more than we should need)
|
|
BOOL fIndexFound = FALSE;
|
|
DWORD dwIndex;
|
|
DWORD dwLength;
|
|
for (dwIndex = 0; dwIndex < *pdwProviderArrayLength; dwIndex++)
|
|
{
|
|
// First check if we've reached a NULL entry in the Array, this indicates we haven't found a match yet
|
|
// and we need to add our Provider to this location
|
|
if ((*ppszUniqueProviderNameArray)[dwIndex] == NULL)
|
|
{
|
|
dwLength = lstrlen(pszProvider) + 1;
|
|
(*ppszUniqueProviderNameArray)[dwIndex] = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
|
|
if (NULL == (*ppszUniqueProviderNameArray)[dwIndex])
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
hr = StringCchCopyEx((*ppszUniqueProviderNameArray)[dwIndex], dwLength, pszProvider, NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
SafeHeapFree((*ppszUniqueProviderNameArray)[dwIndex]);
|
|
return hr;
|
|
}
|
|
fIndexFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
// Now Compare the Current Provider Entry to see if they Match (case-sensitive compare)
|
|
if (CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0,
|
|
(*ppszUniqueProviderNameArray)[dwIndex], -1, pszProvider, -1))
|
|
{
|
|
fIndexFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!fIndexFound)
|
|
{
|
|
// This indicates we finished looping the array and didn't not find a match AND did not have a empty Entry to
|
|
// place this provider in. So we now need to ReAlloc the Array. Our goal is of course to never have to do this,
|
|
// but we support it anyway.
|
|
DWORD dwNewProviderArrayLength = *pdwProviderArrayLength * 2; // double
|
|
LPTSTR* ppszTemp = (LPTSTR *) HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *ppszUniqueProviderNameArray, dwNewProviderArrayLength * sizeof(LPTSTR));
|
|
if (NULL == ppszTemp)
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
*pdwProviderArrayLength = dwNewProviderArrayLength;
|
|
*ppszUniqueProviderNameArray = ppszTemp; // Since this was 'realloced', the previous memory block should be cleared up.
|
|
|
|
// Now increment the current Index counter and place the new Provider in that location.
|
|
dwIndex++;
|
|
dwLength = lstrlen(pszProvider) + 1;
|
|
(*ppszUniqueProviderNameArray)[dwIndex] = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR));
|
|
if (NULL == (*ppszUniqueProviderNameArray)[dwIndex])
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
hr = StringCchCopyEx((*ppszUniqueProviderNameArray)[dwIndex], dwLength, pszProvider, NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
SafeHeapFree((*ppszUniqueProviderNameArray)[dwIndex]);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// We now have a Provider Index Value (dwIndex) that matches our Provider
|
|
|
|
hr = StringCchPrintfEx(pszFilePath, cchFilePath, NULL, NULL, MISTSAFE_STRING_FLAGS,
|
|
_T("%s%s%d.%s"), szDirPath, FILEROOT, dwIndex, 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;
|
|
LPTSTR* ppszUniqueProviderNameArray = NULL;
|
|
|
|
CXmlPrinterCatalogList xmlItemList;
|
|
|
|
if (NULL == bstrXmlPrinterCatalog || NULL == lpDownloadPath || 0 == cchDownloadPath)
|
|
{
|
|
LOG_ErrorMsg(E_INVALIDARG);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Allocate the ppszUniqueProviderNameArray initially to a value of 60 pointers
|
|
DWORD dwProviderArrayLength = 60;
|
|
ppszUniqueProviderNameArray = (LPTSTR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwProviderArrayLength * sizeof(LPTSTR));
|
|
if (NULL == ppszUniqueProviderNameArray)
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_OUTOFMEMORY);
|
|
}
|
|
|
|
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, &ppszUniqueProviderNameArray, &dwProviderArrayLength, 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;
|
|
}
|
|
|
|
// Clear up the ppszUniqueProviderNameArray
|
|
if (NULL != ppszUniqueProviderNameArray)
|
|
{
|
|
for (DWORD dwIndex = 0; dwIndex < dwProviderArrayLength; dwIndex++)
|
|
{
|
|
if (NULL == ppszUniqueProviderNameArray[dwIndex])
|
|
{
|
|
break; // done
|
|
}
|
|
SafeHeapFree(ppszUniqueProviderNameArray[dwIndex]);
|
|
}
|
|
SafeHeapFree(ppszUniqueProviderNameArray);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|