Windows平臺C++ 啟用和禁用裝置
在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