SetupAPI简介与设备管理

SetupAPI简介与设备管理

SetupAPI的定位


SetupAPI是一个被用来执行安装设备的一系列操作的方法的集合,主要用于安装设备驱动(device driver),被用在类安装程序(class installers)、协安装程序(co-installers)和设备安装应用程序(device installation applications)中。

下图描绘了设备安装过程中的组件,也包括SetupAPI在其中所扮演的角色

SetupAPI简介与设备管理_第1张图片

原文请进

SetupAPI Functions Set


SetupAPI的方法集分为两大部分:

  1. General setup functions

    • 读取和处理INF文件
    • 确定安装在目标系统上所需的可用空间大小
    • 将文件从源介质移至所安装的目标系统的介质上,如果需要的话要求用户介入
    • 创建在安装期间移动的文件的日志
    • 将日志条目写入SetupAPI text logs

    更多关于using-general-setup-functions

  2. Device installation functions

    • 安装驱动
    • 处理DIF代码
    • 管理设备信息集
    • 管理驱动列表
    • 管理设备接口
    • 管理图标以及其它的位图

    更多关于using-device-installation-functions

SetupAPI与枚举设备


从上一节中可见SetupAPI的方法集中有专门的一部分用以管理设备信息集。那么接下来,首先要做的就是最简单的功能——枚举设备信息。I

在SetupAPI中利用设备信息集(Device Information Sets)这样一种结构来组织各个设备的信息

SetupAPI简介与设备管理_第2张图片

更多关于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中的两个函数: SetupDiSetClassInstallParamsSetupDiCallClassInstaller 前者 用于设置类安装参数,后者用于调用类安装程序和任何注册过的协安装程序。

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_ENABLEDICS_DISABLEDICS_PROPCHANGEDICS_STARTDICS_STOP 可选。

更多关于SP_PROPCHANGE_PARAMS

我们需要实现的禁用和开启,不过是将 PropChangeParams.StateChange 设为 DICS_DISABLEDICS_ENABLE

你可能感兴趣的:(windows编程)