IRP_MN_SURPRISE_REMOVAL 요청 처리

Windows 2000 이상 PnP 관리자는 이 IRP를 보내 디바이스를 I/O 작업에 더 이상 사용할 수 없으며 컴퓨터에서 예기치 않게 제거되었음을 드라이버에 알립니다("깜짝 제거").

PnP 관리자는 다음과 같은 이유로 IRP_MN_SURPRISE_REMOVAL 요청을 보냅니다.

  • 버스에 핫 플러그 알림이 있는 경우 디바이스의 부모 버스 드라이버에 디바이스가 사라졌다는 것을 알 수 있습니다. 버스 드라이버는 IoInvalidateDeviceRelations를 호출합니다. 이에 대한 응답으로 PnP 관리자는 해당 자식(BusRelationsIRP_MN_QUERY_DEVICE_RELATIONS)에 대해 버스 드라이버를 쿼리합니다. PnP 관리자는 디바이스가 새 자식 목록에 없는 것을 확인하고 디바이스에 대한 서프라이즈 제거 작업을 시작합니다.

  • 버스는 또 다른 이유로 열거되며, 깜짝 제거된 디바이스는 자식 목록에 포함되지 않습니다. PnP 관리자는 깜짝 제거 작업을 시작합니다.

  • 디바이스의 함수 드라이버는 디바이스가 더 이상 존재하지 않는다고 확인합니다(예: 요청 시간이 반복적으로 초과됨). 버스는 열거 가능할 수 있지만 핫 플러그 알림이 없습니다. 이 경우 함수 드라이버는 IoInvalidateDeviceState를 호출합니다. 이에 대한 응답으로 PnP 관리자는 디바이스 스택에 IRP_MN_QUERY_PNP_DEVICE_STATE 요청을 보냅니다. 함수 드라이버는 디바이스가 실패했음을 나타내는 PNP_DEVICE_STATE 비트 마스크에서 PNP_DEVICE_FAILED 플래그를 설정합니다.

  • 드라이버 스택은 IRP_MN_STOP_DEVICE 요청을 성공적으로 완료한 다음 후속 IRP_MN_START_DEVICE 요청에 실패합니다. 이러한 경우 디바이스는 여전히 연결되어 있습니다.

모든 PnP 드라이버는 이 IRP를 처리해야 하며 Irp-IoStatus.Status>를 STATUS_SUCCESS 설정해야 합니다. PnP 디바이스용 드라이버는 드라이버의 AddDevice 루틴이 호출된 후 언제든지 IRP_MN_SURPRISE_REMOVAL 처리할 수 있도록 준비해야 합니다. IRP를 적절하게 처리하면 드라이버와 PnP 관리자가 다음을 수행할 수 있습니다.

  1. 디바이스가 여전히 연결되어 있는 경우 디바이스를 사용하지 않도록 설정합니다.

    드라이버 스택이 IRP_MN_STOP_DEVICE 요청을 성공적으로 완료했지만 어떤 이유로 후속 IRP_MN_START_DEVICE 요청에 실패한 경우 디바이스를 사용하지 않도록 설정해야 합니다.

  2. 디바이스에 할당된 하드웨어 리소스를 해제하고 다른 디바이스에서 사용할 수 있도록 합니다.

    디바이스를 더 이상 사용할 수 없는 즉시 하드웨어 리소스를 해제해야 합니다. 그런 다음 PnP 관리자는 사용자가 컴퓨터에 다시 핫 플러그백할 수 있는 동일한 디바이스를 포함하여 리소스를 다른 디바이스에 다시 할당할 수 있습니다.

  3. 데이터 손실 및 시스템 중단의 위험을 최소화합니다.

    핫 플러그링 및 해당 드라이버를 지원하는 디바이스는 서프라이즈 제거를 처리하도록 설계되어야 합니다. 사용자는 언제든지 핫 플러그를 지원하는 디바이스를 제거할 수 있어야 합니다.

PnP 관리자는 시스템 스레드의 컨텍스트에서 IRQL = PASSIVE_LEVEL IRP_MN_SURPRISE_REMOVAL 보냅니다.

PnP 관리자는 사용자 모드 애플리케이션 및 기타 커널 모드 구성 요소에 알리기 전에 이 IRP를 드라이버에 보냅니다. IRP가 완료되면 PnP 관리자는 디바이스에서 이러한 알림을 등록한 커널 모드 구성 요소에 GUID_TARGET_DEVICE_REMOVE_COMPLETE EventCategoryTargetDeviceChange 알림을 보냅니다.

IRP_MN_SURPRISE_REMOVAL IRP는 먼저 디바이스 스택의 상위 드라이버에 의해 처리된 다음 각 하위 드라이버에 의해 처리됩니다.

IRP_MN_SURPRISE_REMOVAL 대한 응답으로 드라이버는 나열된 순서대로 다음을 수행해야 합니다.

  1. 디바이스가 제거되었는지 확인합니다.

    드라이버는 항상 디바이스가 여전히 연결되어 있는지 확인하려고 시도해야 합니다. 이 경우 드라이버는 디바이스를 중지하고 사용하지 않도록 설정해야 합니다.

  2. 디바이스의 하드웨어 리소스(인터럽트, I/O 포트, 메모리 레지스터 및 DMA 채널)를 해제합니다.

  3. 부모 버스 드라이버에서 드라이버가 그렇게 할 수 있는 경우 버스 슬롯의 전원을 니다. PoSetPowerState를 호출하여 전원 관리자에게 알립니다. 자세한 내용은 전원 관리를 참조하세요.

  4. 디바이스에서 새 I/O 작업을 방지합니다.

    드라이버는 후속 IRP_MJ_CLEANUP, IRP_MJ_CLOSE, IRP_MJ_POWERIRP_MJ_PNP 요청을 처리해야 하지만 드라이버는 새 I/O 작업을 방지해야 합니다. 드라이버는 닫기, 클린 및 PnP IRP 외에 디바이스가 있는 경우 드라이버가 처리했을 후속 IRP에 실패해야 합니다.

    드라이버는 디바이스 확장에서 디바이스가 깜짝 제거되었음을 나타내는 비트를 설정할 수 있습니다. 드라이버의 디스패치 루틴은 이 비트를 검사 합니다.

  5. 디바이스에서 미해결 I/O 요청에 실패합니다.

  6. 드라이버가 디바이스에 대해 처리하지 않는 모든 IRP를 계속 전달합니다.

  7. IoSetDeviceInterfaceState를 사용하여 디바이스 인터페이스를 사용하지 않도록 설정합니다.

  8. 디바이스별 할당, 메모리, 이벤트 또는 기타 시스템 리소스를 정리합니다.

    드라이버는 후속 IRP_MN_REMOVE_DEVICE 요청을 받을 때까지 이러한 클린 연기할 수 있지만 레거시 구성 요소에 닫을 수 없는 열린 핸들이 있는 경우 제거 IRP는 전송되지 않습니다.

  9. 디바이스 개체를 디바이스 스택에 연결한 상태로 둡니다.

    후속 IRP_MN_REMOVE_DEVICE 요청까지 디바이스 개체를 분리하고 삭제하지 마세요.

  10. IRP를 완료합니다.

    함수 또는 필터 드라이버에서:

    • Irp-IoStatus.Status>를 STATUS_SUCCESS 설정합니다.

    • IoSkipCurrentIrpStackLocation을 사용하여 다음 스택 위치를 설정하고 IRP를 IoCallDriver를 사용하여 다음 하위 드라이버에 전달합니다.

    • IoCallDriver에서 상태 DispatchPnP 루틴에서 반환 상태 전파합니다.

    • IRP를 완료하지 마세요.

    버스 드라이버에서(자식 PDO에 대해 이 IRP를 처리함):

    • Irp-IoStatus.Status>를 STATUS_SUCCESS 설정합니다.

    • IO_NO_INCREMENT 사용하여 IRP(IoCompleteRequest)를 완료합니다.

    • DispatchPnP 루틴에서 반환합니다.

이 IRP가 성공하고 디바이스에 열려 있는 모든 핸들이 닫힌 후 PnP 관리자는 디바이스 스택에 IRP_MN_REMOVE_DEVICE 요청을 보냅니다. IRP 제거에 대한 응답으로 드라이버는 해당 디바이스 개체를 스택에서 분리하고 삭제합니다. 레거시 구성 요소에 디바이스에 열려 있는 핸들이 있고 I/O 오류에도 불구하고 핸들을 열어 두면 PnP 관리자는 제거 IRP를 보내지 않습니다.

모든 드라이버는 이 IRP를 처리해야 하며 디바이스가 컴퓨터에서 물리적으로 제거되었음을 유의해야 합니다. 그러나 일부 드라이버는 IRP를 처리하지 않으면 부정적인 결과를 일으키지 않습니다. 예를 들어 시스템 하드웨어 리소스를 사용하지 않고 USB 또는 1394와 같은 프로토콜 기반 버스에 상주하는 디바이스는 하드웨어 리소스를 사용하지 않으므로 연결할 수 없습니다. 함수 및 필터 드라이버가 부모 버스 드라이버를 통해서만 디바이스에 액세스하기 때문에 드라이버가 제거된 후 디바이스에 액세스하려고 시도할 위험이 없습니다. 버스는 제거 알림을 지원하므로 디바이스가 사라지고 버스 드라이버가 디바이스에 액세스하려는 모든 후속 시도에 실패하면 부모 버스 드라이버에 알림이 표시됩니다.

Windows 98/Me에서는 PnP 관리자가 이 IRP를 보내지 않습니다. 사용자가 먼저 적절한 사용자 인터페이스를 사용하지 않고 디바이스를 제거하는 경우 PnP 관리자는 디바이스의 드라이버에 IRP_MN_REMOVE_DEVICE 요청만 보냅니다. 모든 WDM 드라이버는 IRP_MN_SURPRISE_REMOVALIRP_MN_REMOVE_DEVICE 모두 처리해야 합니다. IRP_MN_REMOVE_DEVICE 코드는 드라이버가 이전의 서프라이즈 제거 IRP를 수신했는지 여부를 검사 두 경우를 모두 처리해야 합니다.

GUID_REENUMERATE_SELF_INTERFACE_STANDARD 사용

GUID_REENUMERATE_SELF_INTERFACE_STANDARD 인터페이스를 사용하면 드라이버가 디바이스를 다시 열거하도록 요청할 수 있습니다.

이 인터페이스를 사용하려면 InterfaceType = GUID_REENUMERATE_SELF_INTERFACE_STANDARD 사용하여 버스 드라이버에 IRP_MN_QUERY_INTERFACE IRP를 보냅니다. 버스 드라이버는 인터페이스의 개별 루틴에 대한 포인터를 포함하는 REENUMERATE_SELF_INTERFACE_STANDARD 구조체에 대한 포인터를 제공합니다. ReenumerateSelf 루틴은 버스 드라이버가 자식 디바이스를 다시 열거할 것을 요청합니다.

PNP_DEVICE_STATE 정보

PNP_DEVICE_STATE 유형은 디바이스의 PnP 상태를 설명하는 비트 마스크입니다. 드라이버는 IRP_MN_QUERY_PNP_DEVICE_STATE 요청에 대한 응답으로 이 형식의 값을 반환합니다.

typedef ULONG PNP_DEVICE_STATE, *PPNP_DEVICE_STATE;

PNP_DEVICE_STATE 값의 플래그 비트는 다음과 같이 정의됩니다.

플래그 비트 Description
PNP_DEVICE_DISABLED

디바이스는 물리적으로 존재하지만 하드웨어에서는 사용하지 않도록 설정됩니다.

PNP_DEVICE_DONT_DISPLAY_IN_UI

사용자 인터페이스에 디바이스를 표시하지 마세요. 랩톱이 도킹 해제될 때 사용할 수 없는 노트북의 게임 포트와 같이 물리적으로 존재하지만 현재 구성에서 사용할 수 없는 디바이스에 대해 설정합니다. (또한 DEVICE_CAPABILITIES 구조체에서 NoDisplayInUI 플래그를 참조하세요.)

PNP_DEVICE_FAILED

디바이스가 있지만 제대로 작동하지 않습니다.

이 플래그와 PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED 모두 설정되면 PnP 관리자가 새 하드웨어 리소스를 할당하기 전에 디바이스를 중지해야 합니다(논스톱 리밸런스는 디바이스에 대해 지원되지 않음).

PNP_DEVICE_NOT_DISABLEABLE

컴퓨터가 시작될 때 디바이스가 필요합니다. 이러한 디바이스를 사용하지 않도록 설정하면 안 됩니다.

드라이버는 적절한 시스템 작업에 필요한 디바이스에 대해 이 비트를 설정합니다. 예를 들어 드라이버가 디바이스가 페이징 경로(DeviceUsageTypePaging대한 IRP_MN_DEVICE_USAGE_NOTIFICATION)에 있다는 알림을 받으면 드라이버는 IoInvalidateDeviceState를 호출하고 결과 IRP_MN_QUERY_PNP_DEVICE_STATE 요청에서 이 플래그를 설정합니다.

이 비트가 디바이스에 대해 설정된 경우 PnP 관리자는 이 설정을 디바이스의 부모 디바이스, 해당 부모의 부모 디바이스 등에 전파합니다.

이 비트가 루트 열거형 디바이스에 대해 설정된 경우 디바이스를 사용하지 않도록 설정하거나 제거할 수 없습니다.

PNP_DEVICE_REMOVED

디바이스가 물리적으로 제거되었습니다.

PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED

디바이스에 대한 리소스 요구 사항이 변경되었습니다.

일반적으로 버스 드라이버는 새 자식 디바이스를 열거하기 위해 리소스 요구 사항을 확장해야 한다고 결정한 경우 이 플래그를 설정합니다.

PNP_DEVICE_DISCONNECTED

디바이스 드라이버가 로드되었지만 이 드라이버는 디바이스가 더 이상 컴퓨터에 연결되어 있지 않음을 감지했습니다. 일반적으로 이 플래그는 무선 디바이스와 통신하는 함수 드라이버에 사용됩니다. 예를 들어 디바이스가 범위를 벗어나면 플래그가 설정되고 디바이스가 다시 범위로 이동하고 다시 연결한 후 지워집니다.

버스 드라이버는 일반적으로 이 플래그를 설정하지 않습니다. 디바이스가 더 이상 연결되어 있지 않으면 버스 드라이버가 자식 디바이스 열거를 중지해야 합니다. 이 플래그는 함수 드라이버가 연결을 관리하는 경우에만 사용됩니다.

이 플래그의 유일한 목적은 디바이스가 연결되어 있는지 여부를 클라이언트에 알리는 것입니다. 플래그를 설정해도 드라이버가 로드되는지 여부에는 영향을 주지 않습니다.

PnP 관리자는 디바이스 스택에 IRP_MN_QUERY_PNP_DEVICE_STATE 요청을 전송하여 디바이스를 시작한 직후 디바이스의 PNP_DEVICE_STATE 쿼리합니다. 이 IRP에 대한 응답으로 디바이스의 드라이버는 PNP_DEVICE_STATE 적절한 플래그를 설정합니다.

초기 쿼리 후에 상태 특성이 변경되면 드라이버는 IoInvalidateDeviceState를 호출하여 PnP 관리자에게 알린다. IoInvalidateDeviceState에 대한 호출에 대한 응답으로 PnP 관리자는 디바이스의 PNP_DEVICE_STATE 다시 쿼리합니다.

디바이스가 PNP_DEVICE_NOT_DISABLEABLE 표시된 경우 디버거는 devnode에 대한 DNUF_NOT_DISABLEABLE 사용자 플래그를 표시합니다. 디버거에는 디바이스를 사용하지 않도록 설정할 수 없는 이유의 수를 계산하는 DisableableDepends 값도 표시됩니다. 이 값은 X+Y의 합계이며, 디바이스를 사용하지 않도록 설정할 수 없고 Y가 비활성화할 수 없는 디바이스의 자식 디바이스 수인 경우 X는 1입니다.