1. 程式人生 > >Windows平臺C++ 啟用和禁用裝置

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