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.
894 lines
29 KiB
894 lines
29 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
devgenpg.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements CDeviceGeneralPage -- device general property page
|
|
|
|
Author:
|
|
|
|
William Hsieh (williamh) created
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
// devgenpg.cpp : implementation file
|
|
//
|
|
|
|
#include "devmgr.h"
|
|
#include "hwprof.h"
|
|
#include "devgenpg.h"
|
|
#include "tswizard.h"
|
|
#include "utils.h"
|
|
|
|
//
|
|
// help topic ids
|
|
//
|
|
|
|
const DWORD g_a103HelpIDs[]=
|
|
{
|
|
IDC_DEVGEN_TITLE_TYPE, idh_devmgr_general_devicetype,
|
|
IDC_DEVGEN_TITLE_MFG, idh_devmgr_general_manufacturer,
|
|
IDC_DEVGEN_STATUSGROUP, IDH_DISABLEHELP,
|
|
IDC_DEVGEN_ICON, IDH_DISABLEHELP, // General: "" (Static)
|
|
IDC_DEVGEN_DESC, IDH_DISABLEHELP, // General: "" (Static)
|
|
IDC_DEVGEN_USAGETEXT, IDH_DISABLEHELP, // General: "Place a check mark next to the configuration(s) where this device should be used." (Static)
|
|
IDC_DEVGEN_TYPE, idh_devmgr_general_devicetype, // General: "" (Static)
|
|
IDC_DEVGEN_MFG, idh_devmgr_general_manufacturer, // General: "" (Static)
|
|
IDC_DEVGEN_STATUS, idh_devmgr_general_device_status, // General: "" (Static)
|
|
IDC_DEVGEN_PROFILELIST, idh_devmgr_general_device_usage, // General: "Dropdown Combo" (SysListView32)
|
|
IDC_DEVGEN_TITLE_LOCATION, idh_devmgr_general_location, // General: location
|
|
IDC_DEVGEN_LOCATION, idh_devmgr_general_location, // General: location
|
|
IDC_DEVGEN_TROUBLESHOOTING, idh_devmgr_general_trouble, // General: troubleshooting
|
|
0, 0
|
|
};
|
|
|
|
CDeviceGeneralPage::~CDeviceGeneralPage()
|
|
{
|
|
if (m_pHwProfileList) {
|
|
|
|
delete m_pHwProfileList;
|
|
}
|
|
|
|
if (m_pProblemAgent) {
|
|
|
|
delete m_pProblemAgent;
|
|
}
|
|
}
|
|
|
|
HPROPSHEETPAGE
|
|
CDeviceGeneralPage::Create(
|
|
CDevice* pDevice
|
|
)
|
|
{
|
|
ASSERT(pDevice);
|
|
|
|
if (pDevice)
|
|
{
|
|
m_pDevice = pDevice;
|
|
|
|
// override PROPSHEETPAGE structure here...
|
|
m_psp.lParam = (LPARAM)this;
|
|
|
|
pDevice->m_pMachine->DiTurnOffDiExFlags(*pDevice, DI_FLAGSEX_PROPCHANGE_PENDING);
|
|
return CreatePage();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL
|
|
CDeviceGeneralPage::OnInitDialog(
|
|
LPPROPSHEETPAGE ppsp
|
|
)
|
|
{
|
|
m_pDevice->m_pMachine->AttachPropertySheet(m_hDlg);
|
|
|
|
|
|
// notify property sheet data that the property sheet is up
|
|
m_pDevice->m_psd.PageCreateNotify(m_hDlg);
|
|
|
|
return CPropSheetPage::OnInitDialog(ppsp);
|
|
}
|
|
|
|
BOOL
|
|
CDeviceGeneralPage::OnApply()
|
|
{
|
|
try
|
|
{
|
|
HWND hwndCB = GetControl(IDC_DEVGEN_PROFILELIST);
|
|
|
|
m_SelectedDeviceUsage = ComboBox_GetCurSel(hwndCB);
|
|
|
|
if ((-1 != m_SelectedDeviceUsage) &&
|
|
(m_SelectedDeviceUsage != m_CurrentDeviceUsage)) {
|
|
|
|
//
|
|
// User is changing the device usage
|
|
//
|
|
UpdateHwProfileStates();
|
|
SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
catch(CMemoryException* e)
|
|
{
|
|
e->Delete();
|
|
MsgBoxParam(m_hDlg, 0, 0, 0);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
CDeviceGeneralPage::UpdateHwProfileStates()
|
|
{
|
|
// first decide what to do: enabling or disabling
|
|
BOOL Canceled;
|
|
Canceled = FALSE;
|
|
|
|
if (m_SelectedDeviceUsage == m_CurrentDeviceUsage) {
|
|
|
|
return;
|
|
}
|
|
|
|
m_pDevice->m_pMachine->DiTurnOnDiFlags(*m_pDevice, DI_NODI_DEFAULTACTION);
|
|
|
|
SP_PROPCHANGE_PARAMS pcp;
|
|
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
|
|
|
|
//
|
|
// specific enabling/disabling
|
|
//
|
|
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
|
|
CHwProfile* phwpf;
|
|
|
|
if (m_pHwProfileList->GetCurrentHwProfile(&phwpf))
|
|
{
|
|
if (DEVICE_ENABLE == m_SelectedDeviceUsage) {
|
|
pcp.StateChange = DICS_ENABLE;
|
|
} else {
|
|
pcp.StateChange = DICS_DISABLE;
|
|
}
|
|
|
|
pcp.HwProfile = phwpf->GetHwProfile();
|
|
|
|
m_pDevice->m_pMachine->DiSetClassInstallParams(*m_pDevice,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
|
|
m_pDevice->m_pMachine->DiCallClassInstaller(DIF_PROPERTYCHANGE, *m_pDevice);
|
|
|
|
Canceled = (ERROR_CANCELLED == GetLastError());
|
|
}
|
|
|
|
//
|
|
// class installer has not objection of our enabling/disabling,
|
|
// do real enabling/disabling.
|
|
//
|
|
if (!Canceled)
|
|
{
|
|
if (m_pHwProfileList->GetCurrentHwProfile(&phwpf))
|
|
{
|
|
if (DEVICE_ENABLE == m_SelectedDeviceUsage)
|
|
{
|
|
//
|
|
// we are enabling the device,
|
|
// do a specific enabling then a globally enabling.
|
|
// the globally enabling will start the device
|
|
// The implementation here is different from
|
|
// Win9x which does a global enabling, a config
|
|
// specific enabling and then a start.
|
|
//
|
|
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
|
|
pcp.HwProfile = phwpf->GetHwProfile();
|
|
|
|
m_pDevice->m_pMachine->DiSetClassInstallParams(*m_pDevice,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
|
|
m_pDevice->m_pMachine->DiChangeState(*m_pDevice);
|
|
|
|
//
|
|
// this call will start the device is it not started.
|
|
//
|
|
pcp.Scope = DICS_FLAG_GLOBAL;
|
|
m_pDevice->m_pMachine->DiSetClassInstallParams(*m_pDevice,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
|
|
m_pDevice->m_pMachine->DiChangeState(*m_pDevice);
|
|
|
|
} else {
|
|
//
|
|
// Do either a global disable or a configspecific disable
|
|
//
|
|
if (DEVICE_DISABLE == m_SelectedDeviceUsage) {
|
|
|
|
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
|
|
|
|
} else {
|
|
|
|
pcp.Scope = DICS_FLAG_GLOBAL;
|
|
}
|
|
|
|
pcp.StateChange = DICS_DISABLE;
|
|
pcp.HwProfile = phwpf->GetHwProfile();
|
|
|
|
m_pDevice->m_pMachine->DiSetClassInstallParams(*m_pDevice,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
|
|
m_pDevice->m_pMachine->DiChangeState(*m_pDevice);
|
|
}
|
|
}
|
|
|
|
// signal that the property of the device is changed.
|
|
m_pDevice->m_pMachine->DiTurnOnDiFlags(*m_pDevice, DI_PROPERTIES_CHANGE);
|
|
m_RestartFlags |= (m_pDevice->m_pMachine->DiGetFlags(*m_pDevice)) & (DI_NEEDRESTART | DI_NEEDREBOOT);
|
|
}
|
|
|
|
// remove class install parameters, this also reset
|
|
// DI_CLASSINATLLPARAMS
|
|
m_pDevice->m_pMachine->DiSetClassInstallParams(*m_pDevice, NULL, 0);
|
|
|
|
m_pDevice->m_pMachine->DiTurnOffDiFlags(*m_pDevice, DI_NODI_DEFAULTACTION);
|
|
}
|
|
|
|
BOOL
|
|
CDeviceGeneralPage::OnLastChanceApply()
|
|
{
|
|
//
|
|
// The last chance apply message is sent in reverse order page n to page 0.
|
|
//
|
|
// Since we are the first page in the set of property sheets we can gaurentee
|
|
// that the last PSN_LASTCHANCEAPPLY message will be the last message for all
|
|
// the pages.
|
|
//
|
|
// The property sheet is going away, consolidate the changes on the device. We
|
|
// do this during the PSN_LASTCHANCEAPPLY message so that the property sheet is
|
|
// still displayed while we do the DIF_PROPERTYCHANGE. There are some cases where
|
|
// the DIF_PROPERTYCHANGE can take quite a long time so we want the property sheet
|
|
// to still be shown while this is happening. If we do this during the DestroyCallback
|
|
// the property sheet UI has already been torn down and so the user won't realize
|
|
// we are still saving the changes.
|
|
//
|
|
try
|
|
{
|
|
ASSERT(m_pDevice);
|
|
|
|
if (m_pDevice->m_pMachine->DiGetExFlags(*m_pDevice) & DI_FLAGSEX_PROPCHANGE_PENDING)
|
|
{
|
|
DWORD Status = 0, Problem = 0;
|
|
|
|
//
|
|
// If the device has the DN_WILL_BE_REMOVED flag set and the user is
|
|
// attempting to change settings on the driver then we will prompt them for a
|
|
// reboot and include text in the prompt that explains this device
|
|
// is in the process of being removed.
|
|
//
|
|
if (m_pDevice->GetStatus(&Status, &Problem) &&
|
|
(Status & DN_WILL_BE_REMOVED)) {
|
|
|
|
//
|
|
// First try and send a MMCPropertyChangeNotify message to our
|
|
// CComponent so that it can prompt for a reboot inside the
|
|
// device manager thread instead of our thread. If this is not
|
|
// done then the property sheet will hang around after device
|
|
// manager has gone away...which will cause a "hung app" dialog
|
|
// to appear.
|
|
//
|
|
CNotifyRebootRequest* pNRR = new CNotifyRebootRequest(NULL, DI_NEEDRESTART, IDS_WILL_BE_REMOVED_NO_CHANGE_SETTINGS);
|
|
|
|
if (pNRR) {
|
|
if (!m_pDevice->m_psd.PropertyChangeNotify(reinterpret_cast<LONG_PTR>(pNRR))) {
|
|
//
|
|
// There isn't a CComponent around, so this is just a property
|
|
// sheet running outside of MMC.
|
|
//
|
|
pNRR->Release();
|
|
PromptForRestart(m_hDlg, DI_NEEDRESTART, IDS_WILL_BE_REMOVED_NO_CHANGE_SETTINGS);
|
|
}
|
|
} else {
|
|
//
|
|
// We couldn't allocate memory to create our CNotifyRebootRequest
|
|
// instance, so just prompt for a reboot in this thread.
|
|
//
|
|
PromptForRestart(m_hDlg, DI_NEEDRESTART, IDS_WILL_BE_REMOVED_NO_CHANGE_SETTINGS);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// property change pending, issue a DICS_PROPERTYCHANGE to the
|
|
// class installer. A DICS_PROPCHANGE would basically stop the
|
|
// device and restart it. If each property page issues
|
|
// its own DICS_PROPCHANGE command, the device would
|
|
// be stopped/started several times even though one is enough.
|
|
// A property page sets DI_FLAGEX_PROPCHANGE_PENDING when it needs
|
|
// a DICS_PROPCHANGE command to be issued.
|
|
//
|
|
SP_PROPCHANGE_PARAMS pcp;
|
|
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
|
|
|
|
HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
pcp.Scope = DICS_FLAG_GLOBAL;
|
|
pcp.StateChange = DICS_PROPCHANGE;
|
|
m_pDevice->m_pMachine->DiSetClassInstallParams(*m_pDevice,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
m_pDevice->m_pMachine->DiCallClassInstaller(DIF_PROPERTYCHANGE, *m_pDevice);
|
|
m_pDevice->m_pMachine->DiTurnOnDiFlags(*m_pDevice, DI_PROPERTIES_CHANGE);
|
|
m_pDevice->m_pMachine->DiTurnOffDiExFlags(*m_pDevice, DI_FLAGSEX_PROPCHANGE_PENDING);
|
|
|
|
if (hCursorOld) {
|
|
|
|
SetCursor(hCursorOld);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
catch(CMemoryException* e)
|
|
{
|
|
e->Delete();
|
|
MsgBoxParam(m_hDlg, 0, 0, 0);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
UINT
|
|
CDeviceGeneralPage::DestroyCallback()
|
|
{
|
|
//
|
|
// We do this because this is the page we are sure will be created --
|
|
// this page is ALWAYS the first page.
|
|
//
|
|
ASSERT(m_pDevice);
|
|
|
|
//
|
|
// We don't want to prompt for a reboot if the device has the problem
|
|
// DN_WILL_BE_REMOVED, because we have already prompted for a reboot
|
|
// before we get to this destroy callback.
|
|
//
|
|
DWORD Status = 0, Problem = 0;
|
|
if (m_pDevice->GetStatus(&Status, &Problem) &&
|
|
!(Status & DN_WILL_BE_REMOVED)) {
|
|
|
|
m_RestartFlags |= m_pDevice->m_pMachine->DiGetFlags(*m_pDevice);
|
|
if (m_RestartFlags & (DI_NEEDRESTART | DI_NEEDREBOOT))
|
|
{
|
|
//
|
|
// Do not use our window handle(or its parent) as the parent
|
|
// to the newly create dialog because they are in "detroyed state".
|
|
// WM_CLOSE does not help either.
|
|
// NULL window handle(Desktop) should be okay here.
|
|
//
|
|
// We only want to prompt for a restart if device manager is connected
|
|
// to the local machine.
|
|
//
|
|
if (m_pDevice->m_pMachine->IsLocal())
|
|
{
|
|
//
|
|
// First try and send a MMCPropertyChangeNotify message to our
|
|
// CComponent so that it can prompt for a reboot inside the
|
|
// device manager thread instead of our thread. If this is not
|
|
// done then the property sheet will hang around after device
|
|
// manager has gone away...which will cause a "hung app" dialog
|
|
// to appear.
|
|
//
|
|
CNotifyRebootRequest* pNRR = new CNotifyRebootRequest(NULL, m_RestartFlags, 0);
|
|
|
|
if (pNRR) {
|
|
if (!m_pDevice->m_psd.PropertyChangeNotify(reinterpret_cast<LONG_PTR>(pNRR))) {
|
|
//
|
|
// There isn't a CComponent around, so this is just a property
|
|
// sheet running outside of MMC.
|
|
//
|
|
pNRR->Release();
|
|
PromptForRestart(NULL, m_RestartFlags);
|
|
}
|
|
} else {
|
|
//
|
|
// We couldn't allocate memory to create our CNotifyRebootRequest
|
|
// instance, so just prompt for a reboot in this thread.
|
|
//
|
|
PromptForRestart(NULL, m_RestartFlags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// notify CPropSheetData that the property sheet is going away
|
|
m_pDevice->m_psd.PageDestroyNotify(m_hDlg);
|
|
if (m_RestartFlags & DI_PROPERTIES_CHANGE)
|
|
{
|
|
// Device properties changed. We need to refresh the machine.
|
|
// Since we are running in a separate thread, we can not
|
|
// call the refresh function, instead, we schedule it.
|
|
// This should be done before enabling the refresh
|
|
if (m_pDevice->m_pMachine->m_ParentMachine) {
|
|
|
|
m_pDevice->m_pMachine->m_ParentMachine->ScheduleRefresh();
|
|
|
|
} else {
|
|
|
|
m_pDevice->m_pMachine->ScheduleRefresh();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Detach this property page from the CMachine so that the CMachine can be
|
|
// destroyed now if it needs to be.
|
|
//
|
|
m_pDevice->m_pMachine->DetachPropertySheet(m_hDlg);
|
|
|
|
ASSERT(!::IsWindow(m_hwndLocationTip));
|
|
|
|
//
|
|
// Destory the CMachine.
|
|
//
|
|
CMachine* pMachine;
|
|
pMachine = m_pDevice->m_pMachine;
|
|
|
|
if (pMachine->ShouldPropertySheetDestroy()) {
|
|
|
|
delete pMachine;
|
|
}
|
|
|
|
return CPropSheetPage::DestroyCallback();
|
|
}
|
|
|
|
void
|
|
CDeviceGeneralPage::UpdateControls(
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
if (lParam)
|
|
m_pDevice = (CDevice*)lParam;
|
|
|
|
m_RestartFlags = 0;
|
|
|
|
try
|
|
{
|
|
//
|
|
// Calling PropertyChanged() will update the display name for the device. We need
|
|
// to do this in case a 3rd party property sheet did something that could change
|
|
// the device's display name.
|
|
//
|
|
m_pDevice->PropertyChanged();
|
|
|
|
SetDlgItemText(m_hDlg, IDC_DEVGEN_DESC, m_pDevice->GetDisplayName());
|
|
TCHAR TempString[512];
|
|
String str;
|
|
m_pDevice->GetMFGString(str);
|
|
SetDlgItemText(m_hDlg, IDC_DEVGEN_MFG, str);
|
|
SetDlgItemText(m_hDlg, IDC_DEVGEN_TYPE, m_pDevice->GetClassDisplayName());
|
|
|
|
HICON hIconNew;
|
|
hIconNew = m_pDevice->LoadClassIcon();
|
|
if (hIconNew)
|
|
{
|
|
HICON hIconOld;
|
|
m_IDCicon = IDC_DEVGEN_ICON; // Save for cleanup in OnDestroy.
|
|
hIconOld = (HICON)SendDlgItemMessage(m_hDlg, IDC_DEVGEN_ICON,
|
|
STM_SETICON, (WPARAM)hIconNew,
|
|
0);
|
|
if (hIconOld)
|
|
DestroyIcon(hIconOld);
|
|
}
|
|
|
|
//
|
|
// get the device location information
|
|
//
|
|
if (::GetLocationInformation(m_pDevice->GetDevNode(),
|
|
TempString,
|
|
ARRAYLEN(TempString),
|
|
m_pDevice->m_pMachine->GetHMachine()) != CR_SUCCESS)
|
|
{
|
|
// if the devnode does not have location information,
|
|
// use the word "unknown"
|
|
LoadString(g_hInstance, IDS_UNKNOWN, TempString, ARRAYLEN(TempString));
|
|
}
|
|
|
|
SetDlgItemText(m_hDlg, IDC_DEVGEN_LOCATION, TempString);
|
|
|
|
AddToolTips(m_hDlg, IDC_DEVGEN_LOCATION, TempString, &m_hwndLocationTip);
|
|
|
|
ShowWindow(GetControl(IDC_DEVGEN_TROUBLESHOOTING), SW_HIDE);
|
|
|
|
DWORD Problem, Status;
|
|
if (m_pDevice->GetStatus(&Status, &Problem) || m_pDevice->IsPhantom())
|
|
{
|
|
//
|
|
// if the device is a phantom device, use the CM_PROB_PHANTOM
|
|
//
|
|
if (m_pDevice->IsPhantom())
|
|
{
|
|
Problem = CM_PROB_PHANTOM;
|
|
Status = DN_HAS_PROBLEM;
|
|
}
|
|
|
|
//
|
|
// 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 && m_pDevice->IsRAW())
|
|
{
|
|
Problem = CM_PROB_FAILED_START;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// if a device has a private problem then give it special text telling
|
|
// the user to contact the manufacturer of this driver.
|
|
//
|
|
if (Status & DN_PRIVATE_PROBLEM)
|
|
{
|
|
str.LoadString(g_hInstance, IDS_SPECIALPROB_PRIVATEPROB);
|
|
}
|
|
|
|
//
|
|
// if a device is not started and still does not have a problem
|
|
// then give it special problem text saying that this device
|
|
// does not have a driver.
|
|
//
|
|
else if (!(Status & DN_STARTED) && !(Status & DN_HAS_PROBLEM))
|
|
{
|
|
if (::GetSystemMetrics(SM_CLEANBOOT) == 0) {
|
|
|
|
str.LoadString(g_hInstance, IDS_SPECIALPROB_NODRIVERS);
|
|
|
|
} else {
|
|
|
|
str.LoadString(g_hInstance, IDS_SPECIALPROB_SAFEMODE);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Display normal problem text
|
|
//
|
|
else
|
|
{
|
|
UINT len = GetDeviceProblemText(Problem, TempString, ARRAYLEN(TempString));
|
|
if (len)
|
|
{
|
|
if (len < ARRAYLEN(TempString))
|
|
{
|
|
SetDlgItemText(m_hDlg, IDC_DEVGEN_STATUS, TempString);
|
|
str = TempString;
|
|
}
|
|
else
|
|
{
|
|
BufferPtr<TCHAR> TextPtr(len + 1);
|
|
GetDeviceProblemText(Problem, TextPtr, len + 1);
|
|
|
|
str = (LPTSTR)TextPtr;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
str.LoadString(g_hInstance, IDS_PROB_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add the 'related driver blocked' text if the device has the devnode
|
|
// flag DN_DRIVER_BLOCKED set and it does not have the problem
|
|
// CM_PROB_DRIVER_BLOCKED.
|
|
//
|
|
if ((Status & DN_DRIVER_BLOCKED) &&
|
|
(Problem != CM_PROB_DRIVER_BLOCKED)) {
|
|
LoadString(g_hInstance, IDS_DRIVER_BLOCKED, TempString, ARRAYLEN(TempString));
|
|
str += TempString;
|
|
}
|
|
|
|
//
|
|
// Add the 'child device with invalid Id' text if the device has
|
|
// the devnode flag DN_CHILD_WITH_INVALID_ID set.
|
|
//
|
|
if (Status & DN_CHILD_WITH_INVALID_ID) {
|
|
LoadString(g_hInstance, IDS_CHILD_WITH_INVALID_ID, TempString, ARRAYLEN(TempString));
|
|
str += TempString;
|
|
}
|
|
|
|
//
|
|
// Add the 'will be removed' text if the device is going to be removed
|
|
// on the next restart.
|
|
//
|
|
if (Status & DN_WILL_BE_REMOVED) {
|
|
|
|
LoadString(g_hInstance, IDS_WILL_BE_REMOVED, TempString, ARRAYLEN(TempString));
|
|
str += TempString;
|
|
}
|
|
|
|
//
|
|
// Add the restart text if the device needs to be restarted
|
|
//
|
|
if (Status & DN_NEED_RESTART) {
|
|
|
|
LoadString(g_hInstance, IDS_NEED_RESTART, TempString, ARRAYLEN(TempString));
|
|
str += TempString;
|
|
|
|
m_RestartFlags |= DI_NEEDRESTART;
|
|
}
|
|
|
|
//
|
|
// Create the problem agent and update the control text
|
|
//
|
|
if (!(Status & DN_PRIVATE_PROBLEM))
|
|
{
|
|
if (m_pProblemAgent) {
|
|
|
|
delete m_pProblemAgent;
|
|
}
|
|
|
|
m_pProblemAgent = new CProblemAgent(m_pDevice, Problem, FALSE);
|
|
|
|
DWORD Len;
|
|
Len = m_pProblemAgent->InstructionText(TempString, ARRAYLEN(TempString));
|
|
if (Len)
|
|
{
|
|
str += TempString;
|
|
}
|
|
|
|
Len = m_pProblemAgent->FixitText(TempString, ARRAYLEN(TempString));
|
|
if (Len)
|
|
{
|
|
::ShowWindow(GetControl(IDC_DEVGEN_TROUBLESHOOTING), SW_SHOW);
|
|
SetDlgItemText(m_hDlg, IDC_DEVGEN_TROUBLESHOOTING, TempString);
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
TRACE((TEXT("%s has not status, devnode =%lx, cr = %lx\n"),
|
|
m_pDevice->GetDisplayName(), m_pDevice->GetDevNode(),
|
|
m_pDevice->m_pMachine->GetLastCR()));
|
|
str.LoadString(g_hInstance, IDS_PROB_UNKNOWN);
|
|
::ShowWindow(GetControl(IDC_DEVGEN_TROUBLESHOOTING), SW_HIDE);
|
|
}
|
|
|
|
SetDlgItemText(m_hDlg, IDC_DEVGEN_STATUS, str);
|
|
|
|
if (m_pDevice->NoChangeUsage() ||
|
|
!m_pDevice->IsDisableable() ||
|
|
(CM_PROB_HARDWARE_DISABLED == Problem))
|
|
{
|
|
// the device disallows any changes on hw profile.
|
|
// disable all profile related controls.
|
|
::EnableWindow(GetControl(IDC_DEVGEN_PROFILELIST), FALSE);
|
|
::EnableWindow(GetControl(IDC_DEVGEN_USAGETEXT), FALSE);
|
|
}
|
|
|
|
else
|
|
{
|
|
HWND hwndCB = GetControl(IDC_DEVGEN_PROFILELIST);
|
|
|
|
DWORD ConfigFlags;
|
|
|
|
if (!m_pDevice->GetConfigFlags(&ConfigFlags)) {
|
|
|
|
ConfigFlags = 0;
|
|
}
|
|
|
|
//
|
|
// only want the disabled bit
|
|
//
|
|
ConfigFlags &= CONFIGFLAG_DISABLED;
|
|
|
|
//
|
|
// rebuild the profile list.
|
|
//
|
|
if (m_pHwProfileList) {
|
|
|
|
delete m_pHwProfileList;
|
|
}
|
|
|
|
m_pHwProfileList = new CHwProfileList();
|
|
|
|
if (m_pHwProfileList->Create(m_pDevice, ConfigFlags))
|
|
{
|
|
ComboBox_ResetContent(hwndCB);
|
|
|
|
//
|
|
// Get the current device usage
|
|
//
|
|
if (m_pDevice->IsStateDisabled()) {
|
|
|
|
if ((m_pHwProfileList->GetCount() > 1) && ConfigFlags) {
|
|
|
|
m_CurrentDeviceUsage = DEVICE_DISABLE_GLOBAL;
|
|
|
|
} else {
|
|
|
|
m_CurrentDeviceUsage = DEVICE_DISABLE;
|
|
}
|
|
|
|
} else {
|
|
|
|
m_CurrentDeviceUsage = DEVICE_ENABLE;
|
|
}
|
|
|
|
|
|
//
|
|
// Always add the Enable item
|
|
//
|
|
String Enable;
|
|
|
|
Enable.LoadString(g_hInstance, IDS_ENABLE_CURRENT);
|
|
ComboBox_AddString(hwndCB, Enable);
|
|
|
|
//
|
|
// Add the disable items. There will either be one disable
|
|
// item if there is only one hardware profile or two if there
|
|
// are more than one hardware profile.
|
|
//
|
|
if (m_pHwProfileList->GetCount() > 1) {
|
|
|
|
String DisableInCurrent;
|
|
DisableInCurrent.LoadString(g_hInstance, IDS_DISABLE_IN_PROFILE);
|
|
ComboBox_AddString(hwndCB, DisableInCurrent);
|
|
|
|
String DisableGlobal;
|
|
DisableGlobal.LoadString(g_hInstance, IDS_DISABLE_GLOBAL);
|
|
ComboBox_AddString(hwndCB, DisableGlobal);
|
|
|
|
} else {
|
|
|
|
String Disable;
|
|
Disable.LoadString(g_hInstance, IDS_DISABLE_CURRENT);
|
|
ComboBox_AddString(hwndCB, Disable);
|
|
}
|
|
|
|
ComboBox_SetCurSel(hwndCB, m_CurrentDeviceUsage);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a remote computer or the user is not an administrator
|
|
// then disable the enable/disable drop down list along with the
|
|
// TroubleShooter button.
|
|
//
|
|
if (!m_pDevice->m_pMachine->IsLocal() ||
|
|
!g_IsAdmin)
|
|
{
|
|
::EnableWindow(GetControl(IDC_DEVGEN_PROFILELIST), FALSE);
|
|
::EnableWindow(GetControl(IDC_DEVGEN_TROUBLESHOOTING), FALSE);
|
|
}
|
|
|
|
//
|
|
// Check if we need to autolauch the troubleshooter
|
|
//
|
|
if (m_pDevice->m_pMachine->IsLocal() &&
|
|
g_IsAdmin &&
|
|
m_pDevice->m_bLaunchTroubleShooter) {
|
|
|
|
m_pDevice->m_bLaunchTroubleShooter = FALSE;
|
|
::PostMessage(m_hDlg, WM_COMMAND, MAKELONG(IDC_DEVGEN_TROUBLESHOOTING, BN_CLICKED), 0);
|
|
}
|
|
|
|
}
|
|
catch (CMemoryException* e)
|
|
{
|
|
e->Delete();
|
|
MsgBoxParam(m_hDlg, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CDeviceGeneralPage::OnQuerySiblings(
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
DMQUERYSIBLINGCODE Code = (DMQUERYSIBLINGCODE)wParam;
|
|
|
|
if (QSC_TO_FOREGROUND == Code)
|
|
{
|
|
HWND hwndSheet;
|
|
|
|
hwndSheet = m_pDevice->m_psd.GetWindowHandle();
|
|
|
|
if (GetForegroundWindow() != hwndSheet)
|
|
{
|
|
SetForegroundWindow(hwndSheet);
|
|
}
|
|
|
|
SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, 1);
|
|
return TRUE;
|
|
}
|
|
|
|
return CPropSheetPage::OnQuerySiblings(wParam, lParam);
|
|
}
|
|
|
|
BOOL
|
|
CDeviceGeneralPage::OnCommand(
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
|
|
if (BN_CLICKED == HIWORD(wParam) &&
|
|
IDC_DEVGEN_TROUBLESHOOTING == LOWORD(wParam)) {
|
|
|
|
BOOL fChanged = FALSE;
|
|
|
|
if (m_pProblemAgent) {
|
|
|
|
fChanged = m_pProblemAgent->FixIt(GetParent(m_hDlg));
|
|
}
|
|
|
|
if (fChanged) {
|
|
|
|
m_pDevice->PropertyChanged();
|
|
m_pDevice->GetClass()->PropertyChanged();
|
|
m_pDevice->m_pMachine->DiTurnOnDiFlags(*m_pDevice, DI_PROPERTIES_CHANGE);
|
|
UpdateControls();
|
|
PropSheet_SetTitle(GetParent(m_hDlg), PSH_PROPTITLE, m_pDevice->GetDisplayName());
|
|
PropSheet_CancelToClose(GetParent(m_hDlg));
|
|
|
|
//
|
|
// ISSUE: JasonC 2/7/2000
|
|
//
|
|
// A refresh on m_pDevice->m_pMachine is necessary here.
|
|
// Since we are running on a different thread and each
|
|
// property page may have cached the HDEVINFO and the
|
|
// SP_DEVINFO_DATA, refresh on the CMachine object can not
|
|
// be done here. The problem is worsen by the fact that
|
|
// user can go back to the device tree and work on the tree
|
|
// while this property sheet is still up.
|
|
// It would be nice if MMC would support modal dialog boxes.
|
|
//
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDeviceGeneralPage::OnHelp(
|
|
LPHELPINFO pHelpInfo
|
|
)
|
|
{
|
|
WinHelp((HWND)pHelpInfo->hItemHandle, DEVMGR_HELP_FILE_NAME, HELP_WM_HELP,
|
|
(ULONG_PTR)g_a103HelpIDs);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CDeviceGeneralPage::OnContextMenu(
|
|
HWND hWnd,
|
|
WORD xPos,
|
|
WORD yPos
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(xPos);
|
|
UNREFERENCED_PARAMETER(yPos);
|
|
|
|
WinHelp(hWnd, DEVMGR_HELP_FILE_NAME, HELP_CONTEXTMENU,
|
|
(ULONG_PTR)g_a103HelpIDs);
|
|
return FALSE;
|
|
}
|
|
|