Notifications Plug and Play : user mode, services et kernel mode

Ce cours dédiée au développement de drivers en kernel mode sans Framework

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 144
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

Notifications Plug and Play : user mode, services et kernel mode

Post by Hydraxx »

Notifications Plug and Play : user mode, services et kernel mode

Un driver ou une application a souvent besoin d'être informé lorsqu'un périphérique apparaît, disparaît ou change d'état.

Windows fournit plusieurs couches de notifications PnP :
  • messages de fenêtre en user mode ;
  • notifications pour services ;
  • callbacks en kernel mode ;
  • notifications PnP personnalisées dans certains scénarios.
Il est important de distinguer ces notifications des IRP PnP. Une IRP comme IRP_MN_START_DEVICE pilote directement la stack ; une notification informe un autre composant qu'un événement a eu lieu.

1. Vue générale

Code: Select all

PnP Manager
    |
    +--> IRP_MJ_PNP vers la device stack
    |
    +--> callbacks kernel enregistrés
    |
    +--> notifications aux services
    |
    +--> WM_DEVICECHANGE aux applications concernées
2. Notifications user mode avec WM_DEVICECHANGE

Une application possédant une fenêtre peut recevoir :

WM_DEVICECHANGE

Dans sa Window Procedure :

Code: Select all

case WM_DEVICECHANGE:
    // wParam indique le type d'événement
    // lParam peut pointer vers une structure DEV_BROADCAST_*
    break;
Evénements courants :
  • DBT_DEVICEARRIVAL : arrivée d'un périphérique/interface.
  • DBT_DEVICEREMOVECOMPLETE : retrait terminé.
  • DBT_DEVICEQUERYREMOVE : demande de retrait dans certains scénarios.
  • DBT_DEVICEQUERYREMOVEFAILED : retrait finalement non autorisé/annulé.
3. RegisterDeviceNotification

Pour demander certaines notifications liées à une classe d'interface ou un handle :

RegisterDeviceNotificationW

Exemple pour une classe d'interface :

Code: Select all

DEV_BROADCAST_DEVICEINTERFACE_W filter = {0};
filter.dbcc_size = sizeof(filter);
filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
filter.dbcc_classguid = GUID_DEVINTERFACE_MYDEVICE;

HDEVNOTIFY notify = RegisterDeviceNotificationW(
    hwnd,
    &filter,
    DEVICE_NOTIFY_WINDOW_HANDLE
);
Lorsque l'inscription n'est plus nécessaire :

UnregisterDeviceNotification

4. DEV_BROADCAST_DEVICEINTERFACE

Lorsque l'événement concerne une interface, lParam de WM_DEVICECHANGE peut pointer vers une structure de type :

Code: Select all

DEV_BROADCAST_DEVICEINTERFACE
Elle fournit notamment :
  • le type d'événement ;
  • le GUID de classe d'interface ;
  • le nom/chemin de l'interface concernée.
L'application peut alors comparer le GUID avec la classe qu'elle surveille puis réénumérer si nécessaire.

5. Alternative moderne sans fenêtre

Pour du code moderne ciblant Windows récents, le Configuration Manager expose aussi :

CM_Register_Notification

avec :

CM_Unregister_Notification

Ce modèle permet de recevoir des notifications PnP via callback sans exiger une fenêtre recevant WM_DEVICECHANGE.

Il utilise notamment des structures CM_NOTIFY_FILTER pour préciser ce qui doit être surveillé.

6. Notifications dans un service Windows

Un service peut recevoir des événements device via le Service Control Manager.

Dans les modèles de service classiques, il enregistre des notifications avec RegisterDeviceNotification en utilisant un handle de service et le flag approprié :

Code: Select all

DEVICE_NOTIFY_SERVICE_HANDLE
Le handler étendu du service reçoit alors des événements tels que :

Code: Select all

SERVICE_CONTROL_DEVICEEVENT
Ce modèle est utile pour un service qui doit réagir à l'arrivée ou au retrait d'un périphérique sans interface graphique.

7. Notifications kernel avec IoRegisterPlugPlayNotification

Un driver kernel peut enregistrer un callback :

IoRegisterPlugPlayNotification

Forme logique :

Code: Select all

NTSTATUS IoRegisterPlugPlayNotification(
    IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
    ULONG EventCategoryFlags,
    PVOID EventCategoryData,
    PDRIVER_OBJECT DriverObject,
    PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
    PVOID Context,
    PVOID* NotificationEntry
);
Le système appellera CallbackRoutine lorsque l'événement correspondant se produit.

8. Catégories de notifications kernel

Les catégories importantes sont :
  • EventCategoryDeviceInterfaceChange : activation/désactivation d'interfaces de périphérique appartenant à une classe GUID.
  • EventCategoryTargetDeviceChange : événements relatifs à un périphérique cible précis identifié par un FILE_OBJECT.
  • EventCategoryHardwareProfileChange : changements de profil matériel.
Pour un driver qui veut découvrir l'arrivée d'une classe de device, DeviceInterfaceChange est souvent la catégorie la plus intéressante.

9. INCLUDE_EXISTING_INTERFACES

Avec EventCategoryDeviceInterfaceChange, le flag :

Code: Select all

PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
permet de demander des callbacks non seulement pour les changements futurs, mais également pour les interfaces déjà présentes lors de l'enregistrement.

C'est important pour éviter une race du type :

Code: Select all

Périphérique déjà présent
        |
Driver s'enregistre seulement pour les événements futurs
        |
Aucun événement d'arrivée ne se produit
        |
Driver ne découvre jamais l'interface existante
10. Callback kernel

Prototype conceptuel :

Code: Select all

NTSTATUS PnpNotifyCallback(
    PVOID NotificationStructure,
    PVOID Context
)
{
    // analyser le header et le GUID d'événement
    return STATUS_SUCCESS;
}
Toutes les structures de notification commencent par un header compatible avec :

PLUGPLAY_NOTIFICATION_HEADER

Il contient notamment le GUID décrivant le type précis d'événement.

11. GUID d'événements courants

Pour les interfaces de périphériques, on rencontre notamment :
  • GUID_DEVICE_INTERFACE_ARRIVAL : une interface a été activée.
  • GUID_DEVICE_INTERFACE_REMOVAL : une interface a été désactivée/retirée.
Pour les target device notifications, on peut rencontrer des événements associés à la demande ou à la réalisation d'un retrait.

Le callback doit toujours identifier le type exact de structure à partir de la catégorie et du GUID avant de caster NotificationStructure.

12. IoUnregisterPlugPlayNotification

L'inscription retourne un handle opaque via NotificationEntry.

Avant que le driver puisse être déchargé ou que le contexte utilisé par le callback soit détruit :

IoUnregisterPlugPlayNotification

ou, selon le besoin de synchronisation et les APIs disponibles dans le modèle ciblé, la variante Ex documentée pour attendre la fin des callbacks en cours.

Règle : ne jamais libérer le contexte tant qu'un callback enregistré peut encore être appelé.

13. Target Device Change

Une target-device notification concerne un périphérique précis plutôt qu'une classe entière.

Elle s'appuie généralement sur un FILE_OBJECT obtenu après ouverture/référence du device cible.

Elle est utile lorsqu'un composant veut être informé que CE périphérique particulier va être retiré ou a changé d'état.

14. Custom PnP notifications

Un driver peut signaler une notification personnalisée liée à un target device avec :
  • IoReportTargetDeviceChange
  • IoReportTargetDeviceChangeAsynchronous
Ces fonctions sont destinées à des événements personnalisés d'un driver.

Elles ne doivent pas être utilisées pour recréer artificiellement les notifications standard déjà générées par le PnP Manager.

Une structure de notification personnalisée inclut un GUID d'événement choisi par le fournisseur ainsi qu'un payload éventuel.

15. Ordre d'un surprise removal

Lorsqu'un device disparaît brutalement, la stack PnP est informée avant les observateurs externes.

Conceptuellement :

Code: Select all

Hardware disparaît
      |
PnP Manager détecte la disparition
      |
IRP_MN_SURPRISE_REMOVAL vers la stack
      |
Drivers arrêtent les accès hardware
      |
Notifications kernel/user mode
      |
IRP_MN_REMOVE_DEVICE plus tard
Cela permet au driver du périphérique de sécuriser son état avant que les applications réagissent à la disparition.

16. Notifications vs device interfaces

Le lien est direct :

Code: Select all

Driver A
IoRegisterDeviceInterface
IoSetDeviceInterfaceState(TRUE)
        |
        v
PnP publie l'interface
        |
        +--> IoRegisterPlugPlayNotification callbacks kernel
        +--> CM_Register_Notification callbacks user mode
        +--> WM_DEVICECHANGE pour applications inscrites
Une interface PnP fournit donc non seulement un chemin d'ouverture mais aussi un point naturel de découverte dynamique.

17. API essentielles : user mode
  • RegisterDeviceNotificationW : enregistre une fenêtre ou un service pour certaines notifications device.
  • UnregisterDeviceNotification : supprime l'inscription.
  • WM_DEVICECHANGE : message reçu par une fenêtre.
  • CM_Register_Notification : modèle de callback moderne du Configuration Manager.
  • CM_Unregister_Notification : supprime cette inscription.
18. API essentielles : kernel mode
  • IoRegisterPlugPlayNotification : enregistre un callback PnP kernel.
  • IoUnregisterPlugPlayNotification : désenregistre le callback.
  • IoReportTargetDeviceChange : envoie une notification target-device personnalisée synchrone.
  • IoReportTargetDeviceChangeAsynchronous : variante asynchrone.
  • PLUGPLAY_NOTIFICATION_HEADER : header commun permettant d'identifier le type d'événement.
19. Erreurs fréquentes
  • Confondre une notification PnP avec une IRP PnP.
  • Oublier de désenregistrer un callback avant l'unload du driver.
  • Libérer le Context alors qu'un callback peut encore arriver.
  • Casteur NotificationStructure sans vérifier le type/GUID de l'événement.
  • En user mode, supposer que WM_DEVICECHANGE donne toujours assez d'informations sans examiner la structure DEV_BROADCAST appropriée.
  • Ne surveiller que les futures interfaces et oublier celles déjà présentes.
  • Réutiliser les limitations Windows 98/Me décrites dans les vieux ouvrages comme si elles s'appliquaient encore à Windows 10/11.
20. Résumé

Trois niveaux principaux :

Code: Select all

USER MODE GUI
WM_DEVICECHANGE
RegisterDeviceNotification

USER MODE MODERNE / SERVICE
CM_Register_Notification
ou notifications SCM selon le modèle

KERNEL MODE
IoRegisterPlugPlayNotification
Les notifications permettent à un composant d'observer le monde PnP sans devenir lui-même le driver qui gère directement les IRP de la stack.

Who is online

Users browsing this forum: No registered users and 1 guest