//======================================================================= // // 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 #include #include #include #include #include #include #include #include "iuxml.h" #include #include #include #include "cdmp.h" #include "schemamisc.h" #include const DWORD MAX_INF_STRING = 512; // From DDK docs "General Syntax Rules for INF Files" section const OLECHAR szXmlClientInfo[] = L""; const OLECHAR szXmlPrinterCatalogQuery[] = L""; const OLECHAR szXmlDriverDownloadQuery[] = L""; ///////////////////////////////////////////////////////////////////////////// // 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: // // // // // nvidia.569 // nvidia // // // // // // We expose m_pItemNodeList so it can be used directly to retrieve the value // attribute of the item. // // NOTE: for CDM there will only be one item downloaded at a time, so the list // will only contain a single 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 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 // 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: // // * // * Only a single with printerCatalog // * returned is not used by CDM // * identity and 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 elements can contain more than one element, // but the complete list of elements provides all printers in // the given catalog // // Sample start of a "printerCatalog" catalog: // ------------------------------------------ // // - // - // printerCatalog // ver_platform_win32_nt.5.0.x86.en // + // hp.3 // - // - // - // // DOT4PRT\HEWLETT-PACKARDPSC_59784 // // // - // ... etc. // // // + // ... 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 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 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, // 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 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 // 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 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 text // // NOTE: Each item is restricted to a single element due to INF syntax, // however our catalog schema doesn't make similar restrictions and currently our // backend doesn't distinguish between and values, so it is // possible we could get more than one returned. For the purpose of // generating INFs for Add Printer Wizard, any 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 list and number of items // // NOTE: each 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 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 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(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(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 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 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 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 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 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 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; }