想要实现类似设备管理器的功能,其实也不是很难,无非就是调用一些API函数,就像本文描述的,采用的API函数就是SetupDi系列的函数。不过这类函数有很多,具体的请参见MSDN,而实现设备启用、停用仅需要用到的就只有5个函数:
SetupDiGetClassDevs // 获取设备信息集
SetupDiEnumDeviceInfo // 从设备信息集中枚举每个设备的具体信息
SetupDiGetDeviceRegistryProperty // 从注册表中读取PnP设备的属性
SetupDiSetClassInstallParams // 设置(包括取消)设备类的安装参数
SetupDiCallClassInstaller // 安装指定设备
以上函数均在setupapi.h头文件中声明,该头文件包含在setupapi.lib函数库中(使用以上函数前需要声明这个头文件)。
接下来就是如何实现设备的启用与停用。
从原理上讲,设备的启用与停用其实就是对该设备进行重安装。
首先,我们需要声明两个变量用来保存指定设备类的属性信息:
HDEVINFO m_hDevInfo; // 类似设备句柄,以下暂且称为设备句柄
SP_DEVINFO_DATAm_DeviceInfoData; // 设备详细属性信息
然后调用SetupDiGetClassDevs函数获取设备句柄的值。(在这个函数中,需要指定设备类的GUID,如果不清楚这个GUID,可以在相应的安装文件.inf中查找。注意:是设备类的GUID,不是设备的GUID!)
接着循环使用SetupDiEnumDeviceInfo函数枚举对应设备类中的设备,并使用SetupDiGetDeviceRegistryProperty函数获取得到的设备的详细信息,进行判断是否为所需的设备(判断的方式有多种,具体参考MSDN,本文采用设备描述进行判断)。
一旦枚举结束(即枚举不成功,而且用GetLastError()可以得到错误码259)即可退出循环。当然如果找到设备,即可break退出。
如果找到对应的设备,就调用SetupDiSetClassInstallParams函数设置安装的属性。这里有个注意的地方需要详细说明一下:
SetupDiSetClassInstallParams的函数原型如下:
WINSETUPAPI BOOL WINAPI
SetupDiSetClassInstallParams(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
IN PSP_CLASSINSTALL_HEADER ClassInstallParams, OPTIONAL
IN DWORD ClassInstallParamsSize
);
注意第三个参数PSP_CLASSINSTALL_HEADER ClassInstallParams,
这里我们不采用这个结构,而是采用另外一个结构:SP_PROPCHANGE_PARAMS
并在这个结构中,
设置ClassInstallHeader字段中(我们发现这个字段也是一个结构,就是PSP_CLASSINSTALL_HEADER结构)的InstallFunction字段值为DIF_PROPERTYCHANGE,
设置StateChange值为DICS_ENABLE(该值为启用,若是停用则为DICS_DISABLE)
然后采用强行转换将其转为PSP_CLASSINSTALL_HEADER结构。
最后,调用SetupDiCallClassInstaller函数执行设备的安装(即:启用或者停用),注意该函数第一个参数值应为DIF_PROPERTYCHANGE。
从设备管理器中,可以验证我们的做法。
int index = 0;
DWORD DataType, length;
HDEVINFO hDevInfo;
SP_DEVINFO_DATA devInfoElem;
TCHAR szDevDetail[SPDRP_MAXIMUM_PROPERTY][NORMAL_LEN];
GUIDnguid{0X4D36E96F,0XE325,0X11CE,0XBF,0XC1,0X08,0X00,0X2B,0XE1,0X03,0X18 };
hDevInfo = SetupDiGetClassDevs((LPGUID)&nguid, NULL, NULL, DIGCF_PRESENT);
if (INVALID_HANDLE_VALUE != hDevInfo)
{
devInfoElem.cbSize = sizeof(SP_DEVINFO_DATA);
while (SetupDiEnumDeviceInfo(hDevInfo, index++, &devInfoElem))
{
memset((TCHAR*)szDevDetail, 0x00, sizeof(szDevDetail) / sizeof(TCHAR));
SetupDiGetDeviceRegistryProperty(
hDevInfo,
&devInfoElem,
SPDRP_DEVICEDESC,
&DataType,
(PBYTE)szDevDetail[SPDRP_DEVICEDESC],
NORMAL_LEN,
(LPDWORD)&length);
strrchr(szDevDetail[SPDRP_DEVICEDESC], _T(' '));
SetupDiGetDeviceRegistryProperty(
hDevInfo,
&devInfoElem,
SPDRP_HARDWAREID,
&DataType,
(PBYTE)szDevDetail[SPDRP_HARDWAREID],
NORMAL_LEN,
(LPDWORD)&length);
_strlwr_s(szDevDetail[SPDRP_HARDWAREID]);
CString gszHardwareID_PC,gszHardwareID_Phone(szDevDetail[SPDRP_HARDWAREID]);
#if 1
CString Outstr;
Outstr.Format(_T("\nAct data is %s,Act data is %s"), szDevDetail[SPDRP_DEVICEDESC], szDevDetail[SPDRP_HARDWAREID]);
OutputDebugString(Outstr);
#endif
SP_PROPCHANGE_PARAMS PropChangeParams;
SP_DEVINSTALL_PARAMS devParams;
DWORD dwResult;
//设置设备属性变化参数
PropChangeParams.ClassInstallHeader.cbSize= sizeof(SP_CLASSINSTALL_HEADER);
PropChangeParams.ClassInstallHeader.InstallFunction= DIF_PROPERTYCHANGE;
PropChangeParams.Scope= DICS_FLAG_GLOBAL; //使修改的属性保存在所有的硬件属性文件
PropChangeParams.StateChange= DICS_DISABLE;
PropChangeParams.HwProfile= 0;
//改变设备属性
if (!SetupDiSetClassInstallParams(hDevInfo,
&devInfoElem,
(SP_CLASSINSTALL_HEADER *)&PropChangeParams,
sizeof(PropChangeParams)))
{
dwResult =GetLastError();
Outstr.Format("\nSetupDiSetClassInstallParams1FAILED, Error Code is %d", dwResult);
OutputDebugString(Outstr);
return;
}
PropChangeParams.ClassInstallHeader.cbSize= sizeof(SP_CLASSINSTALL_HEADER);
PropChangeParams.ClassInstallHeader.InstallFunction= DIF_PROPERTYCHANGE;
PropChangeParams.Scope= DICS_FLAG_CONFIGSPECIFIC;//使修改的属性保存在指定的属性文件
PropChangeParams.StateChange= DICS_DISABLE;
PropChangeParams.HwProfile= 0;
//改变设备属性并调用安装服务
if (!SetupDiSetClassInstallParams(hDevInfo,&devInfoElem,(SP_CLASSINSTALL_HEADER *)&PropChangeParams,sizeof(PropChangeParams)))
{
dwResult =GetLastError();
Outstr.Format("\nSetupDiSetClassInstallParams2FAILED, Error Code is %d", dwResult);
OutputDebugString(Outstr);
return;
}
if (!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, &devInfoElem))
{
dwResult =GetLastError();
Outstr.Format("\nSetupDiCallClassInstallerFAILED, Error Code is %d", dwResult);
OutputDebugString(Outstr);
return;
}
else
{
//判断是否需要重新启动
devParams.cbSize= sizeof(devParams);
if (!SetupDiGetDeviceInstallParams(hDevInfo, &devInfoElem, &devParams))
{
Outstr.Format("\nSetupDiGetDeviceInstallParams, Error Code is %d", dwResult);
OutputDebugString(Outstr);
return;
}
if (devParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))
{
OutputDebugString("Need Restart Computer");
return;
}
}
} //while
SetupDiDestroyDeviceInfoList(hDevInfo);
}