711 lines
22 KiB
C++
711 lines
22 KiB
C++
//=======================================================================
|
|
//
|
|
// Copyright (c) 1998-1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// File: ccdm.cpp
|
|
//
|
|
// Owner: YanL
|
|
//
|
|
// Description:
|
|
//
|
|
// CDM support from the site
|
|
//
|
|
//=======================================================================
|
|
|
|
#include "stdafx.h"
|
|
#include <winspool.h>
|
|
#include <setupapi.h>
|
|
#include <wustl.h>
|
|
#define LOGGING_LEVEL 1
|
|
#include <log.h>
|
|
#include <wuv3sys.h>
|
|
#include <drvinfo.h>
|
|
#include <ccdm.h>
|
|
#include <findoem.h>
|
|
#include <shlwapi.h>
|
|
#include "printers.h"
|
|
|
|
static DWORD OpenReinstallKey(HKEY* phkeyReinstall);
|
|
static DWORD GetReinstallString(LPCTSTR szHwID, tchar_buffer& bchReinstallString);
|
|
static DWORD DeleteReinstallKey(LPCTSTR szHwID);
|
|
|
|
|
|
bool DownloadToBuffer(
|
|
IN LPCTSTR szPath,
|
|
IN CWUDownload *pDownload, //pointer to internet server download class.
|
|
IN CDiamond *pDiamond, //pointer to diamond de-compression class.
|
|
OUT byte_buffer& bufOut
|
|
) {
|
|
byte_buffer bufTmp;
|
|
if (!pDownload->MemCopy(szPath, bufTmp))
|
|
return false;
|
|
if (pDiamond->IsValidCAB(bufTmp))
|
|
{
|
|
if (!pDiamond->Decompress(bufTmp, bufOut))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//else the oem table is in uncompressed format.
|
|
bufOut << bufTmp;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//Note: This method works specifically with a CCdm class. The CCdm class contains
|
|
//the returned inventory item array. So if multiple threads are used then
|
|
//multiple instances of the CCdm class will be required. This should not ever be a
|
|
//problem since we do not ever plan or have any reason to do single instance groveling
|
|
//for device drivers.
|
|
|
|
//This method adds CDM records to an internal CDM inventory list. The CCatalog
|
|
//prune method performs the final copy and insertion of these records into the
|
|
//final inventory catalog.
|
|
|
|
void CCdm::CreateInventoryList(
|
|
IN CBitmask *pBm, //bitmask to be used to prune the inventory list.
|
|
IN CWUDownload *pDownload, //pointer to internet server download class.
|
|
IN CDiamond *pDiamond, //pointer to diamond de-compression class.
|
|
IN PUID puidCatalog, //puid identifier of catalog where device drivers are stored.
|
|
IN PBYTE pOemInfoTable //Pointer OEM info table that OEM detection needs.
|
|
) {
|
|
|
|
LOG_block("CCdm::CreateInventoryList");
|
|
|
|
USES_CONVERSION;
|
|
// Start
|
|
m_iCDMTotalItems = 0;
|
|
|
|
//check input arguments
|
|
if (NULL == pBm || NULL == pDownload || NULL == pDiamond)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//First prune list based on bitmask
|
|
//This is accomplished by anding the appropriate bitmasks to form a global bitmask.
|
|
|
|
|
|
DWORD langid = GetMachineLangDW();
|
|
PBYTE pBitmaskBits = pBm->GetClientBits(GetMachinePnPID(pOemInfoTable), langid);
|
|
if (NULL == pBitmaskBits)
|
|
{
|
|
return;
|
|
}
|
|
{
|
|
//Note: a cdm catalog cannot have a 0 puid since that the inventory.plt will
|
|
//never have a device driver insertion record. So this function will not be
|
|
//called for the inventory.plt catalog.
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
wsprintf(szPath, _T("%d/inventory.cdm"), puidCatalog);
|
|
|
|
if (!DownloadToBuffer(szPath, pDownload, pDiamond, m_bufInventory))
|
|
throw HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
vector<SCdmItem> aitemUpdates;
|
|
/* Regular drivers */ {
|
|
CDrvInfoEnum DrvInfoEnum;
|
|
auto_pointer<IDrvInfo> pDrvInfo;
|
|
while (DrvInfoEnum.GetNextDrvInfo(&pDrvInfo))
|
|
{
|
|
tchar_buffer bufDeviceInstanceID;
|
|
if (!pDrvInfo->GetDeviceInstanceID(bufDeviceInstanceID))
|
|
{
|
|
LOG_error("!pDrvInfo->GetDeviceInstanceID()");
|
|
continue;
|
|
}
|
|
LOG_block(T2A(bufDeviceInstanceID));
|
|
if (pDrvInfo->IsPrinter())
|
|
{
|
|
LOG_error("!pDrvInfo->IsPrinter()");
|
|
continue;
|
|
}
|
|
tchar_buffer bufHardwareIDs;
|
|
if (!pDrvInfo->GetAllHardwareIDs(bufHardwareIDs))
|
|
{
|
|
LOG_error("!pDrvInfo->GetAllHardwareIDs()");
|
|
continue;
|
|
}
|
|
tchar_buffer bufMatchingDeviceId;
|
|
pDrvInfo->GetMatchingDeviceId(bufMatchingDeviceId); // It's OK not to get it
|
|
// #ifdef _WUV3TEST
|
|
if (pDrvInfo.valid() && pDrvInfo->HasDriver())
|
|
{
|
|
if (bufMatchingDeviceId.valid())
|
|
LOG_out("driver installed on MatchingDeviceId %s", (LPCTSTR)bufMatchingDeviceId);
|
|
else
|
|
LOG_error("driver installed, but HWID is not available");
|
|
}
|
|
else
|
|
{
|
|
if (bufMatchingDeviceId.valid())
|
|
LOG_error("driver is not installed, but MatchingDeviceId is %s", (LPCTSTR)bufMatchingDeviceId);
|
|
else
|
|
LOG_out("no driver installed");
|
|
}
|
|
// #endif
|
|
|
|
|
|
// Updates
|
|
bool fMoreSpecific = true;
|
|
for (LPCTSTR szHardwareId = bufHardwareIDs; fMoreSpecific && *szHardwareId; szHardwareId += lstrlen(szHardwareId) + 1)
|
|
{
|
|
// MatchingDeviceID is the last one to pay attention to
|
|
fMoreSpecific = !bufMatchingDeviceId.valid() || 0 != lstrcmpi(szHardwareId, bufMatchingDeviceId);
|
|
|
|
ULONG ulHashIndex = IsInMap(szHardwareId);
|
|
if (-1 == ulHashIndex)
|
|
continue;
|
|
|
|
//else download the server bucket file
|
|
byte_buffer& bufBucket = ReadBucketFile(pDownload, pDiamond, puidCatalog, ulHashIndex);
|
|
|
|
FILETIME ftDriverInstalled = {0,0};
|
|
if (!fMoreSpecific)
|
|
{
|
|
// Then it has to have a driver - Matching device ID is set
|
|
if (!pDrvInfo->GetDriverDate(ftDriverInstalled))
|
|
{
|
|
LOG_error("!pDrvInfo->GetDriverDate(ftDriverInstalled)");
|
|
break;
|
|
}
|
|
}
|
|
DRIVER_MATCH_INFO DriverMatchInfo;
|
|
PUID puid = CDM_FindUpdateInBucket(szHardwareId, fMoreSpecific ? NULL : &ftDriverInstalled, bufBucket, bufBucket.size(), pBitmaskBits, &DriverMatchInfo);
|
|
// See if it's being added already
|
|
if (0 == puid)
|
|
{
|
|
LOG_out("CDM_FindUpdateInBucket returns 0");
|
|
continue;
|
|
}
|
|
|
|
// something has been found
|
|
AddItem(aitemUpdates, puid, bufMatchingDeviceId.valid() ? CDM_UPDATED_DRIVER : CDM_NEW_DRIVER, A2T(DriverMatchInfo.pszHardwareID), A2T(DriverMatchInfo.pszDriverVer));
|
|
break;
|
|
}
|
|
|
|
// Installed
|
|
if (bufMatchingDeviceId.valid())
|
|
{
|
|
LPCTSTR szHardwareId = bufMatchingDeviceId;
|
|
tchar_buffer bchReinstallString;
|
|
if (NO_ERROR != GetReinstallString(szHardwareId, bchReinstallString))
|
|
{
|
|
LOG_out("No reinstall string for %s", szHardwareId);
|
|
continue;
|
|
}
|
|
|
|
ULONG ulHashIndex = IsInMap(szHardwareId);
|
|
if (-1 == ulHashIndex)
|
|
continue;
|
|
|
|
//else download the server bucket file
|
|
byte_buffer& bufBucket = ReadBucketFile(pDownload, pDiamond, puidCatalog, ulHashIndex);
|
|
DRIVER_MATCH_INFO DriverMatchInfo;
|
|
PUID puid = CDM_FindInstalledInBucket(pDrvInfo, szHardwareId, bufBucket,
|
|
bufBucket.size(), pBitmaskBits, &DriverMatchInfo);
|
|
// See if it's being added already
|
|
if (0 == puid)
|
|
{
|
|
LOG_out("CDM_FindInstalledInBucket returns 0");
|
|
continue;
|
|
}
|
|
// something has been found
|
|
AddItem(aitemUpdates, puid, CDM_CURRENT_DRIVER, A2T(DriverMatchInfo.pszHardwareID), A2T(DriverMatchInfo.pszDriverVer));
|
|
}
|
|
} // while (DrvInfoEnum.GetNextDrvInfo(&pDrvInfo))
|
|
}
|
|
/* Printer drivers */ {
|
|
CPrinterDriverInfoArray ainfo;
|
|
for(DWORD dwDriverIdx = 0; dwDriverIdx < ainfo.GetNumDrivers(); dwDriverIdx ++)
|
|
{
|
|
LPDRIVER_INFO_6 pinfo = ainfo.GetDriverInfo(dwDriverIdx);
|
|
if (NULL == pinfo)
|
|
continue;
|
|
|
|
tchar_buffer bufHardwareID;
|
|
if (!ainfo.GetHardwareID(pinfo, bufHardwareID))
|
|
continue;
|
|
|
|
ULONG ulHashIndex = IsInMap(bufHardwareID);
|
|
if (-1 == ulHashIndex)
|
|
continue;
|
|
|
|
//else download the server bucket file
|
|
byte_buffer& bufBucket = ReadBucketFile(pDownload, pDiamond, puidCatalog, ulHashIndex);
|
|
DRIVER_MATCH_INFO DriverMatchInfo;
|
|
PUID puid = CDM_FindUpdateInBucket(bufHardwareID, &(pinfo->ftDriverDate), bufBucket,
|
|
bufBucket.size(), pBitmaskBits, &DriverMatchInfo);
|
|
if (0 == puid)
|
|
{
|
|
LOG_out("CDM_FindInstalledInBucket returns 0");
|
|
continue;
|
|
}
|
|
AddItem(aitemUpdates, puid, CDM_UPDATED_DRIVER, A2T(DriverMatchInfo.pszHardwareID), A2T(DriverMatchInfo.pszDriverVer), pinfo->pName, ainfo.GetArchitecture(pinfo));
|
|
}
|
|
}
|
|
//Do converson
|
|
AddInventoryRecords(aitemUpdates);
|
|
}
|
|
|
|
|
|
// Check if given PNPID is current hash table mapping.
|
|
//if PnpID is in hash table then return index
|
|
|
|
ULONG CCdm::IsInMap(
|
|
IN LPCTSTR pHwID //hardware id to be retrieved
|
|
) {
|
|
LOG_block("CCdm::IsInMap");
|
|
|
|
// check if the member variable m_bufInventory is not NULL and the input argument is not NULL
|
|
if (NULL == (PCDM_HASHTABLE)(LPBYTE)m_bufInventory || NULL == pHwID)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
PCDM_HASHTABLE pHashTable = (PCDM_HASHTABLE)(LPBYTE)m_bufInventory;
|
|
ULONG ulTableEntry = CDM_HwID2Hash(pHwID, pHashTable->hdr.iTableSize);
|
|
|
|
if(GETBIT(pHashTable->pData, ulTableEntry))
|
|
{
|
|
LOG_out("%s (hash %d) is found", pHwID, ulTableEntry);
|
|
return ulTableEntry;
|
|
}
|
|
|
|
LOG_out("%s (hash %d) is not found", pHwID, ulTableEntry);
|
|
return -1;
|
|
}
|
|
|
|
//Reads and initializes a compressed CDM bucket file from an internet server.
|
|
//Returnes the array index where the bucket file is stored.
|
|
|
|
byte_buffer& CCdm::ReadBucketFile(
|
|
IN CWUDownload *pDownload, //pointer to internet server download class.
|
|
IN CDiamond *pDiamond, //pointer to diamond de-compression class.
|
|
IN PUID puidCatalog, //PUID id of catalog for which cdm hash table is to be retrieved.
|
|
IN ULONG ulHashIndex //Hash table index of bucket file to be retrieved
|
|
) {
|
|
LOG_block("CCdm::ReadBucketFile");
|
|
// If it there return it
|
|
for(int i = 0; i < m_aBuckets.size(); i ++)
|
|
{
|
|
if (ulHashIndex == m_aBuckets[i].first)
|
|
return m_aBuckets[i].second;
|
|
}
|
|
|
|
// download it
|
|
byte_buffer bufBucket;
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
wsprintf(szPath, _T("%d/%d.bkf"), puidCatalog, ulHashIndex);
|
|
if (!DownloadToBuffer(szPath, pDownload, pDiamond, bufBucket))
|
|
throw HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
m_aBuckets.push_back(my_pair(ulHashIndex, bufBucket));
|
|
return m_aBuckets[m_aBuckets.size() - 1].second;
|
|
}
|
|
|
|
PINVENTORY_ITEM CCdm::ConvertCDMItem(
|
|
int index //Index of cdm record to be converted
|
|
) {
|
|
if ( index < 0 || index >= m_iCDMTotalItems )
|
|
return NULL;
|
|
|
|
// this is a no-op, we just hand off the existing pointer
|
|
return m_items[index];
|
|
}
|
|
|
|
// add detected item to a list
|
|
void CCdm::AddItem(
|
|
vector<SCdmItem>& acdmItem,
|
|
PUID puid,
|
|
enumDriverDisposition fDisposition,
|
|
LPCTSTR szHardwareId,
|
|
LPCTSTR szDriverVer,
|
|
LPCTSTR szPrinterDriverName /*= NULL*/,
|
|
LPCTSTR szArchitecture /*= NULL*/
|
|
) {
|
|
LOG_block("CCdm::AddItem");
|
|
LOG_out("%s PUID = %d, HardwareId = %s", szPrinterDriverName ? _T("Prt") : _T("Dev"), puid, szHardwareId);
|
|
|
|
// First check if this puid is included
|
|
bool fNew = true;
|
|
for (int nItem = 0; nItem < acdmItem.size(); nItem ++)
|
|
{
|
|
if ((acdmItem[nItem].puid == puid) && (acdmItem[nItem].fInstallState == fDisposition))
|
|
{
|
|
fNew = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fNew)
|
|
{ // new item;
|
|
LOG_out("Adding as a new item");
|
|
acdmItem.push_back(SCdmItem());
|
|
SCdmItem& item = acdmItem.back();
|
|
item.puid = puid;
|
|
item.fInstallState = fDisposition;
|
|
|
|
item.sDriverVer = szDriverVer;
|
|
if (szArchitecture)
|
|
item.sArchitecture = szArchitecture;
|
|
if (szPrinterDriverName)
|
|
item.sPrinterDriverName = szPrinterDriverName;
|
|
item.bufHardwareIDs.resize(lstrlen(szHardwareId) + 2);
|
|
if(!item.bufHardwareIDs.valid())
|
|
return; //out of memory
|
|
lstrcpy(item.bufHardwareIDs, szHardwareId);
|
|
((LPTSTR)item.bufHardwareIDs)[item.bufHardwareIDs.size() - 2] = 0;
|
|
((LPTSTR)item.bufHardwareIDs)[item.bufHardwareIDs.size() - 1] = 0;
|
|
}
|
|
else
|
|
{
|
|
SCdmItem& item = acdmItem[nItem];
|
|
// check if Hardware ID is already included
|
|
for (LPCTSTR szCurHardwareId = acdmItem[nItem].bufHardwareIDs; *szCurHardwareId; szCurHardwareId += lstrlen(szCurHardwareId) + 1)
|
|
{
|
|
if (0 == lstrcmpi(szCurHardwareId, szHardwareId))
|
|
{
|
|
// Got It
|
|
LOG_out("Already present");
|
|
return;
|
|
}
|
|
}
|
|
LOG_out("Appending hardware ID");
|
|
int cnOldSize = item.bufHardwareIDs.size();
|
|
item.bufHardwareIDs.resize(cnOldSize + lstrlen(szHardwareId) + 1);
|
|
if(!item.bufHardwareIDs.valid())
|
|
return; //out of memory
|
|
lstrcpy((LPTSTR)item.bufHardwareIDs + cnOldSize - 1, szHardwareId);
|
|
((LPTSTR)item.bufHardwareIDs)[item.bufHardwareIDs.size() - 2] = 0;
|
|
((LPTSTR)item.bufHardwareIDs)[item.bufHardwareIDs.size() - 1] = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//This function is used to create a CDM inventory record. This record is initialized with
|
|
//the CDM bucket information.
|
|
|
|
void CCdm::AddInventoryRecords(
|
|
vector<SCdmItem>& acdmItem
|
|
) {
|
|
LOG_block("CCdm::AddInventoryRecords");
|
|
for (int nItem = 0; nItem < acdmItem.size(); nItem ++)
|
|
{
|
|
PINVENTORY_ITEM pItem = 0;
|
|
try
|
|
{
|
|
//allocate and initialize a new catalog item
|
|
//note that this memory is never freed by this class
|
|
//We rely on the fact that this pointer is going to be
|
|
//handed off (in ConvertCDMItem()) and someone else will free it.
|
|
|
|
pItem = (PINVENTORY_ITEM)V3_malloc(sizeof(INVENTORY_ITEM) + sizeof(WU_INV_FIXED) + sizeof(WU_VARIABLE_FIELD));
|
|
|
|
//we need to set these up by the inventory catalog copy and insert routine.
|
|
pItem->pd = NULL;
|
|
|
|
//first setup the fixed part of the inventory item
|
|
pItem->pf = (PWU_INV_FIXED)(((PBYTE)pItem) + sizeof(INVENTORY_ITEM));
|
|
|
|
//We need to fix the record type here as it has to be correct before it is
|
|
//inserted into the inventory list.
|
|
|
|
if (acdmItem[nItem].sPrinterDriverName.length())
|
|
{ //printer
|
|
pItem->pf->d.type = SECTION_RECORD_TYPE_PRINTER;
|
|
pItem->recordType = WU_TYPE_RECORD_TYPE_PRINTER;
|
|
}
|
|
else
|
|
{ // pnp device
|
|
pItem->pf->d.type = SECTION_RECORD_TYPE_DRIVER_RECORD;
|
|
pItem->recordType = WU_TYPE_CDM_RECORD; //Corporate catalog device driver
|
|
}
|
|
pItem->pf->d.puid = acdmItem[nItem].puid;
|
|
pItem->pf->d.flags = 0;
|
|
pItem->pf->d.link = 0;
|
|
|
|
//set up the state of the item. This used to take place in ConvertCDMItem()
|
|
//initialize and store the internal item state structure
|
|
pItem->ps = (PWU_INV_STATE)V3_malloc(sizeof(WU_INV_STATE));
|
|
|
|
//translate the internal CDM item states to the public WUV3IS item
|
|
//states.
|
|
switch (acdmItem[nItem].fInstallState)
|
|
{
|
|
case CDM_NEW_DRIVER:
|
|
pItem->ps->state = WU_ITEM_STATE_INSTALL;
|
|
break;
|
|
case CDM_UPDATED_DRIVER:
|
|
pItem->ps->state = WU_ITEM_STATE_UPDATE;
|
|
break;
|
|
case CDM_CURRENT_DRIVER:
|
|
pItem->ps->state = WU_ITEM_STATE_CURRENT;
|
|
break;
|
|
default:
|
|
pItem->ps->state = WU_ITEM_STATE_UNKNOWN;
|
|
};
|
|
|
|
pItem->ps->bChecked = FALSE;
|
|
pItem->ps->bHidden = FALSE;
|
|
pItem->ps->dwReason = WU_STATE_REASON_NONE;
|
|
|
|
//setup the variable part of the item. In the main catalog inventory case this
|
|
//is all that we need to do. In the case of the corporate catalog we will need
|
|
//to add one more variable field. This field is the CDM bucket hashIndex id.
|
|
//This allows the corporate catalog to perform defered detection on device
|
|
//driver records.
|
|
|
|
//We need to add the ending variable size record since the list needs to
|
|
//be initialized before AddVariableSizeField() will work.
|
|
|
|
pItem->pv = (PWU_VARIABLE_FIELD)((PBYTE)pItem + sizeof(INVENTORY_ITEM) + sizeof(WU_INV_FIXED));
|
|
|
|
pItem->pv->id = WU_VARIABLE_END;
|
|
pItem->pv->len = sizeof(WU_VARIABLE_FIELD);
|
|
|
|
//Add in the needed variable field items. (Note: Only variable detection
|
|
//items are placed in the catalog inventory list to mimimize download size).
|
|
|
|
PWU_VARIABLE_FIELD pVf = CreateVariableField(WU_CDM_HARDWARE_ID, (PBYTE)(LPTSTR)acdmItem[nItem].bufHardwareIDs,
|
|
acdmItem[nItem].bufHardwareIDs.size() * sizeof(TCHAR));
|
|
AddVariableSizeField(&pItem, pVf);
|
|
V3_free(pVf);
|
|
|
|
pVf = CreateVariableField(WU_VARIABLE_DRIVERVER, (PBYTE)(LPTSTR)acdmItem[nItem].sDriverVer.c_str(),
|
|
(acdmItem[nItem].sDriverVer.length() + 1) * sizeof(TCHAR));
|
|
AddVariableSizeField(&pItem, pVf);
|
|
V3_free(pVf);
|
|
|
|
if (CDM_CURRENT_DRIVER == acdmItem[nItem].fInstallState)
|
|
{
|
|
pVf = CreateVariableField(WU_KEY_UNINSTALLKEY, (PBYTE)"Yes", 4); // Just to say that uninstall key is present
|
|
AddVariableSizeField(&pItem, pVf);
|
|
V3_free(pVf);
|
|
}
|
|
if ( acdmItem[nItem].sPrinterDriverName.length() )
|
|
{
|
|
pVf = CreateVariableField(WU_CDM_DRIVER_NAME, (PBYTE)(LPTSTR)acdmItem[nItem].sPrinterDriverName.c_str(),
|
|
(acdmItem[nItem].sPrinterDriverName.length() + 1) * sizeof(TCHAR));
|
|
AddVariableSizeField(&pItem, pVf);
|
|
V3_free(pVf);
|
|
}
|
|
if ( acdmItem[nItem].sArchitecture.length() )
|
|
{
|
|
pVf = CreateVariableField(WU_CDM_PRINTER_DRIVER_ARCH, (PBYTE)(LPTSTR)acdmItem[nItem].sArchitecture.c_str(),
|
|
(acdmItem[nItem].sArchitecture.length() + 1) * sizeof(TCHAR));
|
|
AddVariableSizeField(&pItem, pVf);
|
|
V3_free(pVf);
|
|
}
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
if ( pItem )
|
|
{
|
|
V3_free(pItem);
|
|
pItem = 0;
|
|
}
|
|
}
|
|
if (pItem)
|
|
m_items[m_iCDMTotalItems++] = pItem;
|
|
}
|
|
}
|
|
|
|
//This function installs a driver on Windows NT.
|
|
//The active function that is called on NT to install a device driver resides in newdev.dll
|
|
//Its prototype is:
|
|
//BOOL
|
|
//InstallWindowsUpdateDriver(
|
|
// HWND hwndParent,
|
|
// LPCWSTR HardwareId,
|
|
// LPCWSTR InfPathName,
|
|
// LPCWSTR DisplayName,
|
|
// BOOL Force,
|
|
// BOOL Backup,
|
|
// PDWORD pReboot
|
|
// )
|
|
//This API takes a HardwareID. Newdev will cycle through all devices that match this hardware ID
|
|
//and install the specified driver on them all.
|
|
//It also takes a BOOL value Backup which specifies whether or not to backup the current drivers.
|
|
// Note that newdev will only backup the drivers once if we find multiple matches for the HardwareID.
|
|
static DWORD InstallNT(
|
|
EDriverStatus eds,
|
|
LPCTSTR szHwIDs,
|
|
LPCTSTR szInfPathName,
|
|
LPCTSTR szDisplayName,
|
|
PDWORD pReboot
|
|
|
|
) {
|
|
LOG_block("InstallNT");
|
|
|
|
USES_CONVERSION;
|
|
|
|
typedef BOOL (*PFN_InstallWindowsUpdateDriver)(HWND hwndParent, LPCWSTR HardwareId, LPCWSTR InfPathName, LPCWSTR DisplayName, BOOL Force, BOOL Backup, PDWORD pReboot);
|
|
|
|
// Load newdev.dll and get pointer to our function
|
|
auto_hlib hlib = LoadLibrary(_T("newdev.dll"));
|
|
return_error_if_false(hlib.valid());
|
|
|
|
PFN_InstallWindowsUpdateDriver pfnInstallWindowsUpdateDriver = (PFN_InstallWindowsUpdateDriver)GetProcAddress(hlib,"InstallWindowsUpdateDriver");
|
|
return_error_if_false(pfnInstallWindowsUpdateDriver);
|
|
|
|
// make sure the hardware ID's are aligned
|
|
LPCTSTR szTempHwIDs;
|
|
TSTR_ALIGNED_STACK_COPY(&szTempHwIDs, szHwIDs);
|
|
szHwIDs = szTempHwIDs;
|
|
|
|
// walk through Hardware IDs
|
|
DWORD dwRebootFinal = 0;
|
|
for (LPCTSTR szHwID = szHwIDs; *szHwID; szHwID += lstrlen(szHwID) + 1)
|
|
{
|
|
tchar_buffer bchInfPathName;
|
|
if (edsBackup == eds)
|
|
{ // Old INF name is in the registry
|
|
return_if_error(GetReinstallString(szHwID, bchInfPathName));
|
|
TCHAR* ptr = _tcsrchr((LPCTSTR)bchInfPathName, _T('\\'));
|
|
if (ptr)
|
|
*ptr = 0; // cut file title
|
|
|
|
}
|
|
else
|
|
{
|
|
bchInfPathName.resize(lstrlen(szInfPathName) + 1);
|
|
return_error_if_false(bchInfPathName.valid());
|
|
lstrcpy(bchInfPathName, szInfPathName);
|
|
}
|
|
|
|
BOOL fForce = edsNew != eds;
|
|
BOOL fBackup = edsNew == eds;
|
|
LOG_out("InstallWindowsUpdateDriver(%s, %s, %s, fForce=%d, fBackup=%d)",
|
|
szHwID, (LPCTSTR)bchInfPathName, szDisplayName, fForce, fBackup);
|
|
DWORD dwReboot = 0;
|
|
BOOL bRc = (pfnInstallWindowsUpdateDriver)(GetActiveWindow(),
|
|
T2W((LPTSTR)szHwID), T2W(bchInfPathName), T2W((LPTSTR)szDisplayName), fForce, fBackup, &dwReboot);
|
|
DWORD dwError = GetLastError();
|
|
LOG_out("InstallWindowsUpdateDriver() returns %d, LastError = %d, need reboot = %d", bRc, dwError, dwReboot);
|
|
if ( !bRc )
|
|
{
|
|
if (NO_ERROR == dwError)
|
|
dwError = SPAPI_E_DI_DONT_INSTALL;
|
|
return dwError;
|
|
|
|
}
|
|
// cleanup
|
|
if ( edsBackup == eds )
|
|
{
|
|
DeleteReinstallKey(szHwID);
|
|
|
|
// Delete directory
|
|
DeleteNode(bchInfPathName);
|
|
|
|
// Remove empty directory tree
|
|
do
|
|
{
|
|
//remove *.*
|
|
TCHAR* psz = _tcsrchr((LPTSTR)bchInfPathName, _T('\\'));
|
|
if (NULL == psz)
|
|
break;
|
|
*psz = 0;
|
|
} while (RemoveDirectory(bchInfPathName));
|
|
}
|
|
if (dwReboot)
|
|
dwRebootFinal = 1;
|
|
}
|
|
*pReboot = dwRebootFinal;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void CdmInstallDriver(BOOL bWindowsNT, EDriverStatus eds, LPCTSTR szHwIDs, LPCTSTR szInfPathName, LPCTSTR szDisplayName, PDWORD pReboot)
|
|
{
|
|
DWORD dwError = InstallNT(eds, szHwIDs, szInfPathName, szDisplayName, pReboot);
|
|
if (NO_ERROR != dwError )
|
|
{
|
|
throw HRESULT_FROM_WIN32(dwError);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//We need to create a unique backup directory for this device, so we will
|
|
// use the unique Hardware ID to come up with this unique directory.
|
|
//Basically I will replace all of the illegal file/registry characters
|
|
// \/:*?"<>| with # (for 98) , for NT will only do this with \
|
|
//
|
|
static void HwID2Key(LPCTSTR szHwID, tchar_buffer& bufKey)
|
|
{
|
|
static TCHAR szIllegal[] = _T("\\/:*?\"<>|");
|
|
if (IsWindowsNT())
|
|
szIllegal[1] = 0;
|
|
|
|
bufKey.resize(ua_lstrlen(szHwID) + 1);
|
|
if (!bufKey.valid())
|
|
return; // out of memory
|
|
|
|
// use the macro to copy unligned strings
|
|
ua_tcscpy(bufKey, szHwID);
|
|
for (TCHAR* pch = bufKey; *pch; pch ++)
|
|
{
|
|
if (_tcschr(szIllegal, *pch) != NULL)
|
|
*pch = _T('#');
|
|
}
|
|
}
|
|
|
|
static DWORD RegQueryValueBuf(HKEY hKey, LPCTSTR szValue, tchar_buffer& buf)
|
|
{
|
|
DWORD dwSize = 0;
|
|
DWORD dwError = RegQueryValueEx(hKey, szValue, NULL, NULL, NULL, &dwSize);
|
|
if (NO_ERROR != dwError)
|
|
return dwError;
|
|
|
|
buf.resize(dwSize/sizeof(TCHAR));
|
|
if (!buf.valid())
|
|
return ERROR_OUTOFMEMORY;
|
|
return RegQueryValueEx(hKey, szValue, NULL, NULL, (LPBYTE)(LPTSTR)buf, &dwSize);
|
|
}
|
|
|
|
|
|
static DWORD OpenReinstallKey(HKEY* phkeyReinstall)
|
|
{
|
|
return RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Reinstall"),
|
|
0, KEY_ALL_ACCESS, phkeyReinstall);
|
|
}
|
|
|
|
static DWORD GetReinstallString(LPCTSTR szHwID, tchar_buffer& bchReinstallString)
|
|
{
|
|
auto_hkey hkeyReinstall;
|
|
DWORD dwError = OpenReinstallKey(&hkeyReinstall);
|
|
if (NO_ERROR != dwError)
|
|
return dwError;
|
|
|
|
tchar_buffer bufKey;
|
|
HwID2Key(szHwID, bufKey);
|
|
|
|
auto_hkey hkeyHwID;
|
|
dwError = RegOpenKeyEx(hkeyReinstall, bufKey, 0, KEY_READ, &hkeyHwID);
|
|
if (NO_ERROR != dwError)
|
|
return dwError;
|
|
|
|
return RegQueryValueBuf(hkeyHwID, _T("ReinstallString"), bchReinstallString);
|
|
}
|
|
|
|
static DWORD DeleteReinstallKey(LPCTSTR szHwID)
|
|
{
|
|
LOG_block("DeleteReinstallKey");
|
|
|
|
auto_hkey hkeyReinstall;
|
|
return_if_error(OpenReinstallKey(&hkeyReinstall));
|
|
|
|
tchar_buffer bufKey;
|
|
HwID2Key(szHwID, bufKey);
|
|
return_if_error(RegDeleteKey(hkeyReinstall, bufKey));
|
|
|
|
return NO_ERROR;
|
|
}
|