SetupAPI是一个被用来执行安装设备的一系列操作的方法的集合,主要用于安装设备驱动(device driver),被用在类安装程序(class installers)、协安装程序(co-installers)和设备安装应用程序(device installation applications)中。
下图描绘了设备安装过程中的组件,也包括SetupAPI在其中所扮演的角色
原文请进
SetupAPI的方法集分为两大部分:
General setup functions
更多关于using-general-setup-functions
Device installation functions
更多关于using-device-installation-functions
从上一节中可见SetupAPI的方法集中有专门的一部分用以管理设备信息集。那么接下来,首先要做的就是最简单的功能——枚举设备信息。I
在SetupAPI中利用设备信息集(Device Information Sets)这样一种结构来组织各个设备的信息
更多关于device-information-sets
当需要获取一个或多个设备的描述信息时,必须先创建一个设备信息集,然后通过枚举列表中元素的方式获取设备信息元素(Device Information Element),进而获得所需的设备信息。
/*************************************
* BOOL EnumAllDevice( )
* 功能 列举当前存在的设备
* 返回值 是否成功
**************************************/
BOOL EnumAllDevice()
{
HDEVINFO hDevInfo;
//包含了设备的ClassGUID和设备实例句柄,大体上相当于设备信息元素(无device interface)
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
printf("Displaying the Installed Devices\n\n");
TCHAR szGUID[] = _T("4D36E97C-E325-11CE-BFC1-08002BE10318");
GUID guid; //全局唯一标识符(GUID,Globally Unique Identifier)
UuidFromString((RPC_WSTR)szGUID, &guid);
//创建一个设备信息集(device information set),其中包含了我们请求的设备信息元素(device information element)
//该方法只用于获取本机设备,远程计算机上的设备需要使用SetupDiGetClassDevsEx
//成功则返回一个设备信息集的Handle,失败则返回INVALID_HANDLE_VALUE
hDevInfo = SetupDiGetClassDevs(
&guid, // 指定一个GUID(for a device setup class or a device interface class)用以过滤元素
0, // Enumerator 可为NULL
0, // hwndParent 可为NULL
DIGCF_PRESENT | DIGCF_ALLCLASSES ); //列表的过滤选项,当为DIGCF_ALLCLASSES时,参数1指定的guid将不再起作用
if (hDevInfo == INVALID_HANDLE_VALUE)
{
return FALSE;
}
// 循环列举
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
//枚举设备信息列表中的元素,将元素以SP_DEVINFO_DATA结构体的方式输出
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i, &DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
// 获取详细信息
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_DEVICEDESC, //属性标识,用以指示buffer输出的是哪个属性值,在此是要求输出设备描述字符串
&DataT,
(PBYTE)buffer, //输出值
buffersize,
&buffersize)) //所需的buff空间
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// 内存不足,则重新分配buff空间
if (buffer)
HeapFree(GetProcessHeap(), 0, buffer);
buffer = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, buffersize);
}
else
break;
}
// 输出
wprintf(L"GUID:{%.8X-%.4X-%.4X--%.2X%.2X-%.2X%.2X%.2X%.2X%.2X%.2X} \tDevice: %s\n",
DeviceInfoData.ClassGuid.Data1,
DeviceInfoData.ClassGuid.Data2,
DeviceInfoData.ClassGuid.Data3,
DeviceInfoData.ClassGuid.Data4[0],
DeviceInfoData.ClassGuid.Data4[1],
DeviceInfoData.ClassGuid.Data4[2],
DeviceInfoData.ClassGuid.Data4[3],
DeviceInfoData.ClassGuid.Data4[4],
DeviceInfoData.ClassGuid.Data4[5],
DeviceInfoData.ClassGuid.Data4[6],
DeviceInfoData.ClassGuid.Data4[7],
buffer);
if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
}
if ( GetLastError() != NO_ERROR
&& GetLastError() != ERROR_NO_MORE_ITEMS )
{
return FALSE;
}
// 释放
SetupDiDestroyDeviceInfoList(hDevInfo);
return TRUE;
}
Device Setup Class 和 Device Interface Class之间的区别 在于:
To facilitate device installation, devices that are set up and configured in the same manner are grouped into a device setup class.
更多关于device-setup-class
Device Setup Class是对于设备的安装进行分类,在安装过程中具有相同方式的设备被分为一类。一个设备的Device Setup Class决定了在安装过程中哪个类安装程序(class installer)和协安装程序(co-installers)将会参与进来。
A device interface class is a way of exporting device and driver functionality to other system components, including other drivers, as well as user-mode applications. A driver can register a device interface class, then enable an instance of the class for each device object to which user-mode I/O requests might be sent.
更多关于device-interface-class
Device Interface Class是在设备的使用过程中,对具有同样功能的设备进行的分类。驱动不再为每个设备单独命名,并将该名字交由用户模式下的程序来进行I/O请求,相反,对某个设备的直接请求转化为对该类的请求,也就是说,在请求一个打印机类打印文件的时候,用户将无法预测实际上是哪一台打印机实际打印了文件(当然,我们有多台打印机)。
无论开启或禁用,都需要调用SetupAPI中的两个函数: SetupDiSetClassInstallParams
和 SetupDiCallClassInstaller
前者 用于设置类安装参数,后者用于调用类安装程序和任何注册过的协安装程序。
BOOL SetupDiSetClassInstallParams(
_In_ HDEVINFO DeviceInfoSet,
_In_opt_ PSP_DEVINFO_DATA DeviceInfoData,
_In_opt_ PSP_CLASSINSTALL_HEADER ClassInstallParams,
_In_ DWORD ClassInstallParamsSize
);
SetupDiSetClassInstallParams
函数的核心在于第三个参数。
PSP_CLASSINSTALL_HEADER
是任何类安装参数(class install parameter)的第一个成员,里面主要内容是一个 DI_FUNCTION
类型的设备安装方法(DIF,Device Installation Function),且被定义为 DIF_XXX
的形式。
这里的 ClassInstallParams
要填入的也就是一个类安装参数的指针,当然它的第一个成员必须是 PSP_CLASSINSTALL_HEADER
。参数4的size指的是整个类安装参数的大小而不仅仅是 PSP_CLASSINSTALL_HEADER
的大小。
BOOL SetupDiCallClassInstaller(
_In_ DI_FUNCTION InstallFunction,
_In_ HDEVINFO DeviceInfoSet,
_In_opt_ PSP_DEVINFO_DATA DeviceInfoData
);
这是改变设备状态的代码,如下:
BOOL StateChange( DWORD dwNewState, DWORD dwDevID, HDEVINFO hDevInfo)
{
SP_PROPCHANGE_PARAMS PropChangeParams;
SP_DEVINFO_DATA DevInfoData = {sizeof(SP_DEVINFO_DATA)};
SP_DEVINSTALL_PARAMS devParams;
//查询设备信息
if (!SetupDiEnumDeviceInfo( hDevInfo, dwDevID, &DevInfoData))
{
OutputDebugString("SetupDiEnumDeviceInfo FAILED");
return FALSE;
}
//设置设备属性变化参数
PropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
PropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
PropChangeParams.Scope = DICS_FLAG_GLOBAL; //使修改的属性保存在所有的硬件属性文件
PropChangeParams.StateChange = dwNewState;
PropChangeParams.HwProfile = 0;
//改变设备属性
if (!SetupDiSetClassInstallParams( hDevInfo,
&DevInfoData,
(SP_CLASSINSTALL_HEADER *)&PropChangeParams,
sizeof(PropChangeParams)))
{
OutputDebugString("SetupDiSetClassInstallParams FAILED");
return FALSE;
}
PropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
PropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
PropChangeParams.Scope = DICS_FLAG_CONFIGSPECIFIC;//使修改的属性保存在指定的属性文件
PropChangeParams.StateChange = dwNewState;
PropChangeParams.HwProfile = 0;
//改变设备属性并调用安装服务
if (!SetupDiSetClassInstallParams( hDevInfo,
&DevInfoData,
(SP_CLASSINSTALL_HEADER *)&PropChangeParams,
sizeof(PropChangeParams))
||!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, &DevInfoData))
{
OutputDebugString("SetupDiSetClassInstallParams or SetupDiCallClassInstaller FAILED");
return TRUE;
}
else
{
//判断是否需要重新启动
devParams.cbSize = sizeof(devParams);
if (!SetupDiGetDeviceInstallParams( hDevInfo, &DevInfoData, &devParams))
{
OutputDebugString("SetupDiGetDeviceInstallParams FAILED");
return FALSE;
}
if (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))
{
OutputDebugString("Need Restart Computer");
return TRUE;
}
return TRUE;
}
}
这里的类安装参数为 SP_PROPCHANGE_PARAMS
结构体,其 StateChange
参数指定了状态改变的动作,有 DICS_ENABLE
、 DICS_DISABLE
、 DICS_PROPCHANGE
、 DICS_START
、 DICS_STOP
可选。
更多关于SP_PROPCHANGE_PARAMS
我们需要实现的禁用和开启,不过是将 PropChangeParams.StateChange
设为 DICS_DISABLE
和 DICS_ENABLE
。