첫 번째 USB 클라이언트 드라이버(UMDF)를 작성하는 방법

이 문서에서는 Microsoft Visual Studio 2022와 함께 제공되는 사용자 모드 드라이버, USB(UMDF V2) 템플릿을 사용하여 UMDF(사용자 모드 드라이버 프레임워크) 기반 클라이언트 드라이버를 작성합니다. 클라이언트 드라이버를 빌드하고 설치한 후에는 장치 관리자 클라이언트 드라이버를 보고 디버거에서 드라이버 출력을 볼 수 있습니다.

UMDF(이 문서의 프레임워크라고 함)는 COM(구성 요소 개체 모델)을 기반으로 합니다. 모든 프레임워크 개체는 기본적으로 IUnknown 및 해당 메서드 QueryInterface, AddRefRelease를 구현해야 합니다. AddRefRelease 메서드는 개체의 수명을 관리하므로 클라이언트 드라이버는 참조 횟수를 유지할 필요가 없습니다. QueryInterface 메서드를 사용하면 클라이언트 드라이버가 WDF(Windows 드라이버 프레임워크) 개체 모델의 다른 프레임워크 개체에 대한 인터페이스 포인터를 가져올 수 있습니다. 프레임워크 개체는 복잡한 드라이버 작업을 수행하고 Windows와 상호 작용합니다. 특정 프레임워크 개체는 클라이언트 드라이버가 프레임워크와 상호 작용할 수 있도록 하는 인터페이스를 노출합니다.

UMDF 기반 클라이언트 드라이버는 DLL(In-Process COM 서버)로 구현되며, C++는 USB 디바이스용 클라이언트 드라이버를 작성하는 데 선호되는 언어입니다. 일반적으로 클라이언트 드라이버는 프레임워크에서 노출하는 여러 인터페이스를 구현합니다. 이 문서에서는 프레임워크 인터페이스를 콜백 클래스로 구현하는 클라이언트 드라이버 정의 클래스를 참조합니다. 이러한 클래스가 인스턴스화되면 결과 콜백 개체는 특정 프레임워크 개체와 파트너 관계를 집니다. 이 파트너 관계를 통해 클라이언트 드라이버는 프레임워크에서 보고한 디바이스 또는 시스템 관련 이벤트에 응답할 수 있습니다. Windows에서 특정 이벤트에 대해 프레임워크에 알줄 때마다 프레임워크는 클라이언트 드라이버의 콜백(사용 가능한 경우)을 호출합니다. 그렇지 않으면 프레임워크가 이벤트의 기본 처리를 진행합니다. 템플릿 코드는 드라이버, 디바이스 및 큐 콜백 클래스를 정의합니다.

템플릿에서 생성된 소스 코드에 대한 설명은 USB 클라이언트 드라이버에 대한 UMDF 템플릿 코드 이해를 참조하세요.

시작하기 전에

사용자 모드 드라이버를 개발, 디버깅 및 설치하려면 다음 두 대의 컴퓨터가 필요합니다.

  • Windows 10 이상 버전의 Windows 운영 체제를 실행하는 호스트 컴퓨터입니다. 호스트 컴퓨터는 드라이버를 작성하고 디버그하는 개발 환경입니다.
  • 드라이버를 테스트하려는 운영 체제 버전을 실행하는 대상 컴퓨터(예: Windows 11 버전 22H2). 대상 컴퓨터에는 디버그하려는 사용자 모드 드라이버와 디버거 중 하나가 있습니다.

호스트 및 대상 컴퓨터가 동일한 버전의 Windows를 실행하는 경우 Windows 10 이상 버전의 Windows를 실행하는 컴퓨터가 하나만 있을 수 있습니다. 이 문서에서는 두 대의 컴퓨터를 사용하여 사용자 모드 드라이버를 개발, 디버깅 및 설치한다고 가정합니다.

시작하기 전에 다음 요구 사항을 충족하는지 확인합니다.

소프트웨어 요구 사항

  • 호스트 컴퓨터에 Visual Studio 2022가 있습니다.

  • 호스트 컴퓨터에는 Windows 11 버전 22H2용 최신 WDK(Windows 드라이버 키트)가 있습니다.

    이 키트에는 USB 클라이언트 드라이버를 개발, 빌드 및 디버그하는 데 필요한 헤더, 라이브러리, 도구, 설명서 및 디버깅 도구가 포함됩니다. WDK를 가져오는 방법에서 최신 버전의 WDK를 가져올 수 있습니다.

  • 호스트 컴퓨터에는 Windows용 디버깅 도구의 최신 버전이 있습니다. WDK에서 최신 버전을 다운로드하거나 Windows용 디버깅 도구를 다운로드하여 설치할 수 있습니다.

  • 두 대의 컴퓨터를 사용하는 경우 사용자 모드 디버깅을 위해 호스트 및 대상 컴퓨터를 구성해야 합니다. 자세한 내용은 Visual Studio에서 User-Mode 디버깅 설정을 참조하세요.

하드웨어 요구 사항

클라이언트 드라이버를 작성할 USB 디바이스를 가져옵니다. 대부분의 경우 USB 디바이스 및 해당 하드웨어 사양이 제공됩니다. 사양은 디바이스 기능 및 지원되는 공급업체 명령을 설명합니다. 사양을 사용하여 USB 드라이버의 기능 및 관련 디자인 결정을 결정합니다.

USB 드라이버 개발을 접하는 경우 OSR USB FX2 학습 키트 를 사용하여 WDK에 포함된 USB 샘플을 연구합니다. 여기에는 USB FX2 디바이스와 클라이언트 드라이버를 구현하는 데 필요한 모든 하드웨어 사양이 포함되어 있습니다.

1단계: 드라이버 코드 생성

UMDF 드라이버 코드 작성에 대한 자세한 내용은 템플릿을 기반으로 UMDF 드라이버 작성을 참조하세요.

USB 관련 코드의 경우 Visual Studio 2022에서 다음 옵션을 선택합니다.

  1. 새 프로젝트 대화 상자의 맨 위에 있는 검색 상자에 USB를 입력합니다.
  2. 가운데 창에서 사용자 모드 드라이버, USB(UMDF V2)를 선택합니다.
  3. 다음을 선택합니다.
  4. 프로젝트 이름을 입력하고 저장 위치를 선택한 다음 만들기를 선택합니다.

다음 스크린샷은 USB User-Mode 드라이버 템플릿에 대한 새 프로젝트 대화 상자를 보여 줍니다.

Visual Studio 프로젝트 만들기 옵션의 스크린샷

Visual Studio 프로젝트 구성 만들기 화면의 스크린샷

이 문서에서는 프로젝트의 이름이 MyUSBDriver_UMDF_ 가정합니다. 여기에는 다음 파일이 포함됩니다.

파일 설명
Driver.h; Driver.c 드라이버 모듈의 진입점 구현을 포함합니다. DriverEntry 및 WDFDRIVER 관련 기능 및 콜백.
Device.h; Device.c WDFDEVICE 및 WDFUSBDEVICE 관련 기능 및 콜백.
Queue.h; Queue.c WDFQUEUE 관련 기능 및 콜백.
Trace.h 디바이스 인터페이스 GUID를 정의합니다. 또한 추적 함수 및 매크로를 선언합니다.
<프로젝트 이름>.inf 대상 컴퓨터에 클라이언트 드라이버를 설치하는 데 필요한 INF 파일입니다.

2단계: 디바이스에 대한 정보 추가

드라이버를 빌드하기 전에 디바이스, 특히 하드웨어 ID에 대한 정보를 추가해야 합니다. 하드웨어 ID를 제공하려면 다음을 수행합니다.

  1. 솔루션 탐색기 창에서 MyUSBDriver_UMDF_ 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.
  2. MyUSBDriver_UMDF_ 속성 페이지 창에서 다음과 같이 구성 속성 > 드라이버 설치 > 배포로 이동합니다. Visual Studio 2022 속성 페이지 창의 스크린샷
  3. 배포 전에 이전 드라이버 버전 제거를 선택합니다.
  4. 대상 디바이스 이름에 대해 테스트 및 디버깅을 위해 구성한 컴퓨터의 이름을 선택합니다.
  5. 하드웨어 ID 드라이버 업데이트를 선택하고 드라이버의 하드웨어 ID를 입력합니다. 이 연습에서 하드웨어 ID는 Root\MyUSBDriver_UMDF_. 확인을 선택합니다.

참고

이 연습에서는 하드웨어 ID가 실제 하드웨어 부분을 식별하지 않습니다. 디바이스 트리에 루트 노드의 자식으로 배치될 가상 디바이스를 식별합니다. 실제 하드웨어의 경우 하드웨어 ID 드라이버 업데이트를 선택하지 마세요. 대신 설치 및 확인을 선택합니다. INF(드라이버 정보) 파일에서 하드웨어 ID를 볼 수 있습니다. 솔루션 탐색기 창에서 MyUSBDriver_UMDF_ > 드라이버 파일로 이동하고 MyUSBDriver_UMDF_.inf를 두 번 클릭합니다. 하드웨어 ID는 [Standard.NT$ARCH$]에 있습니다.

모든 UMDF 기반 USB 클라이언트 드라이버에는 두 개의 Microsoft 제공 드라이버인 리플렉터와 WinUSB가 필요합니다.

  • 리플렉터: 드라이버가 성공적으로 로드되면 리플렉터가 커널 모드 스택에서 가장 많은 드라이버로 로드됩니다. 리플렉터를 커널 모드 스택의 최상위 드라이버여야 합니다. 이 요구 사항을 충족하기 위해 템플릿의 INF 파일은 리플렉터를 서비스로 지정하고 WinUSB는 INF에서 하위 필터 드라이버로 지정합니다.

    [MyDevice_Install.NT.Services]
    AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall  ; flag 0x2 sets this as the service for the device
    AddService=WinUsb,0x000001f8,WinUsb_ServiceInstall  ; this service is installed because its a filter.
    
  • WinUSB: 설치 패키지에는 클라이언트 드라이버의 경우 WinUSB가 커널 모드 USB 드라이버 스택의 게이트웨이이므로 Winusb.sys 위한 coinstaller가 포함되어야 합니다. 로드되는 또 다른 구성 요소는 클라이언트 드라이버의 호스트 프로세스(Wudfhost.exe)에서 WinUsb.dll 라는 사용자 모드 DLL입니다. Winusb.dll 클라이언트 드라이버와 WinUSB 간의 통신 프로세스를 간소화하는 WinUSB 함수를 노출합니다.

3단계: USB 클라이언트 드라이버 코드 빌드

드라이버를 빌드하려면 다음을 수행합니다.

  1. Visual Studio 2022에서 드라이버 프로젝트 또는 솔루션을 엽니다.
  2. 솔루션 탐색기 솔루션을 마우스 오른쪽 단추로 클릭하고 Configuration Manager 선택합니다.
  3. Configuration Manager활성 솔루션 구성(예: 디버그 또는 릴리스) 및 관심 있는 빌드 유형에 해당하는 활성 솔루션 플랫폼(예: x64)을 선택합니다.
  4. 디바이스 인터페이스 GUID가 프로젝트 전체에서 정확한지 확인합니다.
    • 디바이스 인터페이스 GUID는 Trace.h에 정의되며 Device.c에서 MyUSBDriverUMDFCreateDevice 참조됩니다. 이름이 MyUSBDriver_UMDF_ 프로젝트를 만들 때 Visual Studio 2022는 이름으로 GUID_DEVINTERFACE_MyUSBDriver_UMDF_ 디바이스 인터페이스 GUID를 정의하지만 잘못된 매개 변수 &GUID_DEVINTERFACE_MyUSBDriverUMDF를 사용하여 를 호출 WdfDeviceCreateDeviceInterface 합니다. 드라이버가 제대로 빌드되도록 잘못된 매개 변수를 Trace.h에 정의된 이름으로 바꿉니다.
  5. 빌드 메뉴에서 솔루션 빌드를 선택합니다.

자세한 내용은 드라이버 빌드를 참조하세요.

4단계: 테스트 및 디버깅을 위한 컴퓨터 구성

드라이버를 테스트하고 디버그하려면 호스트 컴퓨터에서 디버거를 실행하고 대상 컴퓨터에서 드라이버를 실행합니다. 지금까지 호스트 컴퓨터에서 Visual Studio를 사용하여 드라이버를 빌드했습니다. 다음으로 대상 컴퓨터를 구성해야 합니다. 대상 컴퓨터를 구성하려면 드라이버 배포 및 테스트를 위해 컴퓨터 프로비전의 지침을 따릅니다.

5단계: 커널 디버깅에 추적 사용

템플릿 코드에는 함수 호출을 추적하는 데 도움이 되는 여러 추적 메시지(TraceEvents)가 포함되어 있습니다. 소스 코드의 모든 함수에는 루틴의 진입 및 종료를 표시하는 추적 메시지가 포함됩니다. 오류의 경우 추적 메시지에는 오류 코드와 의미 있는 문자열이 포함됩니다. 드라이버 프로젝트에 WPP 추적이 사용되므로 빌드 프로세스 중에 만든 PDB 기호 파일에는 추적 메시지 서식 지정 지침이 포함되어 있습니다. WPP 추적을 위해 호스트 및 대상 컴퓨터를 구성하는 경우 드라이버는 추적 메시지를 파일 또는 디버거로 보낼 수 있습니다.

WPP 추적을 위해 호스트 컴퓨터를 구성하려면

  1. PDB 기호 파일에서 추적 메시지 서식 지정 지침을 추출하여 TMF(추적 메시지 형식) 파일을 만듭니다.

    Tracepdb.exe 사용하여 TMF 파일을 만들 수 있습니다. 도구는 WDK의 <Windows Kits\10\bin<\architecture> 폴더 설치> 폴더에 있습니다. 다음 명령은 드라이버 프로젝트에 대한 TMF 파일을 만듭니다.

    tracepdb -f <PDBFiles> -p <TMFDirectory>
    

    -f 옵션은 PDB 기호 파일의 위치와 이름을 지정합니다. -p 옵션은 Tracepdb에서 만든 TMF 파일의 위치를 지정합니다. 자세한 내용은 Tracepdb 명령을 참조하세요.

    지정된 위치에는 프로젝트의 C 코드 파일당 하나씩 세 개의 파일이 있습니다. GUID 파일 이름이 지정됩니다.

  2. 디버거에서 다음 명령을 입력합니다.

    .load Wmitrace
    .chain
    !wmitrace.searchpath + <TMF file location>
    

다음 명령을 실행합니다.

  • Wmitrace.dll 확장을 로드합니다.
  • 디버거 확장이 로드되어 있는지 확인합니다.
  • 디버거 확장의 검색 경로에 TMF 파일의 위치를 추가합니다.

다음과 유사한 출력이 표시됩니다.

Trace Format search path is: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE;c:\drivers\tmf

WPP 추적을 위해 대상 컴퓨터를 구성하려면

  1. 대상 컴퓨터에 Tracelog 도구가 있는지 확인합니다. 도구는 WDK의 <install_folder>Windows Kits\10\Tools\<arch> 폴더에 있습니다. 자세한 내용은 Tracelog 명령 구문을 참조하세요.

  2. 명령 창을 열고 관리자 권한으로 실행합니다.

  3. 다음 명령을 입력합니다.

    tracelog -start MyTrace -guid \#c918ee71-68c7-4140-8f7d-c907abbcb05d -flag 0xFFFF -level 7-rt -kd
    

명령은 MyTrace라는 추적 세션을 시작합니다.

guid 인수는 클라이언트 드라이버인 추적 공급자의 GUID를 지정합니다. Visual Studio 2022 프로젝트에서 Trace.h에서 GUID를 가져올 수 있습니다. 다른 옵션으로 다음 명령을 입력하고 .guid 파일에서 GUID를 지정할 수 있습니다. 파일에는 하이픈 형식의 GUID가 포함되어 있습니다.

tracelog -start MyTrace -guid c:\\drivers\\Provider.guid -flag 0xFFFF -level 7-rt -kd

다음 명령을 입력하여 추적 세션을 중지할 수 있습니다.

tracelog -stop MyTrace

6단계: 대상 컴퓨터에 드라이버 배포

  1. 솔루션 탐색기 창에서 프로젝트 이름(MyUSBDriver_UMDF_)을 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.
  2. 왼쪽 창에서 구성 속성 > 드라이버 설치 > 배포로 이동합니다.
  3. 대상 디바이스 이름에 대상 컴퓨터의 이름을 지정합니다.
  4. 설치/다시 설치 및 확인을 선택합니다.
  5. 확인을 선택합니다.
  6. 디버그 메뉴에서 디버깅 시작을 선택하거나 키보드에서 F5 키를 누릅니다.

참고

하드웨어 ID 드라이버 업데이트에서 디바이스의 하드웨어 ID를 지정하지 마세요. 하드웨어 ID는 드라이버의 정보(INF) 파일에서만 지정해야 합니다.

7단계: 장치 관리자 드라이버 보기

  1. 다음 명령을 입력하여 장치 관리자 엽니다.

    devmgmt
    
  2. 장치 관리자 다음 노드가 표시되는지 확인합니다.

    USB 디바이스

    MyUSBDriver_UMDF_Device

8단계: 디버거에서 출력 보기

추적 메시지가 호스트 컴퓨터의 디버거 직접 실행 창 에 표시되는지 확인합니다.

출력은 다음과 같은 형태가 됩니다.

[0]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::OnPrepareHardware Entry
[0]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::OnPrepareHardware Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::CreateInstanceAndInitialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Initialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Initialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::CreateInstanceAndInitialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Configure Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::CreateInstanceAndInitialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::Initialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::Initialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::CreateInstanceAndInitialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Configure Exit

설명

프레임워크와 클라이언트 드라이버가 함께 작동하여 Windows와 상호 작용하고 USB 디바이스로 전송된 요청을 처리하는 방법을 살펴보겠습니다. 이 그림에서는 UMDF 기반 USB 클라이언트 드라이버에 대해 시스템에 로드된 모듈을 보여 줍니다.

사용자 모드 클라이언트 드라이버 아키텍처의 다이어그램.

각 모듈의 용도는 다음과 같습니다.

  • 애플리케이션 - USB 디바이스와 통신하기 위해 I/O 요청을 발급하는 사용자 모드 프로세스입니다.
  • I/O 관리자 - 수신된 애플리케이션 요청을 나타내는 I/O 요청 패킷(IRP)을 만들고 대상 디바이스에 대한 커널 모드 디바이스 스택의 맨 위로 전달하는 Windows 구성 요소입니다.
  • Reflector - 커널 모드 디바이스 스택(WUDFRd.sys) 맨 위에 설치된 Microsoft 제공 커널 모드 드라이버입니다. 리플렉터에서 I/O 관리자에서 받은 IRP를 클라이언트 드라이버 호스트 프로세스로 리디렉션합니다. 요청을 받으면 프레임워크와 클라이언트 드라이버가 요청을 처리합니다.
  • 호스트 프로세스 - 사용자 모드 드라이버가 실행되는 프로세스입니다(Wudfhost.exe). 프레임워크 및 I/O 디스패처도 호스트합니다.
  • 클라이언트 드라이버 - USB 디바이스의 사용자 모드 함수 드라이버입니다.
  • UMDF - 클라이언트 드라이버를 대신하여 Windows와의 대부분의 상호 작용을 처리하는 프레임워크 모듈입니다. 클라이언트 드라이버가 일반적인 드라이버 작업을 수행하는 데 사용할 수 있는 사용자 모드 DDI(디바이스 드라이버 인터페이스)를 노출합니다.
  • Dispatcher - 호스트 프로세스에서 실행되는 메커니즘입니다. 는 사용자 모드 드라이버에서 처리되고 사용자 모드 스택의 맨 아래에 도달한 후 커널 모드로 요청을 전달하는 방법을 결정합니다. 그림에서 디스패처는 Winusb.dll 사용자 모드 DLL에 요청을 전달합니다.
  • Winusb.dll - 클라이언트 드라이버와 WinUSB(커널 모드로 로드된 Winusb.sys) 간의 통신 프로세스를 간소화하는 WinUSB Functions 를 노출하는 Microsoft에서 제공하는 사용자 모드 DLL입니다.
  • Winusb.sys - USB 디바이스에 대한 모든 UMDF 클라이언트 드라이버에 필요한 Microsoft 제공 드라이버입니다. 드라이버는 리플렉터 아래에 설치되어야 하며 커널 모드에서 USB 드라이버 스택에 대한 게이트웨이 역할을 해야 합니다. 자세한 내용은 WinUSB를 참조하세요.
  • USB 드라이버 스택 - MICROSOFT에서 제공하는 USB 디바이스와의 프로토콜 수준 통신을 처리하는 드라이버 집합입니다. 자세한 내용은 Windows의 USB 호스트 쪽 드라이버를 참조하세요.

애플리케이션이 USB 드라이버 스택에 대한 요청을 할 때마다 Windows I/O 관리자는 리플렉터에 요청을 전송하여 사용자 모드의 클라이언트 드라이버로 보냅니다. 클라이언트 드라이버는 WinUSB에 요청을 보내기 위해 WinUSB 함수 를 내부적으로 호출하는 특정 UMDF 메서드를 호출하여 요청을 처리합니다. 요청을 받으면 WinUSB는 요청을 처리하거나 USB 드라이버 스택에 전달합니다.