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

 

评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值