Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1503 lines
38 KiB

/*++
Copyright (C) Microsoft Corporation
Module Name:
api.cpp
Abstract:
This module implements Device Manager exported APIs.
Author:
William Hsieh (williamh) created
Revision History:
--*/
#include "devmgr.h"
#include "devgenpg.h"
#include "devdrvpg.h"
#include "devpopg.h"
#include "api.h"
#include "printer.h"
#include "tswizard.h"
STDAPI_(BOOL)
DeviceManager_ExecuteA(
HWND hwndStub,
HINSTANCE hAppInstance,
LPCWSTR lpMachineName,
int nCmdShow
)
/*++
See DeviceManager_Execute function below.
--*/
{
try
{
CTString tstrMachineName(lpMachineName);
return DeviceManager_Execute(hwndStub,
hAppInstance,
(LPCTSTR)tstrMachineName,
nCmdShow
);
}
catch(CMemoryException* e)
{
e->Delete();
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return FALSE;
}
STDAPI_(BOOL)
DeviceManager_ExecuteW(
HWND hwndStub,
HINSTANCE hAppInstance,
LPCWSTR lpMachineName,
int nCmdShow
)
/*++
See DeviceManager_Execute function below.
--*/
{
try
{
CTString tstrMachineName(lpMachineName);
return DeviceManager_Execute(hwndStub,
hAppInstance,
(LPCTSTR)tstrMachineName,
nCmdShow
);
}
catch(CMemoryException* e)
{
e->Delete();
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return FALSE;
}
BOOL
DeviceManager_Execute(
HWND hwndStub,
HINSTANCE hAppInstance,
LPCTSTR lpMachineName,
int nCmdShow
)
/*++
Routine Description:
This function is executed via a rundll command line and can have the
following form:
rundll32.exe devmgr.dll, DeviceManager_Execute
rundll32.exe devmgr.dll, DeviceManager_Execute <remote machine name>
This function will call ShelExecuteEx to create a new device manager process,
where the new device manager can be for the local machine or for a machine
name that is passed in on the rundll command line.
Arguments:
hwndStub - Windows handle to receive any message boxes that might be
displayed.
hAppInstance - HINSTANCE.
lpMachineName - Name of a remote machine that the new device manager
process should connect to and show it's devices.
nCmdShow - Flag that specifies how device manager should be shown when
it is opened. It can be one of the SW_ values (i.e. SW_SHOW).
Return Value:
returns TRUE if the device manager process was successfully created, or
FALSE otherwise.
--*/
{
SHELLEXECUTEINFO sei;
TCHAR Parameters[MAX_PATH];
String strMachineOptions;
String strParameters;
if (lpMachineName &&
!VerifyMachineName(lpMachineName)) {
//
// We were unable to connect to the remote machine either because it
// doesn't exist, or because we don't have the proper access.
// The VerifyMachineName API sets the appropriate last error code.
//
return FALSE;
}
if (lpMachineName == NULL) {
//
// The lpMachineName was NULL so don't add a machine name to the command
// line.
//
strMachineOptions.Empty();
} else {
strMachineOptions.Format(DEVMGR_MACHINENAME_OPTION, lpMachineName);
}
WCHAR* FilePart;
DWORD Size;
Size = SearchPath(NULL, DEVMGR_MSC_FILE, NULL, ARRAYLEN(Parameters), Parameters, &FilePart);
if (Size && (Size <= MAX_PATH)) {
strParameters = Parameters;
} else {
strParameters = DEVMGR_MSC_FILE;
}
//
// If we have a machine name then add it onto the end.
//
if (!strMachineOptions.IsEmpty()) {
strParameters += MMC_COMMAND_LINE;
strParameters += strMachineOptions;
}
memset(&sei, 0, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.hwnd = hwndStub;
sei.nShow = nCmdShow;
sei.hInstApp = hAppInstance;
sei.lpFile = MMC_FILE;
sei.lpParameters = (LPTSTR)strParameters;
return ShellExecuteEx(&sei);
}
BOOL
AddPageCallback(
HPROPSHEETPAGE hPage,
LPARAM lParam
)
{
CPropSheetData* ppsData = (CPropSheetData*)lParam;
ASSERT(ppsData);
return ppsData->InsertPage(hPage);
}
void
ReportCmdLineError(
HWND hwndParent,
int ErrorStringID,
LPCTSTR Caption
)
{
String strTitle, strMsg;
strMsg.LoadString(g_hInstance, ErrorStringID);
if (!Caption)
{
strTitle.LoadString(g_hInstance, IDS_NAME_DEVMGR);
Caption = (LPTSTR)strTitle;
}
MessageBox(hwndParent, (LPTSTR)strMsg, Caption, MB_OK | MB_ICONERROR);
}
STDAPI_(void)
DeviceProperties_RunDLLA(
HWND hwndStub,
HINSTANCE hAppInstance,
LPSTR lpCmdLine,
int nCmdShow
)
/*++
See DeviceProperties_RunDLL function below.
--*/
{
try
{
CTString tstrCmdLine(lpCmdLine);
DeviceProperties_RunDLL(hwndStub,
hAppInstance,
(LPCTSTR)tstrCmdLine,
nCmdShow
);
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
}
}
STDAPI_(void)
DeviceProperties_RunDLLW(
HWND hwndStub,
HINSTANCE hAppInstance,
LPWSTR lpCmdLine,
int nCmdShow
)
/*++
See DeviceProperties_RunDLL function below.
--*/
{
try
{
CTString tstrCmdLine(lpCmdLine);
DeviceProperties_RunDLL(hwndStub,
hAppInstance,
(LPCTSTR)tstrCmdLine,
nCmdShow
);
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
}
}
void
DeviceProperties_RunDLL(
HWND hwndStub,
HINSTANCE hAppInstance,
LPCTSTR lpCmdLine,
int nCmdShow
)
/*++
Routine Description:
This API will bring up the property pages for the specified device. Other
options, such as the remote machine name, whether the device manager tree
should be shown or not, whether the resource tab should be shows, and
whether the troubleshooter should be automatically launched, can also be
specified.
This function is executed via a rundll command line and can have the
following form:
rundll32.exe devmgr.dll, DeviceProperties_RunDLL <options>
Additional command line options that are excepted are:
/MachineName <machine name>
If this option is specified then the API will bring up the properties
for the specified device on this remote machine.
/DeviceId <device instance Id>
If this option is specified then this will be the device for which
the properties will be displayed for.
NOTE: The caller must either specify a DeviceId or use the
ShowDeviceTree command line option.
/ShowDeviceTree
If this command line option is specified then the property sheet
will be displayed in front of the entire device manager tree.
/Flags <flags>
The following flags are supported:
DEVPROP_SHOW_RESOURCE_TAB 0x00000001
DEVPROP_LAUNCH_TROUBLESHOOTER 0x00000002
Arguments:
hwndStub - Windows handle to receive any message boxes that might be
displayed.
hAppInstance - HINSTANCE.
lpCmdLine - Name of a remote machine that the new device manager
process should connect to and show it's devices.
nCmdShow - Flag that specifies how device manager should be shown when
it is opened. It can be one of the SW_ values (i.e. SW_SHOW).
Return Value:
none
--*/
{
UNREFERENCED_PARAMETER(hAppInstance);
UNREFERENCED_PARAMETER(nCmdShow);
try
{
CRunDLLCommandLine CmdLine;
CmdLine.ParseCommandLine(lpCmdLine);
if (NULL == CmdLine.GetDeviceID())
{
ReportCmdLineError(hwndStub, IDS_NO_DEVICEID);
return;
}
//
// Let the DevicePropertiesEx API do all the appropriate error checking.
//
DevicePropertiesEx(hwndStub,
CmdLine.GetMachineName(),
CmdLine.GetDeviceID(),
CmdLine.GetFlags(),
CmdLine.ToShowDeviceTree()
);
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
return;
}
}
STDAPI_(int)
DevicePropertiesA(
HWND hwndParent,
LPCSTR MachineName,
LPCSTR DeviceID,
BOOL ShowDeviceTree
)
/*++
See DevicePropertiesEx function below.
--*/
{
try
{
CTString tstrMachineName(MachineName);
CTString tstrDeviceID(DeviceID);
return DevicePropertiesEx(hwndParent,
(LPCTSTR)tstrMachineName,
(LPCTSTR)tstrDeviceID,
DEVPROP_SHOW_RESOURCE_TAB,
ShowDeviceTree
);
}
catch (CMemoryException* e)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
e->Delete();
}
return 0;
}
STDAPI_(int)
DevicePropertiesW(
HWND hwndParent,
LPCWSTR MachineName,
LPCWSTR DeviceID,
BOOL ShowDeviceTree
)
/*++
See DevicePropertiesEx function below.
--*/
{
try
{
CTString tstrMachineName(MachineName);
CTString tstrDeviceID(DeviceID);
return DevicePropertiesEx(hwndParent,
(LPCTSTR)tstrMachineName,
(LPCTSTR)tstrDeviceID,
DEVPROP_SHOW_RESOURCE_TAB,
ShowDeviceTree
);
}
catch (CMemoryException* e)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
e->Delete();
}
return 0;
}
STDAPI_(int)
DevicePropertiesExA(
HWND hwndParent,
LPCSTR MachineName,
LPCSTR DeviceID,
DWORD Flags,
BOOL ShowDeviceTree
)
/*++
See DevicePropertiesEx function below.
--*/
{
try
{
CTString tstrMachineName(MachineName);
CTString tstrDeviceID(DeviceID);
return DevicePropertiesEx(hwndParent,
(LPCTSTR)tstrMachineName,
(LPCTSTR)tstrDeviceID,
Flags,
ShowDeviceTree
);
}
catch (CMemoryException* e)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
e->Delete();
}
return 0;
}
STDAPI_(int)
DevicePropertiesExW(
HWND hwndParent,
LPCWSTR MachineName,
LPCWSTR DeviceID,
DWORD Flags,
BOOL ShowDeviceTree
)
/*++
See DevicePropertiesEx function below.
--*/
{
try
{
CTString tstrMachineName(MachineName);
CTString tstrDeviceID(DeviceID);
return DevicePropertiesEx(hwndParent,
(LPCTSTR)tstrMachineName,
(LPCTSTR)tstrDeviceID,
Flags,
ShowDeviceTree
);
}
catch (CMemoryException* e)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
e->Delete();
}
return 0;
}
int
DevicePropertiesEx(
HWND hwndParent,
LPCTSTR MachineName,
LPCTSTR DeviceID,
DWORD Flags,
BOOL ShowDeviceTree
)
/*++
Routine Description:
This API will bring up the property page for the specified device.
Arguments:
hwndParent - the caller's window handle to be used as the owner window
of the property page and any other windows this API may create.
MachineName - optional machine name. If given, it must be in its fully
qualified form. NULL means local machine
DeviceId - device instance id of the device that this API should create
the property sheet for.
Flags - the following flags are supported:
DEVPROP_SHOW_RESOURCE_TAB 0x00000001 - show the resource tab, by
default the resource tab
is not shown.
DEVPROP_LAUNCH_TROUBLESHOOTER 0x00000002 - Automatically launch the
troubleshooter for this
device.
ShowDeviceTree - If specified then the device manager tree is shown. If
the DeviceID is specified then only that device is shown
in the tree, otherwise all devices are shown.
Return Value:
The return value from PropertySheet, including ID_PSREBOOTSYSTEM if
a reboot is needed due to any user actions.
-1 is returned in case of an error.
--*/
{
HPROPSHEETPAGE hPage;
DWORD DiFlags;
DWORD DiFlagsEx;
//
// Verify that a DeviceID was passed in unless they want to show the
// whole device tree.
//
if ((!DeviceID || (TEXT('\0') == *DeviceID)) && !ShowDeviceTree) {
SetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
//
// Verify that valid flags are passed in
//
if (Flags &~ DEVPROP_BITS) {
SetLastError(ERROR_INVALID_FLAGS);
return -1;
}
if (MachineName &&
!VerifyMachineName(MachineName)) {
//
// We were unable to connect to the remote machine either because it
// doesn't exist, or because we don't have the proper access.
// The VerifyMachineName API sets the appropriate last error code.
//
return -1;
}
if (ShowDeviceTree) {
return PropertyRunDeviceTree(hwndParent, MachineName, DeviceID);
}
int Result = -1;
CDevice* pDevice;
PVOID Context;
try {
CMachine TheMachine(MachineName);
// create the machine just for this device
if (!TheMachine.Initialize(hwndParent, DeviceID)) {
SetLastError(ERROR_NO_SUCH_DEVINST);
return -1;
}
if (!TheMachine.GetFirstDevice(&pDevice, Context)) {
SetLastError(ERROR_NO_SUCH_DEVINST);
return -1;
}
//
// If the troubleshooter should be launched then set the appropriate
// BOOL inside of the pDevice class.
//
if (Flags & DEVPROP_LAUNCH_TROUBLESHOOTER) {
pDevice->m_bLaunchTroubleShooter = TRUE;
}
CPropSheetData& psd = pDevice->m_psd;
//
// Initialize CPropSheetData without ConsoleHandle
//
if (psd.Create(g_hInstance, hwndParent, MAX_PROP_PAGES, 0l)) {
psd.m_psh.pszCaption = pDevice->GetDisplayName();
//
// Add any class/device specific property pages.
//
TheMachine.DiGetClassDevPropertySheet(*pDevice, &psd.m_psh,
MAX_PROP_PAGES,
TheMachine.IsLocal() ?
DIGCDP_FLAG_ADVANCED :
DIGCDP_FLAG_REMOTE_ADVANCED);
//
// Add the general tab
//
DiFlags = TheMachine.DiGetFlags(*pDevice);
DiFlagsEx = TheMachine.DiGetExFlags(*pDevice);
if (DiFlags & DI_GENERALPAGE_ADDED) {
String strWarning;
strWarning.LoadString(g_hInstance, IDS_GENERAL_PAGE_WARNING);
MessageBox(hwndParent, (LPTSTR)strWarning, pDevice->GetDisplayName(),
MB_ICONEXCLAMATION | MB_OK);
//
// fall through to create our general page.
//
}
SafePtr<CDeviceGeneralPage> GenPagePtr;
CDeviceGeneralPage* pGeneralPage = new CDeviceGeneralPage();
GenPagePtr.Attach(pGeneralPage);
hPage = pGeneralPage->Create(pDevice);
if (hPage) {
if (psd.InsertPage(hPage, 0)) {
GenPagePtr.Detach();
}
else {
::DestroyPropertySheetPage(hPage);
}
}
//
// Add the driver tab
//
if (!(DiFlags & DI_DRIVERPAGE_ADDED)) {
SafePtr<CDeviceDriverPage> DrvPagePtr;
CDeviceDriverPage* pDriverPage = new CDeviceDriverPage();
DrvPagePtr.Attach(pDriverPage);
hPage = pDriverPage->Create(pDevice);
if (hPage) {
if (psd.InsertPage(hPage)) {
DrvPagePtr.Detach();
}
else {
::DestroyPropertySheetPage(hPage);
}
}
}
//
// Add the resource tab
//
if ((Flags & DEVPROP_SHOW_RESOURCE_TAB) &&
pDevice->HasResources() &&
!(DiFlags & DI_RESOURCEPAGE_ADDED)) {
TheMachine.DiGetExtensionPropSheetPage(*pDevice,
AddPageCallback,
SPPSR_SELECT_DEVICE_RESOURCES,
(LPARAM)&psd
);
}
#ifndef _WIN64
//
// Add the power tab if this is the local machine
//
if (TheMachine.IsLocal() && !(DiFlagsEx & DI_FLAGSEX_POWERPAGE_ADDED))
{
//
// Check if the device support power management
//
CPowerShutdownEnable ShutdownEnable;
CPowerWakeEnable WakeEnable;
if (ShutdownEnable.Open(pDevice->GetDeviceID()) || WakeEnable.Open(pDevice->GetDeviceID())) {
ShutdownEnable.Close();
WakeEnable.Close();
SafePtr<CDevicePowerMgmtPage> PowerMgmtPagePtr;
CDevicePowerMgmtPage* pPowerPage = new CDevicePowerMgmtPage;
PowerMgmtPagePtr.Attach(pPowerPage);
hPage = pPowerPage->Create(pDevice);
if (hPage) {
if (psd.InsertPage(hPage)) {
PowerMgmtPagePtr.Detach();
}
else {
::DestroyPropertySheetPage(hPage);
}
}
}
}
#endif
//
// Add any Bus property pages if this is the local machine
//
if (TheMachine.IsLocal())
{
CBusPropPageProvider* pBusPropPageProvider = new CBusPropPageProvider();
SafePtr<CBusPropPageProvider> ProviderPtr;
ProviderPtr.Attach(pBusPropPageProvider);
if (pBusPropPageProvider->EnumPages(pDevice, &psd)) {
psd.AddProvider(pBusPropPageProvider);
ProviderPtr.Detach();
}
}
Result = (int)psd.DoSheet();
if (-1 != Result) {
if (TheMachine.DiGetExFlags(*pDevice) & DI_FLAGSEX_PROPCHANGE_PENDING) {
//
// property change pending, issue a DICS_PROPERTYCHANGE
// to the class installer
//
SP_PROPCHANGE_PARAMS pcp;
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
pcp.Scope = DICS_FLAG_GLOBAL;
pcp.StateChange = DICS_PROPCHANGE;
TheMachine.DiSetClassInstallParams(*pDevice,
&pcp.ClassInstallHeader,
sizeof(pcp)
);
TheMachine.DiCallClassInstaller(DIF_PROPERTYCHANGE, *pDevice);
TheMachine.DiTurnOnDiFlags(*pDevice, DI_PROPERTIES_CHANGE);
TheMachine.DiTurnOffDiExFlags(*pDevice, DI_FLAGSEX_PROPCHANGE_PENDING);
}
//
// Merge restart/reboot flags
//
DiFlags = TheMachine.DiGetFlags(*pDevice);
if (DI_NEEDREBOOT & DiFlags) {
Result |= ID_PSREBOOTSYSTEM;
}
if (DI_NEEDRESTART & DiFlags) {
Result |= ID_PSRESTARTWINDOWS;
}
}
}
}
catch (CMemoryException* e) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
e->Delete();
}
return -1;
}
STDAPI_(UINT)
DeviceProblemTextA(
HMACHINE hMachine,
DEVNODE DevNode,
ULONG ProblemNumber,
LPSTR Buffer,
UINT BufferSize
)
/*++
Routine Description:
This API gets the problem description associated with the specified
problem code.
Arguments:
hMachine - not used.
DevNode - not used.
ProblemNumber - CM problem code to get the problem text for.
Buffer - Buffer to receive the problem text.
BufferSize - size of Buffer in characters. This can be 0 if the caller
wants to know how large of a buffer they should allocate.
Return Value:
UINT required size to hold the problem text string, or 0 in case of
an error. Use GetLastError() for extended error information.
--*/
{
UNREFERENCED_PARAMETER(hMachine);
UNREFERENCED_PARAMETER(DevNode);
WCHAR* wchBuffer = NULL;
UINT RealSize = 0;
if (BufferSize && !Buffer)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (BufferSize)
{
try
{
wchBuffer = new WCHAR[BufferSize];
}
catch (CMemoryException* e)
{
e->Delete();
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
}
RealSize = GetDeviceProblemText(ProblemNumber, wchBuffer, BufferSize);
if (RealSize && BufferSize > RealSize)
{
ASSERT(wchBuffer);
RealSize = WideCharToMultiByte(CP_ACP, 0, wchBuffer, RealSize,
Buffer, BufferSize, NULL, NULL);
Buffer[RealSize] = '\0';
}
if (wchBuffer)
{
delete [] wchBuffer;
}
return RealSize;
}
STDAPI_(UINT)
DeviceProblemTextW(
HMACHINE hMachine,
DEVNODE DevNode,
ULONG ProblemNumber,
LPWSTR Buffer,
UINT BufferSize
)
/*++
Routine Description:
This API gets the problem description associated with the specified
problem code.
Arguments:
hMachine - not used.
DevNode - not used.
ProblemNumber - CM problem code to get the problem text for.
Buffer - Buffer to receive the problem text.
BufferSize - size of Buffer in characters. This can be 0 if the caller
wants to know how large of a buffer they should allocate.
Return Value:
UINT required size to hold the problem text string, or 0 in case of
an error. Use GetLastError() for extended error information.
--*/
{
UNREFERENCED_PARAMETER(hMachine);
UNREFERENCED_PARAMETER(DevNode);
return GetDeviceProblemText(ProblemNumber, Buffer, BufferSize);
}
int
PropertyRunDeviceTree(
HWND hwndParent,
LPCTSTR MachineName,
LPCTSTR DeviceID
)
{
SHELLEXECUTEINFOW sei;
TCHAR Parameters[MAX_PATH];
String strParameters;
String strMachineOptions;
String strDeviceIdOptions;
String strCommandOptions;
TCHAR* FilePart;
DWORD Size;
Size = SearchPath(NULL, DEVMGR_MSC_FILE, NULL, MAX_PATH, Parameters, &FilePart);
if (Size && Size <= MAX_PATH) {
strParameters = Parameters;
} else {
strParameters = DEVMGR_MSC_FILE;
}
strParameters += MMC_COMMAND_LINE;
if (MachineName != NULL) {
strMachineOptions.Format(DEVMGR_MACHINENAME_OPTION, MachineName);
strParameters += strMachineOptions;
}
if (DeviceID != NULL) {
strDeviceIdOptions.Format(DEVMGR_DEVICEID_OPTION, DeviceID);
strParameters += strDeviceIdOptions;
strCommandOptions.Format(DEVMGR_COMMAND_OPTION, DEVMGR_CMD_PROPERTY);
strParameters += strCommandOptions;
}
memset(&sei, 0, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.hwnd = hwndParent;
sei.nShow = SW_NORMAL;
sei.hInstApp = g_hInstance;
sei.lpFile = MMC_FILE;
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpParameters = (LPTSTR)strParameters;
if (ShellExecuteEx(&sei) && sei.hProcess)
{
WaitForSingleObject(sei.hProcess, INFINITE);
CloseHandle(sei.hProcess);
return 1;
}
return 0;
}
STDAPI_(void)
DeviceProblenWizard_RunDLLA(
HWND hwndStub,
HINSTANCE hAppInstance,
LPSTR lpCmdLine,
int nCmdShow
)
/*++
See DeviceProblemWizard_RunDLL function below.
--*/
{
try
{
CTString tstrCmdLine(lpCmdLine);
DeviceProblenWizard_RunDLL(hwndStub,
hAppInstance,
(LPCTSTR)tstrCmdLine,
nCmdShow
);
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
}
}
STDAPI_(void)
DeviceProblenWizard_RunDLLW(
HWND hwndStub,
HINSTANCE hAppInstance,
LPWSTR lpCmdLine,
int nCmdShow
)
/*++
See DeviceProblemWizard_RunDLL function below.
--*/
{
try
{
CTString tstrCmdLine(lpCmdLine);
DeviceProblenWizard_RunDLL(hwndStub,
hAppInstance,
(LPCTSTR)tstrCmdLine,
nCmdShow
);
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
}
}
void
DeviceProblenWizard_RunDLL(
HWND hwndStub,
HINSTANCE hAppInstance,
LPCTSTR lpCmdLine,
int nCmdShow
)
/*++
Routine Description:
This API will bring up the problem wizard (troubleshooter) for the
specified device.
This function is executed via a rundll command line and can have the
following form:
rundll32.exe devmgr.dll, DeviceProblemWizard_RunDLL /DeviceId <device instance Id>
Arguments:
hwndStub - Windows handle to receive any message boxes that might be
displayed.
hAppInstance - HINSTANCE.
lpCmdLine - Name of a remote machine that the new device manager
process should connect to and show it's devices.
nCmdShow - Flag that specifies how device manager should be shown when
it is opened. It can be one of the SW_ values (i.e. SW_SHOW).
Return Value:
none
--*/
{
UNREFERENCED_PARAMETER(hAppInstance);
UNREFERENCED_PARAMETER(nCmdShow);
try
{
CRunDLLCommandLine CmdLine;
CmdLine.ParseCommandLine(lpCmdLine);
//
// Let the DeviceProblemWizard handle all of the parameter validation.
//
DeviceProblemWizard(hwndStub,
CmdLine.GetMachineName(),
CmdLine.GetDeviceID()
);
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
return;
}
}
STDAPI_(int)
DeviceProblemWizardA(
HWND hwndParent,
LPCSTR MachineName,
LPCSTR DeviceId
)
/*++
See DeviceProblemWizard function below.
--*/
{
try
{
CTString tstrMachineName(MachineName);
CTString tstrDeviceId(DeviceId);
return DeviceProblemWizard(hwndParent, tstrMachineName, tstrDeviceId);
}
catch(CMemoryException* e)
{
e->Delete();
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return 0;
}
STDAPI_(int)
DeviceProblemWizardW(
HWND hwndParent,
LPCWSTR MachineName,
LPCWSTR DeviceId
)
/*++
See DeviceProblemWizard function below.
--*/
{
try
{
CTString tstrMachineName(MachineName);
CTString tstrDeviceId(DeviceId);
return DeviceProblemWizard(hwndParent, tstrMachineName, tstrDeviceId);
}
catch (CMemoryException* e)
{
e->Delete();
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return 0;
}
int
DeviceProblemWizard(
HWND hwndParent,
LPCTSTR MachineName,
LPCTSTR DeviceId
)
/*++
Routine Description:
This API will launch the device troubleshooter for the specified
device instance Id.
The troubleshooter can have any form, including HTML help, help center
pages, or an actual Win32 troubleshooter. The troubleshooter also should
be specific to the CM Problem code that the device has, but if no
troubleshooter exists for the CM Problem code, then a more generic
troubleshooter will be launched.
Arguments:
hwndParent - the caller's window handle to be used as the owner window
of the property page and any other windows this API may create.
MachineName - Must be NULL. Currently there is no support for remote
troubleshooters
DeviceId - device instance id of the device that this API should launch
the troubleshooter.
Return Value:
1 if a troubleshooter is successfully launched, 0 otherwise. Use
GetLastError to get extended error information if 0 is returned.
--*/
{
int iRet = 0;
DWORD Problem, Status;
if (!DeviceId) {
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (MachineName) {
//
// Currently getting troubleshooters are remote machines is not
// implemented.
//
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return 0;
}
try
{
CMachine TheMachine(NULL);
//
// create the machine just for this device
//
if (!TheMachine.Initialize(hwndParent, DeviceId))
{
SetLastError(ERROR_NO_SUCH_DEVINST);
iRet = 0;
goto clean0;
}
PVOID Context;
CDevice* pDevice;
if (!TheMachine.GetFirstDevice(&pDevice, Context))
{
SetLastError(ERROR_NO_SUCH_DEVINST);
iRet = 0;
goto clean0;
}
if (pDevice->GetStatus(&Status, &Problem)) {
//
// if the device is a phantom device, use the CM_PROB_PHANTOM
//
if (pDevice->IsPhantom()) {
Problem = CM_PROB_PHANTOM;
}
//
// if the device is not started and no problem is assigned to it
// fake the problem number to be failed start.
//
if (!(Status & DN_STARTED) && !Problem && pDevice->IsRAW()) {
Problem = CM_PROB_FAILED_START;
}
}
CProblemAgent* pProblemAgent = new CProblemAgent(pDevice, Problem, TRUE);
if (pProblemAgent) {
pProblemAgent->FixIt(hwndParent);
}
iRet = 1;
clean0:;
} catch(CMemoryException* e) {
e->Delete();
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return iRet;
}
STDAPI_(int)
DeviceAdvancedPropertiesA(
HWND hwndParent,
LPTSTR MachineName,
LPTSTR DeviceId
)
/*++
See DeviceAdvancedProperties function below.
--*/
{
try
{
CTString tstrMachineName(MachineName);
CTString tstrDeviceID(DeviceId);
return DeviceAdvancedProperties(hwndParent,
(LPCTSTR)tstrMachineName,
(LPCTSTR)tstrDeviceID
);
}
catch (CMemoryException* e)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
e->Delete();
}
return 0;
}
STDAPI_(int)
DeviceAdvancedPropertiesW(
HWND hwndParent,
LPCWSTR MachineName,
LPCWSTR DeviceId
)
/*++
See DeviceAdvancedProperties function below.
--*/
{
try
{
CTString tstrMachineName(MachineName);
CTString tstrDeviceID(DeviceId);
return DeviceAdvancedProperties(hwndParent,
(LPCTSTR)tstrMachineName,
(LPCTSTR)tstrDeviceID
);
}
catch (CMemoryException* e)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
e->Delete();
}
return 0;
}
int DeviceAdvancedProperties(
HWND hwndParent,
LPCTSTR MachineName,
LPCTSTR DeviceId
)
/*++
Routine Description:
This API creates a property sheet and asks the given device's property
page provider to add any advanced pages to the property sheet.
The purpose of this API is for an application to manage device advanced
properties only. Standard property pages (General, Driver, Resource,
Power, Bus pages) are not added.
We get these advanced pages by calling SetupDiGetClassDevPropertySheet
with DIGCDP_FLAG_ADVANCED, for the local machine case, and
DIGCDP_FLAG_REMOTE_ADVANCED if a remote MachineName is passed in.
NOTE: If the device does not have any advanced property pages, then no UI
is displayed.
Arguments:
hwndParent - the caller's window handle to be used as the owner window
of the property page and any other windows this API may create.
MachineName - optional machine name. If given, it must be in its fully
qualified form. NULL means local machine
DeviceId - device instance id of the device that this API should create
the property sheet for.
Return Value:
The return value from PropertySheet, including ID_PSREBOOTSYSTEM if
a reboot is needed due to any user actions.
-1 is returned in case of an error.
--*/
{
if (!DeviceId)
{
SetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
if (MachineName &&
!VerifyMachineName(MachineName)) {
//
// We were unable to connect to the remote machine either because it
// doesn't exist, or because we don't have the proper access.
// The VerifyMachineName API sets the appropriate last error code.
//
return -1;
}
CMachine TheMachine(MachineName);
CDevice* pDevice;
PVOID Context;
try
{
if (TheMachine.Initialize(hwndParent, DeviceId) &&
TheMachine.GetFirstDevice(&pDevice, Context))
{
TheMachine.EnableRefresh(FALSE);
CPropSheetData& psd = pDevice->m_psd;
//initialize CPropSheetData without ConsoleHandle
if (psd.Create(g_hInstance, hwndParent, MAX_PROP_PAGES, 0l))
{
psd.m_psh.pszCaption = pDevice->GetDisplayName();
if (TheMachine.DiGetClassDevPropertySheet(*pDevice, &psd.m_psh,
MAX_PROP_PAGES,
TheMachine.IsLocal() ?
DIGCDP_FLAG_ADVANCED :
DIGCDP_FLAG_REMOTE_ADVANCED))
{
int Result = (int)psd.DoSheet();
if (-1 != Result)
{
// merge restart/reboot flags
DWORD DiFlags = TheMachine.DiGetFlags(*pDevice);
if (DI_NEEDREBOOT & DiFlags)
{
Result |= ID_PSREBOOTSYSTEM;
}
if (DI_NEEDRESTART & DiFlags)
{
Result |= ID_PSRESTARTWINDOWS;
}
}
return Result;
}
}
}
}
catch (CMemoryException* e)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
e->Delete();
}
return -1;
}