You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2139 lines
75 KiB
2139 lines
75 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation
|
|
//
|
|
// File: finish.c
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "newdevp.h"
|
|
#include <help.h>
|
|
|
|
|
|
typedef
|
|
UINT
|
|
(*PDEVICEPROBLEMTEXT)(
|
|
HMACHINE hMachine,
|
|
DEVNODE DevNode,
|
|
ULONG ProblemNumber,
|
|
LPTSTR Buffer,
|
|
UINT BufferSize
|
|
);
|
|
|
|
BOOL
|
|
IsNullDriverInstalled(
|
|
DEVNODE DevNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether a null driver, or no driver at all, is
|
|
installed for this device instance. Currently the test is that I know
|
|
a null driver was installed if the "Driver" value entry doesn't exist.
|
|
|
|
Arguments:
|
|
|
|
DevNode
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if a null driver was installed for this device, otherwise
|
|
returns FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR Buffer[1];
|
|
DWORD dwSize, dwType;
|
|
|
|
dwSize = sizeof(Buffer);
|
|
if (CM_Get_DevNode_Registry_Property(DevNode,
|
|
CM_DRP_DRIVER,
|
|
&dwType,
|
|
(LPVOID)Buffer,
|
|
&dwSize,
|
|
0) == CR_BUFFER_SMALL) {
|
|
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
}
|
|
|
|
PTCHAR
|
|
DeviceProblemText(
|
|
HMACHINE hMachine,
|
|
DEVNODE DevNode,
|
|
ULONG ProblemNumber
|
|
)
|
|
{
|
|
UINT LenChars, ReqLenChars;
|
|
HMODULE hDevMgr = NULL;
|
|
PTCHAR Buffer = NULL;
|
|
PDEVICEPROBLEMTEXT pDeviceProblemText = NULL;
|
|
|
|
hDevMgr = LoadLibrary(TEXT("devmgr.dll"));
|
|
if (hDevMgr)
|
|
{
|
|
pDeviceProblemText = (PDEVICEPROBLEMTEXT)GetProcAddress(hDevMgr, "DeviceProblemTextW");
|
|
}
|
|
|
|
if (pDeviceProblemText)
|
|
{
|
|
LenChars = (pDeviceProblemText)(hMachine,
|
|
DevNode,
|
|
ProblemNumber,
|
|
Buffer,
|
|
0
|
|
);
|
|
if (!LenChars)
|
|
{
|
|
goto DPTExitCleanup;
|
|
}
|
|
|
|
LenChars++; // one extra for terminating NULL
|
|
|
|
Buffer = LocalAlloc(LPTR, LenChars*sizeof(TCHAR));
|
|
if (!Buffer)
|
|
{
|
|
goto DPTExitCleanup;
|
|
}
|
|
|
|
ReqLenChars = (pDeviceProblemText)(hMachine,
|
|
DevNode,
|
|
ProblemNumber,
|
|
Buffer,
|
|
LenChars
|
|
);
|
|
if (!ReqLenChars || ReqLenChars >= LenChars)
|
|
{
|
|
LocalFree(Buffer);
|
|
Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
DPTExitCleanup:
|
|
|
|
if (hDevMgr)
|
|
{
|
|
FreeLibrary(hDevMgr);
|
|
}
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
BOOL
|
|
DeviceHasResources(
|
|
DEVINST DeviceInst
|
|
)
|
|
{
|
|
CONFIGRET ConfigRet;
|
|
ULONG lcType = NUM_LOG_CONF;
|
|
|
|
while (lcType--)
|
|
{
|
|
ConfigRet = CM_Get_First_Log_Conf_Ex(NULL, DeviceInst, lcType, NULL);
|
|
if (ConfigRet == CR_SUCCESS)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
GetClassGuidForInf(
|
|
PTSTR InfFileName,
|
|
LPGUID ClassGuid
|
|
)
|
|
{
|
|
TCHAR ClassName[MAX_CLASS_NAME_LEN];
|
|
DWORD NumGuids;
|
|
|
|
if(!SetupDiGetINFClass(InfFileName,
|
|
ClassGuid,
|
|
ClassName,
|
|
SIZECHARS(ClassName),
|
|
NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (IsEqualGUID(ClassGuid, &GUID_NULL))
|
|
{
|
|
//
|
|
// Then we need to retrieve the GUID associated with the INF's class name.
|
|
// (If this class name isn't installed (i.e., has no corresponding GUID),
|
|
// or if it matches with multiple GUIDs, then we abort.
|
|
//
|
|
if(!SetupDiClassGuidsFromName(ClassName, ClassGuid, 1, &NumGuids) || !NumGuids)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UINT
|
|
QueueCallback(
|
|
IN PVOID Context,
|
|
IN UINT Notification,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2
|
|
)
|
|
{
|
|
PNEWDEVWIZ NewDevWiz = (PNEWDEVWIZ)Context;
|
|
|
|
switch (Notification) {
|
|
|
|
case SPFILENOTIFY_TARGETNEWER:
|
|
//
|
|
// When doing a driver rollback we expect that some of the files will
|
|
// be older then the files currently on the system since most backups
|
|
// will be of older driver packages. So when a user does a rollback we
|
|
// will hide the older vs. newer file prompt and always copy the older
|
|
// backed up file.
|
|
//
|
|
if (NewDevWiz->Flags & IDI_FLAG_ROLLBACK) {
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case SPFILENOTIFY_STARTCOPY:
|
|
if (NewDevWiz->hWnd) {
|
|
SendMessage(NewDevWiz->hWnd,
|
|
WUM_INSTALLPROGRESS,
|
|
INSTALLOP_COPY,
|
|
(WPARAM)((PFILEPATHS)Param1)
|
|
);
|
|
}
|
|
break;
|
|
|
|
case SPFILENOTIFY_STARTRENAME:
|
|
if (NewDevWiz->hWnd) {
|
|
SendMessage(NewDevWiz->hWnd,
|
|
WUM_INSTALLPROGRESS,
|
|
INSTALLOP_RENAME,
|
|
(WPARAM)((PFILEPATHS)Param1)
|
|
);
|
|
}
|
|
break;
|
|
|
|
case SPFILENOTIFY_STARTDELETE:
|
|
if (NewDevWiz->hWnd) {
|
|
SendMessage(NewDevWiz->hWnd,
|
|
WUM_INSTALLPROGRESS,
|
|
INSTALLOP_DELETE,
|
|
(WPARAM)((PFILEPATHS)Param1)
|
|
);
|
|
}
|
|
break;
|
|
|
|
case SPFILENOTIFY_STARTBACKUP:
|
|
if (NewDevWiz->hWnd) {
|
|
SendMessage(NewDevWiz->hWnd,
|
|
WUM_INSTALLPROGRESS,
|
|
INSTALLOP_BACKUP,
|
|
(WPARAM)((PFILEPATHS)Param1)
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return SetupDefaultQueueCallback(NewDevWiz->MessageHandlerContext,
|
|
Notification,
|
|
Param1,
|
|
Param2
|
|
);
|
|
}
|
|
|
|
LONG
|
|
ClassInstallerInstalls(
|
|
HWND hwndParent,
|
|
PNEWDEVWIZ NewDevWiz,
|
|
BOOL BackupOldDrivers,
|
|
BOOL ReadOnlyInstall,
|
|
BOOL DontCreateQueue
|
|
)
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
HSPFILEQ FileQueue = INVALID_HANDLE_VALUE;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
DWORD ScanResult = 0;
|
|
int FileQueueNeedsReboot = 0;
|
|
|
|
ZeroMemory(&DeviceInstallParams, sizeof(DeviceInstallParams));
|
|
|
|
NewDevWiz->MessageHandlerContext = NULL;
|
|
|
|
//
|
|
// If we can't create our own queue and we are doing a read-only install
|
|
// then fail with ERROR_ACCESS_DENIED.
|
|
//
|
|
if (DontCreateQueue && ReadOnlyInstall) {
|
|
Err = ERROR_ACCESS_DENIED;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// verify with class installer, and class-specific coinstallers
|
|
// that the driver is not blacklisted. For DIF_ALLOW_INSTALL we
|
|
// accept ERROR_SUCCESS or ERROR_DI_DO_DEFAULT as good return codes.
|
|
//
|
|
if (!SetupDiCallClassInstaller(DIF_ALLOW_INSTALL,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
) &&
|
|
(GetLastError() != ERROR_DI_DO_DEFAULT)) {
|
|
|
|
Err = GetLastError();
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Create our own queue.
|
|
//
|
|
if (!DontCreateQueue) {
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (!SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
)) {
|
|
|
|
Err = GetLastError();
|
|
goto clean0;
|
|
}
|
|
|
|
FileQueue = SetupOpenFileQueue();
|
|
|
|
if (FileQueue == INVALID_HANDLE_VALUE) {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto clean0;
|
|
}
|
|
|
|
DeviceInstallParams.Flags |= DI_NOVCP;
|
|
DeviceInstallParams.FileQueue = FileQueue;
|
|
|
|
//
|
|
// Only set the DI_FLAGSEX_PREINSTALLBACKUP flag if we are doing a
|
|
// backup...not in the read only install case.
|
|
//
|
|
if (BackupOldDrivers) {
|
|
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_PREINSTALLBACKUP;
|
|
}
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
|
|
//
|
|
// If the IDI_FLAG_SETRESTOREPOINT flag is set then we want to set the
|
|
// SPQ_FLAG_ABORT_IF_UNSIGNED value on the file queue. With this flag
|
|
// setup setupapi will bail out of the copy if it encounters an unsigned
|
|
// file. At that point we will set a system restore point and then
|
|
// do the copy. This way the user can back out of an unsigned driver
|
|
// install using system restore.
|
|
//
|
|
// Note that system restore is currently not supported on 64-bit so
|
|
// don't bother setting the SPQ_FLAG_ABORT_IF_UNSIGNED flag.
|
|
//
|
|
#ifndef _WIN64
|
|
if (NewDevWiz->Flags & IDI_FLAG_SETRESTOREPOINT) {
|
|
SetupSetFileQueueFlags(FileQueue,
|
|
SPQ_FLAG_ABORT_IF_UNSIGNED,
|
|
SPQ_FLAG_ABORT_IF_UNSIGNED
|
|
);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Install the files first in one shot.
|
|
// This allows new coinstallers to run during the install.
|
|
//
|
|
if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
)) {
|
|
|
|
Err = GetLastError();
|
|
goto clean0;
|
|
}
|
|
|
|
if (FileQueue != INVALID_HANDLE_VALUE) {
|
|
//
|
|
// If we created our own FileQueue then we need to
|
|
// scan and possibly commit the queue
|
|
//
|
|
// If we are doing a read only install then we just queued up the files so
|
|
// that we could do a presence check on them. We will throw away the queue
|
|
// so that the files are not copied.
|
|
//
|
|
// Any other install, prune copies as needed
|
|
//
|
|
if (!SetupScanFileQueue(FileQueue,
|
|
ReadOnlyInstall
|
|
? SPQ_SCAN_FILE_PRESENCE
|
|
: (SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_PRUNE_COPY_QUEUE),
|
|
hwndParent,
|
|
NULL,
|
|
NULL,
|
|
&ScanResult
|
|
)) {
|
|
|
|
//
|
|
// If the API failed then set the ScanResult to 0 (failure).
|
|
//
|
|
ScanResult = 0;
|
|
}
|
|
|
|
if (ReadOnlyInstall && (ScanResult != 1)) {
|
|
//
|
|
// ReadOnlyInstall cannot perform copies, deletes or renames
|
|
// bail now!
|
|
//
|
|
Err = ERROR_ACCESS_DENIED;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// We will always commit the file queue, even if we pruned all of the
|
|
// files. The reason for this is that backing up of drivers, for
|
|
// driver rollback, won't work unless the file queue is committed.
|
|
//
|
|
if ((NewDevWiz->Flags & IDI_FLAG_ROLLBACK) &&
|
|
(!ReadOnlyInstall)) {
|
|
//
|
|
// Prepare file queue for rollback
|
|
// we need the directory of the INF
|
|
// that's being used for the install
|
|
//
|
|
SP_DRVINFO_DATA DriverInfoData;
|
|
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
|
|
DWORD RetVal;
|
|
LPTSTR pFileName;
|
|
TCHAR BackupPath[MAX_PATH];
|
|
|
|
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
|
|
if (SetupDiGetSelectedDriver(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DriverInfoData)) {
|
|
|
|
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
|
|
if (SetupDiGetDriverInfoDetail(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DriverInfoData,
|
|
&DriverInfoDetailData,
|
|
sizeof(SP_DRVINFO_DETAIL_DATA),
|
|
NULL) ||
|
|
(GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
|
//
|
|
// we now have path of INF we're using for the restore
|
|
//
|
|
RetVal = GetFullPathName(DriverInfoDetailData.InfFileName,
|
|
SIZECHARS(BackupPath),
|
|
BackupPath,
|
|
&pFileName);
|
|
if(RetVal && pFileName && (pFileName != BackupPath)) {
|
|
if ((*CharPrev(BackupPath,pFileName)==TEXT('\\')) ||
|
|
(*CharPrev(BackupPath,pFileName)==TEXT('/'))) {
|
|
pFileName--;
|
|
}
|
|
*pFileName = TEXT('\0');
|
|
//
|
|
// Prepare queue for rollback
|
|
// if this fails, carry on, it'll work in a degraded way
|
|
//
|
|
SetupPrepareQueueForRestore(FileQueue,BackupPath,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NewDevWiz->MessageHandlerContext = SetupInitDefaultQueueCallbackEx(
|
|
hwndParent,
|
|
(DeviceInstallParams.Flags & DI_QUIETINSTALL)
|
|
? INVALID_HANDLE_VALUE : NewDevWiz->hWnd,
|
|
WUM_INSTALLPROGRESS,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (NewDevWiz->MessageHandlerContext) {
|
|
//
|
|
// Commit the file queue.
|
|
//
|
|
if (!SetupCommitFileQueue(hwndParent,
|
|
FileQueue,
|
|
QueueCallback,
|
|
(PVOID)NewDevWiz
|
|
)) {
|
|
|
|
Err = GetLastError();
|
|
|
|
if (Err == ERROR_SET_SYSTEM_RESTORE_POINT) {
|
|
UINT RestorePointResourceId;
|
|
|
|
//
|
|
// If we get back ERROR_SET_SYSTEM_RESTORE_POINT then
|
|
// we better have the IDI_FLAG_SETRESTOREPOINT flag
|
|
// set.
|
|
//
|
|
ASSERT(NewDevWiz->Flags & IDI_FLAG_SETRESTOREPOINT);
|
|
|
|
if (!(DeviceInstallParams.Flags & DI_QUIETINSTALL) &&
|
|
NewDevWiz->hWnd) {
|
|
PostMessage(NewDevWiz->hWnd,
|
|
WUM_INSTALLPROGRESS,
|
|
INSTALLOP_SETTEXT,
|
|
(LPARAM)IDS_SYSTEMRESTORE_TEXT
|
|
);
|
|
}
|
|
|
|
SetupTermDefaultQueueCallback(NewDevWiz->MessageHandlerContext);
|
|
|
|
NewDevWiz->MessageHandlerContext = SetupInitDefaultQueueCallbackEx(
|
|
hwndParent,
|
|
(DeviceInstallParams.Flags & DI_QUIETINSTALL)
|
|
? INVALID_HANDLE_VALUE : NewDevWiz->hWnd,
|
|
WUM_INSTALLPROGRESS,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (NewDevWiz->MessageHandlerContext) {
|
|
//
|
|
// Set the system restore point.
|
|
//
|
|
if (NewDevWiz->Flags & IDI_FLAG_ROLLBACK) {
|
|
RestorePointResourceId = IDS_ROLLBACK_SETRESTOREPOINT;
|
|
} else if (NewDevWiz->InstallType == NDWTYPE_FOUNDNEW) {
|
|
RestorePointResourceId = IDS_NEW_SETRESTOREPOINT;
|
|
} else {
|
|
RestorePointResourceId = IDS_UPDATE_SETRESTOREPOINT;
|
|
}
|
|
|
|
pSetSystemRestorePoint(TRUE, FALSE, RestorePointResourceId);
|
|
|
|
NewDevWiz->SetRestorePoint = TRUE;
|
|
|
|
if (!(DeviceInstallParams.Flags & DI_QUIETINSTALL) &&
|
|
NewDevWiz->hWnd) {
|
|
PostMessage(NewDevWiz->hWnd,
|
|
WUM_INSTALLPROGRESS,
|
|
INSTALLOP_SETTEXT,
|
|
(LPARAM)NULL
|
|
);
|
|
}
|
|
|
|
//
|
|
// Clear the SPQ_FLAG_ABORT_IF_UNSIGNED flag so the file
|
|
// queue will be commited the next time.
|
|
//
|
|
SetupSetFileQueueFlags(FileQueue,
|
|
SPQ_FLAG_ABORT_IF_UNSIGNED,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Now that we have set the restore point and cleared the
|
|
// SPQ_FLAG_ABORT_IF_UNSIGNED flag from the file queue we
|
|
// can commit the queue again.
|
|
//
|
|
if (!SetupCommitFileQueue(hwndParent,
|
|
FileQueue,
|
|
QueueCallback,
|
|
(PVOID)NewDevWiz
|
|
)) {
|
|
Err = GetLastError();
|
|
|
|
//
|
|
// If the error we get is ERROR_CANCELLED then
|
|
// the user has canceld out of the file copy.
|
|
// This means that no changes have been made
|
|
// to the system, so we will tell system
|
|
// restore to cancel its restore point.
|
|
//
|
|
// Also clear the SetRestorePoint BOOL since
|
|
// we didn't actually set a restore point.
|
|
//
|
|
if (Err == ERROR_CANCELLED) {
|
|
pSetSystemRestorePoint(FALSE, TRUE, 0);
|
|
NewDevWiz->SetRestorePoint = FALSE;
|
|
}
|
|
|
|
goto clean0;
|
|
} else {
|
|
//
|
|
// We were successful in commiting the file queue, so check
|
|
// to see whether a reboot is required as a result of committing
|
|
// the queue (i.e. because files were in use, or the INF requested
|
|
// a reboot).
|
|
//
|
|
FileQueueNeedsReboot = SetupPromptReboot(FileQueue, NULL, TRUE);
|
|
}
|
|
}
|
|
} else {
|
|
goto clean0;
|
|
}
|
|
} else {
|
|
//
|
|
// We were successful in commiting the file queue, so check
|
|
// to see whether a reboot is required as a result of committing
|
|
// the queue (i.e. because files were in use, or the INF requested
|
|
// a reboot).
|
|
//
|
|
FileQueueNeedsReboot = SetupPromptReboot(FileQueue, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
if (BackupOldDrivers) {
|
|
//
|
|
// If the backup succeeded and we have a UpdateDriverInfo structure
|
|
// then we need to call SetupGetBackupInformation so we can get the
|
|
// registry key that the backup was saved into.
|
|
//
|
|
SP_BACKUP_QUEUE_PARAMS BackupQueueParams;
|
|
|
|
BackupQueueParams.cbSize = sizeof(SP_BACKUP_QUEUE_PARAMS);
|
|
if (NewDevWiz->UpdateDriverInfo &&
|
|
SetupGetBackupInformation(FileQueue, &BackupQueueParams)) {
|
|
|
|
if (FAILED(StringCchCopy(NewDevWiz->UpdateDriverInfo->BackupRegistryKey,
|
|
SIZECHARS(NewDevWiz->UpdateDriverInfo->BackupRegistryKey),
|
|
REGSTR_PATH_REINSTALL)) ||
|
|
FAILED(StringCchCat(NewDevWiz->UpdateDriverInfo->BackupRegistryKey,
|
|
SIZECHARS(NewDevWiz->UpdateDriverInfo->BackupRegistryKey),
|
|
TEXT("\\"))) ||
|
|
FAILED(StringCchCat(NewDevWiz->UpdateDriverInfo->BackupRegistryKey,
|
|
SIZECHARS(NewDevWiz->UpdateDriverInfo->BackupRegistryKey),
|
|
BackupQueueParams.ReinstallInstance))) {
|
|
//
|
|
// If the entire backup registry key string could NOT fit into our buffer
|
|
// then set the buffer to 0 since putting a partial key in the registry
|
|
// is useless.
|
|
//
|
|
NewDevWiz->UpdateDriverInfo->BackupRegistryKey[0] = TEXT('\0');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
)) {
|
|
DWORD FileQueueFlags;
|
|
|
|
//
|
|
// If we didn't copy any files when commiting the file queue then the
|
|
// SPQ_FLAG_FILES_MODIFIED flag will NOT be set. In this case set
|
|
// the DI_FLAGSEX_RESTART_DEVICE_ONLY flag so that we only stop/start
|
|
// this single device. By default setupapi will stop/start this device
|
|
// as well as any other device that was using the same driver/filter
|
|
// that this device is using.
|
|
//
|
|
if ((FileQueue != INVALID_HANDLE_VALUE) &&
|
|
SetupGetFileQueueFlags(FileQueue, &FileQueueFlags) &&
|
|
!(FileQueueFlags & SPQ_FLAG_FILES_MODIFIED)) {
|
|
|
|
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_RESTART_DEVICE_ONLY;
|
|
}
|
|
|
|
//
|
|
// Set the DI_NOFILECOPY flag since we already copied the files during
|
|
// the DIF_INSTALLDEVICEFILES, so we don't need to copy them again during
|
|
// the DIF_INSTALLDEVICE.
|
|
//
|
|
DeviceInstallParams.Flags |= DI_NOFILECOPY;
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
//
|
|
// Register any device-specific co-installers for this device,
|
|
//
|
|
if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
)) {
|
|
|
|
Err = GetLastError();
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// install any INF/class installer-specified interfaces.
|
|
// and then finally the real "InstallDevice"!
|
|
//
|
|
if (!SetupDiCallClassInstaller(DIF_INSTALLINTERFACES,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
)
|
|
||
|
|
!SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
)) {
|
|
|
|
Err = GetLastError();
|
|
goto clean0;
|
|
}
|
|
|
|
Err = ERROR_SUCCESS;
|
|
|
|
clean0:
|
|
|
|
if (NewDevWiz->MessageHandlerContext) {
|
|
SetupTermDefaultQueueCallback(NewDevWiz->MessageHandlerContext);
|
|
NewDevWiz->MessageHandlerContext = NULL;
|
|
}
|
|
|
|
//
|
|
// If the file queue said that a reboot was needed then set the
|
|
// DI_NEEDRESTART flag.
|
|
//
|
|
if (FileQueueNeedsReboot) {
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
)) {
|
|
|
|
DeviceInstallParams.Flags |= DI_NEEDRESTART;
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
}
|
|
|
|
if (FileQueue != INVALID_HANDLE_VALUE) {
|
|
//
|
|
// If we have a valid file queue handle and there was an error during
|
|
// the device install then we want to delete any new INFs that were
|
|
// copied into the INF directory. We do this under the assumption that
|
|
// since there was an error during the install these INFs must be bad.
|
|
//
|
|
if (Err != ERROR_SUCCESS) {
|
|
SetupUninstallNewlyCopiedInfs(FileQueue,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
//
|
|
// Clear out our file queue from the device install params. We need
|
|
// to do this or else SetupCloseFileQueue will fail because it will
|
|
// still have a ref count.
|
|
//
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
)) {
|
|
|
|
DeviceInstallParams.Flags &= ~DI_NOVCP;
|
|
DeviceInstallParams.FileQueue = INVALID_HANDLE_VALUE;
|
|
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
SetupCloseFileQueue(FileQueue);
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
//
|
|
// invokable only from finish page!
|
|
//
|
|
DWORD
|
|
InstallDev(
|
|
HWND hwndParent,
|
|
PNEWDEVWIZ NewDevWiz
|
|
)
|
|
{
|
|
SP_DRVINFO_DATA DriverInfoData;
|
|
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
TCHAR ClassGuidString[MAX_GUID_STRING_LEN];
|
|
GUID ClassGuidInf;
|
|
LPGUID ClassGuid;
|
|
int ClassGuidNum;
|
|
DWORD Error = ERROR_SUCCESS;
|
|
BOOL IgnoreRebootFlags = FALSE;
|
|
TCHAR Buffer[MAX_PATH];
|
|
ULONG DevNodeStatus = 0, Problem = 0;
|
|
BOOL Backup = FALSE;
|
|
BOOL DontCreateQueue = FALSE;
|
|
|
|
if (!NewDevWiz->ClassGuidSelected)
|
|
{
|
|
NewDevWiz->ClassGuidSelected = (LPGUID)&GUID_NULL;
|
|
}
|
|
|
|
|
|
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
|
|
if (SetupDiGetSelectedDriver(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DriverInfoData
|
|
))
|
|
{
|
|
//
|
|
// Get details on this driver node, so that we can examine the INF that this
|
|
// node came from.
|
|
//
|
|
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
|
|
if(!SetupDiGetDriverInfoDetail(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DriverInfoData,
|
|
&DriverInfoDetailData,
|
|
sizeof(DriverInfoDetailData),
|
|
NULL
|
|
))
|
|
{
|
|
Error = GetLastError();
|
|
if (Error != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
goto clean0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Verif that the class is installed, if its not then
|
|
// attempt to install it.
|
|
//
|
|
NdwBuildClassInfoList(NewDevWiz, 0);
|
|
|
|
//
|
|
// fetch classguid from inf, (It may be different than what we already
|
|
// have in class guid selected).
|
|
//
|
|
if (!GetClassGuidForInf(DriverInfoDetailData.InfFileName, &ClassGuidInf))
|
|
{
|
|
ClassGuidInf = *(NewDevWiz->ClassGuidSelected);
|
|
}
|
|
|
|
if (IsEqualGUID(&ClassGuidInf, &GUID_NULL))
|
|
{
|
|
ClassGuidInf = GUID_DEVCLASS_UNKNOWN;
|
|
}
|
|
|
|
//
|
|
// if the ClassGuidInf wasn't found then this class hasn't been installed yet.
|
|
// -install the class installer now.
|
|
//
|
|
ClassGuid = NewDevWiz->ClassGuidList;
|
|
ClassGuidNum = NewDevWiz->ClassGuidNum;
|
|
while (ClassGuidNum--)
|
|
{
|
|
if (IsEqualGUID(ClassGuid, &ClassGuidInf))
|
|
{
|
|
break;
|
|
}
|
|
|
|
ClassGuid++;
|
|
}
|
|
|
|
if (ClassGuidNum < 0 &&
|
|
!SetupDiInstallClass(hwndParent,
|
|
DriverInfoDetailData.InfFileName,
|
|
NewDevWiz->SilentMode ? DI_QUIETINSTALL : 0,
|
|
NULL
|
|
))
|
|
{
|
|
Error = GetLastError();
|
|
goto clean0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No selected driver, and no associated class--use "Unknown" class.
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// If the devnode is currently running 'raw', then remember this
|
|
// fact so that we don't require a reboot later (NULL driver installation
|
|
// isn't going to change anything).
|
|
//
|
|
if (CM_Get_DevNode_Status(&DevNodeStatus,
|
|
&Problem,
|
|
NewDevWiz->DeviceInfoData.DevInst,
|
|
0) == CR_SUCCESS)
|
|
{
|
|
if (!SetupDiGetDeviceRegistryProperty(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
SPDRP_SERVICE,
|
|
NULL, // regdatatype
|
|
(PVOID)Buffer,
|
|
sizeof(Buffer),
|
|
NULL
|
|
))
|
|
{
|
|
*Buffer = TEXT('\0');
|
|
}
|
|
|
|
if((DevNodeStatus & DN_STARTED) && (*Buffer == TEXT('\0')))
|
|
{
|
|
IgnoreRebootFlags = TRUE;
|
|
}
|
|
}
|
|
|
|
if (IsEqualGUID(NewDevWiz->ClassGuidSelected, &GUID_NULL))
|
|
{
|
|
pSetupStringFromGuid(&GUID_DEVCLASS_UNKNOWN,
|
|
ClassGuidString,
|
|
SIZECHARS(ClassGuidString)
|
|
);
|
|
|
|
|
|
SetupDiSetDeviceRegistryProperty(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
SPDRP_CLASSGUID,
|
|
(PBYTE)ClassGuidString,
|
|
sizeof(ClassGuidString)
|
|
);
|
|
}
|
|
|
|
ClassGuidInf = *(NewDevWiz->ClassGuidSelected);
|
|
}
|
|
|
|
//
|
|
// We will backup the current drivers in all cases except if any of the following are true:
|
|
//
|
|
// 1) The device is a printer
|
|
// 2) The selected driver is the currently installed driver
|
|
// 3) The DontBackupCurrentDrivers NEWDEVWIZ BOOL is TRUE
|
|
// 4) The device has a problem
|
|
//
|
|
if (IsEqualGUID(&ClassGuidInf, &GUID_DEVCLASS_PRINTER) ||
|
|
IsInstalledDriver(NewDevWiz, NULL) ||
|
|
(NewDevWiz->Flags & IDI_FLAG_NOBACKUP) ||
|
|
((CM_Get_DevNode_Status(&DevNodeStatus, &Problem, NewDevWiz->DeviceInfoData.DevInst, 0) == CR_SUCCESS) &&
|
|
((DevNodeStatus & DN_HAS_PROBLEM) ||
|
|
(DevNodeStatus & DN_PRIVATE_PROBLEM)))) {
|
|
|
|
Backup = FALSE;
|
|
|
|
} else {
|
|
|
|
Backup = TRUE;
|
|
}
|
|
|
|
//
|
|
// We will always create our own queue during device install, except in the
|
|
// following specific cases.
|
|
//
|
|
// 1) The device is a printer
|
|
//
|
|
// Note that if we can't create our own queue then we cannot do any of the
|
|
// operations that need a queue, like backup, rollback, read-only install,
|
|
// or setting a restore point.
|
|
//
|
|
DontCreateQueue = IsEqualGUID(&ClassGuidInf, & GUID_DEVCLASS_PRINTER);
|
|
|
|
Error = ClassInstallerInstalls(hwndParent,
|
|
NewDevWiz,
|
|
Backup,
|
|
(NewDevWiz->Flags & IDI_FLAG_READONLY_INSTALL),
|
|
DontCreateQueue
|
|
);
|
|
|
|
//
|
|
// If this is a WU/CDM install and it was successful then set
|
|
// the DriverWasUpgraded to TRUE
|
|
//
|
|
if (NewDevWiz->UpdateDriverInfo && (Error == ERROR_SUCCESS)) {
|
|
|
|
NewDevWiz->UpdateDriverInfo->DriverWasUpgraded = TRUE;
|
|
}
|
|
|
|
//
|
|
// If this is a new device (currently no drivers are installed) and we encounter
|
|
// an error that is not ERROR_CANCELLED then we will install the NULL driver for
|
|
// this device and set the FAILED INSTALL flag.
|
|
//
|
|
if ((Error != ERROR_SUCCESS) &&
|
|
(Error != ERROR_CANCELLED))
|
|
{
|
|
if (IsNullDriverInstalled(NewDevWiz->DeviceInfoData.DevInst)) {
|
|
|
|
if (SetupDiSetSelectedDriver(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
NULL
|
|
))
|
|
{
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
))
|
|
{
|
|
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_SETFAILEDINSTALL;
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
);
|
|
}
|
|
|
|
SetupDiInstallDevice(NewDevWiz->hDeviceInfo, &NewDevWiz->DeviceInfoData);
|
|
}
|
|
}
|
|
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// See if the device needs to the system to be restarted before it will work.
|
|
//
|
|
if(!IgnoreRebootFlags) {
|
|
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DeviceInstallParams
|
|
) &&
|
|
(DeviceInstallParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
|
|
//
|
|
// If either the DI_NEEDRESTART or the DI_NEEDREBOOT DeviceInstallParams
|
|
// flag is set, then a restart is needed.
|
|
//
|
|
NewDevWiz->Reboot |= DI_NEEDREBOOT;
|
|
|
|
} else if ((CM_Get_DevNode_Status(&DevNodeStatus,
|
|
&Problem,
|
|
NewDevWiz->DeviceInfoData.DevInst,
|
|
0) == CR_SUCCESS) &&
|
|
(DevNodeStatus & DN_NEED_RESTART) ||
|
|
(Problem == CM_PROB_NEED_RESTART)) {
|
|
//
|
|
// If the DN_NEED_RESTART devnode status flag is set, then a restart
|
|
// is needed.
|
|
//
|
|
NewDevWiz->Reboot |= DI_NEEDREBOOT;
|
|
}
|
|
}
|
|
|
|
|
|
clean0:
|
|
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
InstallNullDriver(
|
|
PNEWDEVWIZ NewDevWiz,
|
|
BOOL FailedInstall
|
|
)
|
|
{
|
|
SP_DEVINSTALL_PARAMS DevInstallParams;
|
|
DWORD Err = ERROR_SUCCESS;
|
|
|
|
DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
|
|
//
|
|
// Set the DI_FLAGSEX_SETFAILEDINSTALL flag if this is a failed
|
|
// install.
|
|
//
|
|
if (FailedInstall)
|
|
{
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DevInstallParams
|
|
))
|
|
{
|
|
DevInstallParams.FlagsEx |= DI_FLAGSEX_SETFAILEDINSTALL;
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DevInstallParams
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the selected driver to NULL
|
|
//
|
|
if (SetupDiSetSelectedDriver(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
NULL
|
|
))
|
|
{
|
|
//
|
|
// verify with class installer, and class-specific coinstallers
|
|
// that the driver is not blacklisted. For DIF_ALLOW_INSTALL we
|
|
// accept ERROR_SUCCESS or ERROR_DI_DO_DEFAULT as good return codes.
|
|
//
|
|
if (SetupDiCallClassInstaller(DIF_ALLOW_INSTALL,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
) ||
|
|
(GetLastError() == ERROR_DI_DO_DEFAULT)) {
|
|
|
|
//
|
|
// If the class/co-installers gave the OK then call DIF_INSTALLDEVICE.
|
|
//
|
|
if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
)) {
|
|
Err = GetLastError();
|
|
}
|
|
|
|
} else {
|
|
Err = GetLastError();
|
|
}
|
|
}
|
|
|
|
return Err;
|
|
|
|
} // InstallNullDriver
|
|
|
|
|
|
BOOL
|
|
CALLBACK
|
|
AddPropSheetPageProc(
|
|
IN HPROPSHEETPAGE hpage,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
*((HPROPSHEETPAGE *)lParam) = hpage;
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
DisplayResource(
|
|
PNEWDEVWIZ NewDevWiz,
|
|
HWND hWndParent
|
|
)
|
|
{
|
|
HINSTANCE hLib;
|
|
PROPSHEETHEADER psh;
|
|
HPROPSHEETPAGE hpsPages[1];
|
|
SP_PROPSHEETPAGE_REQUEST PropPageRequest;
|
|
LPFNADDPROPSHEETPAGES ExtensionPropSheetPage = NULL;
|
|
LPTSTR Title;
|
|
SP_DEVINSTALL_PARAMS DevInstallParams;
|
|
|
|
//
|
|
// Now get the resource selection page from setupapi.dll
|
|
//
|
|
hLib = GetModuleHandle(TEXT("setupapi.dll"));
|
|
if (hLib)
|
|
{
|
|
ExtensionPropSheetPage = (LPFNADDPROPSHEETPAGES)GetProcAddress(hLib, "ExtensionPropSheetPageProc");
|
|
}
|
|
|
|
if (!ExtensionPropSheetPage)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PropPageRequest.cbSize = sizeof(SP_PROPSHEETPAGE_REQUEST);
|
|
PropPageRequest.PageRequested = SPPSR_SELECT_DEVICE_RESOURCES;
|
|
PropPageRequest.DeviceInfoSet = NewDevWiz->hDeviceInfo;
|
|
PropPageRequest.DeviceInfoData = &NewDevWiz->DeviceInfoData;
|
|
|
|
if (!ExtensionPropSheetPage(&PropPageRequest,
|
|
AddPropSheetPageProc,
|
|
(LONG_PTR)hpsPages
|
|
))
|
|
{
|
|
// warning ?
|
|
return;
|
|
}
|
|
|
|
//
|
|
// create the property sheet
|
|
//
|
|
psh.dwSize = sizeof(PROPSHEETHEADER);
|
|
psh.dwFlags = PSH_PROPTITLE | PSH_NOAPPLYNOW;
|
|
psh.hwndParent = hWndParent;
|
|
psh.hInstance = hNewDev;
|
|
psh.pszIcon = NULL;
|
|
|
|
switch (NewDevWiz->InstallType) {
|
|
|
|
case NDWTYPE_FOUNDNEW:
|
|
Title = (LPTSTR)IDS_FOUNDDEVICE;
|
|
break;
|
|
|
|
case NDWTYPE_UPDATE:
|
|
Title = (LPTSTR)IDS_UPDATEDEVICE;
|
|
break;
|
|
|
|
default:
|
|
Title = TEXT(""); // unknown
|
|
}
|
|
|
|
psh.pszCaption = Title;
|
|
|
|
psh.nPages = 1;
|
|
psh.phpage = hpsPages;
|
|
psh.nStartPage = 0;
|
|
psh.pfnCallback = NULL;
|
|
|
|
|
|
//
|
|
// Clear the Propchange pending bit in the DeviceInstall params.
|
|
//
|
|
DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DevInstallParams
|
|
))
|
|
{
|
|
DevInstallParams.FlagsEx &= ~DI_FLAGSEX_PROPCHANGE_PENDING;
|
|
SetupDiSetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DevInstallParams
|
|
);
|
|
}
|
|
|
|
if (PropertySheet(&psh) == -1)
|
|
{
|
|
DestroyPropertySheetPage(hpsPages[0]);
|
|
}
|
|
|
|
//
|
|
// If a PropChange occurred invoke the DIF_PROPERTYCHANGE
|
|
//
|
|
if (SetupDiGetDeviceInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
&DevInstallParams
|
|
))
|
|
{
|
|
if (DevInstallParams.FlagsEx & DI_FLAGSEX_PROPCHANGE_PENDING)
|
|
{
|
|
SP_PROPCHANGE_PARAMS PropChangeParams;
|
|
|
|
PropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
PropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
|
|
PropChangeParams.Scope = DICS_FLAG_GLOBAL;
|
|
PropChangeParams.HwProfile = 0;
|
|
|
|
if (SetupDiSetClassInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
(PSP_CLASSINSTALL_HEADER)&PropChangeParams,
|
|
sizeof(PropChangeParams)
|
|
))
|
|
{
|
|
SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
|
|
NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData
|
|
);
|
|
}
|
|
|
|
//
|
|
// Clear the class install parameters.
|
|
//
|
|
SetupDiSetClassInstallParams(NewDevWiz->hDeviceInfo,
|
|
&NewDevWiz->DeviceInfoData,
|
|
NULL,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
InstallDevThreadProc(
|
|
LPVOID lpVoid
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
In the Wizard, we will do the driver installation in a separate thread so that the user
|
|
will see the driver instal wizard page.
|
|
|
|
--*/
|
|
{
|
|
PNEWDEVWIZ NewDevWiz = (PNEWDEVWIZ)lpVoid;
|
|
|
|
//
|
|
// Do the device install
|
|
//
|
|
NewDevWiz->LastError = InstallDev(NewDevWiz->hWnd, NewDevWiz);
|
|
|
|
//
|
|
// Post a message to the window to let it know that we are finished with the install
|
|
//
|
|
PostMessage(NewDevWiz->hWnd, WUM_INSTALLCOMPLETE, TRUE, GetLastError());
|
|
|
|
return GetLastError();
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
NDW_InstallDevDlgProc(
|
|
HWND hDlg,
|
|
UINT wMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
HWND hwndParentDlg = GetParent(hDlg);
|
|
PNEWDEVWIZ NewDevWiz = (PNEWDEVWIZ)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
static HANDLE DeviceInstallThread = NULL;
|
|
HICON hicon;
|
|
TCHAR Text1[MAX_PATH], Text2[MAX_PATH], Target[MAX_PATH], Format[MAX_PATH];
|
|
PTSTR p;
|
|
|
|
switch (wMsg) {
|
|
|
|
case WM_INITDIALOG: {
|
|
|
|
LPPROPSHEETPAGE lppsp = (LPPROPSHEETPAGE)lParam;
|
|
NewDevWiz = (PNEWDEVWIZ)lppsp->lParam;
|
|
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)NewDevWiz);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
hicon = (HICON)SendDlgItemMessage(hDlg, IDC_CLASSICON, STM_GETICON, 0, 0);
|
|
if (hicon) {
|
|
DestroyIcon(hicon);
|
|
}
|
|
break;
|
|
|
|
case WUM_INSTALLCOMPLETE:
|
|
//
|
|
// This message is posted to the window when the device installation is complete.
|
|
//
|
|
WaitForSingleObject(DeviceInstallThread, INFINITE);
|
|
Animate_Stop(GetDlgItem(hDlg, IDC_ANIMATE_INSTALL));
|
|
NewDevWiz->CurrCursor = NULL;
|
|
PropSheet_PressButton(hwndParentDlg, PSBTN_NEXT);
|
|
break;
|
|
|
|
case WUM_INSTALLPROGRESS:
|
|
Text1[0] = Text2[0] = TEXT('\0');
|
|
|
|
//
|
|
// This is the message that is sent from setupapi so we can display our
|
|
// own copy progress.
|
|
//
|
|
// If wParam is 0 then the lParam is the number of files that will be
|
|
// copied.
|
|
// If wParam is 1 then that is a tick for a single file being copied,
|
|
// so the progress bar should be advanced.
|
|
//
|
|
switch (wParam) {
|
|
case 0:
|
|
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS_INSTALL), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_FILECOPY_TEXT1), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_FILECOPY_TEXT2), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_STATUS_TEXT), SW_HIDE);
|
|
SetDlgItemText(hDlg, IDC_FILECOPY_TEXT1, TEXT(""));
|
|
SetDlgItemText(hDlg, IDC_FILECOPY_TEXT2, TEXT(""));
|
|
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS_INSTALL), PBM_SETRANGE,0,MAKELPARAM(0,lParam));
|
|
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS_INSTALL), PBM_SETSTEP,1,0);
|
|
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS_INSTALL), PBM_SETPOS,0,0);
|
|
break;
|
|
case 1:
|
|
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS_INSTALL), PBM_STEPIT,0,0);
|
|
break;
|
|
|
|
case INSTALLOP_COPY:
|
|
StringCchCopy(Target, SIZECHARS(Target), ((PFILEPATHS)lParam)->Target);
|
|
if ((p = _tcsrchr(Target,TEXT('\\'))) != NULL) {
|
|
*p++ = 0;
|
|
StringCchCopy(Text1, SIZECHARS(Text1), p);
|
|
if (LoadString(hNewDev, IDS_FILEOP_TO, Format, SIZECHARS(Format))) {
|
|
StringCchPrintf(Text2, SIZECHARS(Text2), Format, Target);
|
|
}
|
|
} else {
|
|
StringCchCopy(Text1, SIZECHARS(Text1), ((PFILEPATHS)lParam)->Target);
|
|
Text2[0] = TEXT('\0');
|
|
}
|
|
break;
|
|
|
|
case INSTALLOP_RENAME:
|
|
StringCchCopy(Text1, SIZECHARS(Text1), ((PFILEPATHS)lParam)->Source);
|
|
if ((p = _tcsrchr(((PFILEPATHS)lParam)->Target, TEXT('\\'))) != NULL) {
|
|
p++;
|
|
} else {
|
|
p = (PTSTR)((PFILEPATHS)lParam)->Target;
|
|
}
|
|
if (LoadString(hNewDev, IDS_FILEOP_TO, Format, SIZECHARS(Format))) {
|
|
StringCchPrintf(Text2, SIZECHARS(Text2), Format, p);
|
|
}
|
|
break;
|
|
|
|
case INSTALLOP_DELETE:
|
|
StringCchCopy(Target, SIZECHARS(Target), ((PFILEPATHS)lParam)->Target);
|
|
if ((p = _tcsrchr(Target,TEXT('\\'))) != NULL) {
|
|
*p++ = 0;
|
|
StringCchCopy(Text1, SIZECHARS(Text1), p);
|
|
if (LoadString(hNewDev, IDS_FILEOP_FROM, Format, SIZECHARS(Format))) {
|
|
StringCchPrintf(Text2, SIZECHARS(Text2), Format, Target);
|
|
}
|
|
} else {
|
|
StringCchCopy(Text1, SIZECHARS(Text1), ((PFILEPATHS)lParam)->Target);
|
|
Text2[0] = TEXT('\0');
|
|
}
|
|
break;
|
|
|
|
case INSTALLOP_BACKUP:
|
|
StringCchCopy(Target, SIZECHARS(Target), ((PFILEPATHS)lParam)->Source);
|
|
if ((p = _tcsrchr(Target,TEXT('\\'))) != NULL) {
|
|
*p++ = 0;
|
|
if (((PFILEPATHS)lParam)->Target == NULL) {
|
|
if (LoadString(hNewDev, IDS_FILEOP_BACKUP, Format, SIZECHARS(Format))) {
|
|
StringCchPrintf(Text1, SIZECHARS(Text2), Format, p);
|
|
}
|
|
} else {
|
|
StringCchCopy(Text1, SIZECHARS(Text1), p);
|
|
}
|
|
StringCchCopy(Text2, SIZECHARS(Text2), Target);
|
|
} else {
|
|
if (LoadString(hNewDev, IDS_FILEOP_BACKUP, Format, SIZECHARS(Format))) {
|
|
StringCchPrintf(Text1, SIZECHARS(Text2), Format, Target);
|
|
}
|
|
Text2[0] = TEXT('\0');
|
|
}
|
|
break;
|
|
|
|
case INSTALLOP_SETTEXT:
|
|
ShowWindow(GetDlgItem(hDlg, IDC_STATUS_TEXT), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS_INSTALL), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_FILECOPY_TEXT1), SW_HIDE);
|
|
|
|
if (lParam) {
|
|
if (LoadString(hNewDev, (UINT)lParam, Text2, SIZECHARS(Text2))) {
|
|
ShowWindow(GetDlgItem(hDlg, IDC_STATUS_TEXT), SW_SHOW);
|
|
SetDlgItemText(hDlg, IDC_STATUS_TEXT, Text2);
|
|
}
|
|
} else {
|
|
SetDlgItemText(hDlg, IDC_STATUS_TEXT, TEXT(""));
|
|
}
|
|
Text1[0] = TEXT('\0');
|
|
Text2[0] = TEXT('\0');
|
|
break;
|
|
}
|
|
|
|
if ((Text1[0] != TEXT('\0')) && (Text2[0] != TEXT('\0'))) {
|
|
ShowWindow(GetDlgItem(hDlg, IDC_STATUS_TEXT), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_FILECOPY_TEXT1), SW_SHOW);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_FILECOPY_TEXT2), SW_SHOW);
|
|
SetDlgItemText(hDlg, IDC_FILECOPY_TEXT1, Text1);
|
|
SetDlgItemText(hDlg, IDC_FILECOPY_TEXT2, Text2);
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR FAR *)lParam)->code) {
|
|
|
|
case PSN_SETACTIVE: {
|
|
|
|
NewDevWiz->PrevPage = IDD_NEWDEVWIZ_INSTALLDEV;
|
|
|
|
//
|
|
// This is an intermediary status page, no buttons needed.
|
|
// Set the device description
|
|
// Set the class Icon
|
|
//
|
|
PropSheet_SetWizButtons(hwndParentDlg, 0);
|
|
EnableWindow(GetDlgItem(GetParent(hDlg), IDCANCEL), FALSE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS_INSTALL), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_FILECOPY_TEXT1), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_FILECOPY_TEXT2), SW_HIDE);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_STATUS_TEXT), SW_HIDE);
|
|
|
|
SetDriverDescription(hDlg, IDC_NDW_DESCRIPTION, NewDevWiz);
|
|
|
|
if (SetupDiLoadClassIcon(NewDevWiz->ClassGuidSelected, &hicon, NULL)) {
|
|
hicon = (HICON)SendDlgItemMessage(hDlg, IDC_CLASSICON, STM_SETICON, (WPARAM)hicon, 0L);
|
|
if (hicon) {
|
|
DestroyIcon(hicon);
|
|
}
|
|
}
|
|
|
|
NewDevWiz->CurrCursor = NewDevWiz->IdcWait;
|
|
SetCursor(NewDevWiz->CurrCursor);
|
|
|
|
//
|
|
// If we are doing a silent install then do the actual install here in the PSN_SETACTIVE.
|
|
// Doing the install here means that this wizard page will never be displayed. When we
|
|
// are finished calling InstallDev() then we will jump to any FinishInstall pages that
|
|
// the class/co-installers have added, or we will jump to our finish page.
|
|
//
|
|
if (NewDevWiz->SilentMode) {
|
|
//
|
|
// do the Install immediately and move to the next page
|
|
// to prevent any UI from showing.
|
|
//
|
|
NewDevWiz->hWnd = NULL;
|
|
NewDevWiz->LastError =InstallDev(hDlg, NewDevWiz);
|
|
NewDevWiz->CurrCursor = NULL;
|
|
|
|
|
|
//
|
|
// Add the FinishInstall Page and jump to it if the install was successful
|
|
//
|
|
if (NewDevWiz->LastError == ERROR_SUCCESS) {
|
|
|
|
NewDevWiz->WizExtFinishInstall.hPropSheet = CreateWizExtPage(IDD_WIZARDEXT_FINISHINSTALL,
|
|
WizExtFinishInstallDlgProc,
|
|
NewDevWiz
|
|
);
|
|
|
|
if (NewDevWiz->WizExtFinishInstall.hPropSheet) {
|
|
|
|
PropSheet_AddPage(hwndParentDlg, NewDevWiz->WizExtFinishInstall.hPropSheet);
|
|
}
|
|
|
|
SetDlgMsgResult(hDlg, wMsg, IDD_WIZARDEXT_FINISHINSTALL);
|
|
|
|
} else {
|
|
|
|
//
|
|
// There was an error during the install so just jump to our finish page
|
|
//
|
|
SetDlgMsgResult(hDlg, wMsg, -1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Post ourselves a msg, to do the actual install, this allows this
|
|
// page to show itself while the install is actually occuring.
|
|
//
|
|
else {
|
|
DWORD ThreadId;
|
|
NewDevWiz->hWnd = hDlg;
|
|
|
|
ShowWindow(GetDlgItem(hDlg, IDC_ANIMATE_INSTALL), SW_SHOW);
|
|
Animate_Open(GetDlgItem(hDlg, IDC_ANIMATE_INSTALL), MAKEINTRESOURCE(IDA_INSTALLING));
|
|
Animate_Play(GetDlgItem(hDlg, IDC_ANIMATE_INSTALL), 0, -1, -1);
|
|
|
|
//
|
|
// Start up a separate thread to do the device installation on.
|
|
// When the driver installation is complete the InstallDevThreadProc
|
|
// will post us a WUM_INSTALLCOMPLETE message.
|
|
//
|
|
DeviceInstallThread = CreateThread(NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)InstallDevThreadProc,
|
|
(LPVOID)NewDevWiz,
|
|
0,
|
|
&ThreadId
|
|
);
|
|
|
|
//
|
|
// If the CreateThread fails then we will just call InstallDev() ourselves.
|
|
//
|
|
if (!DeviceInstallThread) {
|
|
|
|
NewDevWiz->hWnd = NULL;
|
|
|
|
//
|
|
// Do the device install
|
|
//
|
|
NewDevWiz->LastError = InstallDev(NewDevWiz->hWnd, NewDevWiz);
|
|
|
|
//
|
|
// Post a message to the window to let it know that we are finished with the install
|
|
//
|
|
PostMessage(hDlg, WUM_INSTALLCOMPLETE, TRUE, GetLastError());
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PSN_WIZNEXT:
|
|
|
|
Animate_Stop(GetDlgItem(hDlg, IDC_ANIMATE_INSTALL));
|
|
|
|
//
|
|
// Add the FinishInstall Page and jump to it if the installation succeded.
|
|
//
|
|
if (NewDevWiz->LastError == ERROR_SUCCESS) {
|
|
|
|
NewDevWiz->WizExtFinishInstall.hPropSheet = CreateWizExtPage(IDD_WIZARDEXT_FINISHINSTALL,
|
|
WizExtFinishInstallDlgProc,
|
|
NewDevWiz
|
|
);
|
|
|
|
if (NewDevWiz->WizExtFinishInstall.hPropSheet) {
|
|
PropSheet_AddPage(hwndParentDlg, NewDevWiz->WizExtFinishInstall.hPropSheet);
|
|
}
|
|
|
|
SetDlgMsgResult(hDlg, wMsg, IDD_WIZARDEXT_FINISHINSTALL);
|
|
|
|
} else {
|
|
|
|
//
|
|
// There was an error during the install so just jump to our finish page
|
|
//
|
|
SetDlgMsgResult(hDlg, wMsg, IDD_NEWDEVWIZ_FINISH);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case WM_SETCURSOR:
|
|
if (NewDevWiz->CurrCursor) {
|
|
SetCursor(NewDevWiz->CurrCursor);
|
|
break;
|
|
}
|
|
|
|
// fall thru to return(FALSE);
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
void
|
|
ShowInstallSummary(
|
|
HWND hDlg,
|
|
PNEWDEVWIZ NewDevWiz
|
|
)
|
|
{
|
|
LONG Error;
|
|
ULONG Problem, DevNodeStatus;
|
|
BOOL HasResources;
|
|
HWND hwndParentDlg = GetParent(hDlg);
|
|
PTCHAR ErrorMsg, ProblemText;
|
|
TCHAR TextBuffer[MAX_PATH*4];
|
|
|
|
|
|
Problem = 0;
|
|
*TextBuffer = TEXT('\0');
|
|
|
|
Error = NewDevWiz->LastError;
|
|
|
|
//
|
|
// On Windows Update installs we don't want to show any UI at all, even
|
|
// if there was an error during the installation.
|
|
// We can tell a WU install from a CDM install because only a WU install
|
|
// has a UpdateDriverInfo structure and is SilentMode.
|
|
// We also never want to show the finish page if this is a NonInteractive
|
|
// install or if we are in GUI setup.
|
|
//
|
|
if ((NewDevWiz->SilentMode &&
|
|
NewDevWiz->UpdateDriverInfo) ||
|
|
((pSetupGetGlobalFlags() & PSPGF_NONINTERACTIVE) ||
|
|
GuiSetupInProgress))
|
|
{
|
|
HideWindowByMove(hwndParentDlg);
|
|
PropSheet_PressButton(hwndParentDlg, PSBTN_FINISH);
|
|
return;
|
|
}
|
|
|
|
if (NewDevWiz->hfontTextBigBold) {
|
|
SetWindowFont(GetDlgItem(hDlg, IDC_FINISH_MSG1), NewDevWiz->hfontTextBigBold, TRUE);
|
|
}
|
|
|
|
if (NDWTYPE_UPDATE == NewDevWiz->InstallType) {
|
|
SetDlgText(hDlg, IDC_FINISH_MSG1, IDS_FINISH_MSG1_UPGRADE, IDS_FINISH_MSG1_UPGRADE);
|
|
|
|
} else {
|
|
SetDlgText(hDlg, IDC_FINISH_MSG1, IDS_FINISH_MSG1_NEW, IDS_FINISH_MSG1_NEW);
|
|
}
|
|
|
|
//
|
|
// Installation failed
|
|
//
|
|
if (Error != ERROR_SUCCESS) {
|
|
NewDevWiz->Installed = FALSE;
|
|
|
|
SetDlgText(hDlg, IDC_FINISH_MSG1, IDS_FINISH_MSG1_INSTALL_PROBLEM, IDS_FINISH_MSG1_INSTALL_PROBLEM);
|
|
SetDlgText(hDlg, IDC_FINISH_MSG2, IDS_FINISH_PROB_MSG2, IDS_FINISH_PROB_MSG2);
|
|
|
|
//
|
|
// Display failure message for installation
|
|
//
|
|
// We will special case the following error codes so we can give a more
|
|
// friendly description of the problem to the user:
|
|
//
|
|
// TRUST_E_SUBJECT_FORM_UNKNOWN
|
|
// ERROR_NO_ASSOCIATED_SERVICE
|
|
// TYPE_E_ELEMENTNOTFOUND
|
|
// ERROR_NOT_FOUND
|
|
//
|
|
if ((Error == TRUST_E_SUBJECT_FORM_UNKNOWN) ||
|
|
(Error == CERT_E_EXPIRED) ||
|
|
(Error == TYPE_E_ELEMENTNOTFOUND) ||
|
|
(Error == ERROR_NOT_FOUND)) {
|
|
|
|
LoadText(TextBuffer,
|
|
SIZECHARS(TextBuffer),
|
|
IDS_FINISH_PROB_TRUST_E_SUBJECT_FORM_UNKNOWN,
|
|
IDS_FINISH_PROB_TRUST_E_SUBJECT_FORM_UNKNOWN);
|
|
|
|
} else if (Error == ERROR_NO_ASSOCIATED_SERVICE) {
|
|
|
|
LoadText(TextBuffer,
|
|
SIZECHARS(TextBuffer),
|
|
IDS_FINISH_PROB_ERROR_NO_ASSOCIATED_SERVICE,
|
|
IDS_FINISH_PROB_ERROR_NO_ASSOCIATED_SERVICE);
|
|
|
|
} else {
|
|
|
|
LoadText(TextBuffer, SIZECHARS(TextBuffer), IDS_NDW_ERRORFIN1_PNP, IDS_NDW_ERRORFIN1_PNP);
|
|
|
|
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
HRESULT_FROM_SETUPAPI(Error),
|
|
0,
|
|
(LPTSTR)&ErrorMsg,
|
|
0,
|
|
NULL
|
|
))
|
|
{
|
|
StringCchCat(TextBuffer, SIZECHARS(TextBuffer), TEXT("\n\n"));
|
|
StringCchCat(TextBuffer, SIZECHARS(TextBuffer), ErrorMsg);
|
|
LocalFree(ErrorMsg);
|
|
}
|
|
}
|
|
|
|
SetDlgItemText(hDlg, IDC_FINISH_MSG3, TextBuffer);
|
|
}
|
|
|
|
//
|
|
// No errors installing the drivers for this device
|
|
//
|
|
else {
|
|
//
|
|
// Check to see if the device itself has any problems
|
|
//
|
|
Error = CM_Get_DevNode_Status(&DevNodeStatus,
|
|
&Problem,
|
|
NewDevWiz->DeviceInfoData.DevInst,
|
|
0
|
|
);
|
|
if(Error != CR_SUCCESS) {
|
|
//
|
|
// For some reason, we couldn't retrieve the devnode's status.
|
|
// Default status and problem values to zero.
|
|
//
|
|
DevNodeStatus = Problem = 0;
|
|
}
|
|
|
|
//
|
|
// make sure the reboot flags\Problem are set correctly
|
|
//
|
|
if (NewDevWiz->Reboot || Problem == CM_PROB_NEED_RESTART) {
|
|
if (Problem != CM_PROB_PARTIAL_LOG_CONF) {
|
|
Problem = CM_PROB_NEED_RESTART;
|
|
}
|
|
|
|
NewDevWiz->Reboot |= DI_NEEDREBOOT;
|
|
}
|
|
|
|
|
|
NewDevWiz->Installed = TRUE;
|
|
HasResources = DeviceHasResources(NewDevWiz->DeviceInfoData.DevInst);
|
|
|
|
//
|
|
// The device has a problem
|
|
//
|
|
if ((Error != CR_SUCCESS) || Problem) {
|
|
//
|
|
// If we are going to launch the troubleshooter then change the finish text.
|
|
//
|
|
// We currently launch the troubleshooter if the device has some type of problem,
|
|
// unless the problem is CM_PROB_NEED_RESTART.
|
|
//
|
|
if (Problem && (Problem != CM_PROB_NEED_RESTART)) {
|
|
|
|
SetDlgText(hDlg, IDC_FINISH_MSG1, IDS_FINISH_MSG1_DEVICE_PROBLEM, IDS_FINISH_MSG1_DEVICE_PROBLEM);
|
|
SetDlgText(hDlg, IDC_FINISH_MSG2, IDS_FINISH_PROB_MSG2, IDS_FINISH_PROB_MSG2);
|
|
|
|
NewDevWiz->LaunchTroubleShooter = TRUE;
|
|
SetDlgText(hDlg, IDC_FINISH_MSG4, IDS_FINISH_PROB_MSG4, IDS_FINISH_PROB_MSG4);
|
|
}
|
|
|
|
//
|
|
// Show the resource button if the device has resources and it
|
|
// has the problem CM_PROB_PARTIAL_LOG_CONF
|
|
//
|
|
if (HasResources && (Problem == CM_PROB_PARTIAL_LOG_CONF)) {
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NDW_DISPLAYRESOURCE), SW_SHOW);
|
|
}
|
|
|
|
if (Problem == CM_PROB_NEED_RESTART) {
|
|
LoadText(TextBuffer, SIZECHARS(TextBuffer), IDS_NEEDREBOOT, IDS_NEEDREBOOT);
|
|
}
|
|
|
|
else if (Problem) {
|
|
ProblemText = DeviceProblemText(NULL,
|
|
NewDevWiz->DeviceInfoData.DevInst,
|
|
Problem
|
|
);
|
|
|
|
if (ProblemText) {
|
|
StringCchCat(TextBuffer, SIZECHARS(TextBuffer), TEXT("\n\n"));
|
|
StringCchCat(TextBuffer, SIZECHARS(TextBuffer), ProblemText);
|
|
LocalFree(ProblemText);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Installation was sucessful and the device does not have any problems
|
|
//
|
|
else {
|
|
//
|
|
// If this was a silent install (a Rank 0 match for example) then don't show the finish
|
|
// page.
|
|
//
|
|
if (NewDevWiz->SilentMode) {
|
|
HideWindowByMove(hwndParentDlg);
|
|
PropSheet_PressButton(hwndParentDlg, PSBTN_FINISH);
|
|
return;
|
|
}
|
|
}
|
|
|
|
SetDlgItemText(hDlg, IDC_FINISH_MSG3, TextBuffer);
|
|
}
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
NDW_FinishDlgProc(
|
|
HWND hDlg,
|
|
UINT wMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
PNEWDEVWIZ NewDevWiz = (PNEWDEVWIZ)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
HICON hicon;
|
|
|
|
switch (wMsg) {
|
|
case WM_INITDIALOG:
|
|
{
|
|
LPPROPSHEETPAGE lppsp = (LPPROPSHEETPAGE)lParam;
|
|
NewDevWiz = (PNEWDEVWIZ)lppsp->lParam;
|
|
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)NewDevWiz);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
hicon = (HICON)SendDlgItemMessage(hDlg, IDC_CLASSICON, STM_GETICON, 0, 0);
|
|
if (hicon) {
|
|
DestroyIcon(hicon);
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (wParam) {
|
|
case IDC_NDW_DISPLAYRESOURCE:
|
|
DisplayResource(NewDevWiz, GetParent(hDlg));
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR FAR *)lParam)->code) {
|
|
case PSN_SETACTIVE: {
|
|
//
|
|
// No back button since install is already done.
|
|
// set the device description
|
|
// Hide Resources button until we know if resources exist or not.
|
|
// Set the class Icon
|
|
//
|
|
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_FINISH);
|
|
|
|
EnableWindow(GetDlgItem(GetParent(hDlg), IDCANCEL), FALSE);
|
|
|
|
ShowWindow(GetDlgItem(hDlg, IDC_NDW_DISPLAYRESOURCE), SW_HIDE);
|
|
|
|
if (NewDevWiz->LastError == ERROR_CANCELLED) {
|
|
|
|
if (NewDevWiz->SilentMode)
|
|
{
|
|
HideWindowByMove(GetParent(hDlg));
|
|
}
|
|
|
|
PropSheet_PressButton(GetParent(hDlg), PSBTN_CANCEL);
|
|
|
|
} else {
|
|
|
|
SetDriverDescription(hDlg, IDC_NDW_DESCRIPTION, NewDevWiz);
|
|
|
|
if (SetupDiLoadClassIcon(NewDevWiz->ClassGuidSelected, &hicon, NULL)) {
|
|
hicon = (HICON)SendDlgItemMessage(hDlg, IDC_CLASSICON, STM_SETICON, (WPARAM)hicon, 0L);
|
|
if (hicon) {
|
|
DestroyIcon(hicon);
|
|
}
|
|
}
|
|
|
|
ShowInstallSummary(hDlg, NewDevWiz);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PSN_RESET:
|
|
break;
|
|
|
|
|
|
case PSN_WIZFINISH:
|
|
if (NewDevWiz->LaunchTroubleShooter) {
|
|
|
|
//
|
|
// The command line that we will run is:
|
|
// %windir%\system32\rundll32 %windir%\system32\devmgr.dll, DeviceProblenWizard_RunDLL /deviceid %s
|
|
// where %s is the device instance id.
|
|
//
|
|
TCHAR FullPath[MAX_PATH];
|
|
TCHAR szCmdLine[512];
|
|
TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
|
|
|
|
if ((CM_Get_Device_ID(NewDevWiz->DeviceInfoData.DevInst,
|
|
DeviceInstanceId,
|
|
SIZECHARS(DeviceInstanceId),
|
|
0
|
|
) == CR_SUCCESS) &&
|
|
GetSystemDirectory(FullPath, SIZECHARS(FullPath)) &&
|
|
pSetupConcatenatePaths(FullPath, TEXT("DEVMGR.DLL"), SIZECHARS(FullPath), NULL)) {
|
|
|
|
if (SUCCEEDED(StringCchPrintf(szCmdLine,
|
|
SIZECHARS(szCmdLine),
|
|
TEXT("%s,DeviceProblenWizard_RunDLL /deviceid %s"),
|
|
FullPath,
|
|
DeviceInstanceId))) {
|
|
|
|
//
|
|
// Now get a full path to rundll32.exe, which lives
|
|
// in the %windir%\systrem32 directory.
|
|
//
|
|
if (GetSystemDirectory(FullPath, SIZECHARS(FullPath)) &&
|
|
pSetupConcatenatePaths(FullPath, TEXT("RUNDLL32.EXE"), SIZECHARS(FullPath), NULL)) {
|
|
|
|
ShellExecute(NULL,
|
|
TEXT("open"),
|
|
FullPath,
|
|
szCmdLine,
|
|
NULL,
|
|
SW_SHOWNORMAL
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
WizExtFinishInstallDlgProc(
|
|
HWND hDlg,
|
|
UINT wMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
HWND hwndParentDlg = GetParent(hDlg);
|
|
PNEWDEVWIZ NewDevWiz = (PNEWDEVWIZ )GetWindowLongPtr(hDlg, DWLP_USER);
|
|
int PrevPageId;
|
|
|
|
UNREFERENCED_PARAMETER(wParam);
|
|
|
|
switch (wMsg) {
|
|
|
|
case WM_INITDIALOG: {
|
|
|
|
LPPROPSHEETPAGE lppsp = (LPPROPSHEETPAGE)lParam;
|
|
NewDevWiz = (PNEWDEVWIZ )lppsp->lParam;
|
|
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)NewDevWiz);
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
break;
|
|
|
|
|
|
case WM_NOTIFY:
|
|
|
|
switch (((NMHDR FAR *)lParam)->code) {
|
|
|
|
case PSN_SETACTIVE:
|
|
|
|
PrevPageId = NewDevWiz->PrevPage;
|
|
NewDevWiz->PrevPage = IDD_WIZARDEXT_FINISHINSTALL;
|
|
|
|
if (PrevPageId == IDD_NEWDEVWIZ_INSTALLDEV)
|
|
{
|
|
PROPSHEETPAGE psp;
|
|
HPROPSHEETPAGE hPage = NULL;
|
|
|
|
//
|
|
// Moving forward on first page
|
|
//
|
|
|
|
//
|
|
// If this was a silent install and NOT a NonInteractive install
|
|
// then we need to create the FinishInstallIntro page at this
|
|
// point so we can add it to the wizard. We do this so the wizard
|
|
// has a proper intro and finish page with the FinishInstall
|
|
// pages inbetween.
|
|
//
|
|
if (NewDevWiz->SilentMode &&
|
|
!(pSetupGetGlobalFlags() & PSPGF_NONINTERACTIVE)) {
|
|
|
|
ZeroMemory(&psp, sizeof(psp));
|
|
psp.dwSize = sizeof(PROPSHEETPAGE);
|
|
psp.hInstance = hNewDev;
|
|
psp.dwFlags = PSP_DEFAULT | PSP_USETITLE | PSP_HIDEHEADER;
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_NEWDEVWIZ_FINISHINSTALL_INTRO);
|
|
psp.pfnDlgProc = FinishInstallIntroDlgProc;
|
|
psp.lParam = (LPARAM)NewDevWiz;
|
|
|
|
hPage = CreatePropertySheetPage(&psp);
|
|
}
|
|
|
|
//
|
|
// Add ClassWizard Extension pages for FinishInstall
|
|
//
|
|
if (AddClassWizExtPages(hwndParentDlg,
|
|
NewDevWiz,
|
|
&NewDevWiz->WizExtFinishInstall.DeviceWizardData,
|
|
DIF_NEWDEVICEWIZARD_FINISHINSTALL,
|
|
hPage
|
|
)) {
|
|
|
|
//
|
|
// If this is a NonInteractive install then we need to set the last
|
|
// error at this point so the error is propagated back to the original
|
|
// caller.
|
|
//
|
|
if (pSetupGetGlobalFlags() & PSPGF_NONINTERACTIVE) {
|
|
|
|
NewDevWiz->LastError = ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we have finish install pages then we should also show the finish
|
|
// page.
|
|
//
|
|
NewDevWiz->SilentMode = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add the end page, which is FinishInstall end
|
|
//
|
|
NewDevWiz->WizExtFinishInstall.hPropSheetEnd = CreateWizExtPage(IDD_WIZARDEXT_FINISHINSTALL_END,
|
|
WizExtFinishInstallEndDlgProc,
|
|
NewDevWiz
|
|
);
|
|
|
|
if (NewDevWiz->WizExtFinishInstall.hPropSheetEnd)
|
|
{
|
|
PropSheet_AddPage(hwndParentDlg, NewDevWiz->WizExtFinishInstall.hPropSheetEnd);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// We can't go backwards, so always go forward
|
|
//
|
|
SetDlgMsgResult(hDlg, wMsg, -1);
|
|
break;
|
|
|
|
case PSN_WIZNEXT:
|
|
SetDlgMsgResult(hDlg, wMsg, 0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
WizExtFinishInstallEndDlgProc(
|
|
HWND hDlg,
|
|
UINT wMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
PNEWDEVWIZ NewDevWiz = (PNEWDEVWIZ )GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
UNREFERENCED_PARAMETER(wParam);
|
|
|
|
switch (wMsg) {
|
|
|
|
case WM_INITDIALOG: {
|
|
|
|
LPPROPSHEETPAGE lppsp = (LPPROPSHEETPAGE)lParam;
|
|
NewDevWiz = (PNEWDEVWIZ )lppsp->lParam;
|
|
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)NewDevWiz);
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
break;
|
|
|
|
|
|
case WM_NOTIFY:
|
|
|
|
switch (((NMHDR FAR *)lParam)->code) {
|
|
|
|
case PSN_SETACTIVE:
|
|
|
|
NewDevWiz->PrevPage = IDD_WIZARDEXT_FINISHINSTALL_END;
|
|
|
|
//
|
|
// We can't go backwards, so always go forward
|
|
//
|
|
SetDlgMsgResult(hDlg, wMsg, IDD_NEWDEVWIZ_FINISH);
|
|
break;
|
|
|
|
case PSN_WIZBACK:
|
|
case PSN_WIZNEXT:
|
|
SetDlgMsgResult(hDlg, wMsg, 0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|