3158 lines
103 KiB
C
3158 lines
103 KiB
C
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation
|
|
//
|
|
// File: init.c
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "newdevp.h"
|
|
#include "pnpipc.h"
|
|
|
|
|
|
HMODULE hNewDev = NULL;
|
|
BOOL bQueuedRebootNeeded = FALSE;
|
|
BOOL GuiSetupInProgress = FALSE;
|
|
HANDLE hTrayIconWnd = NULL;
|
|
|
|
BOOL
|
|
DllInitialize(
|
|
IN PVOID hmod,
|
|
IN ULONG ulReason,
|
|
IN PCONTEXT pctx OPTIONAL
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(pctx);
|
|
|
|
hNewDev = hmod;
|
|
|
|
UNREFERENCED_PARAMETER(pctx);
|
|
|
|
switch (ulReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
DisableThreadLibraryCalls(hmod);
|
|
|
|
if (!SHFusionInitializeFromModule(hmod)) {
|
|
return FALSE;
|
|
}
|
|
|
|
IntializeDeviceMapInfo();
|
|
GuiSetupInProgress = GetGuiSetupInProgress();
|
|
|
|
LoadString(hNewDev,
|
|
IDS_UNKNOWN,
|
|
(PTCHAR)szUnknown,
|
|
SIZECHARS(szUnknown)
|
|
);
|
|
|
|
LoadString(hNewDev,
|
|
IDS_UNKNOWNDEVICE,
|
|
(PTCHAR)szUnknownDevice,
|
|
SIZECHARS(szUnknownDevice)
|
|
);
|
|
|
|
hSrClientDll = NULL;
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
SHFusionUninitialize();
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pInstallDeviceInstanceNewDevice(
|
|
HWND hwndParent,
|
|
HWND hBalloonTiphWnd,
|
|
PNEWDEVWIZ NewDevWiz
|
|
)
|
|
{
|
|
ULONG DevNodeStatus = 0, Problem = 0;
|
|
SP_DRVINFO_DATA DriverInfoData;
|
|
SP_DRVINSTALL_PARAMS DriverInstallParams;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
BOOL bHaveDriver = TRUE;
|
|
|
|
//
|
|
// Set the DI_QUIETINSTALL flag for the Found New Hardware case
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
)) {
|
|
|
|
DeviceInstallParams.Flags |= DI_QUIETINSTALL;
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
//
|
|
// Do a driver search by searching the default INF locations.
|
|
//
|
|
DoDriverSearch(hwndParent,
|
|
NewDevWiz,
|
|
SEARCH_DEFAULT,
|
|
SPDIT_COMPATDRIVER,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Check if the Windows Update cache says it has a better driver if we are
|
|
// connected to the Internet.
|
|
//
|
|
if (IsConnectedToInternet() &&
|
|
SearchWindowsUpdateCache(NewDevWiz)) {
|
|
|
|
//
|
|
// The machine is connected to the Internet and the WU cache says it has
|
|
// a better driver, so let's connect to the Internet and download this
|
|
// driver from WU.
|
|
//
|
|
DoDriverSearch(hwndParent,
|
|
NewDevWiz,
|
|
SEARCH_INET,
|
|
SPDIT_COMPATDRIVER,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
//
|
|
// Lets see if we found a driver for this device.
|
|
//
|
|
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
|
|
if (SetupDiGetSelectedDriver(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DriverInfoData
|
|
)) {
|
|
|
|
if (FAILED(StringCchCopy(NewDevWiz->DriverDescription,
|
|
SIZECHARS(NewDevWiz->DriverDescription),
|
|
DriverInfoData.Description))) {
|
|
|
|
NewDevWiz->DriverDescription[0] = TEXT('\0');
|
|
}
|
|
|
|
//
|
|
// fetch rank of driver found.
|
|
//
|
|
DriverInstallParams.cbSize = sizeof(DriverInstallParams);
|
|
if (!SetupDiGetDriverInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DriverInfoData,
|
|
&DriverInstallParams
|
|
)) {
|
|
|
|
DriverInstallParams.Rank = (DWORD)-1;
|
|
}
|
|
|
|
//
|
|
// If we have a balloon tip window then have it update its UI.
|
|
//
|
|
if (hBalloonTiphWnd) {
|
|
//
|
|
// We have a new driver description for this device so use this to update the balloon
|
|
// tip.
|
|
//
|
|
PostMessage(hBalloonTiphWnd,
|
|
WUM_UPDATEUI,
|
|
0,
|
|
(LPARAM)NewDevWiz->DriverDescription
|
|
);
|
|
|
|
} else if (NewDevWiz->Flags & IDI_FLAG_SECONDNEWDEVINSTANCE) {
|
|
//
|
|
// This is the second NEWDEV.DLL instance running with Administrator privileges. We need
|
|
// to send a message to the main NEWDEV.DLL process and have it update it's balloon tooltip.
|
|
//
|
|
SendMessageToUpdateBalloonInfo(NewDevWiz->DriverDescription);
|
|
}
|
|
|
|
} else {
|
|
|
|
*NewDevWiz->DriverDescription = L'\0';
|
|
DriverInstallParams.Rank = (DWORD)-1;
|
|
DriverInstallParams.Flags = 0;
|
|
bHaveDriver = FALSE;
|
|
|
|
//
|
|
// If we have a balloon tip window then have it update its UI.
|
|
//
|
|
if (hBalloonTiphWnd) {
|
|
//
|
|
// We don't have a driver description, most likely because we didn't find a driver for this device,
|
|
// so just update the balloon text using the DeviceInstanceId.
|
|
//
|
|
PostMessage(hBalloonTiphWnd,
|
|
WUM_UPDATEUI,
|
|
(WPARAM)TIP_LPARAM_IS_DEVICEINSTANCEID,
|
|
(LPARAM)NewDevWiz->InstallDeviceInstanceId
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Get the status of this devnode
|
|
//
|
|
CM_Get_DevNode_Status(&DevNodeStatus,
|
|
&Problem,
|
|
NewDevWiz->DeviceInfoData.DevInst,
|
|
0
|
|
);
|
|
|
|
|
|
//
|
|
// If we have a Hardware ID match and the selected (best) driver is not
|
|
// listed as InteractiveInstall in the INF then just install the driver
|
|
// for this device.
|
|
//
|
|
if ((DriverInstallParams.Rank <= DRIVER_HARDWAREID_RANK) &&
|
|
(!IsDriverNodeInteractiveInstall(NewDevWiz, &DriverInfoData))) {
|
|
|
|
NewDevWiz->SilentMode = TRUE;
|
|
|
|
DoDeviceWizard(hwndParent, NewDevWiz, FALSE);
|
|
|
|
//
|
|
// Install any new child devices that have come online due to the installation
|
|
// of this device. If there are any then install them silently.
|
|
//
|
|
if (!(NewDevWiz->Capabilities & CM_DEVCAP_SILENTINSTALL)) {
|
|
InstallSilentChilds(hwndParent, NewDevWiz);
|
|
}
|
|
|
|
} else if (!bHaveDriver &&
|
|
(NewDevWiz->Capabilities & CM_DEVCAP_RAWDEVICEOK) &&
|
|
(NewDevWiz->Capabilities & CM_DEVCAP_SILENTINSTALL) &&
|
|
(DevNodeStatus & DN_STARTED)) {
|
|
|
|
//
|
|
// If the device is both RAW, silent install, and already started,
|
|
// and we didn't find any drivers, then we just want to
|
|
// install the NULL driver.
|
|
//
|
|
InstallNullDriver(NewDevWiz, FALSE);
|
|
|
|
} else {
|
|
//
|
|
// This is the case where we don't have a hardware ID match and we don't have a special
|
|
// RAW, silent, started device. So in this case we will bring up the Found New Hardware
|
|
// Wizard so the user can install a driver for this device.
|
|
//
|
|
|
|
//
|
|
// If we have a balloon tip window then hide it.
|
|
//
|
|
if (hBalloonTiphWnd) {
|
|
PostMessage(hBalloonTiphWnd,
|
|
WUM_UPDATEUI,
|
|
(WPARAM)TIP_HIDE_BALLOON,
|
|
0
|
|
);
|
|
}
|
|
|
|
//
|
|
// We are bringing up the wizard, so clear the DI_QUIETINSTALL flag.
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
)) {
|
|
|
|
DeviceInstallParams.Flags &= ~DI_QUIETINSTALL;
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Bring up the Found New Hardware Wizard
|
|
//
|
|
DoDeviceWizard(GetParent(hwndParent), NewDevWiz, TRUE);
|
|
|
|
//
|
|
// Install any new child devices that have come online due to the installation
|
|
// of this device. If there are any then install them silently.
|
|
//
|
|
if (!(NewDevWiz->Capabilities & CM_DEVCAP_SILENTINSTALL)) {
|
|
|
|
InstallSilentChilds(hwndParent, NewDevWiz);
|
|
}
|
|
}
|
|
|
|
return (GetLastError() == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL
|
|
pInstallDeviceInstanceUpdateDevice(
|
|
HWND hwndParent,
|
|
PNEWDEVWIZ NewDevWiz
|
|
)
|
|
{
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
|
|
//
|
|
// We need to first check with the class/co-installers to give them the
|
|
// change to bring up their own update driver UI. This needs to be done
|
|
// because there are some cases when our default behavior can cause the
|
|
// device not to work. This only currently happens in the multiple
|
|
// identical device case.
|
|
//
|
|
if (SetupDiCallClassInstaller(DIF_UPDATEDRIVER_UI,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
) ||
|
|
(GetLastError() != ERROR_DI_DO_DEFAULT)) {
|
|
|
|
//
|
|
// If the class/co-installer returned NO_ERRROR, or some error other
|
|
// than ERROR_DI_DO_DEFAULT then we will not display our default wizard.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Jump directly into the Update Driver Wizard
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
))
|
|
{
|
|
//
|
|
// This shouldn't be a quiet install, since we are doing a normal Update Driver
|
|
//
|
|
DeviceInstallParams.Flags &= ~DI_QUIETINSTALL;
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
|
|
DoDeviceWizard(hwndParent, NewDevWiz, TRUE);
|
|
}
|
|
|
|
return (GetLastError() == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL
|
|
pInstallDeviceInstanceUpdateDeviceSilent(
|
|
HWND hwndParent,
|
|
PNEWDEVWIZ NewDevWiz
|
|
)
|
|
{
|
|
ULONG SearchOptions;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
SP_DRVINFO_DATA DriverInfoData;
|
|
|
|
if (NewDevWiz->UpdateDriverInfo &&
|
|
NewDevWiz->UpdateDriverInfo->FromInternet) {
|
|
//
|
|
// Drivers are from the Internet (newdev API called from WU)
|
|
//
|
|
if (FAILED(StringCchCopy(NewDevWiz->BrowsePath,
|
|
SIZECHARS(NewDevWiz->BrowsePath),
|
|
NewDevWiz->UpdateDriverInfo->InfPathName))) {
|
|
//
|
|
// The path that was passed in was too large to fit in our buffer
|
|
//
|
|
SetLastError(ERROR_DI_BAD_PATH);
|
|
return FALSE;
|
|
}
|
|
|
|
SearchOptions = SEARCH_WINDOWSUPDATE;
|
|
|
|
} else {
|
|
//
|
|
// Normal app just telling us to update this device using the specified INF
|
|
// or a driver rollback.
|
|
//
|
|
if (FAILED(StringCchCopy(NewDevWiz->SingleInfPath,
|
|
SIZECHARS(NewDevWiz->SingleInfPath),
|
|
NewDevWiz->UpdateDriverInfo->InfPathName))) {
|
|
//
|
|
// The path that was passed in was too large to fit in our buffer
|
|
//
|
|
SetLastError(ERROR_DI_BAD_PATH);
|
|
return FALSE;
|
|
}
|
|
|
|
SearchOptions = SEARCH_SINGLEINF;
|
|
}
|
|
|
|
//
|
|
// If this is not a Force install we want to compare the driver against
|
|
// the currently installed driver. Note that we will only install the
|
|
// device if it was found in the specified directory.
|
|
//
|
|
if (!(NewDevWiz->Flags & IDI_FLAG_FORCE)) {
|
|
|
|
SearchOptions |= SEARCH_CURRENTDRIVER;
|
|
}
|
|
|
|
//
|
|
// The silent update device code path always has the DI_QUIETINSTALL flag
|
|
// set.
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
)) {
|
|
|
|
DeviceInstallParams.Flags |= DI_QUIETINSTALL;
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
//
|
|
// Search the specified directory or INF for drivers
|
|
//
|
|
DoDriverSearch(hwndParent,
|
|
NewDevWiz,
|
|
SearchOptions,
|
|
(NewDevWiz->Flags & IDI_FLAG_ROLLBACK) ?
|
|
SPDIT_CLASSDRIVER : SPDIT_COMPATDRIVER,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// At this point we should already have the best driver selected, but if this is
|
|
// a driver rollback we want to select the driver node ourselves using the
|
|
// DevDesc, ProviderName, and Mfg of the original driver installed on this device.
|
|
// We need to do this because the driver that we rolled back might not be the best
|
|
// driver node in this INF.
|
|
//
|
|
if ((NewDevWiz->Flags & IDI_FLAG_ROLLBACK) &&
|
|
(NewDevWiz->UpdateDriverInfo->Description[0] != TEXT('\0')) &&
|
|
(NewDevWiz->UpdateDriverInfo->MfgName[0] != TEXT('\0')) &&
|
|
(NewDevWiz->UpdateDriverInfo->ProviderName[0] != TEXT('\0'))) {
|
|
|
|
ZeroMemory(&DriverInfoData, sizeof(DriverInfoData));
|
|
|
|
DriverInfoData.cbSize = sizeof(DriverInfoData);
|
|
DriverInfoData.DriverType = SPDIT_CLASSDRIVER;
|
|
DriverInfoData.Reserved = 0;
|
|
|
|
if (FAILED(StringCchCopy(DriverInfoData.Description,
|
|
SIZECHARS(DriverInfoData.Description),
|
|
NewDevWiz->UpdateDriverInfo->Description)) ||
|
|
FAILED(StringCchCopy(DriverInfoData.MfgName,
|
|
SIZECHARS(DriverInfoData.MfgName),
|
|
NewDevWiz->UpdateDriverInfo->MfgName)) ||
|
|
FAILED(StringCchCopy(DriverInfoData.ProviderName,
|
|
SIZECHARS(DriverInfoData.ProviderName),
|
|
NewDevWiz->UpdateDriverInfo->ProviderName))) {
|
|
//
|
|
// If we somehow had strings in the registry that were too large for
|
|
// setupapi's buffers, then just bail out now.
|
|
//
|
|
NewDevWiz->LastError = ERROR_NO_MORE_ITEMS;
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
goto clean0;
|
|
}
|
|
|
|
SetupDiSetSelectedDriver(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DriverInfoData
|
|
);
|
|
}
|
|
|
|
//
|
|
// Since we have UpdateDriverInfo and the caller specified a specfic InfPathName (whether
|
|
// a full path to an INF or just the path where INFs live) then we want to verify
|
|
// that the selected driver's INF lives in that specified path. If it does not then
|
|
// do not automatically install it since that is not what the caller intended.
|
|
//
|
|
if (pVerifyUpdateDriverInfoPath(NewDevWiz)) {
|
|
|
|
NewDevWiz->SilentMode = TRUE;
|
|
|
|
//
|
|
// Install the driver on this device.
|
|
//
|
|
DoDeviceWizard(hwndParent, NewDevWiz, FALSE);
|
|
|
|
//
|
|
// Quietly install any children of this device that are now present after bringing this
|
|
// device online.
|
|
//
|
|
if (!NewDevWiz->UpdateDriverInfo ||
|
|
!NewDevWiz->UpdateDriverInfo->FromInternet) {
|
|
InstallSilentChilds(hwndParent, NewDevWiz);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we get to this point then that means that the best driver we found was
|
|
// not found in the specified directory or INF. In this case we will not
|
|
// install the best driver found and we'll set the appropriate error
|
|
//
|
|
NewDevWiz->LastError = ERROR_NO_MORE_ITEMS;
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
}
|
|
|
|
clean0:
|
|
return (GetLastError() == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL
|
|
InstallDeviceInstance(
|
|
HWND hwndParent,
|
|
HWND hBalloonTiphWnd,
|
|
LPCTSTR DeviceInstanceId,
|
|
PDWORD pReboot,
|
|
PUPDATEDRIVERINFO UpdateDriverInfo,
|
|
DWORD Flags,
|
|
DWORD InstallType,
|
|
HMODULE *hCdmInstance,
|
|
HANDLE *hCdmContext,
|
|
PBOOL pbLogDriverNotFound,
|
|
PBOOL pbSetRestorePoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the main function where most of the exported functions to install drivers end up
|
|
after they do some preprocessing. This function will install or update the device
|
|
depending on the parameters.
|
|
|
|
|
|
Arguments:
|
|
|
|
hwndParent - Window handle of the top-level window to use for any UI related
|
|
to installing the device.
|
|
|
|
hBalloonTiphWnd - Handle to the WNDPROC that does all of the new Balloon tip UI. This
|
|
is currently only used in the NDWTYPE_FOUNDNEW case.
|
|
|
|
DeviceInstanceId - Supplies the ID of the device instance. This is the registry
|
|
path (relative to the Enum branch) of the device instance key.
|
|
|
|
pReboot - Optional address of variable to receive reboot flags (DI_NEEDRESTART,DI_NEEDREBOOT)
|
|
|
|
UpdateDriverInfo -
|
|
|
|
Flags -
|
|
IDI_FLAG_SILENTINSTALL - means the balloon tooltip will not be displayed
|
|
IDI_FLAG_SECONDNEWDEVINSTANCE - means this is the second instance of newdev.dll
|
|
that is running and the UI data should be sent
|
|
over to the first instance of newdev.dll that
|
|
is running.
|
|
IDI_FLAG_NOBACKUP - Don't backup the old drivers.
|
|
IDI_FLAG_READONLY_INSTALL - means the install is readonly (no file copy)
|
|
IDI_FLAG_NONINTERACTIVE - Any UI will cause the API to fail.
|
|
IDI_FLAG_ROLLBACK - set if we are doing a rollback
|
|
IDI_FLAG_FORCE - set if we are to force install this driver, which means
|
|
install it even if it is not better then the currently
|
|
installed driver.
|
|
IDI_FLAG_MANUALINSTALL - set if this is a manuall installed device.
|
|
IDI_FLAG_SETRESTOREPOINT - set if we are to set a restore point if the
|
|
drivers that are getting installed are not
|
|
digitally signed. Currently we only set
|
|
restore points if the INF, catalog, or one
|
|
of the copied files is not signed.
|
|
|
|
InstallType - There are currently three different install types.
|
|
NDWTYPE_FOUNDNEW - used to install drivers on a brand new device.
|
|
NDWTYPE_UPDATE - used to bring up the Update Driver Wizard.
|
|
NDWTYPE_UPDATE_SILENT - used to silently update the drivers for a device. The Update Driver Wizard
|
|
won't be dispalyed in this case.
|
|
|
|
hCdmInstance - A pointer to a hmodule that will receive the handle of the CDM
|
|
library when when and if we need to load it.
|
|
|
|
hCdmContext - A pointer to a Cdm context handle that will receive the Cdm
|
|
context handle if it is opened.
|
|
|
|
pbLogDriverNotFound - pointer to a BOOL that receives information on whether or not we
|
|
logged to Cdm.dll that we could not find a driver for this device.
|
|
|
|
pbSetRestorePoint - pointer to a BOOL that is set to TRUE if we needed to set
|
|
a system restore point because the drivers we were
|
|
installing were not digitally signed. It is assumed that
|
|
if the caller wants to know if we called SRSetRestorePoint
|
|
then it is their responsiblity to call it again with
|
|
END_NESTED_SYSTEM_CHANGE to end the restore point. If
|
|
the caller does not want this responsibility then they
|
|
should pass in NULL for this value and this function
|
|
will handle calling SRSetRestorePoint with
|
|
END_NESTED_SYSTEM_CHANGE.
|
|
|
|
|
|
Return Value:
|
|
|
|
BOOL TRUE for success (does not mean device was installed or updated),
|
|
FALSE unexpected error. GetLastError returns the winerror code.
|
|
|
|
--*/
|
|
{
|
|
ULONG ConfigFlag;
|
|
NEWDEVWIZ NewDevWiz;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
CLOSE_CDM_CONTEXT_PROC pfnCloseCDMContext;
|
|
|
|
if (pbSetRestorePoint) {
|
|
*pbSetRestorePoint = FALSE;
|
|
}
|
|
|
|
//
|
|
// ensure we have a device instance.
|
|
//
|
|
if (!DeviceInstanceId || !*DeviceInstanceId) {
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the InstallType is NDWTYPE_UPDATE_SILENT then they must pass in
|
|
// an UpdateDriverInfo structure
|
|
//
|
|
if ((InstallType == NDWTYPE_UPDATE_SILENT) && !UpdateDriverInfo) {
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
memset(&NewDevWiz, 0, sizeof(NewDevWiz));
|
|
|
|
NewDevWiz.InstallType = InstallType;
|
|
NewDevWiz.SilentMode = FALSE;
|
|
NewDevWiz.UpdateDriverInfo = UpdateDriverInfo;
|
|
|
|
NewDevWiz.hDeviceInfo = SetupDiCreateDeviceInfoList(NULL, hwndParent);
|
|
|
|
if (NewDevWiz.hDeviceInfo == INVALID_HANDLE_VALUE) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NewDevWiz.LastError = ERROR_SUCCESS;
|
|
|
|
NewDevWiz.Flags = Flags;
|
|
|
|
try {
|
|
//
|
|
// Set the PSPGF_NONINTERACTIVE SetupGlobalFlag if we are in NonInteractive
|
|
// mode. This means that setupapi will fail if it needs to display any UI
|
|
// at all.
|
|
//
|
|
if (Flags & IDI_FLAG_NONINTERACTIVE) {
|
|
|
|
pSetupSetGlobalFlags(pSetupGetGlobalFlags() | PSPGF_NONINTERACTIVE);
|
|
}
|
|
|
|
NewDevWiz.DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
if (!SetupDiOpenDeviceInfo(NewDevWiz.hDeviceInfo,
|
|
DeviceInstanceId,
|
|
hwndParent,
|
|
0,
|
|
&NewDevWiz.DeviceInfoData
|
|
))
|
|
{
|
|
NewDevWiz.LastError = GetLastError();
|
|
goto IDIExit;
|
|
}
|
|
|
|
if (FAILED(StringCchCopy(NewDevWiz.InstallDeviceInstanceId,
|
|
SIZECHARS(NewDevWiz.InstallDeviceInstanceId),
|
|
DeviceInstanceId))) {
|
|
|
|
NewDevWiz.LastError = ERROR_INVALID_DEVINST_NAME;
|
|
goto IDIExit;
|
|
}
|
|
|
|
SetupDiSetSelectedDevice(NewDevWiz.hDeviceInfo, &NewDevWiz.DeviceInfoData);
|
|
|
|
|
|
//
|
|
// Get the ConfigFlags
|
|
//
|
|
if (SetupDiGetDeviceRegistryProperty(NewDevWiz.hDeviceInfo,
|
|
&NewDevWiz.DeviceInfoData,
|
|
SPDRP_CONFIGFLAGS,
|
|
NULL,
|
|
(PVOID)&ConfigFlag,
|
|
sizeof(ConfigFlag),
|
|
NULL) &&
|
|
(ConfigFlag & CONFIGFLAG_MANUAL_INSTALL)) {
|
|
|
|
NewDevWiz.Flags |= IDI_FLAG_MANUALINSTALL;
|
|
}
|
|
|
|
//
|
|
// Get the device capabilities
|
|
//
|
|
if (!SetupDiGetDeviceRegistryProperty(NewDevWiz.hDeviceInfo,
|
|
&NewDevWiz.DeviceInfoData,
|
|
SPDRP_CAPABILITIES,
|
|
NULL,
|
|
(PVOID)&NewDevWiz.Capabilities,
|
|
sizeof(NewDevWiz.Capabilities),
|
|
NULL)) {
|
|
|
|
NewDevWiz.Capabilities = 0;
|
|
}
|
|
|
|
//
|
|
// initialize DeviceInstallParams
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz.hDeviceInfo,
|
|
&NewDevWiz.DeviceInfoData,
|
|
&DeviceInstallParams
|
|
))
|
|
{
|
|
|
|
DeviceInstallParams.Flags |= DI_SHOWOEM;
|
|
DeviceInstallParams.hwndParent = hwndParent;
|
|
|
|
//
|
|
// If not manually installed, allow excluded drivers.
|
|
//
|
|
if (!(NewDevWiz.Flags & IDI_FLAG_MANUALINSTALL)) {
|
|
|
|
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS;
|
|
}
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz.hDeviceInfo,
|
|
&NewDevWiz.DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
else {
|
|
|
|
NewDevWiz.LastError = GetLastError();
|
|
goto IDIExit;
|
|
}
|
|
|
|
//
|
|
// Set the ClassGuidSelected and ClassName field of NewDevWiz so we can have
|
|
// the correct icon and class name for the device.
|
|
//
|
|
if (!IsEqualGUID(&NewDevWiz.DeviceInfoData.ClassGuid, &GUID_NULL)) {
|
|
|
|
NewDevWiz.ClassGuidSelected = &NewDevWiz.DeviceInfoData.ClassGuid;
|
|
|
|
if (!SetupDiClassNameFromGuid(NewDevWiz.ClassGuidSelected,
|
|
NewDevWiz.ClassName,
|
|
sizeof(NewDevWiz.ClassName),
|
|
NULL
|
|
)) {
|
|
|
|
NewDevWiz.ClassGuidSelected = NULL;
|
|
*(NewDevWiz.ClassName) = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the CancelEvent in case the user wants to cancel out of the driver search
|
|
//
|
|
NewDevWiz.CancelEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
//
|
|
// At this point we have three different cases.
|
|
//
|
|
// 1) Normal Update Driver case - In this case we jump directly to the Update Driver wizard code
|
|
// 2) Silent Update Driver case - This case is treated very similar to the new hardware case. We
|
|
// will silently search for an updated driver in the given path or INF and if we find a (better)
|
|
// driver then we will install it.
|
|
// 3) Normal New Hardware case - In this case we will do an initial search for drivers with the only
|
|
// UI being the balloon tip on the tray. If we can't find a driver for this device then we will
|
|
// Jump into the wizard case.
|
|
//
|
|
|
|
//
|
|
// For UPDATE, search all drivers, including old internet drivers
|
|
//
|
|
switch (NewDevWiz.InstallType) {
|
|
|
|
case NDWTYPE_FOUNDNEW:
|
|
pInstallDeviceInstanceNewDevice(hwndParent, hBalloonTiphWnd, &NewDevWiz);
|
|
break;
|
|
|
|
case NDWTYPE_UPDATE:
|
|
pInstallDeviceInstanceUpdateDevice(hwndParent, &NewDevWiz);
|
|
break;
|
|
|
|
case NDWTYPE_UPDATE_SILENT:
|
|
pInstallDeviceInstanceUpdateDeviceSilent(hwndParent, &NewDevWiz);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
if (NewDevWiz.CancelEvent) {
|
|
|
|
CloseHandle(NewDevWiz.CancelEvent);
|
|
}
|
|
|
|
//
|
|
// Launch Help Center if we could not find a driver for this device.
|
|
//
|
|
if (NewDevWiz.LogDriverNotFound) {
|
|
|
|
OpenCdmContextIfNeeded(&NewDevWiz.hCdmInstance,
|
|
&NewDevWiz.hCdmContext
|
|
);
|
|
|
|
CdmLogDriverNotFound(NewDevWiz.hCdmInstance,
|
|
NewDevWiz.hCdmContext,
|
|
DeviceInstanceId,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Let the caller know that we logged to cdm.dll that a driver
|
|
// was not found.
|
|
//
|
|
if (pbLogDriverNotFound) {
|
|
*pbLogDriverNotFound = TRUE;
|
|
} else {
|
|
//
|
|
// If the caller did not want to know if we logged a 'not found'
|
|
// driver to cdm.dll then at this point we need to tell Cdm
|
|
// to call help center with it's list of 'not found' drivers.
|
|
//
|
|
CdmLogDriverNotFound(NewDevWiz.hCdmInstance,
|
|
NewDevWiz.hCdmContext,
|
|
NULL,
|
|
0x00000002
|
|
);
|
|
}
|
|
|
|
} else if (pbLogDriverNotFound) {
|
|
*pbLogDriverNotFound = FALSE;
|
|
}
|
|
|
|
//
|
|
// Let the caller know whether we had to set a system restore point or
|
|
// not, if they want to know.
|
|
//
|
|
if (pbSetRestorePoint) {
|
|
*pbSetRestorePoint = NewDevWiz.SetRestorePoint;
|
|
} else if (NewDevWiz.SetRestorePoint) {
|
|
//
|
|
// If the caller did not want to know if we set a restore point and
|
|
// we did set a restore point, then we need to END the restore point
|
|
// by calling SRSetRestorePoint with END_NESTED_SYSTEM_CHANGE.
|
|
//
|
|
pSetSystemRestorePoint(FALSE, FALSE, 0);
|
|
}
|
|
|
|
//
|
|
// copy out the reboot flags for the caller
|
|
// or put up the restart dialog if caller didn't ask for the reboot flag
|
|
//
|
|
if (pReboot) {
|
|
|
|
*pReboot = NewDevWiz.Reboot;
|
|
}
|
|
|
|
else if (NewDevWiz.Reboot) {
|
|
|
|
RestartDialogEx(hwndParent, NULL, EWX_REBOOT, REASON_PLANNED_FLAG | REASON_HWINSTALL);
|
|
}
|
|
|
|
IDIExit:
|
|
;
|
|
|
|
} except(NdwUnhandledExceptionFilter(GetExceptionInformation())) {
|
|
NewDevWiz.LastError = RtlNtStatusToDosError(GetExceptionCode());
|
|
}
|
|
|
|
if (NewDevWiz.hDeviceInfo &&
|
|
(NewDevWiz.hDeviceInfo != INVALID_HANDLE_VALUE)) {
|
|
|
|
SetupDiDestroyDeviceInfoList(NewDevWiz.hDeviceInfo);
|
|
NewDevWiz.hDeviceInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// If the caller wants the CdmInstance hmodule and context handle then pass
|
|
// them back, otherwise close them.
|
|
//
|
|
if (hCdmContext) {
|
|
*hCdmContext = NewDevWiz.hCdmContext;
|
|
} else {
|
|
|
|
//
|
|
// The caller doesn't want the cdm context so close it if we have loaded
|
|
// cdm.dll and opened a context.
|
|
//
|
|
if (NewDevWiz.hCdmInstance && NewDevWiz.hCdmContext) {
|
|
|
|
pfnCloseCDMContext = (CLOSE_CDM_CONTEXT_PROC)GetProcAddress(NewDevWiz.hCdmInstance,
|
|
"CloseCDMContext"
|
|
);
|
|
if (pfnCloseCDMContext) {
|
|
pfnCloseCDMContext(NewDevWiz.hCdmContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hCdmInstance) {
|
|
*hCdmInstance = NewDevWiz.hCdmInstance;
|
|
} else {
|
|
FreeLibrary(NewDevWiz.hCdmInstance);
|
|
}
|
|
|
|
//
|
|
// Clear the PSPGF_NONINTERACTIVE SetupGlobalFlag
|
|
//
|
|
if (Flags & IDI_FLAG_NONINTERACTIVE) {
|
|
|
|
pSetupSetGlobalFlags(pSetupGetGlobalFlags() &~ PSPGF_NONINTERACTIVE);
|
|
}
|
|
|
|
SetLastError(NewDevWiz.LastError);
|
|
|
|
return NewDevWiz.LastError == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL
|
|
InstallDevInstEx(
|
|
HWND hwndParent,
|
|
LPCWSTR DeviceInstanceId,
|
|
BOOL UpdateDriver,
|
|
PDWORD pReboot,
|
|
BOOL SilentInstall
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exported Entry point from newdev.dll. Installs an existing Device Instance,
|
|
and is invoked by Device Mgr to update a driver, or by Config mgr when a new
|
|
device was found. In both cases the Device Instance exists in the registry.
|
|
|
|
|
|
Arguments:
|
|
|
|
hwndParent - Window handle of the top-level window to use for any UI related
|
|
to installing the device.
|
|
|
|
DeviceInstanceId - Supplies the ID of the device instance. This is the registry
|
|
path (relative to the Enum branch) of the device instance key.
|
|
|
|
UpdateDriver - TRUE only newer or higher rank drivers are installed.
|
|
|
|
pReboot - Optional address of variable to receive reboot flags (DI_NEEDRESTART,DI_NEEDREBOOT)
|
|
|
|
SilentInstall - TRUE means the "New Hardware Found" dialog will not be displayed
|
|
|
|
|
|
Return Value:
|
|
|
|
BOOL TRUE for success (does not mean device was installed or updated),
|
|
FALSE unexpected error. GetLastError returns the winerror code.
|
|
|
|
--*/
|
|
{
|
|
DWORD InstallType = UpdateDriver ? NDWTYPE_UPDATE : NDWTYPE_FOUNDNEW;
|
|
|
|
//
|
|
// If someone calls the 32-bit newdev.dll on a 64-bit OS then we need
|
|
// to fail and set the last error to ERROR_IN_WOW64.
|
|
//
|
|
if (GetIsWow64()) {
|
|
SetLastError(ERROR_IN_WOW64);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Verify that the process has sufficient Administrator privileges.
|
|
//
|
|
if (!pSetupIsUserAdmin()) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if ((DeviceInstanceId == NULL) ||
|
|
(DeviceInstanceId[0] == TEXT('\0'))) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
return InstallDeviceInstance(hwndParent,
|
|
NULL,
|
|
DeviceInstanceId,
|
|
pReboot,
|
|
NULL,
|
|
IDI_FLAG_SETRESTOREPOINT |
|
|
(SilentInstall ? IDI_FLAG_SILENTINSTALL : 0),
|
|
InstallType,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
BOOL
|
|
InstallDevInst(
|
|
HWND hwndParent,
|
|
LPCWSTR DeviceInstanceId,
|
|
BOOL UpdateDriver,
|
|
PDWORD pReboot
|
|
)
|
|
{
|
|
return InstallDevInstEx(hwndParent,
|
|
DeviceInstanceId,
|
|
UpdateDriver,
|
|
pReboot,
|
|
FALSE);
|
|
}
|
|
|
|
BOOL
|
|
EnumAndUpgradeDevices(
|
|
HWND hwndParent,
|
|
LPCWSTR HardwareId,
|
|
PUPDATEDRIVERINFO UpdateDriverInfo,
|
|
DWORD Flags,
|
|
PDWORD pReboot
|
|
)
|
|
{
|
|
HDEVINFO hDevInfo;
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
DWORD Index;
|
|
DWORD Size;
|
|
TCHAR DeviceIdList[REGSTR_VAL_MAX_HCID_LEN];
|
|
LPWSTR SingleDeviceId;
|
|
TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
|
|
BOOL Match;
|
|
BOOL Result = TRUE;
|
|
BOOL NoSuchDevNode = TRUE;
|
|
ULONG InstallFlags = Flags;
|
|
DWORD SingleNeedsReboot;
|
|
DWORD TotalNeedsReboot = 0;
|
|
DWORD Err = ERROR_SUCCESS;
|
|
int i, count;
|
|
HKEY hKey;
|
|
BOOL bSingleDeviceSetRestorePoint = FALSE;
|
|
BOOL bSetRestorePoint = FALSE;
|
|
|
|
count = 0;
|
|
|
|
if (pReboot) {
|
|
*pReboot = 0;
|
|
}
|
|
|
|
hDevInfo = SetupDiGetClassDevs(NULL,
|
|
NULL,
|
|
hwndParent,
|
|
DIGCF_ALLCLASSES | DIGCF_PRESENT
|
|
);
|
|
|
|
if (INVALID_HANDLE_VALUE == hDevInfo) {
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(&DeviceInfoData, sizeof(DeviceInfoData));
|
|
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
Index = 0;
|
|
|
|
//
|
|
// Enumerate through all of the devices until we hit an installation error
|
|
// or we run out of devices
|
|
//
|
|
while (Result &&
|
|
SetupDiEnumDeviceInfo(hDevInfo,
|
|
Index++,
|
|
&DeviceInfoData
|
|
)) {
|
|
Match = FALSE;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
ZeroMemory(DeviceIdList, sizeof(DeviceIdList));
|
|
Size = sizeof(DeviceIdList);
|
|
if (SetupDiGetDeviceRegistryProperty(hDevInfo,
|
|
&DeviceInfoData,
|
|
(i ? SPDRP_HARDWAREID : SPDRP_COMPATIBLEIDS),
|
|
NULL,
|
|
(PBYTE)DeviceIdList,
|
|
Size,
|
|
&Size
|
|
)) {
|
|
|
|
//
|
|
// If any of the devices Hardware Ids or Compatible Ids match the given ID then
|
|
// we have a match and need to upgrade the drivers on this device.
|
|
//
|
|
for (SingleDeviceId = DeviceIdList;
|
|
*SingleDeviceId;
|
|
SingleDeviceId += lstrlen(SingleDeviceId) + 1) {
|
|
|
|
if (_wcsicmp(SingleDeviceId, HardwareId) == 0) {
|
|
|
|
Match = TRUE;
|
|
NoSuchDevNode = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have a match then install the drivers on this device instance
|
|
//
|
|
if (Match) {
|
|
|
|
if (SetupDiGetDeviceInstanceId(hDevInfo,
|
|
&DeviceInfoData,
|
|
DeviceInstanceId,
|
|
SIZECHARS(DeviceInstanceId),
|
|
&Size
|
|
)) {
|
|
|
|
SingleNeedsReboot = 0;
|
|
|
|
//
|
|
// Since this API is used only by InstallWindowsUpdateDriver and
|
|
// UpdateDriverForPlugAndPlayDevice then specifiy the NDWTYPE_UPDATE_SILENT
|
|
// Flag. This will tell the device install code not to show the Found New Hardware
|
|
// balloon tip in the tray and not to bring up the Update Driver Wizard if it can't
|
|
// find a driver in the specified location.
|
|
//
|
|
Result = InstallDeviceInstance(hwndParent,
|
|
NULL,
|
|
DeviceInstanceId,
|
|
&SingleNeedsReboot,
|
|
UpdateDriverInfo,
|
|
InstallFlags,
|
|
NDWTYPE_UPDATE_SILENT,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&bSingleDeviceSetRestorePoint
|
|
);
|
|
|
|
//
|
|
// Save the last error code from the install. Since we will be
|
|
// doing multiple installs we want to save any error codes that
|
|
// we get so we will only reset the last error code if our saved
|
|
// error is ERROR_SUCCESS.
|
|
//
|
|
if (!Result && (Err == ERROR_SUCCESS)) {
|
|
Err = GetLastError();
|
|
}
|
|
|
|
count++;
|
|
|
|
TotalNeedsReboot |= SingleNeedsReboot;
|
|
|
|
//
|
|
// We only want to backup the first device we install...not every one.
|
|
//
|
|
InstallFlags |= IDI_FLAG_NOBACKUP;
|
|
|
|
//
|
|
// If we just set a restore point when installing the last device
|
|
// then clear the IDI_FLAG_SETRESTOREPOINT flag so we don't do
|
|
// it for any of the other devices we install.
|
|
//
|
|
if (bSingleDeviceSetRestorePoint) {
|
|
bSetRestorePoint = TRUE;
|
|
InstallFlags &= ~IDI_FLAG_SETRESTOREPOINT;
|
|
}
|
|
|
|
//
|
|
// If we performed a backup and this is not the first device, then we need to add
|
|
// this devices DeviceInstanceId to the backup key.
|
|
//
|
|
if ((count > 1) &&
|
|
(UpdateDriverInfo->BackupRegistryKey[0] != TEXT('\0'))) {
|
|
|
|
DWORD cbData, cbTotalSize;
|
|
PTSTR DeviceIdsBuffer;
|
|
PTSTR p;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
UpdateDriverInfo->BackupRegistryKey,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKey) == ERROR_SUCCESS) {
|
|
|
|
|
|
//
|
|
// Lets see how big the DeviceInstanceIds buffer is so we can allocate enough
|
|
// memory.
|
|
//
|
|
cbData = 0;
|
|
if ((RegQueryValueEx(hKey,
|
|
REGSTR_VAL_REINSTALL_DEVICEINSTANCEIDS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&cbData
|
|
) == ERROR_SUCCESS) &&
|
|
(cbData)) {
|
|
|
|
//
|
|
// Allocate a buffer large enough to hold the current list of DeviceInstanceIds,
|
|
// as well as the current DeviceInstanceId.
|
|
//
|
|
cbTotalSize = cbData + ((lstrlen(DeviceInstanceId) + 1) * sizeof(TCHAR));
|
|
DeviceIdsBuffer = malloc(cbTotalSize);
|
|
|
|
if (DeviceIdsBuffer) {
|
|
|
|
ZeroMemory(DeviceIdsBuffer, cbTotalSize);
|
|
|
|
if (RegQueryValueEx(hKey,
|
|
REGSTR_VAL_REINSTALL_DEVICEINSTANCEIDS,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)DeviceIdsBuffer,
|
|
&cbData) == ERROR_SUCCESS) {
|
|
|
|
for (p = DeviceIdsBuffer; *p; p+= (lstrlen(p) + 1)) {
|
|
;
|
|
}
|
|
|
|
//
|
|
// p now points to the second terminating NULL character at the end of
|
|
// the MULTI_SZ buffer. This is where we'll put the new DeviceInstanceId.
|
|
//
|
|
lstrcpyn(p, DeviceInstanceId, lstrlen(DeviceInstanceId));
|
|
|
|
//
|
|
// Write the new string back into the registry.
|
|
//
|
|
RegSetValueEx(hKey,
|
|
REGSTR_VAL_REINSTALL_DEVICEINSTANCEIDS,
|
|
0,
|
|
REG_MULTI_SZ,
|
|
(LPBYTE)DeviceIdsBuffer,
|
|
cbTotalSize
|
|
);
|
|
}
|
|
|
|
free(DeviceIdsBuffer);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
|
|
|
//
|
|
// If we had to set a system restore point because one of the drivers we
|
|
// installed was not digitally signed, then at this point we need to call
|
|
// SRSetRestorePoint with END_NESTED_SYSTEM_CHANGE.
|
|
//
|
|
if (bSetRestorePoint) {
|
|
pSetSystemRestorePoint(FALSE, FALSE, 0);
|
|
}
|
|
|
|
//
|
|
// If the caller wants to handle the reboot themselves then pass the information
|
|
// back to them.
|
|
//
|
|
if (pReboot) {
|
|
*pReboot = TotalNeedsReboot;
|
|
}
|
|
|
|
//
|
|
// The caller did not specify a pointer to a Reboot DWORD so we will handle the
|
|
// rebooting ourselves if necessary
|
|
//
|
|
else {
|
|
if (TotalNeedsReboot & (DI_NEEDRESTART | DI_NEEDREBOOT)) {
|
|
RestartDialogEx(hwndParent, NULL, EWX_REBOOT, REASON_PLANNED_FLAG | REASON_HWINSTALL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If NoSuchDevNode is TRUE then we were unable to match the specified Hardware ID against
|
|
// any of the devices on the system. In this case we will set the last error to
|
|
// ERROR_NO_SUCH_DEVINST.
|
|
//
|
|
if (NoSuchDevNode) {
|
|
Err = ERROR_NO_SUCH_DEVINST;
|
|
}
|
|
|
|
SetLastError(Err);
|
|
|
|
return (Err == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL
|
|
pDoRollbackDriverCleanup(
|
|
LPCSTR RegistryKeyName,
|
|
PDELINFNODE pDelInfNodeHead
|
|
)
|
|
{
|
|
HKEY hKey, hSubKey;
|
|
DWORD Error;
|
|
DWORD cbData;
|
|
TCHAR ReinstallString[MAX_PATH];
|
|
PDELINFNODE pDelInfNodeCur;
|
|
|
|
if ((Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_REINSTALL,
|
|
0,
|
|
DELETE | KEY_READ,
|
|
&hKey)) != ERROR_SUCCESS) {
|
|
|
|
SetLastError(Error);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Open up the subkey so we can get the ReinstallString which will give us the directory
|
|
// that we need to delete.
|
|
//
|
|
if (RegOpenKeyEx(hKey,
|
|
(PTSTR)RegistryKeyName,
|
|
0,
|
|
KEY_READ,
|
|
&hSubKey) == ERROR_SUCCESS) {
|
|
|
|
|
|
cbData = sizeof(ReinstallString);
|
|
if (RegQueryValueEx(hSubKey,
|
|
REGSTR_VAL_REINSTALL_STRING,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)ReinstallString,
|
|
&cbData) == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// We have verified that this directory is a subdirectory of
|
|
// %windir%\system32\ReinstallBackups so let's delete it.
|
|
// Note that the string contains a foo.inf on the end, so strip that
|
|
// off first.
|
|
//
|
|
PTSTR p = _tcsrchr(ReinstallString, TEXT('\\'));
|
|
|
|
if (p) {
|
|
|
|
*p = 0;
|
|
|
|
RemoveCdmDirectory(ReinstallString);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hSubKey);
|
|
}
|
|
|
|
RegDeleteKey(hKey, (PTSTR)RegistryKeyName);
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
//
|
|
// Now attempt to uninstall any 3rd party INFs that were just rolled
|
|
// back over. SetupUninstallOEMInf will fail if another device is still
|
|
// using this INF.
|
|
//
|
|
if (pDelInfNodeHead) {
|
|
|
|
for (pDelInfNodeCur = pDelInfNodeHead;
|
|
pDelInfNodeCur;
|
|
pDelInfNodeCur = pDelInfNodeCur->pNext) {
|
|
|
|
SetupUninstallOEMInf(pDelInfNodeCur->szInf,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
RollbackDriver(
|
|
HWND hwndParent,
|
|
LPCSTR RegistryKeyName,
|
|
DWORD Flags,
|
|
PDWORD pReboot OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exported Entry point from newdev.dll. It is invoked by Windows Update to update a driver.
|
|
This function will scan through all of the devices on the machine and attempt to install
|
|
these drivers on any devices that match the given HardwareId.
|
|
|
|
|
|
Arguments:
|
|
|
|
hwndParent - Window handle of the top-level window to use for any UI related
|
|
to installing the device.
|
|
|
|
RegistryKeyName - This is a subkey of HKLM\Software\Microsoft\Windows\CurrentVersion\Reinstall
|
|
|
|
Flags - The following flags are defined:
|
|
|
|
ROLLBACK_FLAG_FORCE - Force the rollback even if it is not better than the current driver
|
|
ROLLBACK_FLAG_DO_CLEANUP - Do the necessary cleanup if the rollback was successful. This
|
|
includes deleting the registry key as well as deleting the backup directory.
|
|
|
|
pReboot - Optional address of variable to receive reboot flags (DI_NEEDRESTART,DI_NEEDREBOOT)
|
|
|
|
Return Value:
|
|
|
|
BOOL TRUE if driver rollback succeedes.
|
|
FALSE if no rollback occured.
|
|
GetLastError() will return one of the following values:
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
HKEY hKey;
|
|
TCHAR DriverRollbackKey[MAX_DEVICE_ID_LEN];
|
|
TCHAR ReinstallString[MAX_PATH];
|
|
DWORD InstallDeviceFlags;
|
|
DWORD cbData;
|
|
DWORD TotalNeedsReboot = 0, SingleNeedsReboot;
|
|
BOOL Result = FALSE;
|
|
BOOL bSingleDeviceSetRestorePoint = FALSE;
|
|
BOOL bSetRestorePoint = FALSE;
|
|
UPDATEDRIVERINFO UpdateDriverInfo;
|
|
LPTSTR DeviceInstanceIds = NULL;
|
|
LPTSTR p;
|
|
TCHAR CurrentlyInstalledInf[MAX_PATH];
|
|
DWORD cbSize;
|
|
PDELINFNODE pDelInfNodeHead = NULL, pDelInfNodeCur;
|
|
|
|
//
|
|
// If someone calls the 32-bit newdev.dll on a 64-bit OS then we need
|
|
// to fail and set the last error to ERROR_IN_WOW64.
|
|
//
|
|
if (GetIsWow64()) {
|
|
SetLastError(ERROR_IN_WOW64);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Verify that the process has sufficient Administrator privileges.
|
|
//
|
|
if (!pSetupIsUserAdmin()) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!RegistryKeyName || (RegistryKeyName[0] == TEXT('\0'))) {
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Flags &~ ROLLBACK_BITS) {
|
|
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCchCopy(DriverRollbackKey, SIZECHARS(DriverRollbackKey), REGSTR_PATH_REINSTALL)) ||
|
|
FAILED(StringCchCat(DriverRollbackKey, SIZECHARS(DriverRollbackKey), TEXT("\\"))) ||
|
|
FAILED(StringCchCat(DriverRollbackKey, SIZECHARS(DriverRollbackKey), (PTSTR)RegistryKeyName))) {
|
|
//
|
|
// The RegistryKeyName the user passed in is too large to fit in our buffer
|
|
// which means they must have passed in a bad key name.
|
|
//
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
DriverRollbackKey,
|
|
0,
|
|
KEY_READ,
|
|
&hKey)) != ERROR_SUCCESS) {
|
|
//
|
|
// The registry key does not exist, the user probably passed us in a
|
|
// bad RegistryKeyName value.
|
|
//
|
|
SetLastError(Error);
|
|
return FALSE;
|
|
}
|
|
|
|
InstallDeviceFlags = (IDI_FLAG_NOBACKUP | IDI_FLAG_ROLLBACK);
|
|
|
|
if (Flags & ROLLBACK_FLAG_FORCE) {
|
|
|
|
InstallDeviceFlags |= IDI_FLAG_FORCE;
|
|
}
|
|
|
|
//
|
|
// Set the IDI_FLAG_SETRESTOREPOINT so if the driver we are rolling back
|
|
// to is not digitally signed then we will set a system restore point
|
|
// in case the user needs to rollback from the rollback.
|
|
//
|
|
InstallDeviceFlags |= IDI_FLAG_SETRESTOREPOINT;
|
|
|
|
ZeroMemory(&UpdateDriverInfo, sizeof(UpdateDriverInfo));
|
|
|
|
//
|
|
// Assume failure
|
|
//
|
|
UpdateDriverInfo.DriverWasUpgraded = FALSE;
|
|
|
|
//
|
|
// Read the "ReinstallString" string value. This contains the path to rollback the drivers
|
|
// from.
|
|
//
|
|
cbData = sizeof(ReinstallString);
|
|
if ((Error = RegQueryValueEx(hKey,
|
|
REGSTR_VAL_REINSTALL_STRING,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)ReinstallString,
|
|
&cbData)) != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// If we can't read the ReinstallString then we can't rollback any drivers!
|
|
//
|
|
SetLastError(Error);
|
|
goto clean0;
|
|
|
|
} else if (!cbData) {
|
|
|
|
//
|
|
// The ReinstallString value must contain something!
|
|
//
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto clean0;
|
|
}
|
|
|
|
UpdateDriverInfo.InfPathName = ReinstallString;
|
|
UpdateDriverInfo.FromInternet = FALSE;
|
|
|
|
//
|
|
// Get the DevDesc, ProviderName, and Mfg from the Reinstall registry key
|
|
// so we know which specific driver node to reinstall from this INF.
|
|
//
|
|
// We have to have a Description and a MfgName value, but there are rare
|
|
// cases where there might not be a ProviderName value.
|
|
//
|
|
cbData = sizeof(UpdateDriverInfo.Description);
|
|
if ((Error = RegQueryValueEx(hKey,
|
|
REGSTR_VAL_DEVDESC,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)UpdateDriverInfo.Description,
|
|
&cbData
|
|
)) != ERROR_SUCCESS) {
|
|
SetLastError(Error);
|
|
goto clean0;
|
|
}
|
|
|
|
cbData = sizeof(UpdateDriverInfo.MfgName);
|
|
if ((Error = RegQueryValueEx(hKey,
|
|
REGSTR_VAL_MFG,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)UpdateDriverInfo.MfgName,
|
|
&cbData
|
|
)) != ERROR_SUCCESS) {
|
|
SetLastError(Error);
|
|
goto clean0;
|
|
}
|
|
|
|
cbData = sizeof(UpdateDriverInfo.ProviderName);
|
|
Error = RegQueryValueEx(hKey,
|
|
REGSTR_VAL_PROVIDER_NAME,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)UpdateDriverInfo.ProviderName,
|
|
&cbData
|
|
);
|
|
|
|
//
|
|
// For the ProviderName, it is valid if we get back FileNotFound because
|
|
// the value is not present in the registry, however any other error is
|
|
// not acceptable.
|
|
//
|
|
if (Error == ERROR_FILE_NOT_FOUND) {
|
|
UpdateDriverInfo.ProviderName[0] = TEXT('\0');
|
|
} else if (Error != ERROR_SUCCESS) {
|
|
SetLastError(Error);
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// We need to get the DeviceInstanceIds MULTI_SZ string. This will contain a list of
|
|
// DeviceInstanceIds that we need to rollback.
|
|
//
|
|
if ((Error = RegQueryValueEx(hKey,
|
|
REGSTR_VAL_REINSTALL_DEVICEINSTANCEIDS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&cbData)) != ERROR_SUCCESS) {
|
|
|
|
SetLastError(Error);
|
|
goto clean0;
|
|
|
|
} else if (!cbData) {
|
|
|
|
//
|
|
// No DeviceInstanceIds to reinstall
|
|
//
|
|
SetLastError(ERROR_SUCCESS);
|
|
goto clean0;
|
|
}
|
|
|
|
DeviceInstanceIds = malloc(cbData + sizeof(TCHAR));
|
|
|
|
if (!DeviceInstanceIds) {
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto clean0;
|
|
}
|
|
|
|
ZeroMemory(DeviceInstanceIds, cbData + sizeof(TCHAR));
|
|
|
|
if ((Error = RegQueryValueEx(hKey,
|
|
REGSTR_VAL_REINSTALL_DEVICEINSTANCEIDS,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)DeviceInstanceIds,
|
|
&cbData)) != ERROR_SUCCESS) {
|
|
|
|
SetLastError(Error);
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Enumerate through the list of DeviceInstanceIds and call InstallDeviceInstance() on
|
|
// each one.
|
|
//
|
|
for (p = DeviceInstanceIds; *p; p += lstrlen(p) + 1) {
|
|
|
|
SingleNeedsReboot = 0;
|
|
|
|
//
|
|
// We we are going to do the cleanup then we need to remember the INF files
|
|
// that were installed before we do the rollback.
|
|
//
|
|
if (Flags & ROLLBACK_FLAG_DO_CLEANUP) {
|
|
|
|
cbSize = sizeof(CurrentlyInstalledInf);
|
|
|
|
if (GetInstalledInf(0, p, CurrentlyInstalledInf, &cbSize) &&
|
|
IsInfFromOem(CurrentlyInstalledInf)) {
|
|
|
|
//
|
|
// Let's check to see if this Inf is already in our list
|
|
//
|
|
for (pDelInfNodeCur = pDelInfNodeHead;
|
|
pDelInfNodeCur;
|
|
pDelInfNodeCur = pDelInfNodeCur->pNext) {
|
|
|
|
if (!lstrcmpi(pDelInfNodeCur->szInf, CurrentlyInstalledInf)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if pDelInfNodeCur is NULL then that means we walked all the way
|
|
// through the linked list and did not find a match for the
|
|
// CurrentlyInstalledInf...so we will add a node.
|
|
//
|
|
if (!pDelInfNodeCur) {
|
|
|
|
pDelInfNodeCur = malloc(sizeof(DELINFNODE));
|
|
|
|
if (pDelInfNodeCur) {
|
|
|
|
if (SUCCEEDED(StringCchCopy(pDelInfNodeCur->szInf,
|
|
SIZECHARS(pDelInfNodeCur->szInf),
|
|
CurrentlyInstalledInf))) {
|
|
pDelInfNodeCur->pNext = pDelInfNodeHead;
|
|
|
|
pDelInfNodeHead = pDelInfNodeCur;
|
|
} else {
|
|
//
|
|
// This should never happen, but if we can't copy the name of
|
|
// the INF file into our local buffer so we know to delete
|
|
// it later, then free the buffer now since we won't
|
|
// be using it.
|
|
//
|
|
free(pDelInfNodeCur);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Result = InstallDeviceInstance(hwndParent,
|
|
NULL,
|
|
p,
|
|
&SingleNeedsReboot,
|
|
&UpdateDriverInfo,
|
|
InstallDeviceFlags,
|
|
NDWTYPE_UPDATE_SILENT,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&bSingleDeviceSetRestorePoint
|
|
);
|
|
|
|
TotalNeedsReboot |= SingleNeedsReboot;
|
|
|
|
if (bSingleDeviceSetRestorePoint) {
|
|
bSetRestorePoint = TRUE;
|
|
InstallDeviceFlags &= ~IDI_FLAG_SETRESTOREPOINT;
|
|
}
|
|
|
|
//
|
|
// If we hit an installation error, bail out.
|
|
//
|
|
if (!Result) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
clean0:
|
|
RegCloseKey(hKey);
|
|
|
|
if (DeviceInstanceIds) {
|
|
free(DeviceInstanceIds);
|
|
}
|
|
|
|
//
|
|
// If we were successful then lets see if the caller wants us to do the cleanup
|
|
//
|
|
if ((Flags & ROLLBACK_FLAG_DO_CLEANUP) &&
|
|
Result &&
|
|
UpdateDriverInfo.DriverWasUpgraded) {
|
|
|
|
pDoRollbackDriverCleanup(RegistryKeyName, pDelInfNodeHead);
|
|
}
|
|
|
|
//
|
|
// If we had to set a system restore point because one of the drivers we
|
|
// installed was not digitally signed, then at this point we need to call
|
|
// SRSetRestorePoint with END_NESTED_SYSTEM_CHANGE.
|
|
//
|
|
if (bSetRestorePoint) {
|
|
pSetSystemRestorePoint(FALSE, FALSE, 0);
|
|
}
|
|
|
|
//
|
|
// Free up any memory we allocated to store 3rd party Infs that we want to delete.
|
|
//
|
|
while (pDelInfNodeHead) {
|
|
pDelInfNodeCur = pDelInfNodeHead->pNext;
|
|
free(pDelInfNodeHead);
|
|
pDelInfNodeHead = pDelInfNodeCur;
|
|
}
|
|
|
|
//
|
|
// If the caller wants to handle the reboot themselves then pass the information
|
|
// back to them.
|
|
//
|
|
if (pReboot) {
|
|
*pReboot = TotalNeedsReboot;
|
|
} else {
|
|
//
|
|
// The caller did not specify a pointer to a Reboot DWORD so we will handle the
|
|
// rebooting ourselves if necessary
|
|
if (TotalNeedsReboot & (DI_NEEDRESTART | DI_NEEDREBOOT)) {
|
|
RestartDialogEx(hwndParent, NULL, EWX_REBOOT, REASON_PLANNED_FLAG | REASON_HWINSTALL);
|
|
}
|
|
}
|
|
|
|
return UpdateDriverInfo.DriverWasUpgraded;
|
|
}
|
|
|
|
BOOL
|
|
InstallWindowsUpdateDriver(
|
|
HWND hwndParent,
|
|
LPCWSTR HardwareId,
|
|
LPCWSTR InfPathName,
|
|
LPCWSTR Reserved,
|
|
BOOL Force,
|
|
BOOL Backup,
|
|
PDWORD pReboot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exported Entry point from newdev.dll. It is invoked by Windows Update to update a driver.
|
|
This function will scan through all of the devices on the machine and attempt to install
|
|
these drivers on any devices that match the given HardwareId.
|
|
|
|
|
|
Arguments:
|
|
|
|
hwndParent - Window handle of the top-level window to use for any UI related
|
|
to installing the device.
|
|
|
|
HardwareId - Supplies the Hardware ID to match agaist existing devices on the
|
|
system.
|
|
|
|
InfPathName - Inf Pathname and associated driver files.
|
|
|
|
Reserved - ignored, should be NULL.
|
|
|
|
Force - if TRUE this API will only look for infs in the directory specified by InfLocation.
|
|
|
|
Backup - if TRUE this API will backup the existing drivers before installing the drivers
|
|
from Windows Update.
|
|
|
|
pReboot - Optional address of variable to receive reboot flags (DI_NEEDRESTART,DI_NEEDREBOOT)
|
|
|
|
Return Value:
|
|
|
|
BOOL TRUE if a device was upgraded to a CDM driver.
|
|
FALSE if no devices were upgraded to a CDM driver. GetLastError()
|
|
will be ERROR_SUCCESS if nothing went wrong, this driver
|
|
simply wasn't for any devices on the machine or wasn't
|
|
better than the current driver. If GetLastError() returns
|
|
any other error then there was an error during the installation
|
|
of this driver.
|
|
|
|
--*/
|
|
{
|
|
UPDATEDRIVERINFO UpdateDriverInfo;
|
|
DWORD Flags = 0;
|
|
TCHAR FullyQualifiedInfPath[MAX_PATH];
|
|
WIN32_FIND_DATA finddata;
|
|
LPTSTR lpFilePart;
|
|
|
|
UNREFERENCED_PARAMETER(Reserved);
|
|
|
|
//
|
|
// If someone calls the 32-bit newdev.dll on a 64-bit OS then we need
|
|
// to fail and set the last error to ERROR_IN_WOW64.
|
|
//
|
|
if (GetIsWow64()) {
|
|
SetLastError(ERROR_IN_WOW64);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Verify that the process has sufficient Administrator privileges.
|
|
//
|
|
if (!pSetupIsUserAdmin()) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Validate the input paramters
|
|
//
|
|
if ((HardwareId == NULL) ||
|
|
(HardwareId[0] == TEXT('\0'))) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((InfPathName == NULL) ||
|
|
(InfPathName[0] == TEXT('\0'))) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure we get the fully qualified path and not a partial
|
|
// or relative path.
|
|
//
|
|
if (GetFullPathName(InfPathName,
|
|
SIZECHARS(FullyQualifiedInfPath),
|
|
FullyQualifiedInfPath,
|
|
&lpFilePart
|
|
) == 0) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure that the FullyQualifiedInfPath exists and that it is
|
|
// a directory.
|
|
//
|
|
if (!FileExists(FullyQualifiedInfPath, &finddata) ||
|
|
!(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(&UpdateDriverInfo, sizeof(UpdateDriverInfo));
|
|
|
|
UpdateDriverInfo.InfPathName = FullyQualifiedInfPath;
|
|
UpdateDriverInfo.FromInternet = TRUE;
|
|
|
|
if (!Backup) {
|
|
|
|
Flags = IDI_FLAG_NOBACKUP;
|
|
}
|
|
|
|
if (Force) {
|
|
|
|
Flags = IDI_FLAG_FORCE;
|
|
}
|
|
|
|
//
|
|
// Assume that the upgrade will fail
|
|
//
|
|
UpdateDriverInfo.DriverWasUpgraded = FALSE;
|
|
|
|
//
|
|
// Call EnumAndUpgradeDevices which will enumerate through all the devices on the machine
|
|
// and for any that match the given hardware ID it will attempt to upgrade to the specified
|
|
// drivers.
|
|
//
|
|
EnumAndUpgradeDevices(hwndParent, HardwareId, &UpdateDriverInfo, Flags, pReboot);
|
|
|
|
return UpdateDriverInfo.DriverWasUpgraded;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
UpdateDriverForPlugAndPlayDevicesW(
|
|
HWND hwndParent,
|
|
LPCWSTR HardwareId,
|
|
LPCWSTR FullInfPath,
|
|
DWORD InstallFlags,
|
|
PBOOL bRebootRequired OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will scan through all of the devices on the machine and attempt to install
|
|
the drivers in FullInfPath on any devices that match the given HardwareId. The default
|
|
behavior is to only install the specified drivers if the are better then the currently
|
|
installed driver.
|
|
|
|
|
|
Arguments:
|
|
|
|
hwndParent - Window handle of the top-level window to use for any UI related
|
|
to installing the device.
|
|
|
|
HardwareId - Supplies the Hardware ID to match agaist existing devices on the
|
|
system.
|
|
|
|
FullInfPath - Full path to an Inf and associated driver files.
|
|
|
|
InstallFlags - INSTALLFLAG_FORCE - If this flag is specified then newdev will not compare the
|
|
specified INF file with the current driver. The specified INF file and drivers
|
|
will always be installed unless an error occurs.
|
|
- INSTALLFALG_READONLY - if this flag is specified then newdev will attempt
|
|
a read-only install. This means that no file copy will be performed and
|
|
only the registry will be updated. Newdev.dll will do a presence check
|
|
on all of the files to verify that they are present first before
|
|
completing the install. If all of the files are not present then
|
|
ERROR_ACCESS_DENIED is returned.
|
|
- INSTALLFLAG_NONINTERACTIVE - absolutely no UI. If any UI needs to be displayed
|
|
then the API will fail!
|
|
|
|
pReboot - Optional address of BOOL to determine if a reboot is required or not.
|
|
If pReboot is NULL then newdev.dll will prompt for a reboot if one is needed. If
|
|
pReboot is a valid BOOL pointer then the reboot status is passed back to the
|
|
caller and it is the callers responsibility to prompt for a reboot if one is
|
|
needed.
|
|
|
|
Return Value:
|
|
|
|
BOOL TRUE if a device was upgraded to the specified driver.
|
|
FALSE if no devices were upgraded to the specified driver. GetLastError()
|
|
will be ERROR_SUCCESS if nothing went wrong, this driver
|
|
wasn't better than the current driver. If GetLastError() returns
|
|
any other error then there was an error during the installation
|
|
of this driver.
|
|
|
|
--*/
|
|
{
|
|
UPDATEDRIVERINFO UpdateDriverInfo;
|
|
DWORD NeedsReboot = 0;
|
|
TCHAR FullyQualifiedInfPath[MAX_PATH];
|
|
WIN32_FIND_DATA finddata;
|
|
LPTSTR lpFilePart;
|
|
DWORD Flags = 0;
|
|
|
|
//
|
|
// If someone calls the 32-bit newdev.dll on a 64-bit OS then we need
|
|
// to fail and set the last error to ERROR_IN_WOW64.
|
|
//
|
|
if (GetIsWow64()) {
|
|
SetLastError(ERROR_IN_WOW64);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Verify that the process has sufficient Administrator privileges.
|
|
//
|
|
if (!pSetupIsUserAdmin()) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Verify the parameters
|
|
//
|
|
if ((!HardwareId || (HardwareId[0] == TEXT('\0'))) ||
|
|
(!FullInfPath || (FullInfPath[0] == TEXT('\0')))) {
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (InstallFlags &~ INSTALLFLAG_BITS) {
|
|
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure we get the fully qualified path and not a partial path.
|
|
//
|
|
if (GetFullPathName(FullInfPath,
|
|
SIZECHARS(FullyQualifiedInfPath),
|
|
FullyQualifiedInfPath,
|
|
&lpFilePart
|
|
) == 0) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure that the FullyQualifiedInfPath exists and that it is not
|
|
// a directory.
|
|
//
|
|
if (!FileExists(FullyQualifiedInfPath, &finddata) ||
|
|
(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(&UpdateDriverInfo, sizeof(UpdateDriverInfo));
|
|
|
|
UpdateDriverInfo.InfPathName = FullyQualifiedInfPath;
|
|
UpdateDriverInfo.FromInternet = FALSE;
|
|
|
|
//
|
|
// Assume that the upgrade will fail
|
|
//
|
|
UpdateDriverInfo.DriverWasUpgraded = FALSE;
|
|
|
|
//
|
|
// If the INSTALLFLAG_READONLY is set then we will set the internal
|
|
// IDI_FLAG_READONLY_INSTALL flag. The IDI_FLAG_NOBACKUP is also set since
|
|
// we don't want to attempt to backup files when doing a read-only
|
|
// install.
|
|
//
|
|
if (InstallFlags & INSTALLFLAG_READONLY) {
|
|
|
|
Flags |= (IDI_FLAG_READONLY_INSTALL | IDI_FLAG_NOBACKUP);
|
|
}
|
|
|
|
//
|
|
// If the INSTALLFLAG_NONINTERACTIVE flag is set then we will set the
|
|
// PSPGF_NONINTERACTIVE SetupGlobalFlag which tells setupapi to fail
|
|
// if any UI at all needs to be displayed.
|
|
//
|
|
if (InstallFlags & INSTALLFLAG_NONINTERACTIVE) {
|
|
|
|
Flags |= IDI_FLAG_NONINTERACTIVE;
|
|
}
|
|
|
|
//
|
|
// If the INSTALLFLAG_FORCE flag is set then we will set the
|
|
// IDI_FLAGS_FORCE flag which will tell us to not include the
|
|
// currently installed driver in our search for the best driver.
|
|
//
|
|
if (InstallFlags & INSTALLFLAG_FORCE) {
|
|
|
|
Flags |= IDI_FLAG_FORCE;
|
|
}
|
|
|
|
//
|
|
// Call EnumAndUpgradeDevices which will enumerate through all the devices on the machine
|
|
// and for any that match the given hardware ID it will attempt to upgrade to the specified
|
|
// drivers.
|
|
//
|
|
EnumAndUpgradeDevices(hwndParent, HardwareId, &UpdateDriverInfo, Flags, &NeedsReboot);
|
|
|
|
if (bRebootRequired) {
|
|
if (NeedsReboot & (DI_NEEDRESTART | DI_NEEDREBOOT)) {
|
|
*bRebootRequired = TRUE;
|
|
} else {
|
|
*bRebootRequired = FALSE;
|
|
}
|
|
} else {
|
|
if (NeedsReboot & (DI_NEEDRESTART | DI_NEEDREBOOT)) {
|
|
RestartDialogEx(hwndParent, NULL, EWX_REBOOT, REASON_PLANNED_FLAG | REASON_HWINSTALL);
|
|
}
|
|
}
|
|
|
|
return UpdateDriverInfo.DriverWasUpgraded;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
UpdateDriverForPlugAndPlayDevicesA(
|
|
HWND hwndParent,
|
|
LPCSTR HardwareId,
|
|
LPCSTR FullInfPath,
|
|
DWORD InstallFlags,
|
|
PBOOL bRebootRequired OPTIONAL
|
|
)
|
|
{
|
|
WCHAR UnicodeHardwareId[MAX_DEVICE_ID_LEN];
|
|
WCHAR UnicodeFullInfPath[MAX_PATH];
|
|
|
|
//
|
|
// Convert the HardwareId and FullInfPath to UNICODE and call
|
|
// InstallDriverForPlugAndPlayDevicesW
|
|
//
|
|
UnicodeHardwareId[0] = TEXT('\0');
|
|
UnicodeFullInfPath[0] = TEXT('\0');
|
|
MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, UnicodeHardwareId, SIZECHARS(UnicodeHardwareId));
|
|
MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, UnicodeFullInfPath, SIZECHARS(UnicodeFullInfPath));
|
|
|
|
return UpdateDriverForPlugAndPlayDevicesW(hwndParent,
|
|
UnicodeHardwareId,
|
|
UnicodeFullInfPath,
|
|
InstallFlags,
|
|
bRebootRequired
|
|
);
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
DevInstallW(
|
|
HWND hwnd,
|
|
HINSTANCE hInst,
|
|
LPWSTR szCmd,
|
|
int nShow
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by newdev.dll itself when the current user is not an Admin.
|
|
UMPNPMGR.DLL calls NEWDEV.DLL ClientSideInstall to install devices. If the currently logged on
|
|
user does not have Administrator privilleges then NEWDEV.DLL prompts the user for an Administator
|
|
username and password. It then spawns another instance of newdev.dll using the
|
|
CreateProcessWithLogonW() API and calls this entry point. This entry point verifies that the
|
|
process has Administrator privileges and if it does it calls InstallDeviceInstance() to install
|
|
the device.
|
|
|
|
Arguments:
|
|
|
|
hwnd - Handle to the parent window.
|
|
|
|
hInst - This parameter is ignored.
|
|
|
|
szCmd - The command line is the DeviceInstanceId to install.
|
|
|
|
nShow - This parameter is ignored.
|
|
|
|
|
|
Return Value:
|
|
|
|
returns the last error set from InstallDeviceInstance if the API fails or ERROR_SUCCESS
|
|
if it succeedes.
|
|
|
|
--*/
|
|
{
|
|
DWORD RebootNeeded = 0;
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(hInst);
|
|
UNREFERENCED_PARAMETER(nShow);
|
|
|
|
//
|
|
// If someone calls the 32-bit newdev.dll on a 64-bit OS then we need
|
|
// to fail and set the last error to ERROR_IN_WOW64.
|
|
//
|
|
if (GetIsWow64()) {
|
|
ExitProcess(ERROR_IN_WOW64);
|
|
}
|
|
|
|
//
|
|
// First verify that the process has sufficient Administrator privileges.
|
|
//
|
|
if (!pSetupIsUserAdmin()) {
|
|
ExitProcess(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
InstallDeviceInstance(hwnd,
|
|
NULL,
|
|
szCmd,
|
|
&RebootNeeded,
|
|
NULL,
|
|
IDI_FLAG_SECONDNEWDEVINSTANCE | IDI_FLAG_SETRESTOREPOINT,
|
|
NDWTYPE_FOUNDNEW,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
LastError = GetLastError();
|
|
|
|
if ((LastError == ERROR_SUCCESS) &&
|
|
(RebootNeeded & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
|
|
|
|
LastError = ERROR_SUCCESS_REBOOT_REQUIRED;
|
|
}
|
|
|
|
ExitProcess(LastError);
|
|
}
|
|
|
|
|
|
BOOL
|
|
SpecialRawDeviceInstallProblem(
|
|
DEVNODE DevNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
There are certain devices that have the RAW capability flag set that need
|
|
a driver to work. This means the results of the install for these devices
|
|
are special cased so that we display a negative finish balloon message
|
|
instead of a positive one.
|
|
|
|
We can tell these bus types by looking up the bus type GUID flags and
|
|
checking for the BIF_RAWDEVICENEEDSDRIVER flag.
|
|
|
|
Arguments:
|
|
|
|
DevNode
|
|
|
|
Return Value:
|
|
|
|
TRUE if this is one of the special RAW devices and we couldn't find
|
|
a driver to install.
|
|
|
|
--*/
|
|
{
|
|
BOOL bDeviceHasProblem = FALSE;
|
|
DWORD Capabilities = 0;
|
|
DWORD cbData, dwType;
|
|
|
|
cbData = sizeof(Capabilities);
|
|
if ((CM_Get_DevNode_Registry_Property(DevNode,
|
|
CM_DRP_CAPABILITIES,
|
|
&dwType,
|
|
(PVOID)&Capabilities,
|
|
&cbData,
|
|
0) == CR_SUCCESS) &&
|
|
(Capabilities & CM_DEVCAP_RAWDEVICEOK) &&
|
|
(GetBusInformation(DevNode) & BIF_RAWDEVICENEEDSDRIVER) &&
|
|
(IsNullDriverInstalled(DevNode))) {
|
|
//
|
|
// This is a RAW device that has the BIF_RAWDEVICENEEDSDRIVER bus
|
|
// information flag set and it doesn't have any drivers installed on
|
|
// it. This means it has a problem so we can show the correct balloon
|
|
// text.
|
|
//
|
|
bDeviceHasProblem = TRUE;
|
|
}
|
|
|
|
return bDeviceHasProblem;
|
|
}
|
|
|
|
BOOL
|
|
PromptAndRunClientAsAdmin(
|
|
PCTSTR DeviceInstanceId,
|
|
DWORD *RebootRequired
|
|
)
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
CREDUI_INFO ci;
|
|
PTSTR UserName = NULL;
|
|
PTSTR User = NULL;
|
|
PTSTR Domain = NULL;
|
|
PTSTR Password = NULL;
|
|
PTSTR Caption = NULL;
|
|
PTSTR Message = NULL;
|
|
PTSTR Format = NULL;
|
|
DWORD Status = ERROR_SUCCESS;
|
|
BOOL bInstallSuccessful = FALSE;
|
|
BOOL bInstallComplete = FALSE;
|
|
int AlreadyTriedCount = 0;
|
|
PTCHAR FriendlyName;
|
|
TCHAR RunDll32Path[MAX_PATH];
|
|
HRESULT hr;
|
|
|
|
if (RebootRequired) {
|
|
*RebootRequired = 0;
|
|
}
|
|
|
|
//
|
|
// Allocate the memory that we need.
|
|
//
|
|
UserName = LocalAlloc(LPTR, CREDUI_MAX_USERNAME_LENGTH);
|
|
User = LocalAlloc(LPTR, CREDUI_MAX_USERNAME_LENGTH);
|
|
Domain = LocalAlloc(LPTR, CREDUI_MAX_DOMAIN_TARGET_LENGTH);
|
|
Password = LocalAlloc(LPTR, CREDUI_MAX_PASSWORD_LENGTH);
|
|
Caption = LocalAlloc(LPTR, CREDUI_MAX_CAPTION_LENGTH);
|
|
Message = LocalAlloc(LPTR, CREDUI_MAX_MESSAGE_LENGTH);
|
|
Format = LocalAlloc(LPTR, CREDUI_MAX_MESSAGE_LENGTH);
|
|
|
|
if (!UserName || !User || !Domain || !Password || !Caption ||
|
|
!Message || !Format) {
|
|
//
|
|
// Not enough memory to create all of the buffers we need to call
|
|
// CredUIPromptForCredentials, so bail out.
|
|
//
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto clean0;
|
|
}
|
|
|
|
LoadString(hNewDev, IDS_FOUNDNEWHARDWARE, Caption, CREDUI_MAX_CAPTION_LENGTH/sizeof(TCHAR));
|
|
|
|
if (LoadString(hNewDev, IDS_LOGON_TEXT, Format, CREDUI_MAX_MESSAGE_LENGTH/sizeof(TCHAR))) {
|
|
|
|
DEVNODE DevInst = 0;
|
|
|
|
CM_Locate_DevNode(&DevInst, (DEVINSTID)DeviceInstanceId, 0);
|
|
|
|
if ((DevInst != 0) &&
|
|
((FriendlyName = BuildFriendlyName(DevInst, FALSE, NULL)) != NULL)) {
|
|
|
|
hr = StringCchPrintf(Message, CREDUI_MAX_MESSAGE_LENGTH/sizeof(TCHAR), Format, FriendlyName);
|
|
|
|
if (FAILED(hr) && (hr != STRSAFE_E_INSUFFICIENT_BUFFER)) {
|
|
Message[0] = TEXT('\0');
|
|
}
|
|
|
|
} else {
|
|
|
|
hr = StringCchPrintf(Message, CREDUI_MAX_MESSAGE_LENGTH/sizeof(TCHAR), Format, szUnknownDevice);
|
|
|
|
if (FAILED(hr) && (hr != STRSAFE_E_INSUFFICIENT_BUFFER)) {
|
|
Message[0] = TEXT('\0');
|
|
}
|
|
}
|
|
}
|
|
|
|
ZeroMemory(&ci, sizeof(ci));
|
|
|
|
ci.cbSize = sizeof( ci );
|
|
ci.pszCaptionText = Caption;
|
|
ci.pszMessageText = Message;
|
|
|
|
do {
|
|
|
|
//
|
|
// The user has not provided valid Admin credentials and they have not tried to
|
|
// provide them MAX_PASSWORD_TRIES times. So, we need to prompt them to provide
|
|
// valid Admin credentials.
|
|
//
|
|
Status = CredUIPromptForCredentials(&ci,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
UserName,
|
|
CREDUI_MAX_USERNAME_LENGTH/sizeof(TCHAR),
|
|
Password,
|
|
CREDUI_MAX_PASSWORD_LENGTH/sizeof(TCHAR),
|
|
NULL,
|
|
CREDUI_FLAGS_DO_NOT_PERSIST |
|
|
CREDUI_FLAGS_REQUEST_ADMINISTRATOR |
|
|
CREDUI_FLAGS_INCORRECT_PASSWORD |
|
|
CREDUI_FLAGS_GENERIC_CREDENTIALS |
|
|
CREDUI_FLAGS_COMPLETE_USERNAME);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO si;
|
|
TCHAR szCmdLine[MAX_PATH];
|
|
DWORD dwExitCode = ERROR_SUCCESS;
|
|
BOOL bCreateProcessSuccess = FALSE;
|
|
|
|
User[0] = TEXT('\0');
|
|
Domain[0] = TEXT('\0');
|
|
|
|
CredUIParseUserName(UserName,
|
|
User,
|
|
CREDUI_MAX_USERNAME_LENGTH/sizeof(TCHAR),
|
|
Domain,
|
|
CREDUI_MAX_DOMAIN_TARGET_LENGTH/sizeof(TCHAR)
|
|
);
|
|
|
|
//
|
|
// We want to create a separate process using CreateProcessEx
|
|
//
|
|
ZeroMemory(&si, sizeof(si));
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
si.cb = sizeof(si);
|
|
si.wShowWindow = SW_SHOW;
|
|
|
|
//
|
|
// Create a new process with the administrator credentials. The process
|
|
// that will be lanuched is:
|
|
//
|
|
// %windir%\system32\rundll32.exe newdev.dll,DevInstall <device instance Id>
|
|
//
|
|
if (GetSystemDirectory(RunDll32Path, SIZECHARS(RunDll32Path)) &&
|
|
pSetupConcatenatePaths(RunDll32Path, TEXT("RUNDLL32.EXE"), SIZECHARS(RunDll32Path), NULL) &&
|
|
SUCCEEDED(StringCchPrintf(szCmdLine,
|
|
SIZECHARS(szCmdLine),
|
|
TEXT("%s newdev.dll,DevInstall %s"),
|
|
RunDll32Path,
|
|
DeviceInstanceId))) {
|
|
|
|
bCreateProcessSuccess = CreateProcessWithLogonW(User,
|
|
Domain,
|
|
Password,
|
|
0,
|
|
NULL,
|
|
szCmdLine,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi
|
|
);
|
|
}
|
|
|
|
SecureZeroMemory(Password, CREDUI_MAX_PASSWORD_LENGTH);
|
|
|
|
if (bCreateProcessSuccess) {
|
|
//
|
|
// Close the thread handle since all we need is the process handle.
|
|
//
|
|
CloseHandle(pi.hThread);
|
|
|
|
//
|
|
// The process was successfully created so we need to wait for it to finish.
|
|
//
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
|
|
//
|
|
// Check the return value from the process. It should be one of the following
|
|
// return values:
|
|
// ERROR_SUCCESS if the install went successfully.
|
|
// ERROR_SUCCESS_REBOOT_REQUIRED if the install went successfully and a
|
|
// reboot is needed.
|
|
// ERROR _ACCESS_DENIED if the credentials provided were not admin credentials.
|
|
// Other - a error code returned because the install failed for some reason.
|
|
//
|
|
GetExitCodeProcess(pi.hProcess, &dwExitCode);
|
|
|
|
if ((dwExitCode == ERROR_SUCCESS) ||
|
|
(dwExitCode == ERROR_SUCCESS_REBOOT_REQUIRED)) {
|
|
|
|
//
|
|
// Mark this install as complete so we will break out of our loop.
|
|
//
|
|
bInstallComplete = TRUE;
|
|
|
|
bInstallSuccessful = TRUE;
|
|
|
|
//
|
|
// Check if we need to reboot.
|
|
//
|
|
if ((dwExitCode == ERROR_SUCCESS_REBOOT_REQUIRED) &&
|
|
RebootRequired) {
|
|
|
|
*RebootRequired = DI_NEEDREBOOT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the error code is not ERROR_SUCCESS, ERROR_SUCCESS_REBOOT_REQUIRED,
|
|
// or ERROR_ACCESS_DENIED then it means the installed failed for some reason.
|
|
// for this case we will set bInstallComplete so we will break out
|
|
// of the loop since we don't want to attempt another install on this
|
|
// device.
|
|
//
|
|
else if (dwExitCode != ERROR_ACCESS_DENIED) {
|
|
|
|
//
|
|
// Mark this install as complete so we will break out of our loop.
|
|
//
|
|
bInstallComplete = TRUE;
|
|
}
|
|
|
|
else {
|
|
if (dwExitCode == ERROR_CANCELLED) {
|
|
Status = ERROR_CANCELLED;
|
|
}
|
|
|
|
//
|
|
// Some type of failure occured while installing this hardware.
|
|
//
|
|
Err = dwExitCode;
|
|
}
|
|
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
|
|
//
|
|
// If the CreateProcessWithLogonW failed or the exit code for the process
|
|
// was ERROR_ACCESS_DENIED then we need to display the bad credentials
|
|
// message box.
|
|
//
|
|
if (!bCreateProcessSuccess ||
|
|
(dwExitCode == ERROR_ACCESS_DENIED)) {
|
|
|
|
//
|
|
// The process failed, most likely because the user did not provide a username
|
|
// and password. So prompt a dialog and do it again.
|
|
//
|
|
TCHAR szWarningMsg[MAX_PATH];
|
|
TCHAR szWarningCaption[MAX_PATH];
|
|
|
|
if (LoadString(hNewDev,
|
|
IDS_NOTADMIN_ERROR,
|
|
szWarningMsg,
|
|
SIZECHARS(szWarningMsg))
|
|
&&
|
|
LoadString(hNewDev,
|
|
IDS_NEWDEVICENAME,
|
|
szWarningCaption,
|
|
SIZECHARS(szWarningCaption)))
|
|
{
|
|
MessageBox(NULL, szWarningMsg, szWarningCaption, MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
|
|
}
|
|
|
|
//
|
|
// Increment AlreadyTriedCount. If this gets passed a certain threshold
|
|
// then we should bail out.
|
|
//
|
|
AlreadyTriedCount++;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Increment AlreadyTriedCount. If this gets passed a certain threshold
|
|
// then we should bail out.
|
|
//
|
|
AlreadyTriedCount++;
|
|
}
|
|
|
|
SecureZeroMemory(Password, CREDUI_MAX_PASSWORD_LENGTH);
|
|
|
|
//
|
|
// We will keep looping until one of the following things happen:
|
|
// 1) we successfully lauch the second instance of newdev to install the device.
|
|
// 2) the user cancels out of the password prompt dialog
|
|
// 3) the user entered bogus admin credentials more than MAX_PASSWORD_TRIES tims.
|
|
//
|
|
} while ((Status != ERROR_CANCELLED) &&
|
|
!bInstallComplete &&
|
|
(AlreadyTriedCount < MAX_PASSWORD_TRIES));
|
|
|
|
//
|
|
// If the install was not completed then the user either cancelled out of could not provide a
|
|
// valid admin credentials.
|
|
//
|
|
if (!bInstallComplete) {
|
|
NoPrivilegeWarning(NULL);
|
|
}
|
|
|
|
clean0:
|
|
|
|
//
|
|
// Free all of the memory that we allocated.
|
|
//
|
|
if (UserName) {
|
|
LocalFree(UserName);
|
|
}
|
|
|
|
if (User) {
|
|
LocalFree(User);
|
|
}
|
|
|
|
if (Domain) {
|
|
LocalFree(Domain);
|
|
}
|
|
|
|
if (Password) {
|
|
LocalFree(Password);
|
|
}
|
|
|
|
if (Caption) {
|
|
LocalFree(Caption);
|
|
}
|
|
|
|
if (Message) {
|
|
LocalFree(Message);
|
|
}
|
|
|
|
if (Format) {
|
|
LocalFree(Format);
|
|
}
|
|
|
|
SetLastError(Err);
|
|
|
|
return bInstallSuccessful;
|
|
}
|
|
|
|
DWORD
|
|
ClientSideInstallThread(
|
|
HANDLE hPipeRead
|
|
)
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
HMODULE hCdmInstance = NULL;
|
|
HANDLE hCdmContext = NULL;
|
|
BOOL bRunAsAdmin = TRUE;
|
|
HANDLE hDeviceInstallEvent = NULL;
|
|
ULONG InstallFlags = 0;
|
|
DWORD Flags = IDI_FLAG_SETRESTOREPOINT;
|
|
DEVNODE DevNode;
|
|
ULONG Status, Problem;
|
|
ULONG DeviceInstallEventLength = 0, DeviceInstanceIdLength = 0, BytesRead = 0;
|
|
TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
|
|
TCHAR DeviceInstallEventName[MAX_PATH];
|
|
DWORD InstallDeviceCount = 0;
|
|
TCHAR FinishText[MAX_PATH];
|
|
BOOL bTotalLogDriverNotFound = FALSE;
|
|
BOOL bSingleDeviceSetRestorePoint = FALSE;
|
|
BOOL bSetRestorePoint = FALSE;
|
|
CLOSE_CDM_CONTEXT_PROC pfnCloseCDMContext;
|
|
|
|
bQueuedRebootNeeded = FALSE;
|
|
|
|
//
|
|
// The very first thing in the pipe should be the size of the name of the
|
|
// event that we will signal after each device is finished being installed.
|
|
//
|
|
if (ReadFile(hPipeRead,
|
|
(LPVOID)&DeviceInstallEventLength,
|
|
sizeof(ULONG),
|
|
&BytesRead,
|
|
NULL)) {
|
|
|
|
ASSERT(DeviceInstallEventLength != 0);
|
|
if ((DeviceInstallEventLength == 0) ||
|
|
(DeviceInstallEventLength >= SIZECHARS(DeviceInstallEventName))) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// The next thing in the pipe should be the name of the event that we
|
|
// will signal after each device is finished being installed.
|
|
//
|
|
if (!ReadFile(hPipeRead,
|
|
(LPVOID)DeviceInstallEventName,
|
|
DeviceInstallEventLength,
|
|
&BytesRead,
|
|
NULL)) {
|
|
|
|
goto clean0;
|
|
}
|
|
|
|
} else {
|
|
if (GetLastError() == ERROR_INVALID_HANDLE) {
|
|
//
|
|
// The handle to the named pipe is not valid. Make sure we don't
|
|
// try to close it on exit.
|
|
//
|
|
hPipeRead = NULL;
|
|
}
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Open a handle to the specified named event that we can set and wait on.
|
|
//
|
|
hDeviceInstallEvent = OpenEventW(EVENT_MODIFY_STATE | SYNCHRONIZE,
|
|
FALSE,
|
|
DeviceInstallEventName);
|
|
if (!hDeviceInstallEvent) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Continue reading from the pipe until the other end is closed.
|
|
//
|
|
// The first thing in the pipe is a ULONG Flags value that tells us whether
|
|
// this is a full install or UI only.
|
|
//
|
|
while(ReadFile(hPipeRead,
|
|
(LPVOID)&InstallFlags,
|
|
sizeof(DWORD),
|
|
&BytesRead,
|
|
NULL)) {
|
|
|
|
//
|
|
// Check to see if server side install needs a reboot.
|
|
//
|
|
if (InstallFlags & DEVICE_INSTALL_FINISHED_REBOOT) {
|
|
bQueuedRebootNeeded = TRUE;
|
|
}
|
|
|
|
if (InstallFlags & DEVICE_INSTALL_BATCH_COMPLETE) {
|
|
//
|
|
// This is the last message that we should get from umpnpmgr.dll
|
|
// when it has drained it's device install queue. We will
|
|
// display a "Windows finished installing hardware" balloon that
|
|
// will hang around until umpnpmgr.dll closes the named pipe,
|
|
// or sends a new device install message.
|
|
//
|
|
// There are three different balloon messages that we can diaplay.
|
|
// 1) all successful
|
|
// 2) need reboot before hardware will work
|
|
// 3) problem installing one or more devices.
|
|
//
|
|
UINT FinishId;
|
|
|
|
//
|
|
// Check to see if one of the devices that was installed server-side
|
|
// ended up with a problem.
|
|
//
|
|
if (InstallFlags & DEVICE_INSTALL_PROBLEM) {
|
|
Err = ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
if (bQueuedRebootNeeded) {
|
|
FinishId = IDS_FINISH_BALLOON_REBOOT;
|
|
} else if (Err != ERROR_SUCCESS) {
|
|
FinishId = IDS_FINISH_BALLOON_ERROR;
|
|
} else {
|
|
FinishId = IDS_FINISH_BALLOON_SUCCESS;
|
|
}
|
|
|
|
if (!LoadString(hNewDev,
|
|
FinishId,
|
|
FinishText,
|
|
SIZECHARS(FinishText)
|
|
)) {
|
|
FinishText[0] = TEXT('\0');
|
|
}
|
|
|
|
PostMessage(hTrayIconWnd,
|
|
WUM_UPDATEUI,
|
|
(WPARAM)TIP_PLAY_SOUND,
|
|
(LPARAM)FinishText
|
|
);
|
|
|
|
//
|
|
// If we could not find a driver for any of the new devices we just
|
|
// installed then we need to call Cdm.dll one last time telling it
|
|
// that we are done and it should send it's list to helpcenter.exe
|
|
//
|
|
// Note that we do this here as well as at the bottom of the loop
|
|
// since this finish message hangs around for 10 seconds and that
|
|
// is a long time to wait before we launch help center.
|
|
//
|
|
if (bTotalLogDriverNotFound) {
|
|
|
|
bTotalLogDriverNotFound = FALSE;
|
|
|
|
OpenCdmContextIfNeeded(&hCdmInstance,
|
|
&hCdmContext
|
|
);
|
|
|
|
CdmLogDriverNotFound(hCdmInstance,
|
|
hCdmContext,
|
|
NULL,
|
|
0x00000002
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read the DeviceInstanceId from the pipe if the DeviceInstanceIdLength
|
|
// is valid.
|
|
//
|
|
if (ReadFile(hPipeRead,
|
|
(LPVOID)&DeviceInstanceIdLength,
|
|
sizeof(ULONG),
|
|
&BytesRead,
|
|
NULL) &&
|
|
(DeviceInstanceIdLength)) {
|
|
|
|
if (DeviceInstanceIdLength >= SIZECHARS(DeviceInstanceId)) {
|
|
goto clean0;
|
|
}
|
|
|
|
if (!ReadFile(hPipeRead,
|
|
(LPVOID)DeviceInstanceId,
|
|
DeviceInstanceIdLength,
|
|
&BytesRead,
|
|
NULL)) {
|
|
|
|
//
|
|
// If this read fails then just close the UI and close the process.
|
|
//
|
|
goto clean0;
|
|
}
|
|
|
|
if (InstallFlags & DEVICE_INSTALL_UI_ONLY) {
|
|
|
|
//
|
|
// If this is a UI only install then send a WUM_UPDATEUI message to the installer
|
|
// window so that it can update the icon and message in the tray.
|
|
//
|
|
PostMessage(hTrayIconWnd,
|
|
WUM_UPDATEUI,
|
|
(WPARAM)(TIP_LPARAM_IS_DEVICEINSTANCEID |
|
|
((InstallFlags & DEVICE_INSTALL_PLAY_SOUND) ? TIP_PLAY_SOUND : 0)),
|
|
(LPARAM)DeviceInstanceId
|
|
);
|
|
|
|
InstallDeviceCount++;
|
|
|
|
//
|
|
// If we are only installing a small amount of devices (less than 5) then we
|
|
// want to delay between each device so the user has time to read the balloon
|
|
// tip. If we are installing more than 5 devices then we want to skip the
|
|
// delay altogether since we have a lot of devices to install and the user
|
|
// probably wants this done as quickly as possible.
|
|
//
|
|
if (InstallDeviceCount < DEVICE_COUNT_FOR_DELAY) {
|
|
Sleep(DEVICE_COUNT_DELAY);
|
|
}
|
|
|
|
} else {
|
|
|
|
DWORD RebootNeeded = 0;
|
|
BOOL bLogDriverNotFound = FALSE;
|
|
|
|
bSingleDeviceSetRestorePoint= FALSE;
|
|
|
|
//
|
|
// This is a full installation.
|
|
//
|
|
PostMessage(hTrayIconWnd,
|
|
WUM_UPDATEUI,
|
|
(WPARAM)TIP_LPARAM_IS_DEVICEINSTANCEID,
|
|
(LPARAM)DeviceInstanceId
|
|
);
|
|
|
|
if (pSetupIsUserAdmin()) {
|
|
//
|
|
// This user is an Admin so simply install the device.
|
|
//
|
|
InstallDeviceInstance(NULL,
|
|
hTrayIconWnd,
|
|
DeviceInstanceId,
|
|
&RebootNeeded,
|
|
NULL,
|
|
Flags,
|
|
NDWTYPE_FOUNDNEW,
|
|
&hCdmInstance,
|
|
&hCdmContext,
|
|
&bLogDriverNotFound,
|
|
&bSingleDeviceSetRestorePoint
|
|
);
|
|
|
|
} else {
|
|
|
|
if (bRunAsAdmin) {
|
|
bRunAsAdmin = PromptAndRunClientAsAdmin(DeviceInstanceId,
|
|
&RebootNeeded
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remember if there is a problem installing any of the devices.
|
|
//
|
|
if (GetLastError() != ERROR_SUCCESS) {
|
|
Err = GetLastError();
|
|
}
|
|
|
|
if (CM_Locate_DevNode(&DevNode, DeviceInstanceId, 0) == CR_SUCCESS) {
|
|
//
|
|
// If we located the devnode and it has a problem set the Err
|
|
// code so we can tell the user that something failed.
|
|
// If we cannot locate the devnode then the user most likely
|
|
// removed the device during the install process, so don't
|
|
// show this as an error.
|
|
//
|
|
if ((CM_Get_DevNode_Status(&Status, &Problem, DevNode, 0) != CR_SUCCESS) ||
|
|
(Status & DN_HAS_PROBLEM) ||
|
|
SpecialRawDeviceInstallProblem(DevNode)) {
|
|
//
|
|
// Either we couldn't locate the device, or it has some problem,
|
|
// so set Err to ERROR_INSTALL_FAILURE. This error won't be
|
|
// shown, but it will trigger us to put up a different finish
|
|
// message in the balloon.
|
|
//
|
|
Err = ERROR_INSTALL_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (RebootNeeded & (DI_NEEDRESTART | DI_NEEDREBOOT)) {
|
|
bQueuedRebootNeeded = TRUE;
|
|
}
|
|
|
|
if (bLogDriverNotFound) {
|
|
bTotalLogDriverNotFound = TRUE;
|
|
}
|
|
|
|
//
|
|
// We only want to do one system restore point be batch of device
|
|
// installs, so if the last driver that was installed was not
|
|
// digitally signed and we did a system restore point, then
|
|
// clear the IDI_FLAG_SETRESTOREPOINT.
|
|
//
|
|
if (bSingleDeviceSetRestorePoint) {
|
|
bSetRestorePoint = TRUE;
|
|
Flags &= ~IDI_FLAG_SETRESTOREPOINT;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to set the hDeviceInstallEvent event to let umpnpmgr.dll know that we are finished.
|
|
//
|
|
if (hDeviceInstallEvent) {
|
|
SetEvent(hDeviceInstallEvent);
|
|
}
|
|
}
|
|
|
|
clean0:
|
|
|
|
//
|
|
// If we could not find a driver for any of the new devices we just
|
|
// installed then we need to call Cdm.dll one last time telling it
|
|
// that we are done and it should send it's list to helpcenter.exe
|
|
//
|
|
if (bTotalLogDriverNotFound) {
|
|
|
|
OpenCdmContextIfNeeded(&hCdmInstance,
|
|
&hCdmContext
|
|
);
|
|
|
|
CdmLogDriverNotFound(hCdmInstance,
|
|
hCdmContext,
|
|
NULL,
|
|
0x00000002
|
|
);
|
|
}
|
|
|
|
if (hCdmInstance) {
|
|
|
|
if (hCdmContext) {
|
|
pfnCloseCDMContext = (CLOSE_CDM_CONTEXT_PROC)GetProcAddress(hCdmInstance,
|
|
"CloseCDMContext"
|
|
);
|
|
if (pfnCloseCDMContext) {
|
|
pfnCloseCDMContext(hCdmContext);
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hCdmInstance);
|
|
}
|
|
|
|
//
|
|
// If we had to set a system restore point because one of the drivers we
|
|
// installed was not digitally signed, then at this point we need to call
|
|
// SRSetRestorePoint with END_NESTED_SYSTEM_CHANGE.
|
|
//
|
|
if (bSetRestorePoint) {
|
|
pSetSystemRestorePoint(FALSE, FALSE, 0);
|
|
}
|
|
|
|
//
|
|
// Close the event handle
|
|
//
|
|
if (hDeviceInstallEvent) {
|
|
CloseHandle(hDeviceInstallEvent);
|
|
}
|
|
|
|
if (hPipeRead) {
|
|
CloseHandle(hPipeRead);
|
|
}
|
|
|
|
//
|
|
// Tell the UI to go away because we are done
|
|
//
|
|
PostMessage(hTrayIconWnd, WUM_EXIT, 0, 0);
|
|
|
|
return bQueuedRebootNeeded;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
ClientSideInstallW(
|
|
HWND hwnd,
|
|
HINSTANCE hInst,
|
|
LPWSTR szCmd,
|
|
int nShow
|
|
)
|
|
{
|
|
HANDLE hThread;
|
|
DWORD ThreadId;
|
|
HANDLE hPipeRead;
|
|
MSG Msg;
|
|
WNDCLASS wndClass;
|
|
|
|
UNREFERENCED_PARAMETER(hInst);
|
|
UNREFERENCED_PARAMETER(nShow);
|
|
|
|
//
|
|
// If someone calls the 32-bit newdev.dll on a 64-bit OS then we need
|
|
// to fail and set the last error to ERROR_IN_WOW64.
|
|
//
|
|
if (GetIsWow64()) {
|
|
ExitProcess(ERROR_IN_WOW64);
|
|
}
|
|
|
|
//
|
|
// NOTE: This API can be called in the process of a user that does NOT
|
|
// have administrator credentials. If it is then we will later prompt
|
|
// the user for administrator credentials and complet the install
|
|
// if they are provided.
|
|
//
|
|
|
|
//
|
|
// Make sure that a named pipe was specified in the cmd line.
|
|
//
|
|
if(!szCmd || !*szCmd) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Wait 3 minutes for the specified named pipe to become available from
|
|
// the server.
|
|
//
|
|
if (!WaitNamedPipe(szCmd,
|
|
180000)
|
|
) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Open a handle to the specified named pipe
|
|
//
|
|
hPipeRead = CreateFile(szCmd,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE == hPipeRead) {
|
|
//
|
|
// If we can't open the specified global named pipe, there is nothing
|
|
// more we can do.
|
|
//
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Lets see if the class has been registered.
|
|
//
|
|
if (!GetClassInfo(hNewDev, NEWDEV_CLASS_NAME, &wndClass)) {
|
|
|
|
//
|
|
// register the class
|
|
//
|
|
memset(&wndClass, 0, sizeof(wndClass));
|
|
wndClass.lpfnWndProc = BalloonInfoProc;
|
|
wndClass.hInstance = hNewDev;
|
|
wndClass.lpszClassName = NEWDEV_CLASS_NAME;
|
|
|
|
if (!RegisterClass(&wndClass)) {
|
|
CloseHandle(hPipeRead);
|
|
goto clean0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a window.
|
|
//
|
|
hTrayIconWnd = CreateWindowEx(WS_EX_TOOLWINDOW,
|
|
NEWDEV_CLASS_NAME,
|
|
TEXT(""),
|
|
WS_DLGFRAME | WS_BORDER | WS_DISABLED,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
hNewDev,
|
|
NULL
|
|
);
|
|
|
|
if (hTrayIconWnd == NULL) {
|
|
CloseHandle(hPipeRead);
|
|
goto clean0;
|
|
}
|
|
|
|
|
|
//
|
|
// Create the device install thread that will read from the named pipe.
|
|
// Note that once the ClientSideInstallThread is successfully created, it is
|
|
// responsible for closing the handle to the named pipe when its done with
|
|
// it.
|
|
//
|
|
hThread = CreateThread(NULL,
|
|
0,
|
|
ClientSideInstallThread,
|
|
(PVOID)hPipeRead,
|
|
0,
|
|
&ThreadId
|
|
);
|
|
|
|
if (!hThread) {
|
|
DestroyWindow(hTrayIconWnd);
|
|
CloseHandle(hPipeRead);
|
|
goto clean0;
|
|
}
|
|
|
|
while (IsWindow(hTrayIconWnd)) {
|
|
|
|
if (GetMessage(&Msg, NULL, 0, 0)) {
|
|
|
|
TranslateMessage(&Msg);
|
|
DispatchMessage(&Msg);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if a reboot is needed.
|
|
//
|
|
if (bQueuedRebootNeeded) {
|
|
|
|
TCHAR RebootText[MAX_PATH];
|
|
|
|
LoadString(hNewDev, IDS_NEWDEVICE_REBOOT, RebootText, SIZECHARS(RebootText));
|
|
|
|
RestartDialogEx(hwnd, RebootText, EWX_REBOOT, REASON_PLANNED_FLAG | REASON_HWINSTALL);
|
|
}
|
|
|
|
clean0:
|
|
return 0;
|
|
}
|
|
|