938 lines
26 KiB
C++
938 lines
26 KiB
C++
//=======================================================================
|
|
//
|
|
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// File: drvinst.cpp
|
|
//
|
|
// Description:
|
|
//
|
|
// Functions called to install drivers and printer drivers
|
|
//
|
|
//=======================================================================
|
|
|
|
#include <windows.h>
|
|
#include <wuiutest.h>
|
|
#include <tchar.h>
|
|
#include <winspool.h>
|
|
#include <setupapi.h>
|
|
#include <shlwapi.h>
|
|
#include <fileutil.h>
|
|
|
|
#include <install.h>
|
|
#include <logging.h>
|
|
#include <memutil.h>
|
|
#include <stringutil.h>
|
|
#include <iucommon.h>
|
|
#include <wusafefn.h>
|
|
#include <mistsafe.h>
|
|
|
|
#if defined(_X86_) || defined(i386)
|
|
const TCHAR SZ_PROCESSOR[] = _T("Intel");
|
|
#else // defined(_IA64_) || defined(IA64)
|
|
const TCHAR SZ_PROCESSOR[] = _T("IA64");
|
|
#endif
|
|
|
|
const TCHAR SZ_PRINTER[] = _T("Printer");
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InstallPrinterDriver
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
HRESULT InstallPrinterDriver(
|
|
IN LPCTSTR szDriverName,
|
|
IN LPCTSTR pszLocalDir, //Local directory where installation files are.
|
|
IN LPCTSTR szArchitecture,
|
|
OUT DWORD* pdwStatus
|
|
)
|
|
{
|
|
LOG_Block("InstallPrinterDriver");
|
|
|
|
USES_IU_CONVERSION;
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD dwError = ERROR_INVALID_FUNCTION;
|
|
TCHAR szFileName[MAX_PATH + 1];
|
|
HANDLE hFindFile = INVALID_HANDLE_VALUE;
|
|
OSVERSIONINFO osvi;
|
|
WIN32_FIND_DATA ffd;
|
|
HMODULE hLibModule = NULL;
|
|
LPWSTR pszwCmd = NULL;
|
|
HINF hInfFile = INVALID_HANDLE_VALUE;
|
|
|
|
if (NULL == szDriverName || NULL == pszLocalDir || NULL == pdwStatus)
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
|
|
}
|
|
|
|
LOG_Driver(_T("Called with szDriverName = %s, pszLocalDir = %s, szArchitecture = %s"),
|
|
szDriverName, pszLocalDir, (NULL == szArchitecture) ? _T("NULL") : szArchitecture);
|
|
//
|
|
// DecompressFolderCabs may return S_FALSE if it didn't find a cab to decompress...
|
|
//
|
|
hr = DecompressFolderCabs(pszLocalDir);
|
|
if (S_OK != hr)
|
|
{
|
|
CleanUpIfFailedAndSetHr(E_FAIL);
|
|
}
|
|
|
|
//
|
|
// Find the first *.inf file in pszLocalDir
|
|
//
|
|
CleanUpIfFailedAndSetHrMsg(StringCchCopyEx(szFileName, ARRAYSIZE(szFileName), pszLocalDir, \
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS));
|
|
CleanUpIfFailedAndSetHrMsg(PathCchAppend(szFileName, ARRAYSIZE(szFileName), _T("*.inf")));
|
|
|
|
if (INVALID_HANDLE_VALUE == (hFindFile = FindFirstFile(szFileName, &ffd)))
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
//
|
|
// 574593 During site printer install, we pass path to first INF - this may not be correct for MFD's or multi-platform CABs
|
|
//
|
|
// Find the first printer INF by calling SetupOpenInfFile() with class "Printer"
|
|
//
|
|
for (;;)
|
|
{
|
|
//
|
|
// Construct .inf path using FindXxxFile name
|
|
//
|
|
CleanUpIfFailedAndSetHrMsg(StringCchCopyEx(szFileName, ARRAYSIZE(szFileName), pszLocalDir, \
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS));
|
|
CleanUpIfFailedAndSetHrMsg(PathCchAppend(szFileName, ARRAYSIZE(szFileName), ffd.cFileName));
|
|
|
|
if (INVALID_HANDLE_VALUE == (hInfFile = SetupOpenInfFile(szFileName, SZ_PRINTER, INF_STYLE_WIN4, NULL)))
|
|
{
|
|
if (ERROR_CLASS_MISMATCH != GetLastError())
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
//
|
|
// If this isn't a Printer INF (ERROR_CLASS_MISMATCH) try the next file
|
|
//
|
|
if (0 == FindNextFile(hFindFile, &ffd))
|
|
{
|
|
//
|
|
// We ran out of *.inf files or hit other FindNextFile error before finding class match
|
|
//
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We found the printer INF in the cab. NOTE: WHQL assumption that only one "Printer" class
|
|
// INF will exist in any particular cab.
|
|
//
|
|
SetupCloseInfFile(hInfFile);
|
|
hInfFile = INVALID_HANDLE_VALUE;
|
|
//
|
|
// Go use szFileName
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// We've broken out of for (;;) loop without jumping to CleanUp, so we have a
|
|
// "Printer" class INF path in szFileName
|
|
//
|
|
|
|
// Only works on NT 5 up and Millennium
|
|
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&osvi);
|
|
if( VER_PLATFORM_WIN32_WINDOWS == osvi.dwPlatformId && 4 == osvi.dwMajorVersion && 90 == osvi.dwMinorVersion)
|
|
{
|
|
#if !(defined(_UNICODE) || defined(UNICODE))
|
|
//
|
|
// Millennium (ANSI only)
|
|
//
|
|
typedef DWORD (WINAPI *PFN_InstallPrinterDriver)(LPCSTR lpszDriverName, LPCSTR lpszINF);
|
|
|
|
if (NULL == (hLibModule = LoadLibraryFromSystemDir(_T("msprint2.dll"))))
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
PFN_InstallPrinterDriver pfnIPD;
|
|
|
|
if (NULL == (pfnIPD= (PFN_InstallPrinterDriver) GetProcAddress(hLibModule, "InstallPrinterDriver")))
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
if (NO_ERROR != (dwError = pfnIPD(szDriverName, szFileName)))
|
|
{
|
|
LOG_Driver("pfnIPD(%s, %s) returns %d", szDriverName, szFileName, dwError);
|
|
Win32MsgSetHrGotoCleanup(dwError);
|
|
}
|
|
#endif
|
|
}
|
|
else if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && 5 <= osvi.dwMajorVersion)
|
|
{
|
|
//
|
|
// Windows 2000 and Whistler: PrintUIEntryW is the only supported method of installing printer drivers.
|
|
// Don't try and use PnPInterface() defined in printui.dll (and don't ask *me* why PrintUIEntryW isn't
|
|
// typedef'ed there...)
|
|
//
|
|
// Type "rundll32.exe printui.dll,PrintUIEntry /?" from a cmd prompt for help on the command parameters.
|
|
//
|
|
// Private typedef since this isn't exposed in any internal or external SDK headers
|
|
//
|
|
typedef DWORD (*PFN_PrintUIEntryW)(
|
|
IN HWND hwnd,
|
|
IN HINSTANCE hInstance,
|
|
IN LPCWSTR pszCmdLine,
|
|
IN UINT nCmdShow
|
|
);
|
|
///////////////////////////////////
|
|
|
|
if (NULL == szArchitecture)
|
|
{
|
|
szArchitecture = (LPCTSTR) &SZ_PROCESSOR;
|
|
}
|
|
|
|
//
|
|
// 491157 Trying to update an English language printer driver installed on a German build through the German WU website fails.
|
|
//
|
|
// Don't pass optional /u, /h, and /v parameters (localized). They aren't required since we always provide
|
|
// drivers for the client architecture and OS.
|
|
//
|
|
// 574593 Per attached discussion we need to pass an undocumented upper-case 'U' flag.
|
|
//
|
|
const WCHAR szwCmdLineFormat[] = L"/ia /m \"%s\" /f \"%s\" /q /U";
|
|
const size_t nCmdLineFormatLength = wcslen(szwCmdLineFormat);
|
|
#define MAX_PLATFORMVERSION 20 // NOTE:: Max Version Length Needs to be Updated if the OS Strings in the Below Command Line Change
|
|
|
|
// NOTE: this doesn't bother to remove the length of the %s characters from nCmdLineFormatLength
|
|
|
|
DWORD dwLength=(nCmdLineFormatLength + lstrlen(szDriverName) + lstrlen(szArchitecture) + MAX_PLATFORMVERSION + lstrlen(szFileName) + 1);
|
|
pszwCmd = (LPWSTR) HeapAlloc(
|
|
GetProcessHeap(),
|
|
0,
|
|
dwLength * sizeof(WCHAR));
|
|
CleanUpFailedAllocSetHrMsg(pszwCmd);
|
|
|
|
// OK to cast away const-ness on string params so T2OLE works, since it doesn't modify them anyway
|
|
|
|
hr=StringCchPrintfExW(pszwCmd,dwLength,NULL,NULL,MISTSAFE_STRING_FLAGS,(LPCWSTR) szwCmdLineFormat,
|
|
T2OLE(const_cast<TCHAR*>(szDriverName)),
|
|
T2OLE(const_cast<TCHAR*>(szFileName)) );
|
|
|
|
CleanUpIfFailedAndSetHr(hr);
|
|
|
|
// Load printui.dll
|
|
if (NULL == (hLibModule = LoadLibraryFromSystemDir(_T("printui.dll"))))
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
PFN_PrintUIEntryW pfnPrintUIEntryW;
|
|
if (NULL == (pfnPrintUIEntryW = (PFN_PrintUIEntryW) GetProcAddress(hLibModule, "PrintUIEntryW")))
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
if (NO_ERROR != (dwError = pfnPrintUIEntryW(GetActiveWindow(), 0, pszwCmd, SW_HIDE)))
|
|
{
|
|
LOG_Driver(_T("pfnPrintUIEntryW(%s) returns %d"), OLE2T(pszwCmd), dwError);
|
|
Win32MsgSetHrGotoCleanup(dwError);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_NOTIMPL);
|
|
}
|
|
|
|
*pdwStatus = ITEM_STATUS_SUCCESS;
|
|
|
|
CleanUp:
|
|
|
|
SafeHeapFree(pszwCmd);
|
|
|
|
if (INVALID_HANDLE_VALUE != hFindFile)
|
|
{
|
|
FindClose(hFindFile);
|
|
}
|
|
|
|
if (INVALID_HANDLE_VALUE != hInfFile)
|
|
{
|
|
SetupCloseInfFile(hInfFile);
|
|
}
|
|
|
|
if (NULL != hLibModule)
|
|
{
|
|
FreeLibrary(hLibModule);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (NULL != pdwStatus)
|
|
{
|
|
*pdwStatus = ITEM_STATUS_FAILED;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InstallDriver and helper functions
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
DWORD OpenReinstallKey(HKEY* phKeyReinstall)
|
|
{
|
|
return RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Reinstall"),
|
|
0, KEY_ALL_ACCESS, phKeyReinstall);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
// LaunchProcess
|
|
// Launches pszCmd and optionally waits till the process terminates
|
|
//-----------------------------------------------------------------------------------
|
|
static HRESULT LaunchProcess(LPTSTR pszCmd, LPCTSTR pszDir, UINT uShow, BOOL bWait)
|
|
{
|
|
LOG_Block("LaunchProcess");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
STARTUPINFO startInfo;
|
|
PROCESS_INFORMATION processInfo;
|
|
|
|
ZeroMemory(&startInfo, sizeof(startInfo));
|
|
startInfo.cb = sizeof(startInfo);
|
|
startInfo.dwFlags |= STARTF_USESHOWWINDOW;
|
|
startInfo.wShowWindow = (USHORT)uShow;
|
|
|
|
BOOL bRet = CreateProcess(NULL, pszCmd, NULL, NULL, FALSE,
|
|
NORMAL_PRIORITY_CLASS, NULL, pszDir, &startInfo, &processInfo);
|
|
if (!bRet)
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
CloseHandle(processInfo.hThread);
|
|
|
|
if (bWait)
|
|
{
|
|
BOOL bDone = FALSE;
|
|
|
|
while (!bDone)
|
|
{
|
|
DWORD dwObject = MsgWaitForMultipleObjects(1, &processInfo.hProcess, FALSE,INFINITE, QS_ALLINPUT);
|
|
if (dwObject == WAIT_OBJECT_0 || dwObject == WAIT_FAILED)
|
|
{
|
|
bDone = TRUE;
|
|
}
|
|
else
|
|
{
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL,0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
} // while
|
|
|
|
} // bWait
|
|
|
|
CloseHandle(processInfo.hProcess);
|
|
|
|
CleanUp:
|
|
|
|
return hr;
|
|
}
|
|
|
|
// "@rundll sysdm.cpl,UpdateDriver_Start"
|
|
// "@rundll sysdm.cpl,UpdateDriver_RunDLL .\,,1,Win98 signed test pkg for System Devices"
|
|
// "@rundll sysdm.cpl,UpdateDriver_Finish 0"
|
|
// "@rundll sysdm.cpl,UpdateDriver_RunDLL .\,,1,Win98 signed test pkg for System Devices"
|
|
//Note: Windows 98 uses rundll.exe to call device manager. This is because sysdm.cpl which
|
|
//is device manager for 98 is a 16 bit dll. Since we would need to create something that
|
|
//worked similar to rundll in order call device manager we have brought the existing code
|
|
//across with some minor clean ups. win98 device manager provides three apis for our use
|
|
//in installing device drivers. These are:
|
|
// UpdateDriver_Start() - Start the device installation
|
|
// UpdateDriver_RunDLL(inf Directory,hardware id, force flag, display string)
|
|
// UpdateDriver_Finish 0 - finish the installation.
|
|
//The UpdateDriver_RunDLL() command
|
|
//Comma separated string in following format:
|
|
//INFPath,HardwareID,flags,DisplayName
|
|
//INFPath = Path to INF and installation files
|
|
//HardwareID = PnpHardware ID
|
|
//flags = '1' = force driver, '0' = do not force driver.
|
|
//Note: A Reinstall driver is detected based on the location of the INF path. If INF path
|
|
//is the same path as the reinstallbackups registry key then reinstall is selected.
|
|
//DisplayName = Name to display in install dialogs.
|
|
|
|
//This method installs a CDM driver for Windows 98.
|
|
static HRESULT Install98(
|
|
LPCTSTR pszHardwareID,
|
|
LPCTSTR pszLocalDir, // location of INF and other driver install files
|
|
LPCTSTR pszDisplayName,
|
|
PDWORD pdwReboot
|
|
)
|
|
{
|
|
LOG_Block("Install98");
|
|
|
|
HRESULT hr = E_NOTIMPL;
|
|
|
|
DWORD dwStatus = 0;
|
|
LPTSTR pszCmd = NULL;
|
|
DWORD dwLen;
|
|
LONG lRet;
|
|
DWORD dwSize;
|
|
|
|
|
|
if (NULL == pdwReboot)
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
|
|
}
|
|
#if defined(DBG)
|
|
// checked by caller
|
|
if (NULL == pszHardwareID || NULL == pszLocalDir || NULL == pszDisplayName)
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
|
|
}
|
|
#endif
|
|
|
|
#if !(defined(_UNICODE) || defined(UNICODE))
|
|
//
|
|
// Win98 and WinME (ANSI only)
|
|
//
|
|
|
|
// Start
|
|
CleanUpIfFailedAndSetHr(LaunchProcess(_T("rundll32 sysdm.cpl,UpdateDriver_Start"), NULL, SW_NORMAL, TRUE));
|
|
|
|
TCHAR szShortInfPathName[MAX_PATH] = {0};
|
|
dwLen = GetShortPathName(pszLocalDir, szShortInfPathName, ARRAYSIZE(szShortInfPathName));
|
|
|
|
//Note: The maximum a hardware or compatible ID can be is 200 characters
|
|
// (MAX_DEVICE_ID_LEN defined in sdk\inc\cfgmgr32.h)
|
|
|
|
DWORD dwBuffLength=( lstrlen(szShortInfPathName) + lstrlen(pszHardwareID) + lstrlen(pszDisplayName) + 64);
|
|
CleanUpFailedAllocSetHrMsg(pszCmd = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
|
|
dwLen * sizeof(TCHAR)));
|
|
|
|
|
|
hr=StringCchPrintfEx(pszCmd,dwBuffLength,NULL,NULL,MISTSAFE_STRING_FLAGS,
|
|
_T("rundll32 sysdm.cpl,UpdateDriver_RunDLL %s,%s,%d,%s"),
|
|
szShortInfPathName, pszHardwareID,0,pszDisplayName);
|
|
|
|
CleanUpIfFailedAndSetHr(hr);
|
|
|
|
// RunDLL
|
|
LOG_Driver(_T("LaunchProcess(%s)"), pszCmd);
|
|
CleanUpIfFailedAndSetHr(LaunchProcess(pszCmd, NULL, SW_NORMAL, TRUE));
|
|
|
|
// Get resulting code
|
|
HKEY hKeyReinstall;
|
|
if (ERROR_SUCCESS == (lRet = OpenReinstallKey(&hKeyReinstall)))
|
|
{
|
|
dwSize = sizeof(dwStatus);
|
|
if (ERROR_SUCCESS == (lRet = RegQueryValueEx(hKeyReinstall, _T("LastInstallStatus"), NULL, NULL, (LPBYTE)&dwStatus, &dwSize)))
|
|
{
|
|
if (3 == dwStatus)
|
|
{
|
|
//Check if we need to reboot
|
|
HKEY hKeySysDM;
|
|
*pdwReboot = 0;
|
|
dwSize = sizeof(*pdwReboot);
|
|
if (ERROR_SUCCESS == (lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SysDM"), 0, KEY_READ, &hKeySysDM)))
|
|
{
|
|
if (ERROR_SUCCESS != (lRet = RegQueryValueEx(hKeySysDM, "UpgradeDeviceFlags", NULL, NULL, (LPBYTE)&pdwReboot, &dwSize)))
|
|
{
|
|
LOG_ErrorMsg(lRet);
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
}
|
|
|
|
RegCloseKey(hKeySysDM);
|
|
}
|
|
else
|
|
{
|
|
LOG_ErrorMsg(lRet);
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_ErrorMsg(lRet);
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
}
|
|
|
|
LOG_Driver(_T("Reboot %srequired"), *pdwReboot ? _T(" ") : _T("not "));
|
|
|
|
RegCloseKey(hKeyReinstall);
|
|
}
|
|
else
|
|
{
|
|
LOG_ErrorMsg(lRet);
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
}
|
|
|
|
// Finish no reboot
|
|
CleanUpIfFailedAndSetHr(LaunchProcess(_T("rundll32 sysdm.cpl,UpdateDriver_Finish 2"), NULL, SW_NORMAL, TRUE));
|
|
|
|
if (3 != dwStatus)
|
|
{
|
|
LOG_Error("3 != dwStatus");
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
|
|
#endif // #if !(defined(_UNICODE) || defined(UNICODE))
|
|
CleanUp:
|
|
|
|
SafeHeapFree(pszCmd);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//This function installs a driver on Windows NT.
|
|
// 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.
|
|
// This should always be TRUE.
|
|
static HRESULT InstallNT(
|
|
LPCTSTR pszHardwareID,
|
|
LPCTSTR pszLocalDir, // passed to InstallWindowsUpdateDriver(... InfPathName, ...)
|
|
LPCTSTR pszDisplayName,
|
|
PDWORD pdwReboot
|
|
)
|
|
{
|
|
USES_IU_CONVERSION;
|
|
|
|
LOG_Block("InstallNT");
|
|
|
|
//
|
|
// InstallWindowsUpdateDriver function found in $(BASEDIR)\shell\osshell\cpls\newdev\init.c (not in any headers)
|
|
//
|
|
typedef BOOL (*PFN_InstallWindowsUpdateDriver)(HWND hwndParent, LPCWSTR HardwareId, LPCWSTR InfPathName, LPCWSTR DisplayName, BOOL Force, BOOL Backup, PDWORD pReboot);
|
|
|
|
HRESULT hr = S_OK;
|
|
HMODULE hLibModule = NULL;
|
|
PFN_InstallWindowsUpdateDriver pfnInstallWindowsUpdateDriver;
|
|
|
|
if (NULL == pdwReboot)
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
|
|
}
|
|
#if defined(DBG)
|
|
// checked by caller
|
|
if (NULL == pszHardwareID || NULL == pszLocalDir || NULL == pszDisplayName)
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
|
|
}
|
|
#endif
|
|
|
|
|
|
// Load newdev.dll and get pointer to our function
|
|
if (NULL == (hLibModule = LoadLibraryFromSystemDir(_T("newdev.dll"))))
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
if (NULL == (pfnInstallWindowsUpdateDriver = (PFN_InstallWindowsUpdateDriver)GetProcAddress(hLibModule,"InstallWindowsUpdateDriver")))
|
|
{
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
// Industry Update RAID # 461 waltw May need to massage HWID's for site Driver Install for Win2K
|
|
//
|
|
// Linked to: RAID # 12021 in Windows Update Database - This logic (required for Win2K) is implemented
|
|
// on the server rather than the client in IU (V3 Wuv3is implements this on the client)
|
|
//
|
|
// first, we search for a matching SPDRP_HARDWAREID
|
|
// if we didn't find a Hardware ID, we search for a matching SPDRP_COMPATIBLEID,
|
|
// and we pass the last SPDRP_HARDWAREID associated with the same device.
|
|
#if (defined(UNICODE) || defined(_UNICODE))
|
|
LOG_Driver (_T("InstallWindowsUpdateDriver(GetActiveWindow(), %s, %s, %s, fForce=%d, fBackup=%d)"),
|
|
pszHardwareID, pszLocalDir, pszDisplayName, FALSE, TRUE);
|
|
#endif
|
|
//
|
|
// NOTES on calling InstallWindowsUpdateDriver():
|
|
// * Never pass TRUE in Force flag (only used if we are doing uninstall, which we don't support).
|
|
// * Always pass TRUE in Backup flag.
|
|
// * OK to cast away const-ness on strings since InstallWindowsUpdateDriver takes const wide strings
|
|
if(!(pfnInstallWindowsUpdateDriver)(GetActiveWindow(),
|
|
T2OLE(const_cast<TCHAR*>(pszHardwareID)),
|
|
T2OLE(const_cast<TCHAR*>(pszLocalDir)),
|
|
T2OLE(const_cast<TCHAR*>(pszDisplayName)), FALSE, TRUE, pdwReboot))
|
|
{
|
|
LOG_Driver(_T("InstallWindowsUpdateDriver returned false. Driver was not be updated."));
|
|
Win32MsgSetHrGotoCleanup(GetLastError());
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if (NULL != hLibModule)
|
|
{
|
|
FreeLibrary(hLibModule);
|
|
hLibModule = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// MatchHardwareID (used only on Windows 2000)
|
|
//
|
|
// Takes as input a hardware or compatible ID and returns an allocated
|
|
// buffer with the same hardware ID or, if it was a compatible ID the
|
|
// most general hardware ID for the device node that matched the
|
|
// given compatible ID.
|
|
//
|
|
// Return: S_OK if a match was found, else a failure code
|
|
//
|
|
// *ppszMatchingHWID must be NULL on entry, and if S_OK is returned
|
|
// the buffer must be heap-freed by the caller.
|
|
//
|
|
HRESULT MatchHardwareID(LPCWSTR pwszHwOrCompatID, LPWSTR * ppszMatchingHWID)
|
|
{
|
|
LOG_Block("MatchHardwareID");
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
DWORD dwIndex = 0;
|
|
DWORD dwSize = 0;
|
|
|
|
LPWSTR pwszHardwareIDList = NULL;
|
|
LPWSTR pwszCompatibleIDList = NULL;
|
|
LPWSTR pwszSingleID = NULL;
|
|
|
|
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
|
|
BOOL fRet;
|
|
|
|
ZeroMemory((void*)&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
|
|
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
if (NULL == pwszHwOrCompatID || NULL == ppszMatchingHWID || NULL != *ppszMatchingHWID)
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
|
|
}
|
|
|
|
// get a handle to the class devices
|
|
hDevInfo = SetupDiGetClassDevs(NULL,
|
|
NULL,
|
|
GetActiveWindow(),
|
|
DIGCF_ALLCLASSES | DIGCF_PRESENT
|
|
);
|
|
|
|
if (INVALID_HANDLE_VALUE == hDevInfo)
|
|
{
|
|
Win32MsgSetHrGotoCleanup(ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
//loop through all devices
|
|
DWORD dwBufLen=0;
|
|
while ((NULL == *ppszMatchingHWID) && SetupDiEnumDeviceInfo(hDevInfo,
|
|
dwIndex++,
|
|
&DeviceInfoData
|
|
))
|
|
{
|
|
//
|
|
// Free up buffers for each device node loop (if allocated)
|
|
//
|
|
SafeHeapFree(pwszHardwareIDList);
|
|
SafeHeapFree(pwszCompatibleIDList);
|
|
dwSize = 0;
|
|
//
|
|
// Get the list of Hardware Ids for this device
|
|
//
|
|
fRet = SetupDiGetDeviceRegistryPropertyW(hDevInfo,
|
|
&DeviceInfoData,
|
|
SPDRP_HARDWAREID,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&dwSize
|
|
);
|
|
|
|
if (0 == dwSize || (FALSE == fRet && ERROR_INSUFFICIENT_BUFFER != GetLastError()))
|
|
{
|
|
//
|
|
// FIX: NTRAID#NTBUG9-500223-2001/11/28- IU - Dual mode USB camera install fails while installing of web site
|
|
//
|
|
// If we hit a node without a HWID before finding device node we are looking for, just continue. If the node
|
|
// we ARE looking for doesn't have a HWID then we will fail later anyway when we run out of nodes.
|
|
//
|
|
LOG_Out(_T("No HWID's found for device node"));
|
|
continue;
|
|
}
|
|
|
|
if (MAX_SETUP_MULTI_SZ_SIZE_W < dwSize)
|
|
{
|
|
//
|
|
// Something is very wrong - bail
|
|
//
|
|
CleanUpIfFailedAndSetHrMsg(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
//
|
|
// We got the expected ERROR_INSUFFICIENT_BUFFER with a reasonable dwSize
|
|
//
|
|
// Now guarantee we are double-NULL terminated by allocating two extra WCHARs we don't tell SetupDi about
|
|
//
|
|
pwszHardwareIDList = (LPWSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize + (sizeof(WCHAR) * 2));
|
|
CleanUpFailedAllocSetHrMsg(pwszHardwareIDList);
|
|
|
|
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo,
|
|
&DeviceInfoData,
|
|
SPDRP_HARDWAREID,
|
|
NULL,
|
|
(PBYTE)pwszHardwareIDList,
|
|
dwSize,
|
|
&dwSize
|
|
))
|
|
{
|
|
//
|
|
// If any of the devices HardwareIDs match the input ID then
|
|
// we copy the incoming argument to a new buffer and return true
|
|
//
|
|
|
|
for (pwszSingleID = pwszHardwareIDList;
|
|
*pwszSingleID;
|
|
pwszSingleID += lstrlenW(pwszSingleID) + 1)
|
|
{
|
|
|
|
if (0 == lstrcmpiW(pwszSingleID, pwszHwOrCompatID))
|
|
{
|
|
// return the hardware ID we matched
|
|
dwBufLen=(lstrlenW(pwszHwOrCompatID) + 1);
|
|
*ppszMatchingHWID = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
|
|
dwBufLen * sizeof(WCHAR));
|
|
CleanUpFailedAllocSetHrMsg(*ppszMatchingHWID);
|
|
|
|
hr=StringCchCopyExW(*ppszMatchingHWID,dwBufLen,pwszHwOrCompatID,NULL,NULL,MISTSAFE_STRING_FLAGS);
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Hardware match not found, let's try to match to a
|
|
// compatible ID then return the (most generic) Hardware ID
|
|
// associated with the same device node
|
|
//
|
|
fRet = SetupDiGetDeviceRegistryPropertyW(hDevInfo,
|
|
&DeviceInfoData,
|
|
SPDRP_COMPATIBLEIDS,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&dwSize
|
|
);
|
|
|
|
if (0 == dwSize || (FALSE == fRet && ERROR_INSUFFICIENT_BUFFER != GetLastError()))
|
|
{
|
|
LOG_Out(_T("No Compatible ID's found for device node"));
|
|
continue;
|
|
}
|
|
|
|
if (MAX_SETUP_MULTI_SZ_SIZE_W < dwSize)
|
|
{
|
|
//
|
|
// Something is very wrong - bail
|
|
//
|
|
CleanUpIfFailedAndSetHrMsg(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
//
|
|
// We got the expected ERROR_INSUFFICIENT_BUFFER with a reasonable dwSize
|
|
//
|
|
// Now guarantee we are double-NULL terminated by allocating two extra WCHARs we don't tell SetupDi about
|
|
//
|
|
pwszCompatibleIDList = (LPWSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize + (sizeof(WCHAR) * 2));
|
|
CleanUpFailedAllocSetHrMsg(pwszCompatibleIDList);
|
|
|
|
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo,
|
|
&DeviceInfoData,
|
|
SPDRP_COMPATIBLEIDS,
|
|
NULL,
|
|
(PBYTE)pwszCompatibleIDList,
|
|
dwSize,
|
|
&dwSize
|
|
))
|
|
{
|
|
for (pwszSingleID = pwszCompatibleIDList;
|
|
*pwszSingleID;
|
|
pwszSingleID += lstrlenW(pwszSingleID) + 1)
|
|
{
|
|
|
|
if (0 == lstrcmpiW(pwszSingleID, pwszHwOrCompatID))
|
|
{
|
|
//
|
|
// We found a compatible match, now return the most general HWID
|
|
// for this device node. Must be at least one character long.
|
|
//
|
|
if (NULL != pwszHardwareIDList && NULL != *pwszHardwareIDList)
|
|
{
|
|
LPWSTR lpwszLastID = NULL;
|
|
|
|
for(pwszSingleID = pwszHardwareIDList;
|
|
*pwszSingleID;
|
|
pwszSingleID += lstrlenW(pwszSingleID) + 1)
|
|
{
|
|
//
|
|
// Remember last ID before NULL string
|
|
//
|
|
lpwszLastID = pwszSingleID;
|
|
}
|
|
|
|
// copy the last HWID into a new buffer
|
|
dwBufLen=(lstrlenW(lpwszLastID) + 1);
|
|
*ppszMatchingHWID = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
|
|
dwBufLen * sizeof(WCHAR));
|
|
CleanUpFailedAllocSetHrMsg(*ppszMatchingHWID);
|
|
hr=StringCchCopyExW(*ppszMatchingHWID,dwBufLen,lpwszLastID,NULL,NULL,MISTSAFE_STRING_FLAGS);
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end while
|
|
|
|
|
|
CleanUp:
|
|
|
|
if (INVALID_HANDLE_VALUE != hDevInfo)
|
|
{
|
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
|
}
|
|
|
|
//
|
|
// Free up any allocated buffers (except *ppszMatchingHWID)
|
|
//
|
|
if(FAILED(hr))
|
|
{
|
|
SafeHeapFree(*ppszMatchingHWID);
|
|
}
|
|
|
|
SafeHeapFree(pwszHardwareIDList);
|
|
SafeHeapFree(pwszCompatibleIDList);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//This function handles installation of a Device driver package.
|
|
HRESULT InstallDriver(
|
|
LPCTSTR pszLocalDir, // Local directory where installation files are.
|
|
LPCTSTR pszDisplayName, // Description of package, Device Manager displays this in its install dialog.
|
|
LPCTSTR pszHardwareID, // ID from XML matched to client hardware via GetManifest()
|
|
DWORD* pdwStatus
|
|
)
|
|
{
|
|
LOG_Block("InstallDriver");
|
|
USES_IU_CONVERSION;
|
|
|
|
HRESULT hr;
|
|
OSVERSIONINFO osvi;
|
|
DWORD dwReboot = 0;
|
|
LPWSTR pszwMatchingHWID = NULL;
|
|
|
|
if (NULL == pszLocalDir || NULL == pszDisplayName || NULL == pszHardwareID || NULL == pdwStatus)
|
|
{
|
|
SetHrMsgAndGotoCleanUp(E_INVALIDARG);
|
|
}
|
|
|
|
//
|
|
// DecompressFolderCabs may return S_FALSE if it didn't find a cab to decompress...
|
|
//
|
|
hr = DecompressFolderCabs(pszLocalDir);
|
|
if (S_OK != hr)
|
|
{
|
|
CleanUpIfFailedAndSetHr(E_FAIL);
|
|
}
|
|
|
|
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&osvi);
|
|
if(VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && 4 < osvi.dwMajorVersion)
|
|
{
|
|
//
|
|
// Win2K or higher NT
|
|
//
|
|
if (5 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion)
|
|
{
|
|
//
|
|
// Windows 2000
|
|
// NTBUG9-485554 Convert compatible IDs to hardware IDs for site Driver Install for Win2K
|
|
//
|
|
// OK to cast away const-ness on string params so T2OLE works, since it doesn't modify them anyway
|
|
CleanUpIfFailedAndSetHr(MatchHardwareID(T2OLE((LPTSTR)pszHardwareID), &pszwMatchingHWID));
|
|
|
|
hr = InstallNT(OLE2T(pszwMatchingHWID), pszLocalDir, pszDisplayName, &dwReboot);
|
|
|
|
// pszMatchingHWID must be non-null if we got here
|
|
SafeHeapFree(pszwMatchingHWID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Normal case, just install
|
|
//
|
|
CleanUpIfFailedAndSetHr(InstallNT(pszHardwareID, pszLocalDir, pszDisplayName, &dwReboot));
|
|
}
|
|
}
|
|
else if (VER_PLATFORM_WIN32_WINDOWS == osvi.dwPlatformId &&
|
|
(4 < osvi.dwMajorVersion) ||
|
|
( (4 == osvi.dwMajorVersion) &&
|
|
(0 < osvi.dwMinorVersion) ) )
|
|
{
|
|
//
|
|
// Win98 or higher (WinME)
|
|
//
|
|
CleanUpIfFailedAndSetHr(Install98(pszHardwareID, pszLocalDir, pszDisplayName, &dwReboot));
|
|
}
|
|
else
|
|
{
|
|
*pdwStatus = ITEM_STATUS_FAILED;
|
|
SetHrMsgAndGotoCleanUp(E_NOTIMPL);
|
|
}
|
|
|
|
if (DI_NEEDRESTART & dwReboot || DI_NEEDREBOOT & dwReboot)
|
|
*pdwStatus = ITEM_STATUS_SUCCESS_REBOOT_REQUIRED;
|
|
else
|
|
*pdwStatus = ITEM_STATUS_SUCCESS;
|
|
|
|
CleanUp:
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (NULL != pdwStatus)
|
|
{
|
|
*pdwStatus = ITEM_STATUS_FAILED;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|