void GetDeviceRegistryProperty()
{
    HDEVINFO devInfo;
    SP_DEVINFO_DATA devInfoData;
    int devIndex = 0;

    TCHAR buffer[MAX_PATH] = {0, };

    devInfo = SetupDiGetClassDevs(NULL, _T("USB"), NULL, DIGCF_ALLCLASSES);

    ZeroMemory(&devInfoData, sizeof(SP_DEVINFO_DATA));
    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

    while (SetupDiEnumDeviceInfo(devInfo, devIndex, &devInfoData))
    {
        SetupDiGetDeviceRegistryProperty(
            devInfo,
            &devInfoData,
            SPDRP_HARDWAREID,
            NULL,
            (BYTE *)buffer,
            sizeof(buffer),
            NULL);

        devIndex++;
    }
}

SetupDiGetClassDevs()를 이용하여 디바이스 클래스의 핸들을 얻습니다. 두번째 매개변수에는 PnP 장치들의 구분자를 넣습니다. PCI, USB, PCMCIA, SCSI 등을 사용할 수 있고 NULL을 넣으면 모든 장치를 얻어올 수 있습니다.

마지막 매개변수는 플래그로써 다음과 같은 효과를 가집니다. OR 연산으로 여러개의 플래그를 지정할 수 있습니다.

  • DIGCF_ALLCLASSES: 시스템에 설치된 모든 디바이스 인터페이스 클래스를 리턴합니다.(시스템에 장착되지 않은 장치들도 포함합니다.)
  • DIGCF_DEVICEINTERFACE: 두번째 매개변수에 PnP 장치의 구분자를 넣었을 때 사용할 수 있습니다. 디바이스 인터페이스를 지원하는 클래스를 리턴합니다.
  • DIGCF_DEFAULT: 시스템의 기본 디바이스 클래스만 리턴합니다.
  • DIGCF_PRESENT: 시스템에 장착된 장치들만 리턴합니다.
  • DIGCF_PROFILE: 현재 하드웨어 프로파일만 리턴합니다.

SetupDiEnumDeviceInfo()로 SP_DEVINFO_DATA 내용을 채웁니다. devIndex는 0부터 시작하여 1씩 증가하면 되고, devInfoData.cbSize에는 꼭 SP_DEVINFO_DATA의 크기를 구하여 넣어줘야 합니다.

SP_DEVINFO_DATA를 구했다면 SetupDiGetDeviceRegistryProperty()를 이용하여 해당 디바이스의 속성과 설정값을 구할 수 있습니다. 대략 많이 쓰는 속성들은 다음과 같습니다.

  • SPDRP_DEVICEDESC: 디바이스 설명
  • SPDRP_HARDWAREID: 하드웨어 ID
  • SPDRP_COMPATIBLEIDS: Compatible ID, USB에서는 Class, SubClass, Protocol 등의 값 SPDRP_UPPERFILTERS, SPDRP_LOWERFILTERS : Upper, Lower Filter 값

필터 설정

void SetFilter()
{
    HDEVINFO devInfo;
    SP_DEVINFO_DATA devInfoData;

    ... 생략 ...

    TCHAR filterString[MAX_PATH] = _T("hello\0world\0\0");

    SetupDiSetDeviceRegistryProperty(
        devInfo,
        &devInfoData,
        SPDRP_LOWERFILTERS,
        (BYTE *)filterString,
        sizeof(filterString));
}

특정 디바이스의 인스턴스에 필터를 설정하는 방법입니다. 앞서 SetupDiGetClassDevs()와 SetupDiEnumDeviceInfo()를 이용하여 HDEVINFO와 SP_DEVINFO_DATA을 구해옵니다.

SetupDiSetDeviceRegistryProperty()로 해당 장치의 레지스트리에 필터 값을 설정합니다. SPDRP_LOWERFILTERS를 설정하여 LowerFilters에 설정되도록 합니다. 각각의 필터 문자열 값은 널문자(\0)로 구분해주고 마지막에는 두개의 널문자(\0\0)를 넣어줍니다. REG_MULTI_SZ 타입이기 때문입니다. 마지막 버퍼 크기는 실제 문자열 값의 길이를 넣어줘도 되지만, 필터 문자열 값 중간에 널(\0)이 들어가 있어서 _tcslen() 함수로는 길이를 구해 올 수 없습니다. 따라서 버퍼의 크기만 넣어줘도 마지막 두개의 널문자(\0\0)을 체크하여 기록하기 때문에 큰 상관은 없습니다.

필터 드라이버 로드

void PropertyChange()
{
    HDEVINFO devInfo;
    SP_DEVINFO_DATA devInfoData;

    ... 생략 ...

    SP_PROPCHANGE_PARAMS propChangeParams;
    ZeroMemory(&propChangeParams, sizeof(SP_PROPCHANGE_PARAMS));

    propChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
    propChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
    propChangeParams.StateChange = DICS_PROPCHANGE;
    propChangeParams.Scope = DICS_FLAG_CONFIGSPECIFIC;
    propChangeParams.HwProfile = 0;

    SetupDiSetClassInstallParams(
        devInfo,
        devInfoData,
        (PSP_CLASSINSTALL_HEADER)&propChangeParams,
        sizeof(SP_PROPCHANGE_PARAMS));

    SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, devInfo, devInfoData);
}

필터 드라이버는 StartService()와 같은 함수로 로드하는 것이 아니라, 해당 디바이스의 인스턴스 속성(Property)를 변경해주는 것으로 로드할 수 있습니다.

먼저 SP_PROPCHANGE_PARAMS 구조체에 내용을 설정해줍니다. ClassInstallHeader.cbSize에는 SP_CLASSINSTALL_HEADER의 크기를 넣어줍니다. ClassInstallHeader.InstallFunction에는 호출할 DIF 함수를 지정합니다. DIF_PROPERTYCHANGE를 지정하여 속성을 변경하는 함수를 호출 할 수 있도록 합니다.

StateChange에 DICS_PROPCHANGE를 지정하여 속성을 변경할 수 있도록 합니다. Scope에 DICS_FLAG_CONFIGSPECIFIC를 지정하여 현재 선택된 디바이스의 속성만 변경하도록 합니다. HwProfile에는 0(현재 하드웨어 프로파일)을 지정합니다.

실행 가능한 예제: USB 마우스 장치에 필터 설정, 드라이버 로드하기

#include <windows.h>
#include <setupapi.h>

int _tmain(int argc, _TCHAR* argv[])
{
    HDEVINFO devInfo;
    SP_DEVINFO_DATA devInfoData;
    SP_PROPCHANGE_PARAMS propChangeParams;
    int devIndex = 0;

    TCHAR buffer[MAX_PATH];

    devInfo = SetupDiGetClassDevs(
        NULL,
        _T("USB"),
        NULL,
        DIGCF_ALLCLASSES | DIGCF_PRESENT);

    ZeroMemory(&devInfoData, sizeof(SP_DEVINFO_DATA));
    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

    ZeroMemory(&propChangeParams, sizeof(SP_PROPCHANGE_PARAMS));

    while (SetupDiEnumDeviceInfo(devInfo, devIndex, &devInfoData))
    {
        SetupDiGetDeviceRegistryProperty(
            devInfo,
            &devInfoData,
            SPDRP_COMPATIBLEIDS,
            NULL,
            (BYTE *)buffer,
            sizeof(buffer),
            NULL);

        // Protocol 2 마우스
        if (_tcsstr(buffer, _T("Prot_02")) != NULL)
        {
            TCHAR filterString[MAX_PATH] = _T("devlower\0\0");

            SetupDiSetDeviceRegistryProperty(
                devInfo,
                &devInfoData,
                SPDRP_LOWERFILTERS,
                (BYTE *)filterString,
                sizeof(filterString));

                propChangeParams.ClassInstallHeader.cbSize =
                    sizeof(SP_CLASSINSTALL_HEADER);

                propChangeParams.ClassInstallHeader.InstallFunction =
                    DIF_PROPERTYCHANGE;

                propChangeParams.StateChange = DICS_PROPCHANGE;
                propChangeParams.Scope = DICS_FLAG_CONFIGSPECIFIC;
                propChangeParams.HwProfile = 0;

                SetupDiSetClassInstallParams(
                    devInfo,
                    &devInfoData,
                    (PSP_CLASSINSTALL_HEADER)&propChangeParams,
                    sizeof(SP_PROPCHANGE_PARAMS));

                SetupDiCallClassInstaller(
                    DIF_PROPERTYCHANGE,
                    devInfo,
                    &devInfoData);
        }

        devIndex++;
    }
}

저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

02 March 2009