在windows平台上有一套SetupDi系列API可以获取所有的硬件设备,以及对其进行操作。现在我来主要说一下对指定设备的启用和禁用操作。
首先说明一下,我的项目是个MFC对话框程序,我自己定义了一个结构体用来存放相关的设备信息
typedef struct tagDeviceInfo
{
//设备友好名称,很友好……
CString szDeviceName;
//设备类
CString szDeviceClass;
//设备显示名
CString szDeviceDesc;
//设备驱动
CString szDriverName;
//设备实例
DWORD dwDevIns;
//设备类标志
GUID Guid;
//按类名排序
bool operator < (const tagDeviceInfo &tmp) const
{
if (tmp.szDeviceClass != szDeviceClass)
{
return tmp.szDeviceClass.CompareNoCase (szDeviceClass) > 0;
}
else
{
return tmp.szDeviceDesc.CompareNoCase (szDeviceDesc) > 0;
}
}
}DeviceInfo;
接着是进行设备的枚举,并将其信息保存在我的容器中
(PS: 函数里的PrintError函数是我自定义的一个输出错误码的函数,大家可以无视和注释掉)
BOOL DeviceOpt::GetDeviceList(LPGUID lpGuid)
{
BOOL bFlag = TRUE;
do
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
// 得到所有设备 HDEVINFO
hDevInfo = SetupDiGetClassDevs(lpGuid, 0, 0, DIGCF_PRESENT /*| DIGCF_ALLCLASSES */);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
PrintError ("SetUpDi ERR!");
bFlag = FALSE;
break;
}
DeviceInfo theItem;
// 循环列举
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++)
{
TCHAR szClassBuf[MAX_PATH] = { 0 };
TCHAR szDescBuf[MAX_PATH] = { 0 };
TCHAR szDriver[MAX_PATH] = { 0 };
TCHAR szFriName[MAX_PATH] = { 0 };
// 获取类名
if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_CLASS, NULL, (PBYTE)szClassBuf, MAX_PATH - 1, NULL))
{
///*continue*/;
PrintError ("Get szClassBuf Name ERR!");
}
theItem.szDeviceClass = szClassBuf;
//获取设备描述信息
if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC, NULL, (PBYTE)szDescBuf, MAX_PATH - 1, NULL))
{
///*continue*/;
PrintError ("Get szDescBuf Name ERR!");
}
theItem.szDeviceDesc = szDescBuf;
//获取设备驱动名
if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_DRIVER, NULL, (PBYTE)szDriver, MAX_PATH - 1, NULL))
{
///*continue*/;
PrintError ("Get szDriver Name ERR!");
}
theItem.szDriverName = szDriver;
//获取设备友好名
if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_FRIENDLYNAME, NULL, (PBYTE)szFriName, MAX_PATH - 1, NULL))
{
///*continue*/;
PrintError ("Get Friend Name ERR!");
}
//ERROR_INSUFFICIENT_BUFFER
theItem.szDeviceName = szFriName;
theItem.dwDevIns = DeviceInfoData.DevInst;
theItem.Guid = DeviceInfoData.ClassGuid;
theDeviceList.push_back (theItem);
}
// 释放
SetupDiDestroyDeviceInfoList(hDevInfo);
sort(theDeviceList.begin (),theDeviceList.end (), less<DeviceInfo>());
} while (FALSE);
return bFlag;
}
这里要说明一下参数是我要获取的设备的类型名,这是个GUID类型的所有设备类都定义在devguid.h中,具体情况看你想要什么类型的设备,如果想要枚举所有设备,参数应该像下面这个填写。
SetupDiGetClassDevs(NULL, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES );
可以看到我获取了设备的类名,描述信息,驱动名,设备友好名,以及所属类GUID和设备实例(DeviceInfoData.DevInst),
之所以保存了这个设备实例是因为在后面的启用操作中,当我想要启用一个我程序禁用掉的设备时,发现设备列表窜位了,当我启动当前设备上面的一个设备时,我禁用的设备才能正确启动。所以我保存这个实例以便我能找到正确的设备。
下面是我启用/禁用的函数实现
//设置设备状态(启用/停用),1为启用,0为停用
BOOL DeviceOpt::SetDeviceStatus(DeviceInfo &theDevice,BOOL bStatusFlag)
{
BOOL bFlag = TRUE;
do
{
SP_DEVINFO_DATA DeviceInfoData;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
HDEVINFO hDevInfo;
// 得到设备 HDEVINFO
hDevInfo = SetupDiGetClassDevs(&theDevice.Guid, 0, 0, DIGCF_PRESENT /*| DIGCF_ALLCLASSES */);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
PrintError ("SetUpDi ERR!");
bFlag = FALSE;
break;
}
//判断是否有这个设备
bFlag = FALSE;
int index = 0;
while (SetupDiEnumDeviceInfo(hDevInfo, index ++, &DeviceInfoData))
{
if (DeviceInfoData.DevInst == theDevice.dwDevIns)
{
bFlag = TRUE;
break;
}
}
if (!bFlag)
{
PrintError ("Dev Not Found!");
}
else
{
//初始化属性
SP_PROPCHANGE_PARAMS propChange;
propChange.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
propChange.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
propChange.Scope = DICS_FLAG_GLOBAL;
propChange.StateChange = bStatusFlag ? DICS_START: DICS_STOP;
propChange.HwProfile = 0;
if (SetupDiSetClassInstallParams(hDevInfo, &DeviceInfoData, (SP_CLASSINSTALL_HEADER*)&propChange, sizeof(propChange)))
{
if (!SetupDiChangeState(hDevInfo, &DeviceInfoData))
{
PrintError ("Change Device ERR!");
bFlag = FALSE;
}
}
else
{
PrintError ("SetupDiSetClassInstallParams ERR!");
bFlag = FALSE;
}
}
// 释放
SetupDiDestroyDeviceInfoList(hDevInfo);
} while (FALSE);
return bFlag;
}
参数分别是一个设备信息的引用(实际需要的信息只有设备类 和设备实例),和一个启用/禁用标志,
propChange.StateChange = bStatusFlag ? DICS_START: DICS_STOP;
在上面的代码部分就是判断是启用还是禁用。
还有个问题是系统win 64位的时候如果程序是32位的话会提示错误,得需要将程序版本改为64位才可以正常运行。
PS:上面这个问题已经解决了,使用SetupDiChangeState 这个API就可以实现32位的程序操作64位系统的设备,不过需要注意的是使用这个API禁用的设备无法使用设备管理器启用,只能通过这个API再把状态改回去,所以请慎用!!
参考文章:
http://blog.csdn.net/vlily/article/details/8037056
http://blog.sina.com.cn/s/blog_7d1dc9de01011szj.html