Lire les codes d'erreur Windows — la structure à trois couches Win32, HRESULT et NTSTATUS
· Mis à jour le: · Go Komura · Windows, Codes d'erreur, HRESULT, NTSTATUS, Win32 API, Investigation d'incidents, Débogage, Développement Windows
Historique des révisions (première version, publiée le 20 Aug 2026)
- Première publication
Citer cet article(DOI (archive enregistrée): 10.5281/zenodo.22176037)
Les DOI ci-dessous renvoient à des versions déjà archivées et peuvent différer du texte actuel. Pour citer le texte actuel, utilisez l’URL de cette page.
Go Komura (2026). Lire les codes d'erreur Windows — la structure à trois couches Win32, HRESULT et NTSTATUS. KomuraSoft LLC. https://comcomponent.com/fr/blog/windows-error-codes-win32-hresult-ntstatus/
- DOI (archive enregistrée)
- 10.5281/zenodo.22176037
- DOI (dernière version enregistrée)
- 10.5281/zenodo.22176038
« L’écran de l’application a affiché l’erreur 0x80004005. Que cela veut-il dire ? » — Dans les investigations d’incidents, c’est une question classique. Si vous cherchez le nombre tel quel, vous obtenez des remèdes pour des contextes sans rapport : Windows Update, dossiers partagés, VBA, connexions à une base de données.
Les résultats se dispersent parce que 0x80004005 (E_FAIL) est un code générique qui ne signifie rien de plus qu’« un échec aux détails inconnus ». En revanche, 0x80070005 se décompose en « numéro d’erreur Win32 5 = accès refusé, réemballé en HRESULT ». Des codes qui se ressemblent ne portent pas la même quantité d’information.
Windows a trois systèmes — codes d’erreur Win32, HRESULT et NTSTATUS — et un code est converti quand il traverse une couche. Pour accélérer l’investigation, plutôt que d’apprendre les nombres par cœur, il est plus efficace de distinguer « quelle couche, qui a renvoyé le code » et « quel était le code avant le réemballage ».
Cet article s’adresse aux informaticiens de PME et aux développeurs d’applications Windows. C’est un guide pratique. À partir de Microsoft Learn et de la spécification publiée [MS-ERREF] au mois d’août 2026, il enchaîne dans l’ordre : comment distinguer les trois systèmes, comment décomposer un HRESULT, le lien avec les exceptions .NET, et la recherche avec err.exe et PowerShell.
1. D’abord la conclusion
Les trois points à retenir d’emblée sont les suivants.
- Normaliser la notation et identifier le système. Les codes d’erreur Win32, HRESULT et NTSTATUS sont des systèmes distincts. Décimal et hexadécimal sont deux notations de la même valeur, et un HRESULT s’affiche parfois en décimal signé négatif. Alignez d’abord sur 8 chiffres hexadécimaux, puis lisez avec l’API qui a renvoyé le code et l’endroit où il s’est affiché.123
- Décomposer 0x8007xxxx ; pour E_FAIL, passer à l’investigation du contexte. 0x80070005 est un HRESULT FACILITY_WIN32 (7), et ses 16 bits de poids faible valent 5 = ERROR_ACCESS_DENIED. À l’inverse, 0x80004005 (E_FAIL) est un « échec non spécifié » : le code seul ne permet pas de resserrer la cause.456
- Chercher ensemble le sens du code, l’opération qui a échoué et la cible. La même erreur 5 a des causes variées : ACL, élévation, logiciel de sécurité, etc. La cible d’une erreur 2 peut être une DLL dépendante, et un fichier occupé peut apparaître comme erreur 32. Une fois le nom trouvé, passez aux journaux et à Process Monitor pour déterminer « quelle API a échoué contre quoi ».1
Pour les outils, alternez certutil -error et net helpmsg fournis avec Windows, Win32Exception de PowerShell, err.exe sur une machine de développement, et !error de WinDbg pendant l’analyse d’un dump.789 Même devenu une exception .NET, le HRESULT d’origine se retrouve via Exception.HResult.10
Où lire, selon l’objectif
| Ce que vous voulez savoir | Sections à lire |
|---|---|
| Distinguer le code sous les yeux et le chercher tout de suite | Aligner la notation au chapitre 2, puis les commandes du chapitre 7 et la procédure du chapitre 8 |
| Journaliser correctement un échec d’API Win32 | GetLastError et FormatMessage au chapitre 3, P/Invoke à la section 6.3 |
| Comprendre le lien entre 0x80004005, 0x80070005 et les exceptions .NET | Décomposition HRESULT au chapitre 4, COM et .NET au chapitre 6 |
| Chercher un code de plantage tel que 0xC0000005 | NTSTATUS et codes STOP au chapitre 5, WinDbg à la section 7.4 |
| Éviter les confusions fréquentes en investigation | Les lectures erronées du chapitre 9 |
En une phrase, le motif d’une investigation de code d’erreur Windows est « aligner la notation sur l’hexadécimal → juger de quelle couche est le code → décomposer et extraire le code essentiel → le lire avec le contexte ».
Dans le diagramme, un trait continu marque une relation qui vaut toujours et un trait pointillé une relation conditionnelle (les conditions figurent dans l’explication de chaque relation sur la page de détail). La liste complète des relations (18 au total, avec preuve et niveau de certitude) et les définitions des concepts principaux sont rassemblées sur la page de détail de la carte des connaissances (en japonais). Données : JSON-LD / Turtle
2. Windows a trois systèmes de codes d’erreur
D’abord, la vue d’ensemble. Le même nombre ne se lit pas de la même façon selon le système dans lequel il a été passé. En rangeant les principaux acteurs qui renvoient un code et l’apparence typique, on obtient le tableau suivant.
| Système | Principal acteur qui le renvoie | Apparence typique | Exemple représentatif |
|---|---|---|---|
| Code d’erreur Win32 | API Win32 (GetLastError), code de sortie d’une commande |
Un petit décimal (0–15999) | 5 = ERROR_ACCESS_DENIED |
| HRESULT | Un composant COM, le shell, un installeur, de nombreux frameworks | Hexadécimal à 8 chiffres commençant par 0x8, ou un décimal négatif | 0x80004005 = E_FAIL |
| NTSTATUS | Le noyau, un pilote, une API native (ntdll) | Les erreurs sont un hexadécimal à 8 chiffres commençant par 0xC | 0xC0000005 = STATUS_ACCESS_VIOLATION |
L’« apparence » de ce tableau est un indice pour chercher le système. Comme le tableau de jugement du chapitre 8, servez-vous-en comme premier candidat, puis recoupez avec l’API ou le composant qui a renvoyé le code.
Historiquement, se sont empilés les codes d’erreur Win32 héritiers des numéros MS-DOS, le NTSTATUS interne au noyau NT, et le HRESULT conçu à l’arrivée de COM pour empaqueter succès/échec et origine dans 32 bits.
Sur Windows actuel, une conversion NTSTATUS du noyau → code d’erreur Win32 → HRESULT de la couche COM se produit au quotidien. Remonter de la couche supérieure vers la couche inférieure à partir du code vu en haut est le geste de base de l’investigation.114
flowchart TB
accTitle: Flux de conversion d'un système à l'autre
accDescr: Le sous-système Win32 convertit le NTSTATUS renvoyé par le noyau en code d'erreur Win32, puis la couche COM l'emballe encore en HRESULT
kernel["Noyau et pilotes"] --> nt["NTSTATUS (erreur : 0xC…)"]
nt -->|Conversion par le sous-système Win32| win["Code d'erreur Win32 (5, etc.)"]
win -->|Réemballage par la couche COM| hr["HRESULT (0x8007xxxx)"]
Figure 1 : Flux de conversion d’une couche à l’autre. Le NTSTATUS du noyau devient une erreur Win32, puis est réemballé en HRESULT.
2.1. S’habituer à lire décimal et hexadécimal l’un pour l’autre
Avant de chercher le système, éliminez le flou de notation. Le même code s’affiche en décimal, en hexadécimal ou en négatif signé.
| Valeur affichée | La même valeur dans une autre notation | Sens |
|---|---|---|
| Erreur 5 | 0x5 | ERROR_ACCESS_DENIED |
| Erreur 1223 | 0x4C1 | ERROR_CANCELLED |
| 0x80070005 | -2147024891 | Le même HRESULT |
Avec PowerShell, on relit d’une ligne :
# Décimal → hexadécimal
'0x{0:X8}' -f 1223 # 0x000004C1
'0x{0:X8}' -f -2147024891 # 0x80070005 (négatif = HRESULT vers hex)
# Hexadécimal → décimal
0x4C1 # 1223
Devant un décimal négatif qui commence par « -214… », convertissez-le d’instinct en hexadécimal. Cela seul réduit beaucoup les égarements à l’entrée de l’investigation.
flowchart TB
accTitle: Trois apparences du même code
accDescr: L'erreur 5 en décimal, 0x5 en hexadécimal et les 16 bits de poids faible de 0x80070005 désignent tous ERROR_ACCESS_DENIED
d["Notation décimale « erreur 5 »"] --> same["ERROR_ACCESS_DENIED"]
h["Notation hexadécimale « 0x5 »"] --> same
l["16 bits de poids faible de 0x80070005"] --> same
same -.-> memo["Même code, notations différentes"]
Figure 2 : Décimal, hexadécimal et 16 bits de poids faible d’un HRESULT ne sont que des notations différentes du même code.
3. Codes d’erreur Win32 — GetLastError et FORMAT_MESSAGE
3.1. Comportement de base de GetLastError
Confirmer la valeur de retour, puis enregistrer la dernière erreur
De nombreuses API Win32, comme CreateFile, indiquent l’échec par la valeur de retour (FALSE, NULL, INVALID_HANDLE_VALUE, etc.) et laissent le détail dans un « code de dernière erreur » par thread. Pour ces API, on obtient le code avec GetLastError immédiatement après avoir constaté l’échec.12
La façon d’obtenir l’erreur se vérifie toutefois API par API. RegOpenKeyEx, par exemple, renvoie le code d’erreur lui-même plutôt que de s’appuyer sur la dernière erreur. Traitez-le à part des exemples qui utilisent GetLastError.13
Deux précautions quand vous utilisez GetLastError :12
- Lire immédiatement après l’échec. Si vous intercalez un autre appel d’API (une fonction de journalisation, par exemple), cet appel peut écraser le code de dernière erreur.
- Ne pas se fier à la valeur en cas de succès. Certaines API remettent le code de dernière erreur à 0 en cas de succès, d’autres n’y touchent pas. Le principe est de lire après avoir constaté l’échec sur la valeur de retour.
sequenceDiagram
accTitle: Lire GetLastError juste après l'échec
accDescr: Après avoir constaté l'échec sur la valeur de retour, obtenir le code de dernière erreur avec GetLastError sans intercaler d'autre appel d'API
participant app as Application
participant api as Win32 API
app->>api: Appel CreateFile
api-->>app: Valeur de retour d'échec
app->>api: GetLastError
api-->>app: Code 5
Note over app: Un autre appel intercalé peut écraser la valeur
Figure 3 : Lire le code de dernière erreur immédiatement après l’échec. Un autre appel d’API intercalé peut l’écraser.
Laisser le code enregistré dans le journal, avec le message
Pour obtenir une chaîne de message à partir du code, utilisez FormatMessage avec l’indicateur FORMAT_MESSAGE_FROM_SYSTEM. L’ordre est : enregistrer d’abord le code, puis le transformer en chaîne.1
#include <windows.h>
#include <stdio.h>
void PrintLastError(const wchar_t* apiName)
{
DWORD code = GetLastError(); // Appeler juste après l'échec (n'intercalez pas d'autre API)
wchar_t message[512] = L"";
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, code, 0, message, 512, nullptr);
wprintf(L"%s failed: %lu (0x%08lX) %s", apiName, code, code, message);
}
Dans le journal d’une application maison, laisser ainsi le décimal, l’hexadécimal et le texte du message accélère nettement une investigation ultérieure.
flowchart TB
accTitle: Tirer le message du code et le laisser dans le journal
accDescr: Obtenir la chaîne de message du code d'erreur avec FormatMessage et l'indicateur FORMAT_MESSAGE_FROM_SYSTEM, et laisser dans le journal le décimal, l'hexadécimal et le texte
code["Code d'erreur (ex. : 5)"] --> fm["Obtention de la chaîne via FormatMessage"]
fm --> msg["Texte du message"]
msg --> log["Enregistrement dans le journal"]
log -.-> both["Décimal, hexadécimal et texte côte à côte"]
Figure 4 : Convertir le code d’erreur en chaîne de message avec FormatMessage, et laisser dans le journal le décimal, l’hexadécimal et le texte.
3.2. Codes représentatifs fréquents sur le terrain
Les codes d’erreur Win32 sont définis dans la plage 0–15999 ; Microsoft Learn en donne la liste complète.1 Parmi eux, ceux que l’on croise sans cesse en investigation d’incidents sont les suivants.
| Décimal | Hex | Symbole | Sens |
|---|---|---|---|
| 2 | 0x2 | ERROR_FILE_NOT_FOUND | Le fichier spécifié est introuvable |
| 3 | 0x3 | ERROR_PATH_NOT_FOUND | Le chemin spécifié est introuvable |
| 5 | 0x5 | ERROR_ACCESS_DENIED | L’accès a été refusé |
| 32 | 0x20 | ERROR_SHARING_VIOLATION | Impossible d’accéder : un autre processus l’utilise |
| 87 | 0x57 | ERROR_INVALID_PARAMETER | Le paramètre est incorrect |
| 122 | 0x7A | ERROR_INSUFFICIENT_BUFFER | Le tampon fourni est trop petit |
| 998 | 0x3E6 | ERROR_NOACCESS | Accès invalide à un emplacement mémoire |
| 1223 | 0x4C1 | ERROR_CANCELLED | L’opération a été annulée par l’utilisateur |
Ici, le piège est de confondre 5 et 998. 998 (ERROR_NOACCESS) n’est pas « accès refusé » : c’est l’expression Win32 d’une violation d’accès mémoire. C’est l’allure que prend le NTSTATUS STATUS_ACCESS_VIOLATION une fois converti vers la couche Win32.
1223 (ERROR_CANCELLED) apparaît notamment quand on choisit « Non » dans une boîte d’élévation UAC. Ne le lisez pas simplement comme une panne : c’est un code qui indique que l’opération a été « interrompue ».
flowchart TB
accTitle: Les erreurs 998 et 5 sont distinctes
accDescr: 998 est une violation d'accès mémoire, conversion Win32 du NTSTATUS correspondant ; ce n'est pas le même sens que 5, qui signifie accès refusé
nt["NTSTATUS 0xC0000005"] -->|Conversion vers la couche Win32| e998["Erreur 998 (ERROR_NOACCESS)"]
e998 -.-> m1["Sens : violation d'accès mémoire"]
e5["Erreur 5 (accès refusé)"] -.-> m2["Problème de droits. Distinct de 998"]
Figure 5 : L’erreur 998 est la conversion Win32 d’une violation d’accès NTSTATUS ; elle n’est pas la même chose que l’accès refusé 5.
3.3. Le même code change de sens avec le contexte
Retenir les codes représentatifs ne suffit pas à identifier la cause. Le code n’enseigne que le « genre d’échec » ; il reste une cible à examiner ensuite.
| Code | Ce qui change avec le contexte | Ce qu’il faut vérifier ensuite |
|---|---|---|
| Erreur 5 (accès refusé) | ACL NTFS insuffisant, écriture dans une zone protégée sans privilèges d’administrateur, blocage par un antivirus ou AppLocker, droits insuffisants d’un compte de service, etc. | Quelle API s’est vu refuser l’accès à quelle cible |
| Erreur 2 (fichier introuvable) | Pas forcément le fichier indiqué : DLL chargée implicitement, fichier de paramètres vu ailleurs à cause de la redirection de registre 32/64 bits, chemin dont l’expansion de variable d’environnement a échoué, etc. | « Quel fichier » était introuvable |
| Erreur 32 (violation de partage) | Un autre processus utilise la cible | « Quel processus » la tient |
flowchart TB
accTitle: La cause de l'erreur 5 se décide par le contexte
accDescr: Le même accès refusé a plusieurs causes candidates, ACL insuffisant ou absence de privilèges d'administrateur par exemple, et il faut identifier quelle API a échoué contre quoi
e5["Erreur 5 (accès refusé)"] --> c1["ACL insuffisant"]
e5 --> c2["Pas de privilèges d'administrateur"]
e5 --> c3["Blocage par un logiciel de sécurité"]
e5 --> c4["Droits de service insuffisants"]
c1 --> next["Identifier la cible en échec avec Procmon"]
c2 --> next
c3 --> next
c4 --> next
Figure 6 : Le code n’enseigne que le « genre d’échec ». L’erreur 5 a plusieurs causes candidates, et il faut identifier la cible.
L’outil qui mesure « quelle API, contre quel nom d’objet, a renvoyé quel résultat » est Process Monitor. L’usage est détaillé dans « Guide pratique de Process Monitor (ProcMon) — identifier en 10 minutes un « paramètre non pris en compte » ou un ACCESS DENIED ». Chercher le sens du code et identifier la cible qui a échoué sont les deux roues du même véhicule.
4. HRESULT — lire une structure empaquetée en 32 bits
4.1. Disposition des bits
Un HRESULT est un format qui empaquette succès/échec, origine et code de détail dans une seule valeur 32 bits. En regardant d’abord le bit S pour succès/échec, Facility pour l’origine, Code pour le détail, les exemples de décomposition qui suivent se suivent plus facilement. La disposition de la spécification publiée [MS-ERREF] est la suivante.2
| Position des bits | Nom | Sens |
|---|---|---|
| 31 | S | Gravité. 0 = succès, 1 = échec |
| 30 | R | Réservé (fait partie de la gravité lors d’un mapping NTSTATUS) |
| 29 | C | Bit Customer. 1 si le code est défini hors Microsoft |
| 28 | N | 1 si une valeur NTSTATUS a été mappée dans l’espace HRESULT |
| 27 | X | Réservé (0) |
| 26–16 | Facility | Code de facility indiquant l’origine (11 bits) |
| 15–0 | Code | Code de détail dans la facility (16 bits) |
Le bit S de poids fort à 1, c’est-à-dire un HRESULT dont la notation hexadécimale commence par 0x8 ou plus, est un échec. L’afficher en entier 32 bits signé donne un négatif : c’est l’origine du « -214… » déjà vu.
flowchart TB
accTitle: Lien entre le bit S et l'affichage négatif
accDescr: Un HRESULT d'échec a le bit S de poids fort à 1, donc l'hexadécimal commence par 0x8 ou plus, et l'affichage en entier 32 bits signé est négatif
s["Bit S = 1 (échec)"] --> hex["L'hexadécimal commence par 0x8 ou plus"]
hex --> neg["L'affichage signé est négatif"]
neg --> back["Devant un négatif, reconvertir en hexadécimal"]
Figure 7 : Un HRESULT d’échec a le bit S à 1, donc il commence par 0x8 ou plus, et l’affichage signé est négatif.
Facility est un indice pour le document à consulter ensuite
Les valeurs représentatives de Facility sont les suivantes. 7 se lit comme un réemballage d’erreur Win32, 4 comme une définition propre à une interface.5
| Facility | Valeur | Apparence hexadécimale | Sens |
|---|---|---|---|
| FACILITY_NULL | 0 | 0x8000xxxx | Codes largement communs (E_FAIL, E_UNEXPECTED, etc.) |
| FACILITY_RPC | 1 | 0x8001xxxx | Issu de RPC |
| FACILITY_ITF | 4 | 0x8004xxxx | Erreur définie par une interface (le sens dépend de l’interface) |
| FACILITY_WIN32 | 7 | 0x8007xxxx | Réemballage d’un code d’erreur Win32 |
| FACILITY_WINDOWS | 8 | 0x8008xxxx | Interfaces supplémentaires définies par Microsoft |
4.2. Décomposer 0x80004005 et 0x80070005
Comparons deux codes d’apparence voisine en les séparant en S, Facility et Code.
| Élément à lire | 0x80004005 | 0x80070005 |
|---|---|---|
| S | 1 (échec) | 1 (échec) |
| Facility | 0 (FACILITY_NULL) | 7 (FACILITY_WIN32) |
| Code | 0x4005 | 0x0005 = 5 |
| Définition | E_FAIL : échec non spécifié | E_ACCESSDENIED : accès refusé |
| Suite de l’investigation | Examiner le composant qui a renvoyé le code et les journaux associés | En tant qu’erreur Win32 5, examiner l’opération et la cible refusées |
0x80004005, le code seul ne resserre pas la cause. Facility vaut (0x80004005 >> 16) & 0x7FF = 0 : c’est le code générique E_FAIL de FACILITY_NULL. Il ne porte rien de plus qu’« Unspecified failure » (échec non spécifié), donc une fois décomposé, déplacez le centre de gravité de l’investigation vers « quel composant l’a renvoyé » et « le journal des événements ou le journal de l’application à la même heure contient-il un détail ».6
0x80070005 se suit jusqu’à l’erreur Win32 5. Facility = 7, Code = 5 : on voit que c’est ERROR_ACCESS_DENIED réemballé en HRESULT. L’alias E_ACCESSDENIED a la même valeur.6
Le même « échec » ne livre pas la même quantité d’information une fois décomposé. Ne décrétez pas qu’E_FAIL est un accès refusé ; choisissez la suite selon la valeur renvoyée.
flowchart TB
accTitle: Décomposition de 0x80004005 et 0x80070005
accDescr: 0x80004005 est le code générique E_FAIL de FACILITY_NULL, sans détail, donc il faut passer à l'investigation du contexte ; 0x80070005 est FACILITY_WIN32, réemballage de l'erreur Win32 5 accès refusé
a["0x80004005"] --> af["Facility = 0 (FACILITY_NULL)"]
af --> ac["Code = 0x4005 → E_FAIL"]
ac --> ax["Échec non spécifié. Vers l'investigation du contexte"]
b["0x80070005"] --> bf["Facility = 7 (FACILITY_WIN32)"]
bf --> bc["Code = 0x0005 → 5"]
bc --> bx["ERROR_ACCESS_DENIED"]
Figure 8 : Le même « échec » ne livre pas la même information une fois décomposé. 0x80070005 se suit jusqu’à l’erreur Win32 5.
4.3. Motif essentiel : 0x8007xxxx = HRESULT_FROM_WIN32
Réemballer un échec Win32 en HRESULT
Pour transmettre une erreur Win32 d’une couche inférieure à une méthode COM ou au runtime .NET qui renvoie un HRESULT, winerror.h fournit HRESULT_FROM_WIN32. Dans l’exemple de code d’échec ci-dessous, on place l’erreur Win32 dans les 16 bits de poids faible, 7 dans Facility, 1 dans le bit S.4
flowchart TB
accTitle: Fonctionnement de HRESULT_FROM_WIN32
accDescr: Stocker le code d'erreur Win32 dans les 16 bits de poids faible, mettre 7 dans Facility et 1 dans le bit S, pour assembler un HRESULT 0x8007xxxx
win["Code d'erreur Win32 (ex. : 5)"] --> low["Stockage dans les 16 bits de poids faible"]
low --> fac["Facility mis à 7"]
fac --> sbit["Bit S mis à 1"]
sbit --> hr["0x80070005"]
Figure 9 : HRESULT_FROM_WIN32 stocke l’erreur Win32 dans les 16 bits de poids faible et lève Facility = 7 ainsi que le bit S.
ERROR_ACCESS_DENIED (5) --HRESULT_FROM_WIN32--> 0x80070005
ERROR_SHARING_VIOLATION (32) --HRESULT_FROM_WIN32--> 0x80070020
ERROR_INVALID_PARAMETER (87) --HRESULT_FROM_WIN32--> 0x80070057 (= E_INVALIDARG)
ERROR_OUTOFMEMORY (14) --HRESULT_FROM_WIN32--> 0x8007000E (= E_OUTOFMEMORY)
En lisant à l’envers, extraire les 16 bits de poids faible
Pour lire l’erreur Win32 d’origine depuis 0x8007xxxx, extraire les 16 bits de poids faible dans PowerShell.
0x80070005 -band 0xFFFF # 5 → ERROR_ACCESS_DENIED
0x80072EE7 -band 0xFFFF # 12007 → ERROR_INTERNET_NAME_NOT_RESOLVED (WinINet)
Comme le second exemple, les erreurs WinINet et WinHTTP (série 12000) sont aussi définies dans l’espace des codes d’erreur Win321, donc un 0x8007xxxx réseau se décompose par la même procédure. « Devant 0x8007, convertir les 4 chiffres de poids faible en décimal » est, de cet article, le geste de terrain que l’on aimerait vous voir emporter.
Pour 0x8004xxxx, aller vers la documentation du composant qui l’a renvoyé
Il ne faut pas appliquer telle quelle la même lecture à 0x8004xxxx (FACILITY_ITF). Ici, le sujet qui définit le sens diffère selon l’interface, donc la même valeur 32 bits peut changer de sens si l’émetteur change.5
Un 0x8004xxxx peu familier ne se tranche pas par une recherche générique : cherchez-le dans la documentation du composant qui l’a renvoyé (bibliothèque, SDK de pilote, produit serveur).
flowchart TB
accTitle: 0x8007 et 0x8004 ne se cherchent pas de la même façon
accDescr: Un 0x8007xxxx FACILITY_WIN32 se lit par décomposition mécanique des 16 bits de poids faible ; un 0x8004xxxx FACILITY_ITF a un sujet définissant le sens propre à chaque interface, donc on cherche dans la documentation du composant émetteur
hr{"Facility ?"} -->|7, WIN32| w["Convertir les 16 bits de poids faible en décimal"]
hr -->|4, ITF| i["Le sens diffère selon l'émetteur"]
w --> ww["Lire comme une erreur Win32"]
i --> ii["Chercher dans la documentation de l'émetteur"]
Figure 10 : 0x8007xxxx se décompose mécaniquement ; 0x8004xxxx se cherche dans la documentation du composant qui l’a renvoyé.
5. NTSTATUS — codes de la couche noyau et monde des plantages
5.1. Disposition et Severity
NTSTATUS est le code 32 bits utilisé par le noyau, les pilotes de périphériques et les API natives de ntdll ; sa disposition ressemble au HRESULT sans lui être identique.3
| Position des bits | Nom | Sens |
|---|---|---|
| 31–30 | Sev | Gravité. 00 = succès, 01 = information, 10 = avertissement, 11 = erreur |
| 29 | C | Bit Customer |
| 28 | N | Réservé (0, pour permettre le mapping vers HRESULT) |
| 27–16 | Facility | Facility (12 bits) |
| 15–0 | Code | Code de détail |
La grande différence avec HRESULT, c’est que la gravité tient sur 2 bits, avec quatre espèces : succès, information, avertissement, erreur. Sur le premier chiffre hexadécimal, 0xC… correspond à une erreur (11), 0x8… à un avertissement (10), 0x4… à une information (01), 0x0–0x3… au succès.
Il ne faut donc pas conclure à un échec HRESULT sur la seule apparence « commence par 0x8 ». Le NTSTATUS 0x80000003 (STATUS_BREAKPOINT) est une exception de point d’arrêt, et sa gravité est « avertissement », non « erreur ».314
flowchart TB
accTitle: NTSTATUS se lit au premier chiffre
accDescr: La gravité tient sur 2 bits, donc le premier chiffre hexadécimal d'un NTSTATUS se lit 0xC = erreur, 0x8 = avertissement, 0x4 = information, 0x0 à 0x3 = succès
head{"Premier chiffre hexadécimal ?"} -->|0xC| e["Erreur"]
head -->|0x8| w["Avertissement"]
head -->|0x4| i["Information"]
head -->|0x0–0x3| s["Succès"]
w -.-> ex["Ex. : 0x80000003 est un avertissement"]
Figure 11 : NTSTATUS se lit au premier chiffre hexadécimal. 0x80000003 est « un avertissement, pas une erreur ».
5.2. Où on le rencontre — codes d’exception, codes STOP, journal des événements
Les lieux de rencontre d’un NTSTATUS se répartissent en codes d’exception, codes STOP et Process Monitor.
Code d’exception d’un plantage d’application
Le « code d’exception : 0xc0000005 » enregistré dans « Application Error (événement ID 1000) » du journal des événements est un NTSTATUS. Parmi les valeurs représentatives que l’on voit lors d’un plantage ou d’un échec de démarrage :14
| Valeur | Symbole | Sens |
|---|---|---|
| 0xC0000005 | STATUS_ACCESS_VIOLATION | Violation d’accès (accès mémoire illégal) |
| 0xC0000135 | STATUS_DLL_NOT_FOUND | DLL requise introuvable, le démarrage échoue |
| 0xC00000FD | STATUS_STACK_OVERFLOW | Débordement de pile |
| 0xC0000374 | STATUS_HEAP_CORRUPTION | Corruption de tas |
Le code STOP d’un écran bleu est un autre système
Les codes STOP (codes de vérification de bogue) se ressemblent au premier regard, mais c’est un système de numéros propre, distinct de NTSTATUS, du type 0x0000009F (DRIVER_POWER_STATE_FAILURE). On utilise une référence dédiée.15
L’essentiel est de séparer « 0xC0000005 alors NTSTATUS, STOP 0x9F alors code de vérification de bogue », et de ne pas chercher un code STOP dans une table NTSTATUS.
Dans Process Monitor, il apparaît dans la colonne Result
NAME NOT FOUND ou ACCESS DENIED dans la colonne Result de Procmon sont les noms d’affichage des NTSTATUS renvoyés par le noyau (STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED). On y observe l’échec d’E/S fichier dans le vocabulaire NTSTATUS, et la correspondance des couches jusqu’à la conversion en erreur Win32 livrée à l’application.
flowchart TB
accTitle: Distinguer code d'exception et code STOP
accDescr: Lire le code d'exception du journal des événements comme un NTSTATUS, et chercher le code STOP d'un écran bleu dans la référence dédiée des codes de vérification de bogue, un autre système
q{"Où le code est-il apparu ?"} -->|Code d'exception| nt["Lire comme un NTSTATUS"]
q -->|Code STOP| bc["Chercher dans la table des codes de vérification de bogue"]
nt -.-> n1["Ex. : 0xC0000005"]
bc -.-> b1["Ex. : 0x0000009F"]
Figure 12 : Le code d’exception du journal des événements est un NTSTATUS ; le code STOP d’un écran bleu est un autre système. Ne vous trompez pas de table.
Pour la suite d’un code d’exception, c’est-à-dire la capture et l’analyse d’un dump de plantage, voyez « Introduction à la collecte des dumps de crash Windows » et « Lire un dump de plantage avec WinDbg + SOS ».
5.3. Relation avec HRESULT — bit N et RtlNtStatusToDosError
Il y a deux chemins pour passer un NTSTATUS à un autre système. Le mapping vers HRESULT et la conversion vers une erreur Win32 se pensent séparément.
| Chemin | Ce que l’on fait | Exemple |
|---|---|---|
| Mapping vers l’espace HRESULT | Lever le bit N (0x10000000) avec HRESULT_FROM_NT | 0xC0000005 → 0xD0000005 |
| Conversion vers une erreur Win32 | Convertir vers le numéro correspondant avec RtlNtStatusToDosError | STATUS_ACCESS_VIOLATION → ERROR_NOACCESS (998) |
Pour le mapping vers HRESULT, on lève le bit N et on amène la valeur NTSTATUS dans l’espace HRESULT. La procédure est donc : devant un HRESULT commençant par 0xD, ôter le bit N et lire comme un NTSTATUS.2
Pour la conversion vers une erreur Win32, on utilise RtlNtStatusToDosError de ntdll. Une valeur sans correspondance devient ERROR_MR_MID_NOT_FOUND.11 0xC0000005 se convertit en 998, STATUS_OBJECT_NAME_NOT_FOUND (0xC0000034) en ERROR_FILE_NOT_FOUND (2). Le vocabulaire riche du noyau est parfois ramené à un découpage plus grossier dans la couche Win32 : lisez en distinguant l’avant et l’après conversion.
flowchart TB
accTitle: Deux passerelles d'un NTSTATUS vers les autres couches
accDescr: Un NTSTATUS passe aux autres couches soit en levant le bit N pour un mapping dans l'espace HRESULT, soit en le convertissant en code d'erreur Win32 avec RtlNtStatusToDosError
nt["NTSTATUS (0xC0000005)"] -->|Lever le bit N| hr["HRESULT (0xD0000005)"]
nt -->|RtlNtStatusToDosError| win["Erreur Win32 998 (ERROR_NOACCESS)"]
win -.-> memo["Sans correspondance : ERROR_MR_MID_NOT_FOUND"]
Figure 13 : Deux passerelles pour NTSTATUS. Un début 0xD se lit comme NTSTATUS après avoir ôté le bit N.
6. COM et .NET — comment un code d’erreur est mappé à une exception
6.1. L’usage COM — HRESULT + IErrorInfo
Une méthode COM renvoie en principe un HRESULT. L’information que l’on peut faire passer dans 32 bits a toutefois des limites.
Le complément, c’est IErrorInfo. On peut y transmettre à part la chaîne d’explication de l’erreur et l’origine ; en C++, la classe _com_error fournie par le compilateur traite ensemble HRESULT et IErrorInfo. Dans une application dont la boîte de dialogue affiche « code + texte d’explication », c’est souvent ce mécanisme qui transporte le texte.
flowchart TB
accTitle: IErrorInfo en complément du HRESULT
accDescr: L'information que l'on peut empaqueter dans un HRESULT 32 bits a des limites, donc la chaîne d'explication et l'origine se transmettent à part via IErrorInfo, et en C++ la classe _com_error les traite ensemble
hr["HRESULT (32 bits seulement)"] --> lim["L'information empaquetable a des limites"]
lim --> ei["IErrorInfo transporte le texte d'explication"]
ei --> ce["_com_error les traite ensemble"]
ce -.-> dlg["Code + texte d'explication de la boîte de dialogue"]
Figure 14 : La chaîne d’explication qui ne tient pas dans un HRESULT 32 bits est transportée à part par IErrorInfo.
6.2. L’usage .NET — du HRESULT vers un type d’exception
Le runtime .NET, quand il reçoit un échec HRESULT via l’interopérabilité COM, le convertit en exception. Un HRESULT connu est mappé au type d’exception correspondant ; un inconnu devient COMException.10
flowchart TB
accTitle: Mapping d'un HRESULT vers une exception .NET
accDescr: Un HRESULT d'échec reçu via l'interopérabilité COM est converti vers le type d'exception correspondant s'il est connu, vers COMException sinon, et dans les deux cas la valeur d'origine est conservée dans Exception.HResult
hr["HRESULT d'échec"] --> known{"Un mapping connu existe-t-il ?"}
known -->|Oui| typed["Conversion vers le type d'exception correspondant"]
known -->|Non| comex["Conversion vers COMException"]
typed --> keep["La valeur d'origine est conservée dans Exception.HResult"]
comex --> keep
Figure 15 : .NET mappe un HRESULT vers un type d’exception, et quelle que soit l’exception, la valeur d’origine reste dans Exception.HResult.
| HRESULT | Type d’exception .NET |
|---|---|
| E_ACCESSDENIED (0x80070005) | UnauthorizedAccessException |
| E_OUTOFMEMORY (0x8007000E) | OutOfMemoryException |
| E_INVALIDARG (0x80070057) | ArgumentException |
| E_NOTIMPL (0x80004001) | NotImplementedException |
| Valeur sans mapping défini | COMException (valeur d’origine dans la propriété ErrorCode) |
Brancher le traitement d’exception sur le HRESULT d’origine
Quelle que soit l’exception, le HRESULT d’origine est conservé dans Exception.HResult. Si, en E/S fichier, vous ne voulez réessayer que sur une violation de partage, vous pouvez conditionner sur cette valeur. L’exemple suivant ne catch que 0x80070020, qui représente une violation de partage.
try
{
using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException ex) when (ex.HResult == unchecked((int)0x80070020))
{
// 0x80070020 = HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)
// Un autre processus tient le fichier — attendre un peu et réessayer, etc.
}
6.3. P/Invoke et GetLastError
Quand vous appelez directement une API Win32 en P/Invoke, déclarez et obtenez le code en paire.
- Spécifier
SetLastError = truesurDllImport(ouLibraryImport). - Juste après avoir constaté l’échec, obtenir le code avec
Marshal.GetLastWin32Error. À partir de .NET 6, l’équivalentGetLastPInvokeErrorest disponible.16
Évitez de déclarer GetLastError lui-même en P/Invoke pour l’appeler. Un appel d’API interne au runtime peut écraser la valeur, et vous n’obtenez alors pas la vraie raison de l’échec.16
flowchart TB
accTitle: Obtention de la dernière erreur en P/Invoke
accDescr: Le bon usage est SetLastError à true puis Marshal.GetLastWin32Error ; appeler GetLastError directement en P/Invoke devient inexact à cause d'un écrasement par le runtime
pi["Appeler une API Win32 en P/Invoke"] --> ok["Spécifier SetLastError = true"]
ok --> get["Obtenir avec GetLastWin32Error"]
pi --> ng["Déclarer GetLastError pour l'appeler directement"]
ng --> bad["Le runtime écrase, résultat inexact"]
Figure 16 : En P/Invoke, utilisez ensemble SetLastError = true et Marshal.GetLastWin32Error. Un appel direct de GetLastError est inexact.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFileW(string fileName, uint access, uint share,
IntPtr security, uint disposition, uint flags, IntPtr template);
// Recevoir la valeur de retour en SafeFileHandle, pas en IntPtr, et fermer à coup sûr avec using
// (laisser un IntPtr tel quel fuit le handle noyau)
using var handle = CreateFileW(@"C:\ProgramData\MyApp\config.dat",
0x80000000 /*GENERIC_READ*/, 0, IntPtr.Zero, 3 /*OPEN_EXISTING*/, 0, IntPtr.Zero);
if (handle.IsInvalid)
{
int code = Marshal.GetLastWin32Error(); // Ex. : 5
var message = new Win32Exception(code).Message; // Ex. : Accès refusé.
logger.LogError("CreateFileW failed: {Code} (0x{Code:X8}) {Message}",
code, code, message);
}
Win32Exception tire la chaîne de message de l’OS à partir d’un code d’erreur Win32, donc on l’utilise tel quel pour laisser dans le journal à la fois le code et le message. La conception — à quelle couche catcher et comment journaliser — est traitée dans « Où catch et la journalisation doivent-ils se situer dans la gestion des exceptions ? ».
7. Outils de conversion et d’investigation — aide-mémoire à coller tel quel
Choisissez d’abord l’outil selon l’environnement et l’objectif. Chaque section ci-dessous a un exemple utilisable tel quel.
| Situation | Outil | Ce que l’on peut chercher |
|---|---|---|
| Vérifier sans installation supplémentaire | certutil -error, net helpmsg | Nom et message correspondant au code (section 7.2) |
| Traverser les définitions de plusieurs systèmes | err.exe | Candidats de définition dans les en-têtes (section 7.1) |
| Convertir et décomposer un nombre | PowerShell | Conversion décimal/hexadécimal, 16 bits de poids faible, mapping vers une exception .NET (section 7.3) |
| Confirmer le sens pendant l’analyse d’un dump | !error de WinDbg | Interprétation en Win32 ou en NTSTATUS (section 7.4) |
7.1. err.exe (Microsoft Error Lookup Tool)
Outil de recherche d’erreurs, fichier exécutable autonome distribué par Microsoft. Il traverse de nombreux fichiers d’en-tête, winerror.h, ntstatus.h, etc., et énumère les définitions et messages qui correspondent au code indiqué.7
err 0x80070005
err 5
err 0xC0000005
En lisant le résultat, deux précautions.
- Parmi plusieurs candidats, choisir celui qui convient au contexte. « 5 », par exemple, correspond aussi à des définitions autres que ERROR_ACCESS_DENIED Win32. Ne prenez pas le nom trouvé pour la cause telle quelle.
- Vérifier le millésime des définitions incluses. Le nom du fichier téléchargé porte une version (au moment de la rédaction, Err_6.4.5.exe) ; les définitions de codes s’appuient sur les en-têtes au moment de l’empaquetage.7
flowchart TB
accTitle: Choisir le résultat d'err.exe selon le contexte
accDescr: err.exe traverse de nombreux fichiers d'en-tête et énumère les définitions correspondantes, donc si le même nombre a plusieurs candidats, on choisit le plus plausible selon le contexte
in["Saisir err 5"] --> scan["Recherche transversale dans de nombreux en-têtes"]
scan --> hits["Plusieurs définitions correspondent"]
hits --> pick["Choisir le candidat plausible selon le contexte"]
Figure 17 : err.exe traverse les en-têtes, donc plusieurs candidats peuvent sortir ; le plausible se choisit selon le contexte.
7.2. Commandes standard de Windows
Sans installation supplémentaire, on a certutil et net helpmsg. L’option -error de certutil affiche le texte de message associé à un code d’erreur, et accepte un HRESULT hexadécimal comme un décimal.8
certutil -error 0x80070005
certutil -error 5
net helpmsg 5
net helpmsg est réservé au décimal d’un code d’erreur Win32. Sur un environnement français, le message revient en français, donc on peut aussi s’en servir pour expliquer à l’utilisateur. Pour un HRESULT hexadécimal, choisissez certutil -error comme dans l’exemple ci-dessus.
flowchart TB
accTitle: Répartition des commandes standard
accDescr: Un code d'erreur Win32 décimal se cherche avec net helpmsg ; un code qui inclut de l'hexadécimal, HRESULT compris, se cherche avec l'option -error de certutil
q{"Le code sous les yeux ?"} -->|Win32 décimal| net["net helpmsg"]
q -->|Inclut de l'hexadécimal| cert["certutil -error"]
net -.-> jp["Le message revient dans la langue de l'OS"]
cert -.-> any["Accepte hexadécimal et décimal"]
Figure 18 : Répartition des commandes standard. Erreur Win32 décimale : net helpmsg ; dès qu’il y a de l’hexadécimal : certutil -error.
7.3. Recueil de one-liners PowerShell
Aide-mémoire qui rassemble conversion de notation, obtention du message Win32, décomposition HRESULT et mapping vers une exception .NET. Après avoir confirmé le sens du code, l’investigation de l’opération et de la cible qui ont échoué reste nécessaire à part.
# Code d'erreur Win32 → chaîne de message de l'OS
[System.ComponentModel.Win32Exception]::new(5).Message
# → Accès refusé.
# Décimal négatif → notation hexadécimale (confirmer l'identité du HRESULT)
'0x{0:X8}' -f -2147467259 # 0x80004005
# 0x8007xxxx → code d'erreur Win32 des 16 bits de poids faible
0x80070005 -band 0xFFFF # 5
# HRESULT → exception vers laquelle .NET mappe
[System.Runtime.InteropServices.Marshal]::GetExceptionForHR(-2147024891)
# → UnauthorizedAccessException (0x80070005)
# Code d'erreur Win32 → HRESULT (reproduire le réemballage)
'0x{0:X8}' -f (0x80070000 -bor 32) # 0x80070020
7.4. !error de WinDbg
Pour chercher un code pendant l’analyse d’un dump, l’extension !error de WinDbg est rapide. Par défaut, elle interprète comme un code d’erreur Win32 ; un 1 en second argument interprète comme un NTSTATUS.9
0:000> !error 5
Error code: (Win32) 0x5 (5) - Accès refusé.
0:000> !error 0xc0000005 1
Error code: (NTSTATUS) 0xc0000005 - <violation d'accès>
Sur un dump de plantage, !analyze -v affiche automatiquement le code d’exception (NTSTATUS) ; le flux est ensuite de confirmer le sens avec !error <code> 1.
flowchart TB
accTitle: Flux de confirmation d'un code d'exception dans WinDbg
accDescr: Sur un dump de plantage, la commande analyze affiche automatiquement le code d'exception ; on passe ce code à l'extension error avec 1 en second argument pour confirmer le sens en NTSTATUS
dump["Ouvrir le dump de plantage"] --> an["Exécuter !analyze -v"]
an --> exc["Le code d'exception s'affiche"]
exc --> chk["Confirmer le sens avec !error code 1"]
Figure 19 : En analyse de dump, chercher avec !error et le drapeau 1 le code d’exception affiché par !analyze -v.
8. Procédure d’investigation — du jugement de couche au recoupement avec le contexte
Dans une investigation réelle, on avance ces cinq étapes dans l’ordre. Le point n’est pas de s’arrêter une fois le nom trouvé, mais de vérifier jusqu’à l’opération et la cible qui ont échoué.
- Normaliser la notation. Un décimal négatif se convertit en 8 chiffres hexadécimaux. Un hexadécimal de moins de 8 chiffres se lit après remplissage de zéros.
- Juger de quelle couche est le code. Avec le tableau ci-dessous, serrez le premier candidat d’après les premiers chiffres, puis recoupez avec l’API qui a renvoyé le code et l’endroit où il s’est affiché.
- Décomposer et extraire le code essentiel. HRESULT 0x8007xxxx : 16 bits de poids faible. HRESULT 0xDxxxxxxx : ôter le bit N puis lire.
- Tirer nom et définition avec un outil. Confirmez le nom de symbole et le message avec err.exe, certutil,
!error, etc. - Recouper avec le contexte. Quelle application, quelle opération, quelle API a échoué contre quoi : identifiez-le dans le journal de l’application, le journal des événements, Procmon. Le code est le « genre d’échec » ; le contexte est le « lieu de la cause ».
flowchart TB
accTitle: Procédure d'investigation d'un code d'erreur
accDescr: Motif d'investigation : aligner la notation sur l'hexadécimal, juger la couche d'après les premiers chiffres, décomposer pour extraire le code essentiel, tirer nom et définition avec un outil, puis recouper avec le contexte
fix["Normaliser la notation (négatif → 8 chiffres hex)"] --> judge{"Premiers chiffres ?"}
judge -->|Notation décimale| d1["Lire comme une erreur Win32"]
judge -->|0x8007| d2["Convertir les 16 bits de poids faible en décimal"]
judge -->|0xC| d3["Lire comme un NTSTATUS"]
judge -->|0xD| d4["Ôter le bit N puis lire"]
d1 --> tool["Tirer nom et définition avec un outil"]
d2 --> tool
d3 --> tool
d4 --> tool
tool --> ctx["Recouper avec le contexte (Procmon, etc.)"]
Figure 20 : Motif d’investigation. Normaliser la notation, juger la couche et décomposer, tirer le nom, puis recouper avec le contexte.
| Apparence | Premier candidat | Méthode de décomposition / conversion |
|---|---|---|
| Décimal de 1 à 5 chiffres (5, 1223, etc.) | Code d’erreur Win32 | Tel quel vers net helpmsg ou err.exe |
| Décimal négatif (-2147024891, etc.) | HRESULT | Convertir en 8 chiffres hexadécimaux, puis juger selon les lignes suivantes |
| 0x8007xxxx | HRESULT (FACILITY_WIN32) | Convertir les 16 bits de poids faible en décimal et lire comme Win32 |
| 0x8004xxxx | HRESULT (FACILITY_ITF) | Chercher dans la documentation du composant qui l’a renvoyé |
| 0x8000xxxx | HRESULT (FACILITY_NULL) | Code générique E_FAIL, etc. Déplacer le centre de gravité vers l’investigation du contexte |
| 0xCxxxxxxx | NTSTATUS (erreur) | !error <code> 1, et si besoin convertir vers Win32 pour lire |
| 0xDxxxxxxx | Mapping HRESULT d’un NTSTATUS | Ôter le bit N (0x10000000) et lire comme un NTSTATUS |
| Facility propre du type 0x8024xxxx | HRESULT propre à un domaine fonctionnel | Identifier le domaine d’après la valeur Facility et aller vers la documentation dédiée (0x8024… = Windows Update)2 |
Exemple concret de l’étape 5 : de 0x80070002 vers le chemin introuvable
Même si l’application n’affiche que « 0x80070002 », la colonne Result de Procmon montre « quel processus, contre quel chemin, s’est vu renvoyer NAME NOT FOUND ». C’est l’observation qui fait passer de la décomposition du code à l’identification de la cible réellement en échec.
Pour le côté journal des événements, voyez aussi « Introduction au journal des événements Windows et à ETW ».
9. Lectures erronées fréquentes — les motifs qui allongent l’investigation
Pour finir, les motifs de lecture erronée que l’on voit dans les consultations réelles.
Lecture erronée 1 : croire que 0x80004005 « indique une cause précise »
E_FAIL est un « échec non spécifié » ; la même valeur sort pour Windows Update, le réseau ou une base de données. Essayer au hasard les remèdes trouvés en cherchant ce code est presque à coup sûr un détour. Serrer non pas le code, mais « quelle application, quelle opération, les autres journaux à la même heure ».6
Lecture erronée 2 : ne pas voir qu’un décimal négatif est un HRESULT
Le cas du journal « une erreur -2147467259 s’est produite », cherché tel quel ou lu comme « une erreur négative ? ». Devant un négatif, convertir en hexadécimal. Cela seul suffit à voir 0x80004005 (E_FAIL) et à rejoindre la connaissance de la lecture erronée 1.
Lecture erronée 3 : chercher 0x8007xxxx sur 8 chiffres entiers, sans regarder l’erreur Win32 de poids faible
L’essentiel de 0x80070005 est « 5 = accès refusé ». Plutôt que de chercher les 8 chiffres d’un bloc, extraire les 16 bits de poids faible et se demander « ce que l’erreur Win32 5 signifie dans le contexte de cette opération » atteint plus vite le cœur.
Lecture erronée 4 : croire que « le même code = la même cause »
Après une expérience « l’erreur 5, c’était l’antivirus », on a tendance à sauter au même remède dès la prochaine erreur 5. Même code, API et ressource cibles différentes : la cause est autre. Confirmer le sens du code et identifier la cible avec Procmon ou l’équivalent se font à chaque fois en paire.
Lecture erronée 5 : confondre l’erreur Win32 5 et 0xC0000005, le code STOP et NTSTATUS
Identifier ERROR_ACCESS_DENIED et STATUS_ACCESS_VIOLATION parce qu’ils « tiennent du 5 » envoie l’investigation dans deux directions entièrement différentes : un problème de droits, et un bogue de programme. De plus, le code STOP d’un écran bleu est un autre système que NTSTATUS : chercher 0x9F dans une table NTSTATUS ne donne pas de réponse utile.15
flowchart TB
accTitle: L'erreur 5 et 0xC0000005 n'envoient pas l'investigation dans la même direction
accDescr: L'erreur Win32 5 se cherche comme un problème de droits, le NTSTATUS 0xC0000005 comme un bogue de programme ; les identifier l'un à l'autre fait dévier l'investigation
a["Erreur Win32 5"] --> ad["Chercher un problème de droits"]
b["NTSTATUS 0xC0000005"] --> bd["Chercher un bogue de programme"]
a -.-> memo["Codes sans rapport, d'autres systèmes"]
b -.-> memo
Figure 21 : Ne les identifiez pas parce qu’ils « tiennent du 5 ». L’erreur 5 va vers un problème de droits, 0xC0000005 vers un bogue de programme.
10. Synthèse
D’abord distinguer le système. Les principaux codes d’erreur Windows sont trois systèmes : codes d’erreur Win32, HRESULT et NTSTATUS. On aligne le flou de notation décimal / hexadécimal / négatif, et l’on confirme quelle couche, qui a renvoyé le code. On rencontre un NTSTATUS comme code d’exception d’un plantage ou dans la colonne Result de Procmon, mais l’erreur Win32 5 et 0xC0000005 sont distincts, et le code STOP est encore un autre système.
Ensuite lire la structure et extraire l’information nécessaire. Un HRESULT se compose des bits S/R/C/N/X, d’une Facility de 11 bits et d’un Code de 16 bits. Le motif essentiel est 0x8007xxxx : on lit l’erreur Win32 dans les 16 bits de poids faible. En interopérabilité COM, un HRESULT est mappé vers un type d’exception .NET, et la valeur d’origine reste dans Exception.HResult. En P/Invoke, on utilise ensemble SetLastError=true et Marshal.GetLastWin32Error.
Enfin recouper avec le contexte. E_FAIL (0x80004005) n’est pas un code de cause. Quand la décomposition n’ajoute pas d’information, cessez de creuser le code et passez à l’opération, à la cible, aux journaux de la même heure. Les outils s’alternent : certutil et net helpmsg, err.exe, PowerShell, !error de WinDbg.
Le motif d’investigation est « normaliser la notation → juger la couche → décomposer → tirer le nom → recouper avec le contexte ». Ce que le code enseigne, c’est le genre d’échec, pas plus. La prochaine fois que vous croisez un nombre peu familier, avant de le coller dans un moteur de recherche, confirmez les premiers chiffres et la provenance. Cette première décomposition décide l’endroit où vous chercherez ensuite.
Articles connexes
- Lire un dump de plantage avec WinDbg + SOS — Guide pratique d’analyse après la collecte
- Introduction à la collecte des dumps de crash Windows - WER/ProcDump/WinDbg
- Où catch et la journalisation doivent-ils se situer dans la gestion des exceptions ?
- Guide pratique de Process Monitor (ProcMon) — identifier en 10 minutes un « paramètre non pris en compte » ou un ACCESS DENIED
- Introduction au journal des événements Windows et à ETW — placer les journaux de votre application métier sur les mécanismes standard de l’OS
Domaines de conseil associés
Chez KomuraSoft, nous prenons en charge les investigations d’incidents qui partent d’un code d’erreur — « je ne comprends pas le sens de ce code », « 0x80070005 n’apparaît que dans un environnement précis » —, la conception de la gestion d’erreurs d’une application où cohabitent API Win32, COM et .NET, et l’identification de la cause avec un dump de plantage ou Process Monitor. Une capture d’écran de la boîte de dialogue d’erreur suffit pour nous consulter.
- Développement d’applications Windows
- Investigation de bugs et analyse des causes
- Conseil technique et revue de conception
- Contact
Références
-
Microsoft Learn, Debug system error codes. Index de la liste des codes d’erreur système Win32 (0–15999), obtention du message d’un code renvoyé par GetLastError avec FormatMessage et l’indicateur FORMAT_MESSAGE_FROM_SYSTEM, définition des erreurs WinINet/WinHTTP (série 12000) dans cet espace, et méthodes d’investigation avec Microsoft Error Lookup Tool ou la commande !err. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Open Specifications, [MS-ERREF]: HRESULT. Disposition des bits d’un HRESULT (bits S, R, C, N, X, Facility de 11 bits, Code de 16 bits), le bit N indiquant qu’une valeur NTSTATUS a été mappée dans l’espace HRESULT, et la liste des codes de facility, FACILITY_WINDOWS_UPDATE (36) compris. ↩ ↩2 ↩3 ↩4
-
Microsoft Open Specifications, [MS-ERREF]: NTSTATUS. Disposition des bits d’un NTSTATUS (Sev de 2 bits, bit C, bit N, Facility de 12 bits, Code de 16 bits) et le fait que la gravité se répartit en succès (00), information (01), avertissement (10) et erreur (11). ↩ ↩2 ↩3
-
Microsoft Learn, HRESULT_FROM_WIN32 macro. Définition de la macro winerror.h qui mappe un code d’erreur système Win32 vers une valeur HRESULT. ↩ ↩2 ↩3
-
Microsoft Learn, Structure of COM Error Codes. Rôle du bit de gravité et du champ facility d’un HRESULT, valeurs de FACILITY_NULL, FACILITY_RPC, FACILITY_ITF, FACILITY_WIN32 et FACILITY_WINDOWS, et le fait que le sens d’un code FACILITY_ITF est défini par interface, la même valeur pouvant donc changer de sens. ↩ ↩2 ↩3
-
Microsoft Learn, Common HRESULT values. Sur le fait que E_FAIL (0x80004005) est « Unspecified failure » (échec non spécifié), et les définitions des HRESULT fréquents E_ACCESSDENIED (0x80070005), E_INVALIDARG (0x80070057), E_OUTOFMEMORY (0x8007000E), etc. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, The Microsoft Error Lookup Tool. Outil autonome qui affiche le texte de message associé à un code de statut hexadécimal en traversant divers fichiers d’en-tête, Winerror.h compris ; le nom du fichier téléchargé est Err_6.4.5.exe ; les définitions incluses datent du moment de la compilation. ↩ ↩2 ↩3
-
Microsoft Learn, certutil. Sur le fait que l’option -error de certutil affiche le texte de message associé à un code d’erreur, et qu’une notation d’erreur incluant le nom de symbole du type 0x80070002 (WIN32: 2 ERROR_FILE_NOT_FOUND) est employée. ↩ ↩2
-
Microsoft Learn, !error. Sur le fait que l’extension !error de WinDbg décode et affiche des valeurs d’erreur Win32, Winsock, NTSTATUS et NetAPI, et qu’un drapeau 1 les interprète comme NTSTATUS. ↩ ↩2
-
Microsoft Learn, How to: Map HRESULTs and exceptions. Mécanisme de mapping mutuel entre HRESULT COM et exceptions .NET, table de correspondance du type E_NOTIMPL → NotImplementedException, conversion en COMException d’un HRESULT sans mapping explicite, et initialisation de Message, Source, etc. de l’exception à partir des informations IErrorInfo. ↩ ↩2
-
Microsoft Learn, RtlNtStatusToDosError function (winternl.h). Fonction qui convertit un code NTSTATUS vers le code d’erreur système Win32 correspondant, renvoie ERROR_MR_MID_NOT_FOUND en l’absence de correspondance, et n’a pas de fonction inverse. ↩ ↩2
-
Microsoft Learn, Last-Error Code. Sur le fait que le code de dernière erreur est tenu par thread, qu’il faut l’obtenir avec GetLastError immédiatement après l’échec, que certaines API écrasent le code à 0 en cas de succès et d’autres non, et que le bit 29 est réservé aux codes définis par l’application. ↩ ↩2
-
Microsoft Learn, RegOpenKeyExW function. Sur le fait qu’en cas de succès la fonction renvoie ERROR_SUCCESS, et en cas d’échec un code d’erreur non nul défini dans Winerror.h, comme valeur de retour. ↩
-
Microsoft Open Specifications, [MS-ERREF]: NTSTATUS values. Liste des valeurs NTSTATUS, dont STATUS_ACCESS_VIOLATION (0xC0000005), STATUS_DLL_NOT_FOUND (0xC0000135), STATUS_STACK_OVERFLOW (0xC00000FD), STATUS_HEAP_CORRUPTION (0xC0000374) et STATUS_BREAKPOINT (0x80000003). ↩ ↩2
-
Microsoft Learn, Bug check code reference. Liste des codes de vérification de bogue (codes STOP) affichés sur un écran bleu, et affichage des informations du code avec l’extension !analyze de WinDbg. La liste confirme qu’il s’agit d’un système de numéros propre, distinct de NTSTATUS. ↩ ↩2
-
Microsoft Learn, Marshal.GetLastWin32Error Method. Méthode pour obtenir le code de dernière erreur d’un appel P/Invoke qui a le drapeau SetLastError ; appeler GetLastError directement en P/Invoke n’est pas fiable à cause d’un écrasement par un appel d’API interne au runtime ; à partir de .NET 6, GetLastPInvokeError est recommandé. ↩ ↩2
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Time Travel Debugging — Enregistrer et rembobiner les bogues qui ne se reproduisent pas dans les applications de longue durée
Un bogue qui n'apparaît qu'une fois par mois ne laisse dans un dump de plantage que le résultat. Enregistrez et rembobinez l'exécution av...
Pourquoi les arguments se cassent — Les règles des arguments de ligne de commande Windows
Windows passe à CreateProcess une seule chaîne que le destinataire découpe. Traite les règles de CommandLineToArgvW, du CRT et de .NET, A...
Ce qui reste après la mort du parent — garder les processus enfants dans un Job Object
Pourquoi les aides du SDK survivent à une IU tuée et gardent la caméra ou le port COM. Concevoir la durée de vie des processus enfants av...
API du pool de threads Win32 — de la concurrence sans créer de threads, avec CreateThreadpoolWork
Vous multipliez les CreateThread dans le code natif ? Cet article explique, à partir des sources primaires, l'API du pool de threads Win3...
Tubes nommés en pratique — l'IPC standard de Windows, de la conception à la sécurité
Guide pratique des tubes nommés, mécanisme standard de communication inter-processus sous Windows. Cet article organise, à partir des sou...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Applications métier, intégration d'équipements et outils de communication, des besoins au développement.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Que signifie l'erreur 0x80004005 ?
- 0x80004005 est le HRESULT E_FAIL, et il signifie « Unspecified failure » (échec non spécifié). Autrement dit, il indique seulement qu'un échec s'est produit sans pouvoir en rapporter la raison détaillée ; ce n'est pas un code qui représente la cause elle-même. C'est pour cela que le même 0x80004005 apparaît dans des contextes sans rapport : réseau, Windows Update, VBA, pilote de base de données, etc. Face à ce code, ne creusez pas le sens du code : resserrez la cause à partir du contexte (quelle application, quelle opération) et des autres informations d'erreur laissées dans le journal des événements ou un journal détaillé.
- Qu'est-ce qu'un code d'erreur négatif comme -2147467259 ?
- C'est un HRESULT 32 bits affiché en décimal signé. Un HRESULT met le bit de poids fort à 1 en cas d'échec, donc en entier signé il est toujours négatif. Dans PowerShell, '0x{0:X8}' -f -2147467259 le reconvertit en hexadécimal (ici 0x80004005 = E_FAIL). Quand un journal ou un message de script montre un négatif commençant par -214…, le premier geste standard est de le convertir en hexadécimal puis de le rechercher.
- Quel est le moyen le plus simple de chercher le sens d'un code d'erreur ?
- Sans installation supplémentaire, utilisez net helpmsg 5 à l'invite de commandes (erreur Win32 en décimal) et certutil -error 0x80070005. certutil accepte aussi un HRESULT hexadécimal et affiche le nom de symbole et le texte du message. Dans PowerShell, [System.ComponentModel.Win32Exception]::new(5).Message obtient le message dans la langue de l'OS. Sur une machine de développement, gardez l'outil officiel Microsoft err.exe (Microsoft Error Lookup Tool) : il traverse Win32, HRESULT et NTSTATUS et liste les définitions correspondantes d'un coup.
- Quel genre d'erreur est 0xC0000005 ?
- C'est le NTSTATUS STATUS_ACCESS_VIOLATION, c'est-à-dire une violation d'accès (accès mémoire illégal). C'est le code le plus souvent vu comme « code d'exception » dans le journal des événements ou dans un dump de plantage, et il indique un bogue de programme : déréférencement d'un pointeur invalide, accès à de la mémoire déjà libérée, etc. Il ressemble par le nom à l'erreur Win32 5 (ERROR_ACCESS_DENIED = accès refusé), mais c'est un code sans rapport d'un autre système ; ne les confondez pas. Le moyen fiable d'identifier la cause est de capturer un dump de plantage et de l'analyser dans WinDbg.
- Pourquoi la cause est-elle différente à chaque fois, même avec le même code d'erreur ?
- Parce qu'un code d'erreur ne représente que « quel genre d'échec », et que « ce qui a échoué et pourquoi » est décidé par le contexte d'appel. L'erreur 5 (accès refusé), par exemple, est le même code pour des causes totalement différentes : ACL NTFS insuffisant, privilèges d'administrateur manquants, blocage par un antivirus, etc. Dans une situation voisine, si un autre processus a encore le fichier ouvert, vous obtenez un autre code (erreur 32 = violation de partage), et lire le code correctement change l'endroit où vous cherchez. L'erreur 2 (fichier introuvable) n'est souvent pas le fichier principal, mais une DLL dépendante ou un fichier de paramètres. Une fois le sens du code trouvé, confirmer quelle API a échoué contre quelle ressource avec Process Monitor ou un outil similaire est le chemin court vers la cause.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.