WinUSB 템플릿을 기반으로 Windows 데스크톱 앱 작성

USB 디바이스와 통신하는 Windows 데스크톱 앱을 작성하는 가장 쉬운 방법은 C/C++ WinUSB 템플릿을 사용하는 것입니다. 이 템플릿의 경우 WDK(Windows 드라이버 키트)(Windows용 디버깅 도구 사용) 및 Microsoft Visual Studio(Professional 또는 Ultimate)와 통합된 환경이 필요합니다. 템플릿을 시작점으로 사용할 수 있습니다.

시작하기 전에

WinUSB 애플리케이션 만들기

템플릿에서 애플리케이션을 만들려면 다음을 수행합니다.

  1. 새 프로젝트 대화 상자의 맨 위에 있는 검색 상자에 USB를 입력합니다.

  2. 가운데 창에서 WinUSB 애플리케이션(유니버설)을 선택합니다.

  3. 다음을 선택합니다.

  4. 프로젝트 이름을 입력하고 저장 위치를 선택한 다음 만들기를 선택합니다.

    다음 스크린샷은 WinUSB 애플리케이션(유니버설) 템플릿에 대한 새 프로젝트 대화 상자를 보여 줍니다.

    winusb 템플릿 새 프로젝트 만들기 첫 번째 화면

    winusb 템플릿 새 프로젝트 만들기 두 번째 화면

    이 항목에서는 Visual Studio 프로젝트의 이름이 USB Application1이라고 가정합니다.

    Visual Studio는 하나의 프로젝트와 솔루션을 만듭니다. 다음 스크린샷과 같이 솔루션 탐색기 창에서 솔루션, 프로젝트 및 프로젝트에 속한 파일을 볼 수 있습니다. (솔루션 탐색기 창이 표시되지 않으면 보기 메뉴에서 솔루션 탐색기 선택합니다. 솔루션에는 USB Application1이라는 C++ 애플리케이션 프로젝트가 포함되어 있습니다.

    winusb 템플릿 솔루션 탐색기 1.

    USB Application1 프로젝트에는 애플리케이션에 대한 원본 파일이 있습니다. 애플리케이션 소스 코드를 확인하려는 경우 원본 파일 아래에 표시되는 파일을 열 수 있습니다.

  5. 솔루션에 드라이버 패키지 프로젝트를 추가합니다. 솔루션(솔루션 'USB Application1')을 길게 누르거나 마우스 오른쪽 단추로 클릭한 다음, 다음 스크린샷과 같이새 프로젝트추가>를 선택합니다.

    winusb 템플릿 만들기 두 번째 프로젝트 추가.

  6. 새 프로젝트 대화 상자의 맨 위에 있는 검색 상자에 USB를 다시 입력합니다.

  7. 가운데 창에서 WinUSB INF 드라이버 패키지를 선택합니다.

  8. 다음을 선택합니다.

  9. 프로젝트 이름을 입력한 다음 만들기를 선택합니다.

    다음 스크린샷은 WinUSB INF 드라이버 패키지 템플릿에 대한 새 프로젝트 대화 상자를 보여 줍니다.

    winusb 템플릿 두 번째 프로젝트 만들기 첫 번째 화면

    winusb 템플릿 두 번째 프로젝트 만들기 두 번째 화면.

    이 항목에서는 Visual Studio 프로젝트의 이름이 USB Application1 패키지라고 가정합니다.

    USB Application1 패키지 프로젝트에는 Microsoft에서 제공하는 Winusb.sys 드라이버를 디바이스 드라이버로 설치하는 데 사용되는 INF 파일이 포함되어 있습니다.

    이제 다음 스크린샷과 같이 솔루션 탐색기 두 프로젝트가 모두 포함되어야 합니다.

    winusb 템플릿 솔루션 탐색기 2.

  10. INF 파일 USBApplication1.inf에서 다음 코드를 찾습니다. %DeviceName% =USB_Install, USB\VID_vvvv&PID_pppp

  11. VID_vvvv&PID_pppp 디바이스의 하드웨어 ID로 바꿉니다. 장치 관리자 하드웨어 ID를 가져옵니다. 장치 관리자 디바이스 속성을 확인합니다. 세부 정보 탭에서 하드웨어 ID 속성 값을 봅니다.

  12. 솔루션 탐색기 창에서 솔루션 'USB Application1'(프로젝트 2개 중 2개)을 선택하고 마우스 오른쪽 단추로 클릭하고 Configuration Manager 선택합니다. 애플리케이션 프로젝트와 패키지 프로젝트 모두에 대한 구성 및 플랫폼을 선택합니다. 이 연습에서는 다음 스크린샷과 같이 디버그 및 x64를 선택합니다.

프로젝트 빌드, 배포 및 디버깅

지금까지 이 연습에서는 Visual Studio를 사용하여 프로젝트를 만들었습니다. 다음으로 디바이스가 연결된 디바이스를 구성해야 합니다. 템플릿을 사용하려면 Winusb 드라이버를 디바이스의 드라이버로 설치해야 합니다.

테스트 및 디버깅 환경에는 다음이 있을 수 있습니다.

  • 두 컴퓨터 설정: 호스트 컴퓨터 및 대상 컴퓨터. 호스트 컴퓨터의 Visual Studio에서 프로젝트를 개발하고 빌드합니다. 디버거는 호스트 컴퓨터에서 실행되며 Visual Studio 사용자 인터페이스에서 사용할 수 있습니다. 애플리케이션을 테스트하고 디버그하면 드라이버가 대상 컴퓨터에서 실행됩니다.

  • 단일 컴퓨터 설정: 대상 및 호스트가 한 컴퓨터에서 실행됩니다. Visual Studio에서 프로젝트를 개발 및 빌드하고 디버거 및 애플리케이션을 실행합니다.

다음 단계에 따라 애플리케이션 및 드라이버를 배포, 설치, 로드 및 디버그할 수 있습니다.

  • 두 컴퓨터 설정

    1. 드라이버 배포 및 테스트를 위해 컴퓨터 프로비전의 지침에 따라 대상 컴퓨터를 프로비전합니다. 참고: 프로비전은 WDKRemoteUser라는 대상 머신에 사용자를 만듭니다. 프로비전이 완료되면 사용자가 WDKRemoteUser로 전환됩니다.
    2. 호스트 컴퓨터에서 Visual Studio에서 솔루션을 엽니다.
    3. 기본.cpp에서 OpenDevice 호출 앞에 이 줄을 추가합니다.
    system ("pause")
    

    이 줄을 사용하면 시작할 때 애플리케이션이 일시 중지됩니다. 원격 디버깅에 유용합니다.

    1. pch.h에 다음 줄을 포함합니다.
    #include <cstdlib>
    

    이 include 문은 이전 단계의 system() 호출에 필요합니다.

    1. 솔루션 탐색기 창에서 USB Application1 패키지를 길게 누르거나 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.

    2. USB Application1 패키지 속성 페이지 창의 왼쪽 창에서 다음 스크린샷과 같이 구성 속성 > 드라이버 설치 > 배포로 이동합니다.

    3. 배포 전에 이전 드라이버 버전 제거를 선택합니다.

    4. 원격 컴퓨터 이름에 대해 테스트 및 디버깅을 위해 구성한 컴퓨터의 이름을 선택합니다. 이 연습에서는 dbg-target이라는 컴퓨터를 사용합니다.

    5. 설치/다시 설치 및 확인을 선택합니다. 적용을 선택합니다.

      winusb 템플릿 배포.

    6. 다음 스크린샷과 같이 속성 페이지에서 구성 속성 > 디버깅으로 이동하고 Windows용 디버깅 도구 – 원격 디버거를 선택합니다.

      winusb 템플릿 원격 디버거.

    7. 빌드 메뉴에서 솔루션 빌드를 선택합니다. Visual Studio는 출력 창에 빌드 진행률을 표시합니다. 출력 창이 표시되지 않으면 보기 메뉴에서 출력을 선택합니다. 이 연습에서는 Windows 10 실행하는 x64 시스템에 대한 프로젝트를 빌드했습니다.

    8. 빌드 메뉴에서 솔루션 배포를 선택합니다.

대상 컴퓨터에 드라이버 설치 스크립트가 실행되는 것을 볼 수 있습니다. 드라이버 파일은 대상 컴퓨터의 %Systemdrive%\drivertest\drivers 폴더에 복사됩니다. .inf, .cat, 테스트 인증서 및 .sys 파일 및 기타 필요한 파일이 %systemdrive%\drivertest\drivers 폴더에 있는지 확인합니다. 디바이스는 오류 없이 장치 관리자 표시되어야 합니다.

호스트 컴퓨터의 출력 창에 이 메시지가 표시됩니다.

Deploying driver files for project
"<path>\visual studio 14\Projects\USB Application1\USB Application1 Package\USB Application1 Package.vcxproj".
Deployment may take a few minutes...
========== Build: 1 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========

애플리케이션을 디버그하려면

  1. 호스트 컴퓨터에서 솔루션 폴더의 x64 > Win8.1Debug 로 이동합니다.

  2. 대상 컴퓨터에 UsbApplication1.exe 애플리케이션 실행 파일을 복사합니다.

  3. 대상 컴퓨터에서 애플리케이션을 시작합니다.

  4. 호스트 컴퓨터의 디버그 메뉴에서 프로세스에 연결을 선택합니다.

  5. 창에서 이 이미지와 같이 Windows 사용자 모드 디버거 (Windows용 디버깅 도구)를 전송으로 선택하고 대상 컴퓨터의 이름(이 경우 dbg-target)을 선택합니다.

    winusb 템플릿 디버그 설정.

  6. 사용 가능한 프로세스 목록에서 애플리케이션을 선택하고 연결을 선택합니다. 이제 직접 실행 창을 사용하거나 디버그 메뉴의 옵션을 사용하여 디버그 할 수 있습니다.

위의 지침은 Windows용 디버깅 도구 – 원격 디버거를 사용하여 애플리케이션을 디버그합니다. 원격 Windows 디버거(Visual Studio에 포함된 디버거)를 사용하려는 경우 다음 지침을 따릅니다.

  1. 대상 컴퓨터에서 방화벽을 통해 허용되는 앱 목록에 msvsmon.exe 추가합니다.
  2. C:\DriverTest\msvsmon\msvsmon.exe 있는 Visual Studio 원격 디버깅 모니터를 시작합니다.
  3. C:\remotetemp와 같은 작업 폴더를 만듭니다.
  4. 대상 컴퓨터의 작업 폴더에 UsbApplication1.exe 애플리케이션 실행 파일을 복사합니다.
  5. 호스트 컴퓨터의 Visual Studio에서 USB Application1 패키지 프로젝트를 마우스 오른쪽 단추로 클릭하고 프로젝트 언로드를 선택합니다.
  6. USB Application1 프로젝트를 선택하고 마우스 오른쪽 단추로 클릭하고 프로젝트 속성에서 구성 속성 노드를 확장하고 디버깅을 선택합니다.
  7. 디버거를 원격 Windows 디버거로 시작하도록 변경합니다.
  8. 로컬로 빌드된 프로젝트의 원격 디버깅에 제공된 지침에 따라 원격 컴퓨터에서 실행 파일을 실행하도록 프로젝트 설정을 변경합니다. 작업 디렉터리원격 명령 속성이 대상 컴퓨터의 폴더를 반영하는지 확인합니다.
  9. 애플리케이션을 디버그하려면 빌드 메뉴에서 디버깅 시작을 선택하거나 F5 키를 누릅니다.
  • 단일 컴퓨터 설정:

    1. 애플리케이션 및 드라이버 설치 패키지를 빌드하려면 빌드 메뉴에서 솔루션 빌드를 선택합니다. Visual Studio는 출력 창에 빌드 진행률을 표시합니다. 출력 창이 표시되지 않으면 보기 메뉴에서 출력을 선택합니다. 이 연습에서는 Windows 10 실행하는 x64 시스템에 대한 프로젝트를 빌드했습니다.

    2. 빌드된 드라이버 패키지를 보려면 Windows Explorer USB Application1 폴더로 이동한 다음 x64 > USB Application1 패키지 디버그>로 이동합니다. 드라이버 패키지에는 여러 파일이 포함되어 있습니다. MyDriver.inf는 드라이버를 설치할 때 Windows에서 사용하는 정보 파일입니다. mydriver.cat 설치 관리자가 드라이버 패키지에 대한 테스트 서명을 확인하는 데 사용하는 카탈로그 파일입니다. 이러한 파일은 다음 스크린샷에 표시됩니다.

      winusb 애플리케이션 템플릿.

      패키지에 포함된 드라이버 파일이 없습니다. INF 파일이 Windows\System32 폴더에 있는 기본 제공 드라이버 Winusb.sys 참조하기 때문입니다.

    3. 드라이버를 수동으로 설치합니다. 장치 관리자 패키지에서 INF를 지정하여 드라이버를 업데이트합니다. 앞의 섹션에 표시된 솔루션 폴더에 있는 드라이버 패키지를 가리킵니다. 오류가 DriverVer set to a date in the future표시되면 INF 패키지 프로젝트 설정 > Inf2Cat > 일반 > 사용 현지 시간 > 예를 설정합니다.

    4. USB Application1 프로젝트를 선택하고 마우스 오른쪽 단추로 클릭하고 프로젝트 속성에서 구성 속성 노드를 확장하고 디버깅을 선택합니다.

    5. 디버거를 로컬 Windows 디버거로 시작하도록 변경합니다.

    6. USB Application1 패키지 프로젝트를 길게 누르거나 마우스 오른쪽 단추로 클릭하고 프로젝트 언로드를 선택합니다.

    7. 애플리케이션을 디버그하려면 빌드 메뉴에서 디버깅 시작을 선택하거나 F5 키를 누릅니다.

템플릿 코드 토론

템플릿은 데스크톱 애플리케이션의 시작점입니다. USB Application1 프로젝트에는 device.cpp 및 기본.cpp 원본 파일이 있습니다.

기본.cpp 파일에는 애플리케이션 진입점 _tmain 포함되어 있습니다. device.cpp에는 핸들을 열고 디바이스에 닫는 모든 도우미 함수가 포함되어 있습니다.

템플릿에는 device.h라는 헤더 파일도 있습니다. 이 파일에는 디바이스 인터페이스 GUID(나중에 설명)에 대한 정의와 애플리케이션에서 가져온 정보를 저장하는 DEVICE_DATA 구조가 포함되어 있습니다. 예를 들어 OpenDevice에서 가져오고 후속 작업에 사용되는 WinUSB 인터페이스 핸들을 저장합니다.

typedef struct _DEVICE_DATA {

    BOOL                    HandlesOpen;
    WINUSB_INTERFACE_HANDLE WinusbHandle;
    HANDLE                  DeviceHandle;
    TCHAR                   DevicePath[MAX_PATH];

} DEVICE_DATA, *PDEVICE_DATA;

디바이스에 대한 instance 경로 가져오기 - device.cpp의 RetrieveDevicePath를 참조하세요.

USB 디바이스에 액세스하기 위해 애플리케이션은 CreateFile을 호출하여 디바이스에 대한 유효한 파일 핸들을 만듭니다. 해당 호출의 경우 애플리케이션은 instance 디바이스 경로를 가져와야 합니다. 디바이스 경로를 가져오기 위해 앱은 SetupAPI 루틴을 사용하고 Winusb.sys 설치하는 데 사용된 INF 파일의 디바이스 인터페이스 GUID를 지정합니다. Device.h는 GUID_DEVINTERFACE_USBApplication1이라는 GUID 상수를 선언합니다. 이러한 루틴을 사용하여 애플리케이션은 지정된 디바이스 인터페이스 클래스의 모든 디바이스를 열거하고 디바이스의 디바이스 경로를 검색합니다.

HRESULT
RetrieveDevicePath(
    _Out_bytecap_(BufLen) LPTSTR DevicePath,
    _In_                  ULONG  BufLen,
    _Out_opt_             PBOOL  FailureDeviceNotFound
    )
/*++

Routine description:

    Retrieve the device path that can be used to open the WinUSB-based device.

    If multiple devices have the same device interface GUID, there is no
    guarantee of which one will be returned.

Arguments:

    DevicePath - On successful return, the path of the device (use with CreateFile).

    BufLen - The size of DevicePath's buffer, in bytes

    FailureDeviceNotFound - TRUE when failure is returned due to no devices
        found with the correct device interface (device not connected, driver
        not installed, or device is disabled in Device Manager); FALSE
        otherwise.

Return value:

    HRESULT

--*/
{
    BOOL                             bResult = FALSE;
    HDEVINFO                         deviceInfo;
    SP_DEVICE_INTERFACE_DATA         interfaceData;
    PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL;
    ULONG                            length;
    ULONG                            requiredLength=0;
    HRESULT                          hr;

    if (NULL != FailureDeviceNotFound) {

        *FailureDeviceNotFound = FALSE;
    }

    //
    // Enumerate all devices exposing the interface
    //
    deviceInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USBApplication1,
                                     NULL,
                                     NULL,
                                     DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

    if (deviceInfo == INVALID_HANDLE_VALUE) {

        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

    //
    // Get the first interface (index 0) in the result set
    //
    bResult = SetupDiEnumDeviceInterfaces(deviceInfo,
                                          NULL,
                                          &GUID_DEVINTERFACE_USBApplication1,
                                          0,
                                          &interfaceData);

    if (FALSE == bResult) {

        //
        // We would see this error if no devices were found
        //
        if (ERROR_NO_MORE_ITEMS == GetLastError() &&
            NULL != FailureDeviceNotFound) {

            *FailureDeviceNotFound = TRUE;
        }

        hr = HRESULT_FROM_WIN32(GetLastError());
        SetupDiDestroyDeviceInfoList(deviceInfo);
        return hr;
    }

    //
    // Get the size of the path string
    // We expect to get a failure with insufficient buffer
    //
    bResult = SetupDiGetDeviceInterfaceDetail(deviceInfo,
                                              &interfaceData,
                                              NULL,
                                              0,
                                              &requiredLength,
                                              NULL);

    if (FALSE == bResult && ERROR_INSUFFICIENT_BUFFER != GetLastError()) {

        hr = HRESULT_FROM_WIN32(GetLastError());
        SetupDiDestroyDeviceInfoList(deviceInfo);
        return hr;
    }

    //
    // Allocate temporary space for SetupDi structure
    //
    detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)
        LocalAlloc(LMEM_FIXED, requiredLength);

    if (NULL == detailData)
    {
        hr = E_OUTOFMEMORY;
        SetupDiDestroyDeviceInfoList(deviceInfo);
        return hr;
    }

    detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
    length = requiredLength;

    //
    // Get the interface's path string
    //
    bResult = SetupDiGetDeviceInterfaceDetail(deviceInfo,
                                              &interfaceData,
                                              detailData,
                                              length,
                                              &requiredLength,
                                              NULL);

    if(FALSE == bResult)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        LocalFree(detailData);
        SetupDiDestroyDeviceInfoList(deviceInfo);
        return hr;
    }

    //
    // Give path to the caller. SetupDiGetDeviceInterfaceDetail ensured
    // DevicePath is NULL-terminated.
    //
    hr = StringCbCopy(DevicePath,
                      BufLen,
                      detailData->DevicePath);

    LocalFree(detailData);
    SetupDiDestroyDeviceInfoList(deviceInfo);

    return hr;
}

이전 함수에서 애플리케이션은 다음 루틴을 호출하여 디바이스 경로를 가져옵니다.

  1. SetupDiGetClassDevs 는 지정된 디바이스 인터페이스 클래스와 일치하는 설치된 모든 디바이스에 대한 정보를 포함하는 배열인 디바이스 정보 집합에 대한 핸들을 가져오는 GUID_DEVINTERFACE_USBApplication1. 디바이스 인터페이스라는 배열의 각 요소는 시스템에 설치되고 등록된 디바이스에 해당합니다. 디바이스 인터페이스 클래스는 INF 파일에 정의한 디바이스 인터페이스 GUID를 전달하여 식별됩니다. 함수는 디바이스 정보 집합에 HDEVINFO 핸들을 반환합니다.

  2. SetupDiEnumDeviceInterfaces - 디바이스 정보 집합의 디바이스 인터페이스를 열거하고 디바이스 인터페이스에 대한 정보를 가져옵니다.

    이 호출에는 다음 항목이 필요합니다.

    • cbSize 멤버가 구조체의 크기로 설정된 초기화된 호출자 할당 SP_DEVICE_INTERFACE_DATA 구조체입니다.

    • 1단계의 HDEVINFO 핸들입니다.

    • INF 파일에 정의한 디바이스 인터페이스 GUID입니다.

      SetupDiEnumDeviceInterfaces 는 디바이스 인터페이스의 지정된 인덱스에 대한 디바이스 정보 집합 배열을 조회하고 초기화된 SP_DEVICE_INTERFACE_DATA 구조를 인터페이스에 대한 기본 데이터로 채웁니다.

    디바이스 정보 집합의 모든 디바이스 인터페이스를 열거하려면 함수가 FALSE를 반환하고 오류에 대한 오류 코드가 ERROR_NO_MORE_ITEMS 때까지 루프에서 SetupDiEnumDeviceInterfaces 를 호출합니다. GetLastError를 호출하여 ERROR_NO_MORE_ITEMS 오류 코드를 검색할 수 있습니다. 반복할 때마다 멤버 인덱스가 증가합니다.

    또는 디바이스 정보 집합을 열거하고 호출자가 할당한 SP_DEVINFO_DATA 구조에서 인덱스로 지정된 디바이스 인터페이스 요소에 대한 정보를 반환하는 SetupDiEnumDeviceInfo 를 호출할 수 있습니다. 그런 다음 SetupDiEnumDeviceInterfaces 함수의 DeviceInfoData 매개 변수에서 이 구조체에 대한 참조를 전달할 수 있습니다.

  3. SetupDiGetDeviceInterfaceDetail 을 사용하여 디바이스 인터페이스에 대한 자세한 데이터를 가져옵니다. 정보는 SP_DEVICE_INTERFACE_DETAIL_DATA 구조로 반환됩니다. SP_DEVICE_INTERFACE_DETAIL_DATA 구조체의 크기가 다르기 때문에 SetupDiGetDeviceInterfaceDetail이 두 번 호출됩니다. 첫 번째 호출은 SP_DEVICE_INTERFACE_DETAIL_DATA 구조에 할당할 버퍼 크기를 가져옵니다. 두 번째 호출은 할당된 버퍼를 인터페이스에 대한 자세한 정보로 채웁니다.

    1. DeviceInterfaceDetailData 매개 변수가 NULL로 설정된 SetupDiGetDeviceInterfaceDetail 을 호출합니다. 함수는 requiredlength 매개 변수에서 올바른 버퍼 크기를 반환합니다. 이 호출은 ERROR_INSUFFICIENT_BUFFER 오류 코드와 함께 실패합니다. 이 오류 코드가 필요합니다.
    2. requiredlength 매개 변수에서 검색되는 올바른 버퍼 크기에 따라 SP_DEVICE_INTERFACE_DETAIL_DATA 구조체에 대한 메모리를 할당합니다.
    3. SetupDiGetDeviceInterfaceDetail 을 다시 호출하고 DeviceInterfaceDetailData 매개 변수의 초기화된 구조에 대한 참조를 전달합니다. 함수가 반환되면 구조체는 인터페이스에 대한 자세한 정보로 채워집니다. 디바이스 경로는 SP_DEVICE_INTERFACE_DETAIL_DATA 구조체의 DevicePath 멤버에 있습니다.

디바이스에 대한 파일 핸들 만들기

device.cpp의 OpenDevice를 참조하세요.

디바이스와 상호 작용하려면 디바이스의 첫 번째(기본) 인터페이스에 대한 WinUSB 인터페이스 핸들이 필요합니다. 템플릿 코드는 파일 핸들과 WinUSB 인터페이스 핸들을 가져와서 DEVICE_DATA 구조에 저장합니다.

HRESULT
OpenDevice(
    _Out_     PDEVICE_DATA DeviceData,
    _Out_opt_ PBOOL        FailureDeviceNotFound
    )
/*++

Routine description:

    Open all needed handles to interact with the device.

    If the device has multiple USB interfaces, this function grants access to
    only the first interface.

    If multiple devices have the same device interface GUID, there is no
    guarantee of which one will be returned.

Arguments:

    DeviceData - Struct filled in by this function. The caller should use the
        WinusbHandle to interact with the device, and must pass the struct to
        CloseDevice when finished.

    FailureDeviceNotFound - TRUE when failure is returned due to no devices
        found with the correct device interface (device not connected, driver
        not installed, or device is disabled in Device Manager); FALSE
        otherwise.

Return value:

    HRESULT

--*/
{
    HRESULT hr = S_OK;
    BOOL    bResult;

    DeviceData->HandlesOpen = FALSE;

    hr = RetrieveDevicePath(DeviceData->DevicePath,
                            sizeof(DeviceData->DevicePath),
                            FailureDeviceNotFound);

    if (FAILED(hr)) {

        return hr;
    }

    DeviceData->DeviceHandle = CreateFile(DeviceData->DevicePath,
                                          GENERIC_WRITE | GENERIC_READ,
                                          FILE_SHARE_WRITE | FILE_SHARE_READ,
                                          NULL,
                                          OPEN_EXISTING,
                                          FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                                          NULL);

    if (INVALID_HANDLE_VALUE == DeviceData->DeviceHandle) {

        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    bResult = WinUsb_Initialize(DeviceData->DeviceHandle,
                                &DeviceData->WinusbHandle);

    if (FALSE == bResult) {

        hr = HRESULT_FROM_WIN32(GetLastError());
        CloseHandle(DeviceData->DeviceHandle);
        return hr;
    }

    DeviceData->HandlesOpen = TRUE;
    return hr;
}
  1. 앱은 CreateFile 을 호출하여 이전에 검색된 디바이스 경로를 지정하여 디바이스에 대한 파일 핸들을 만듭니다. WinUSB는 이 설정에 따라 달라지므로 FILE_FLAG_OVERLAPPED 플래그를 사용합니다.
  2. 앱은 디바이스에 대한 파일 핸들을 사용하여 WinUSB 인터페이스 핸들을 만듭니다. WinUSB 함수는 이 핸들을 사용하여 파일 핸들 대신 대상 디바이스를 식별합니다. WinUSB 인터페이스 핸들을 가져오기 위해 앱은 파일 핸들을 전달하여 WinUsb_Initialize 호출합니다. 후속 호출에서 수신된 핸들을 사용하여 디바이스에서 정보를 얻고 I/O 요청을 디바이스로 보냅니다.

디바이스 핸들 해제 - device.cpp의 CloseDevice를 참조하세요.

템플릿 코드는 디바이스에 대한 파일 핸들 및 WinUSB 인터페이스 핸들을 해제하는 코드를 구현합니다.

VOID
CloseDevice(
    _Inout_ PDEVICE_DATA DeviceData
    )
/*++

Routine description:

    Perform required cleanup when the device is no longer needed.

    If OpenDevice failed, do nothing.

Arguments:

    DeviceData - Struct filled in by OpenDevice

Return value:

    None

--*/
{
    if (FALSE == DeviceData->HandlesOpen) {

        //
        // Called on an uninitialized DeviceData
        //
        return;
    }

    WinUsb_Free(DeviceData->WinusbHandle);
    CloseHandle(DeviceData->DeviceHandle);
    DeviceData->HandlesOpen = FALSE;

    return;
}

다음 단계

다음으로, 다음 topics 읽고 디바이스 정보 가져오기를 보내고 디바이스로 데이터 전송을 보냅니다.