Windows-Fehlercodes lesen — die Dreischichtstruktur von Win32, HRESULT und NTSTATUS
· Go Komura · Windows, Fehlercodes, HRESULT, NTSTATUS, Win32 API, Fehlersuche, Debugging, Windows-Entwicklung
„Auf dem App-Bildschirm erschien der Fehler 0x80004005. Was bedeutet das?“ — In Beratungen zur Störungsuntersuchung ist diese Art Frage ein Klassiker. Wer die Zahl aus einem Fehlerdialog direkt in eine Suchmaschine eingefügt hat und von einer Flut unzusammenhängender Artikel empfangen wurde — ein Windows-Update-Fehler, ein freigegebener Ordner, der nicht verbindet, ein VBA-Laufzeitfehler, ein Datenbankverbindungsfehler — und nur verwirrter geworden ist, hat viel Gesellschaft.
Das geschieht, weil 0x80004005 (E_FAIL) ein generischer Code ist, dessen einzige Bedeutung „nicht näher bezeichnetes Fehlschlagen“ ist. Derselbe Code wird in zahllosen Situationen verwendet, daher führt die Suche nur nach dem Code nicht zur Ursache. Andererseits kann ein Code wie 0x80070005, wenn Sie die Struktur kennen, in wenigen Sekunden vor der Suche zerlegt werden in „Win32-Fehlernummer 5 = Zugriff verweigert, als HRESULT verpackt“.
Windows-Fehlercodes bilden aus historischen Gründen drei Schichten — Win32-Fehlercodes, HRESULT und NTSTATUS — und sie werden schichtenübergreifend umgewandelt. Sobald Sie diese Struktur im Kopf haben, können Sie selbst beurteilen „welche Schicht, welche Partei diesen Code zurückgegeben hat“ und „was der wesentliche Code ist“, und der Eröffnungszug einer Untersuchung wird viel schneller.
Dieser Artikel richtet sich an IT-Personal in kleinen und mittleren Unternehmen und an Windows-Anwendungsentwickler und ordnet, wie man die drei Fehlercodesysteme unterscheidet und zerlegt, die Beziehung zu .NET-Ausnahmen und die praktische Suche mit err.exe und PowerShell — gestützt auf Microsoft Learn und die veröffentlichte Spezifikation [MS-ERREF] vom August 2026.
1. Zuerst die Kernaussage
- Windows-Fehlercodes sind vor allem drei Systeme. Win32-Fehlercodes (die kleine Dezimalzahl, die
GetLastErrorzurückgibt), HRESULT (der 32-Bit-Code seit COM, Hex beginnend mit 0x8 oder eine negative Dezimalzahl) und NTSTATUS (Codes der Kernelschicht; Fehler beginnen mit 0xC).123 - Dezimal und Hexadezimal sind verschiedene Schreibweisen desselben Codes. „Fehler 5“, „0x5“ und „die unteren 16 Bit von 0x80070005“ bezeichnen alle ERROR_ACCESS_DENIED (Zugriff verweigert).1
- 0x8007xxxx ist „ein verpackter Win32-Fehler“. Es ist ein Win32-Fehlercode, gespeichert in HRESULT FACILITY_WIN32 (7); wandeln Sie die unteren 16 Bit ins Dezimal um, und Sie haben den wesentlichen Code. Das ist das wichtigste Muster beim Lesen von Fehlercodes.45
- 0x80004005 (E_FAIL) ist kein Ursachencode. Er bedeutet „Unspecified failure“ und trägt keine weitere Information. Statt in diesen Code zu graben, sollten Sie den Herkunftskontext und begleitende Protokolle suchen.6
- Eine negative Dezimalzahl (-2147467259 und Ähnliches) ist ein HRESULT. Das höchstwertige Bit der 32 Bit (das Fehlschlagbit) ist gesetzt, daher ist eine vorzeichenbehaftete Anzeige negativ. Wandeln Sie sie ins Hexadezimal um und lesen Sie sie dann.2
- Ein 8-stelliger Wert, der mit 0xC beginnt, ist ein NTSTATUS. 0xC0000005 (Zugriffsverletzung) und 0xC0000135 (DLL nicht gefunden) erscheinen ständig im Ereignisprotokoll und in Abbildern zur Absturzzeit. Sie haben nichts mit der Win32-Fehlernummer 5 zu tun.7
- Derselbe Code ändert die Bedeutung mit dem Kontext. Die Ursache von Fehler 5 reicht über ACL, Erhöhung, Antivirus, eine gehaltene Datei und mehr, und die „Datei nicht gefunden“ von Fehler 2 ist oft eine abhängige DLL. Lesen Sie die Bedeutung des Codes immer zusammen damit, welche API gegen was fehlgeschlagen ist.1
- Umwandlungs- und Nachschlagewerkzeuge sind Standard.
certutil -errorundnet helpmsgsind in Windows eingebaut; PowerShellsWin32Exceptionholt die Nachricht; auf einer Entwicklungsmaschine err.exe (Microsoft Error Lookup Tool); in der Abbildanalyse WinDbgs!error.8910 - In .NET wird ein HRESULT auf einen Ausnahmetyp abgebildet. Ein bekanntes HRESULT geht zum entsprechenden Ausnahmetyp (E_ACCESSDENIED → UnauthorizedAccessException und Ähnliches); ein unbekanntes wird COMException; der ursprüngliche Wert bleibt in
Exception.HResult.11
In einem Satz ist das Muster einer Windows-Fehlercode-Untersuchung „die Schreibweise auf Hex ausrichten → beurteilen, welche Schicht der Code ist → zerlegen und den wesentlichen Code herausnehmen → ihn zusammen mit dem Kontext lesen“.
2. Windows hat drei Fehlercodesysteme
Zuerst die Gesamtkarte. Windows-Fehlercodes teilen sich vor allem in die folgenden drei Systeme, nach der Schicht, die sie zurückgibt.
| System | Hauptpartei, die ihn zurückgibt | Typisches Aussehen | Repräsentatives Beispiel |
|---|---|---|---|
| Win32-Fehlercode | Win32-API (GetLastError), der Exit-Code eines Befehls |
Eine kleine Dezimalzahl (0–15999) | 5 = ERROR_ACCESS_DENIED |
| HRESULT | Eine COM-Komponente, die Shell, ein Installer, viele Frameworks | 8-stelliges Hex beginnend mit 0x8 oder eine negative Dezimalzahl | 0x80004005 = E_FAIL |
| NTSTATUS | Der Kernel, ein Treiber, eine native API (ntdll) | Fehler sind 8-stelliges Hex beginnend mit 0xC | 0xC0000005 = STATUS_ACCESS_VIOLATION |
Historisch stapelten sie sich in dieser Reihenfolge: Win32-Fehlercodes, die MS-DOS-Fehlernummern erbten, NTSTATUS, den der NT-Kernel intern verwendet, und HRESULT, entworfen bei der Einführung von COM, um „Erfolg/Fehlschlag und den Ursprung in 32 Bit zu packen“. Auf aktuellem Windows ist ein Umwandlungsfluss Alltag: der Kernel gibt ein NTSTATUS zurück, das Win32-Subsystem wandelt es in einen Win32-Fehlercode um, und die COM-Schicht verpackt es weiter als HRESULT.124
flowchart TB
accTitle: Umwandlungsfluss über die drei Systeme
accDescr: Das Win32-Subsystem wandelt ein NTSTATUS, das der Kernel zurückgegeben hat, in einen Win32-Fehlercode um, und die COM-Schicht verpackt es weiter als HRESULT
kernel["Kernel und Treiber"] --> nt["NTSTATUS(Fehler sind 0xC…)"]
nt -->|Das Win32-Subsystem wandelt um| win["Win32-Fehlercode(5 und Ähnliches)"]
win -->|Die COM-Schicht verpackt| hr["HRESULT(0x8007xxxx)"]
Abbildung 1: Der Umwandlungsfluss über Schichten. Ein Kernel-NTSTATUS wird ein Win32-Fehler und weiter als HRESULT verpackt.
2.1. Gewöhnen Sie sich daran, Dezimal und Hexadezimal austauschbar zu lesen
Bevor Sie die drei Systeme unterscheiden, müssen Sie Schreibweisenschwankungen aufnehmen. Derselbe Code wird je nach Situation als Dezimal oder Hex angezeigt.
- „Fehler 5“, „Fehlercode: 0x5“ → dasselbe ERROR_ACCESS_DENIED
- „Fehler 1223“, „0x4C1“ → dasselbe ERROR_CANCELLED
- „0x80070005“, „-2147024891“ → dasselbe HRESULT
In PowerShell ist die Umwandlung eine Zeile.
# Decimal → hex
'0x{0:X8}' -f 1223 # 0x000004C1
'0x{0:X8}' -f -2147024891 # 0x80070005 (negative = HRESULT to hex)
# Hex → decimal
0x4C1 # 1223
Wenn Sie eine negative Dezimalzahl sehen, die mit „-214…“ beginnt, wandeln Sie sie reflexartig ins Hexadezimal um. Das allein schneidet viel Verlaufen am Eingang einer Untersuchung.
flowchart TB
accTitle: Drei Erscheinungen desselben Codes
accDescr: Dezimaler Fehler 5, Hex 0x5 und die unteren 16 Bit von 0x80070005 bezeichnen alle dasselbe ERROR_ACCESS_DENIED
d["Dezimalschreibweise: Fehler 5"] --> same["ERROR_ACCESS_DENIED"]
h["Hex-Schreibweise: 0x5"] --> same
l["Die unteren 16 Bit von 0x80070005"] --> same
same -.-> memo["Andere Schreibweise, derselbe Code"]
Abbildung 2: Dezimal, Hex und die unteren 16 Bit eines HRESULT sind nur verschiedene Schreibweisen desselben Codes.
3. Win32-Fehlercodes — GetLastError und FORMAT_MESSAGE
3.1. Grundlegendes GetLastError-Verhalten
Viele Win32-APIs wie CreateFile und RegOpenKeyEx zeigen Fehlschlag mit dem Rückgabewert (FALSE, NULL, INVALID_HANDLE_VALUE und Ähnliches) an und speichern den detaillierten Fehlercode in einem „Last-Error-Code“, der pro Thread gehalten wird. Der Aufrufer holt ihn mit GetLastError unmittelbar nach der Bestätigung des Fehlschlags.13
Es gibt zwei praktische Vorbehalte.13
- Lesen Sie ihn unmittelbar nach dem Fehlschlag. Wenn Sie dazwischen einen anderen API-Aufruf einfügen (eine Protokollierungsfunktion zum Beispiel), kann dieser Aufruf den Last-Error-Code überschreiben.
- Verlassen Sie sich bei Erfolg nicht auf den Wert. Einige APIs setzen den Last-Error-Code bei Erfolg auf 0; einige rühren ihn nicht an. Die Regel ist, den Fehlschlag aus dem Rückgabewert zu bestätigen und dann zu lesen.
sequenceDiagram
accTitle: GetLastError unmittelbar nach dem Fehlschlag lesen
accDescr: Nach Bestätigung des Fehlschlags aus dem Rückgabewert den Last-Error-Code mit GetLastError sofort holen, ohne einen anderen API-Aufruf einzufügen
participant app as App
participant api as Win32 API
app->>api: CreateFile-Aufruf
api-->>app: Fehlschlag-Rückgabewert
app->>api: GetLastError
api-->>app: Code 5
Note over app: Einen anderen API-Aufruf dazwischen einzufügen kann überschreiben
Abbildung 3: Lesen Sie den Last-Error-Code unmittelbar nach dem Fehlschlag. Einen anderen API-Aufruf dazwischen einzufügen kann ihn überschreiben.
Um eine Nachrichtenzeichenfolge aus einem Code zu holen, verwenden Sie FormatMessage mit dem Flag FORMAT_MESSAGE_FROM_SYSTEM.1
#include <windows.h>
#include <stdio.h>
void PrintLastError(const wchar_t* apiName)
{
DWORD code = GetLastError(); // Call immediately after failure (do not insert another 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);
}
Sowohl Dezimal als auch Hex und den Nachrichtentext im Protokoll Ihrer eigenen App zu hinterlassen, wie hier, macht eine spätere Untersuchung einen Schritt schneller.
flowchart TB
accTitle: Eine Nachricht aus einem Code nachschlagen und im Protokoll hinterlassen
accDescr: Das Flag FORMAT_MESSAGE_FROM_SYSTEM an FormatMessage übergeben, um die Nachrichtenzeichenfolge des Fehlercodes zu holen, und Dezimal, Hex und den Nachrichtentext im Protokoll hinterlassen
code["Fehlercode(Beispiel: 5)"] --> fm["Die Zeichenfolge mit FormatMessage holen"]
fm --> msg["Nachrichtentext"]
msg --> log["Im Protokoll aufzeichnen"]
log -.-> both["Dezimal, Hex und Text zusammen schreiben"]
Abbildung 4: Wandeln Sie einen Fehlercode mit FormatMessage in eine Nachrichtenzeichenfolge um und hinterlassen Sie Dezimal, Hex und den Text zusammen im Protokoll.
3.2. Repräsentative Codes, die im Feld ständig auftauchen
Win32-Fehlercodes sind im Bereich 0–15999 definiert, und Microsoft Learn hat eine vollständige Liste.1 Unter ihnen sind die Gesichter, denen Sie in der Störungsuntersuchung immer wieder begegnen, die folgenden.
| Dezimal | Hex | Symbol | Bedeutung |
|---|---|---|---|
| 2 | 0x2 | ERROR_FILE_NOT_FOUND | Die angegebene Datei wurde nicht gefunden |
| 3 | 0x3 | ERROR_PATH_NOT_FOUND | Der angegebene Pfad wurde nicht gefunden |
| 5 | 0x5 | ERROR_ACCESS_DENIED | Der Zugriff wurde verweigert |
| 32 | 0x20 | ERROR_SHARING_VIOLATION | Ein anderer Prozess verwendet sie, Zugriff ist nicht möglich |
| 87 | 0x57 | ERROR_INVALID_PARAMETER | Der Parameter ist falsch |
| 122 | 0x7A | ERROR_INSUFFICIENT_BUFFER | Der übergebene Puffer ist zu klein |
| 998 | 0x3E6 | ERROR_NOACCESS | Ungültiger Zugriff auf eine Speicherstelle |
| 1223 | 0x4C1 | ERROR_CANCELLED | Der Vorgang wurde vom Benutzer abgebrochen |
Davon ist 998 (ERROR_NOACCESS) nicht „Zugriff verweigert“, sondern der Win32-Ausdruck einer Speicherzugriffsverletzung, die Form des später besprochenen NTSTATUS STATUS_ACCESS_VIOLATION nach der Umwandlung in die Win32-Schicht. Achten Sie auf die Verwechslung mit Nummer 5. Auch 1223 (ERROR_CANCELLED) ist ein Code, der erscheint, wenn der Benutzer in einem UAC-Erhöhungsdialog „Nein“ wählt, zum Beispiel — eher „es wurde abgebrochen“ als ein Fehler.
flowchart TB
accTitle: Fehler 998 und 5 sind verschiedene Dinge
accDescr: 998 ist eine Speicherzugriffsverletzung, eine NTSTATUS-Zugriffsverletzung umgewandelt in die Win32-Schicht, und unterscheidet sich von 5, das Zugriff verweigert darstellt
nt["NTSTATUS 0xC0000005"] -->|In die Win32-Schicht umgewandelt| e998["Fehler 998(ERROR_NOACCESS)"]
e998 -.-> m1["Bedeutung ist eine Speicherzugriffsverletzung"]
e5["Fehler 5(Zugriff verweigert)"] -.-> m2["Ein Berechtigungsproblem. Ein anderes Ding als 998"]
Abbildung 5: Fehler 998 ist eine NTSTATUS-Zugriffsverletzung, umgewandelt in die Win32-Schicht, ein anderes Ding als Zugriff-verweigert 5.
3.3. Derselbe Code ändert die Bedeutung mit dem Kontext
Wichtiger als die Tabelle der repräsentativen Codes auswendig zu lernen ist das Gefühl, dass ein Fehlercode Ihnen nur „die Art des Fehlschlags“ sagt.
- Fehler 5 (Zugriff verweigert): Kandidaten für die Ursache reichen weit — unzureichende NTFS-ACL, Schreiben in einen geschützten Bereich ohne Administratorrechte, eine Sperre durch Antivirus oder AppLocker, unzureichende Privilegien eines Dienstkontos und so weiter.
- Fehler 2 (Datei nicht gefunden): Es ist nicht unbedingt die Datei, die der Benutzer angegeben hat. Eine abhängige DLL, die die EXE implizit zu laden versuchte, eine Einstellungsdatei, die wegen Registrierungsumleitung (32-Bit/64-Bit) am falschen Ort gesehen wurde, ein Pfad, dessen Umgebungsvariablenerweiterung fehlgeschlagen ist — „welche Datei“ nicht gefunden wurde, ist aus dem Code nicht sichtbar.
- Fehler 32 (Freigabeverletzung): „Welcher Prozess sie hält“ ist die eigentliche Frage, aber der Code sagt Ihnen das nicht.
flowchart TB
accTitle: Die Ursache von Fehler 5 wird vom Kontext entschieden
accDescr: Selbst derselbe Zugriff verweigert hat mehrere Ursachkandidaten wie unzureichende ACL oder fehlende Administratorrechte, und Sie müssen identifizieren, welche API gegen was fehlgeschlagen ist
e5["Fehler 5(Zugriff verweigert)"] --> c1["Unzureichende ACL"]
e5 --> c2["Keine Adminrechte"]
e5 --> c3["Sperre durch Sicherheitsprodukt"]
e5 --> c4["Dienstprivilegien niedrig"]
c1 --> next["Procmon: fehlgeschlagenes Ziel"]
c2 --> next
c3 --> next
c4 --> next
Abbildung 6: Ein Code sagt Ihnen nur „die Art des Fehlschlags“. Fehler 5 hat mehrere Ursachkandidaten, und das Ziel zu identifizieren ist erforderlich.
Das Werkzeug, das misst „welche API, gegen welchen Objektnamen, welchen Ergebnis zurückgegeben hat“, ist Process Monitor. Die Verwendung ist ausführlich in „Process Monitor (ProcMon) Praxisleitfaden“ behandelt. Die Bedeutung eines Fehlercodes nachzuschlagen und das Ziel zu identifizieren, das fehlgeschlagen ist, sind zwei Räder desselben Wagens.
4. HRESULT — Die in 32 Bit gepackte Struktur lesen
4.1. Bit-Layout
HRESULT ist ein Format, das Erfolg/Fehlschlag, Ursprung und einen Detailcode in einen einzelnen 32-Bit-Wert packt. Die veröffentlichte Spezifikation [MS-ERREF] definiert es mit dem folgenden Layout.2
| Bitposition | Name | Bedeutung |
|---|---|---|
| 31 | S | Severity. 0 = Erfolg, 1 = Fehlschlag |
| 30 | R | Reserviert (Teil der Severity beim Abbilden von NTSTATUS) |
| 29 | C | Customer-Bit. 1 bedeutet einen Code, den jemand anderes als Microsoft definiert hat |
| 28 | N | 1 bedeutet einen NTSTATUS-Wert, der in den HRESULT-Raum abgebildet wurde |
| 27 | X | Reserviert (0) |
| 26–16 | Facility | Ein Facility-Code, der den Ursprung anzeigt (11 Bit) |
| 15–0 | Code | Ein Detailcode innerhalb der Facility (16 Bit) |
Das höchstwertige S-Bit ist 1, das heißt ein HRESULT, dessen Hex-Schreibweise bei 0x8 oder darüber beginnt, ist ein Fehlschlag. Das als vorzeichenbehaftete 32-Bit-Ganzzahl anzuzeigen macht es negativ — das ist die Identität des früher erwähnten „-214…“.
flowchart TB
accTitle: Die Beziehung zwischen dem S-Bit und einer negativen Anzeige
accDescr: Ein Fehlschlag-HRESULT hat das höchstwertige S-Bit auf 1, daher beginnt es in Hex bei 0x8 oder darüber, und als vorzeichenbehaftete 32-Bit-Ganzzahl ist es negativ
s["S-Bit = 1(Fehlschlag)"] --> hex["Hex beginnt bei 0x8 oder darüber"]
hex --> neg["Eine vorzeichenbehaftete Anzeige ist negativ"]
neg --> back["Bei einem Negativen ins Hex umwandeln und lesen"]
Abbildung 7: Ein Fehlschlag-HRESULT beginnt bei 0x8 oder darüber, weil das S-Bit 1 ist, und eine vorzeichenbehaftete Anzeige ist negativ.
Repräsentative Facility-Werte sind die folgenden.5
| Facility | Wert | Hex-Erscheinung | Bedeutung |
|---|---|---|---|
| FACILITY_NULL | 0 | 0x8000xxxx | Weit verbreitete gemeinsame Codes (E_FAIL, E_UNEXPECTED und Ähnliches) |
| FACILITY_RPC | 1 | 0x8001xxxx | RPC-Ursprung |
| FACILITY_ITF | 4 | 0x8004xxxx | Ein schnittstellendefinierter Fehler (Bedeutung hängt von der Schnittstelle ab) |
| FACILITY_WIN32 | 7 | 0x8007xxxx | Ein verpackter Win32-Fehlercode |
| FACILITY_WINDOWS | 8 | 0x8008xxxx | Zusätzliche von Microsoft definierte Schnittstellen |
4.2. 0x80004005 und 0x80070005 zerlegen
Zerlegen wir sie tatsächlich.
Für 0x80004005: S=1 (Fehlschlag), Facility=(0x80004005 » 16) & 0x7FF = 0 (FACILITY_NULL), Code=0x4005. Ein generischer FACILITY_NULL-Code, definiert als E_FAIL „Unspecified failure“.6 Mit anderen Worten trägt dieser Code nur die Bedeutung „ein Fehlschlagen, das kein Detail berichten kann“. Wenn Sie 0x80004005 sehen, hören Sie dort auf, in den Code selbst zu graben, und verschieben Sie das Gewicht der Untersuchung auf „welche Komponente ihn zurückgegeben hat“ und „gibt es Detail im Ereignisprotokoll oder im App-Protokoll zur gleichen Zeit“.
Für 0x80070005: S=1, Facility=7 (FACILITY_WIN32), Code=0x0005=5. Sie können sehen, dass es Win32-Fehlernummer 5 (ERROR_ACCESS_DENIED) als HRESULT verpackt ist. Der Alias E_ACCESSDENIED ist im Wesentlichen dieser Wert.6
Selbst für dasselbe „Zugriff verweigert“ ist 0x80070005 eine Verpackung eines konkreten Fehlschlags, der in der Win32-Schicht aufgetreten ist, und die Informationsmenge ist völlig anders als 0x80004005.
flowchart TB
accTitle: Zerlegung von 0x80004005 und 0x80070005
accDescr: 0x80004005 ist der generische FACILITY_NULL-Code E_FAIL, trägt kein Detail und sollte zu einer Kontextuntersuchung übergehen; 0x80070005 ist FACILITY_WIN32 und lässt sich als Verpackung der Win32-Fehlernummer 5, Zugriff verweigert, lesen
a["0x80004005"] --> af["Facility=0(FACILITY_NULL)"]
af --> ac["Code=0x4005 → E_FAIL"]
ac --> ax["Nicht näher bezeichnetes Fehlschlagen. Weiter zur Kontextuntersuchung"]
b["0x80070005"] --> bf["Facility=7(FACILITY_WIN32)"]
bf --> bc["Code=0x0005 → 5"]
bc --> bx["ERROR_ACCESS_DENIED"]
Abbildung 8: Selbst dasselbe „Fehlschlagen“ hat nach der Zerlegung eine andere Informationsmenge. 0x80070005 lässt sich zur Win32-Fehlernummer 5 gehen.
4.3. Das wichtigste Muster: 0x8007xxxx = HRESULT_FROM_WIN32
Um einen Fehlschlag von einer unteren Schicht, die nur einen Win32-Fehlercode zurückgeben kann, an eine obere Schicht zu übermitteln, die HRESULT zurückgibt (eine COM-Methode oder die .NET-Laufzeit), stellt winerror.h das Makro HRESULT_FROM_WIN32 bereit.4 Das Verhalten ist „den Win32-Fehlercode in den unteren 16 Bit speichern, Facility auf FACILITY_WIN32 (7) setzen und das S-Bit auf 1 setzen“.
flowchart TB
accTitle: Wie HRESULT_FROM_WIN32 funktioniert
accDescr: Den Win32-Fehlercode in den unteren 16 Bit speichern, Facility auf 7 und das S-Bit auf 1 setzen und ein 0x8007xxxx-HRESULT zusammenbauen
win["Win32-Fehlercode(Beispiel: 5)"] --> low["In den unteren 16 Bit speichern"]
low --> fac["Facility auf 7 setzen"]
fac --> sbit["Das S-Bit auf 1 setzen"]
sbit --> hr["0x80070005"]
Abbildung 9: HRESULT_FROM_WIN32 speichert den Win32-Fehler in den unteren 16 Bit und setzt Facility=7 und das S-Bit.
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)
Um in die andere Richtung zu lesen, nehmen Sie die unteren 16 Bit in PowerShell.
0x80070005 -band 0xFFFF # 5 → ERROR_ACCESS_DENIED
0x80072EE7 -band 0xFFFF # 12007 → ERROR_INTERNET_NAME_NOT_RESOLVED (WinINet)
Wie im zweiten Beispiel sind WinINet- und WinHTTP-Fehler (die 12000er) ebenfalls im Win32-Fehlercode-Raum definiert1, daher lässt sich ein Netzwerk-0x8007xxxx mit demselben Verfahren zerlegen. „Wenn Sie 0x8007 sehen, wandeln Sie die unteren 4 Ziffern ins Dezimal um“ ins Muskelgedächtnis zu bringen ist die praktische Fertigkeit Nummer eins, die dieser Artikel Ihnen mitgeben möchte.
Es gibt den gegenteiligen Vorbehalt für 0x8004xxxx (FACILITY_ITF). Ein FACILITY_ITF-Code hat eine andere Partei, die die Bedeutung pro Schnittstelle definiert, daher kann derselbe 32-Bit-Wert etwas anderes bedeuten, wenn die Partei, die ihn zurückgegeben hat, anders ist.5 Für ein unvertrautes 0x8004xxxx schlagen Sie es nicht in einer generischen Suche nach, sondern in der Dokumentation der Komponente, die es zurückgegeben hat (eine Bibliothek, ein Treiber-SDK, ein Serverprodukt).
flowchart TB
accTitle: Wie Sie nachschlagen, ändert sich zwischen 0x8007 und 0x8004
accDescr: Ein FACILITY_WIN32-0x8007xxxx lässt sich lesen, indem man die unteren 16 Bit mechanisch zerlegt, aber ein FACILITY_ITF-0x8004xxxx hat eine andere Partei, die die Bedeutung pro Schnittstelle definiert, daher in den Unterlagen der zurückgebenden Komponente nachschlagen
hr{"Facility ist?"} -->|7, WIN32| w["Die unteren 16 Bit ins Dezimal umwandeln"]
hr -->|4, ITF| i["Bedeutung unterscheidet sich nach der zurückgebenden Partei"]
w --> ww["Als Win32-Fehler lesen"]
i --> ii["In den Unterlagen der zurückgebenden Partei nachschlagen"]
Abbildung 10: 0x8007xxxx lässt sich mechanisch zerlegen; 0x8004xxxx wird in den Unterlagen der zurückgebenden Komponente nachgeschlagen.
5. NTSTATUS — Codes der Kernelschicht und die Welt der Abstürze
5.1. Layout und Severity
NTSTATUS ist ein 32-Bit-Code, den der Kernel, Gerätetreiber und ntdll-native APIs verwenden, und sein Layout ähnelt HRESULT, ohne dasselbe zu sein.3
| Bitposition | Name | Bedeutung |
|---|---|---|
| 31–30 | Sev | Severity. 00 = Erfolg, 01 = Information, 10 = Warnung, 11 = Fehler |
| 29 | C | Customer-Bit |
| 28 | N | Reserviert (0, damit eine Abbildung auf HRESULT möglich ist) |
| 27–16 | Facility | Facility (12 Bit) |
| 15–0 | Code | Detailcode |
Weil Severity 2 Bit hat, können Sie die Art aus der führenden Hex-Ziffer lesen. 0xC… ist ein Fehler (11), 0x8… eine Warnung (10), 0x4… Information (01), 0x0–0x3… Erfolg. Die Haltepunktausnahme 0x80000003 (STATUS_BREAKPOINT) ist ein repräsentatives Beispiel für „eine Warnung, kein Fehler“.37
flowchart TB
accTitle: Ein NTSTATUS lässt sich nach Art aus der führenden Ziffer lesen
accDescr: Weil Severity 2 Bit hat, lässt sich ein NTSTATUS als Fehler lesen, wenn die führende Hex-Ziffer 0xC ist, Warnung wenn 0x8, Information wenn 0x4 und Erfolg wenn 0x0 bis 0x3
head{"Die führende Hex-Ziffer ist?"} -->|0xC| e["Fehler"]
head -->|0x8| w["Warnung"]
head -->|0x4| i["Information"]
head -->|0x0–0x3| s["Erfolg"]
w -.-> ex["Beispiel: 0x80000003 ist eine Warnung"]
Abbildung 11: Ein NTSTATUS lässt sich nach Art aus der führenden Hex-Ziffer lesen. 0x80000003 ist „eine Warnung, kein Fehler“.
5.2. Wo Sie ihm begegnen — Ausnahme-Codes, STOP-Codes und das Ereignisprotokoll
Die Situationen, in denen IT-Personal und Entwickler NTSTATUS begegnen, sind vor allem absturzbezogen.
- Ein Ausnahme-Code eines Anwendungsabsturzes: Der „Exception code: 0xc0000005“, der im Ereignisprotokoll unter „Anwendungsfehler (Ereignis-ID 1000)“ aufgezeichnet wird, ist ein NTSTATUS. Repräsentative Werte sind die folgenden.7
| Wert | Symbol | Bedeutung |
|---|---|---|
| 0xC0000005 | STATUS_ACCESS_VIOLATION | Zugriffsverletzung (ein unzulässiger Speicherzugriff) |
| 0xC0000135 | STATUS_DLL_NOT_FOUND | Eine erforderliche DLL wurde nicht gefunden, Start ist nicht möglich |
| 0xC00000FD | STATUS_STACK_OVERFLOW | Stapelüberlauf |
| 0xC0000374 | STATUS_HEAP_CORRUPTION | Heap-Beschädigung |
- Ein Bluescreen-STOP-Code: Sie sehen auf den ersten Blick ähnlich aus, aber ein STOP-Code (Bug-Check-Code) ist ein eigenes Nummerierungssystem, getrennt von NTSTATUS, etwa 0x0000009F (DRIVER_POWER_STATE_FAILURE), und hat eine eigene Referenz.14 Nur die Unterscheidung zu behalten „0xC0000005 ist NTSTATUS; STOP 0x9F ist ein Bug-Check-Code und Sie dürfen ihn nicht in einer NTSTATUS-Tabelle nachschlagen“ reicht.
- Die Result-Spalte von Process Monitor: NAME NOT FOUND und ACCESS DENIED in der Result-Spalte von Procmon sind Anzeigenamen des NTSTATUS, das der Kernel zurückgegeben hat (STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED). Es ist auch ein Ort, an dem Sie die Schichtentsprechung spüren können: einen Datei-E/A-Fehlschlag im NTSTATUS-Vokabular zu beobachten und dass dieser Fehlschlag in einen Win32-Fehler umgewandelt bei der App ankommt.
flowchart TB
accTitle: Einen Ausnahme-Code von einem STOP-Code unterscheiden
accDescr: Einen Ausnahme-Code des Ereignisprotokolls als NTSTATUS lesen; einen Bluescreen-STOP-Code in der eigenen Bug-Check-Code-Referenz nachschlagen, einem anderen System
q{"Wo ist der Code erschienen?"} -->|Ausnahme-Code| nt["Als NTSTATUS lesen"]
q -->|STOP-Code| bc["In der Bug-Check-Code-Tabelle nachschlagen"]
nt -.-> n1["Beispiel: 0xC0000005"]
bc -.-> b1["Beispiel: 0x0000009F"]
Abbildung 12: Ein Ausnahme-Code des Ereignisprotokolls ist ein NTSTATUS; ein Bluescreen-STOP-Code ist ein anderes System. Schlagen Sie sie nicht in der falschen Tabelle nach.
Die Untersuchung jenseits des Ausnahme-Codes, das heißt das Erfassen und Analysieren eines Absturzabbilds, ist in „Einführung in das Sammeln von Windows-Absturzabbildern“ und „Absturz-Dumps mit WinDbg + SOS lesen“ behandelt.
5.3. Die Beziehung zu HRESULT — das N-Bit und RtlNtStatusToDosError
Die Brücke zwischen NTSTATUS und den anderen zwei Schichten hat zwei Wege.
- Eine Abbildung in den HRESULT-Raum: Das HRESULT-N-Bit (0x10000000) zu setzen bringt einen NTSTATUS-Wert unverändert in den HRESULT-Raum (das Makro HRESULT_FROM_NT in winerror.h). 0xC0000005 abzubilden wird zum Beispiel 0xD0000005. Wenn Sie ein HRESULT sehen, das mit 0xD beginnt, ist das korrekte Verfahren, das N-Bit zu entfernen und es als NTSTATUS zu lesen.2
- Umwandlung in einen Win32-Fehlercode:
RtlNtStatusToDosErrorvon ntdll wandelt ein NTSTATUS in den entsprechenden Win32-Fehlercode um. Ein Wert ohne definierte Entsprechung wird ERROR_MR_MID_NOT_FOUND.12 Zum Beispiel wird STATUS_ACCESS_VIOLATION (0xC0000005) in ERROR_NOACCESS (998) umgewandelt und STATUS_OBJECT_NAME_NOT_FOUND (0xC0000034) in ERROR_FILE_NOT_FOUND (2). Es ist auch nützlich zu behalten, dass das reiche Vokabular des Kernels in der Win32-Schicht manchmal auf eine gröbere Unterscheidung gerundet wird.
flowchart TB
accTitle: Zwei Brücken von NTSTATUS zu den anderen Schichten
accDescr: Ein NTSTATUS wird auf zwei Wegen an die anderen Schichten übergeben: in den HRESULT-Raum abgebildet, indem das N-Bit gesetzt wird, und von RtlNtStatusToDosError in einen Win32-Fehlercode umgewandelt
nt["NTSTATUS(0xC0000005)"] -->|Das N-Bit setzen| hr["HRESULT(0xD0000005)"]
nt -->|RtlNtStatusToDosError| win["Win32-Fehler 998(ERROR_NOACCESS)"]
win -.-> memo["ERROR_MR_MID_NOT_FOUND, wenn keine Entsprechung definiert ist"]
Abbildung 13: Es gibt zwei NTSTATUS-Brücken. Ein 0xD-Beginn wird nach Entfernen des N-Bits als NTSTATUS gelesen.
6. COM und .NET — Wie ein Fehlercode auf eine Ausnahme abgebildet wird
6.1. Der COM-Stil — HRESULT + IErrorInfo
Eine COM-Methode gibt grundlegend HRESULT zurück, aber es gibt eine Grenze, was in 32 Bit packt, daher kann als Ergänzung der IErrorInfo-Mechanismus eine Fehlerbeschreibungszeichenfolge und den Ursprung getrennt übermitteln. In C++ behandelt die vom Compiler unterstützte Klasse _com_error HRESULT und IErrorInfo zusammen. Eine App, deren Fehlerdialog „Code + Beschreibung“ zeigt, trägt die Beschreibung oft durch diesen Mechanismus.
flowchart TB
accTitle: IErrorInfo, das HRESULT ergänzt
accDescr: Es gibt eine Grenze, was in ein 32-Bit-HRESULT packt, daher werden eine Fehlerbeschreibungszeichenfolge und der Ursprung getrennt mit IErrorInfo übermittelt, und in C++ behandelt die Klasse _com_error beide zusammen
hr["HRESULT(nur 32 Bit)"] --> lim["Es gibt eine Grenze, was packt"]
lim --> ei["IErrorInfo trägt die Beschreibung"]
ei --> ce["_com_error behandelt sie zusammen"]
ce -.-> dlg["Der Code + Beschreibung des Dialogs"]
Abbildung 14: Eine Beschreibungszeichenfolge, die nicht in ein 32-Bit-HRESULT passt, wird getrennt von IErrorInfo getragen.
6.2. Der .NET-Stil — von einem HRESULT zu einem Ausnahmetyp
Wenn die .NET-Laufzeit in COM-Interop einen HRESULT-Fehlschlag empfängt, wandelt sie ihn in eine Ausnahme um. Ein bekanntes HRESULT wird auf den entsprechenden Ausnahmetyp abgebildet; ein unbekanntes wird COMException.11
flowchart TB
accTitle: Abbildung von HRESULT auf eine .NET-Ausnahme
accDescr: Ein Fehlschlag-HRESULT, das in COM-Interop empfangen wird, wird in den entsprechenden Ausnahmetyp umgewandelt, wenn bekannt, oder in COMException, wenn unbekannt, und in beiden Fällen bleibt der ursprüngliche Wert in Exception.HResult
hr["Fehlschlag-HRESULT"] --> known{"Eine bekannte Abbildung?"}
known -->|Yes| typed["In den entsprechenden Ausnahmetyp umwandeln"]
known -->|No| comex["In COMException umwandeln"]
typed --> keep["Der ursprüngliche Wert bleibt in Exception.HResult"]
comex --> keep
Abbildung 15: .NET bildet ein HRESULT auf einen Ausnahmetyp ab, und der ursprüngliche Wert bleibt in Exception.HResult auf jeder Ausnahme.
| HRESULT | .NET-Ausnahmetyp |
|---|---|
| E_ACCESSDENIED (0x80070005) | UnauthorizedAccessException |
| E_OUTOFMEMORY (0x8007000E) | OutOfMemoryException |
| E_INVALIDARG (0x80070057) | ArgumentException |
| E_NOTIMPL (0x80004001) | NotImplementedException |
| Ein Wert ohne definierte Abbildung | COMException (der ursprüngliche Wert in der Eigenschaft ErrorCode) |
Auf jeder Ausnahme bleibt das ursprüngliche HRESULT in der Eigenschaft Exception.HResult. Eine Verzweigung in der Datei-E/A-Ausnahmebehandlung wie „nur bei einer Freigabeverletzung erneut versuchen“ lässt sich mit diesem Wert schreiben.
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)
// Another process is holding the file — wait a little and retry, for example
}
6.3. P/Invoke und GetLastError
Wenn Sie eine Win32-API direkt über P/Invoke aufrufen, geben Sie SetLastError = true auf DllImport (oder LibraryImport) an und holen Sie dann mit Marshal.GetLastWin32Error (ab .NET 6 das Äquivalent GetLastPInvokeError). GetLastError selbst als P/Invoke zu definieren und aufzurufen ist ungenau, weil ein API-Aufruf innerhalb der Laufzeit den Wert überschreiben kann.15
flowchart TB
accTitle: Den letzten Fehler in P/Invoke holen
accDescr: SetLastError als true anzugeben und mit Marshal.GetLastWin32Error zu holen ist korrekt; GetLastError direkt zu P/Invoken ist ungenau wegen einer Laufzeitüberschreibung
pi["Eine Win32-API über P/Invoke aufrufen"] --> ok["SetLastError=true angeben"]
ok --> get["Mit GetLastWin32Error holen"]
pi --> ng["Eine Definition, die GetLastError direkt aufruft"]
ng --> bad["Die Laufzeit überschreibt ihn und es ist ungenau"]
Abbildung 16: In P/Invoke verwenden Sie SetLastError=true und Marshal.GetLastWin32Error als Satz. GetLastError direkt aufzurufen ist ungenau.
[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);
// Receive the return value as SafeFileHandle, not IntPtr, and close it reliably with using
// (leaving it as IntPtr leaks a kernel handle)
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(); // Example: 5
var message = new Win32Exception(code).Message; // Example: Access is denied.
logger.LogError("CreateFileW failed: {Code} (0x{Code:X8}) {Message}",
code, code, message);
}
Win32Exception schlägt die OS-Nachrichtenzeichenfolge aus einem Win32-Fehlercode nach, daher können Sie sie so verwenden, um sowohl den Code als auch die Nachricht in einem Protokoll zu hinterlassen. Die Entwurfsfrage, in welcher Schicht eine Ausnahme zu fangen und wie sie in einem Protokoll zu hinterlassen ist, ist in „Wo sollten catch und Logging in der Ausnahmebehandlung stehen?“ behandelt.
7. Umwandlungs- und Untersuchungswerkzeuge in der Praxis — Eine Kopier-Einfügen-Kurzreferenz
7.1. err.exe (Microsoft Error Lookup Tool)
Ein eigenständiges Fehler-Nachschlagewerkzeug, das Microsoft verteilt. Es durchläuft eine große Zahl von Header-Dateien wie winerror.h und ntstatus.h und listet Definitionen und Nachrichten auf, die zum angegebenen Code passen.8
err 0x80070005
err 5
err 0xC0000005
Eine einzelne Zahl kann in mehreren Headern treffen (zum Beispiel trifft „5“ Definitionen an verschiedenen Stellen außer Win32 ERROR_ACCESS_DENIED), daher muss welcher der Kandidaten plausibel ist nach Kontext gewählt werden. Der Download-Dateiname ist versioniert (Err_6.4.5.exe zum Zeitpunkt des Schreibens), und beachten Sie auch, dass die Codedefinitionen auf den Headern zum Zeitpunkt des Bündelns beruhen.8
flowchart TB
accTitle: err.exe-Suchergebnisse nach Kontext wählen
accDescr: err.exe durchläuft eine große Zahl von Header-Dateien und listet passende Definitionen auf, daher wenn mehrere Kandidaten für dieselbe Zahl erscheinen, nach Kontext wählen, welcher plausibel ist
in["err 5 eingeben"] --> scan["Eine große Zahl von Headern durchlaufen"]
scan --> hits["Mehrere Definitionen treffen"]
hits --> pick["Einen plausiblen Kandidaten nach Kontext wählen"]
Abbildung 17: err.exe ist eine headerübergreifende Suche, daher können mehrere Kandidaten erscheinen, und der plausible wird nach Kontext gewählt.
7.2. In Windows eingebaute Befehle
Was Sie ohne Extra-Installation verwenden können, sind certutil und net helpmsg. Die Option -error von certutil zeigt den Nachrichtentext, der einem Fehlercode entspricht, und akzeptiert entweder ein hexadezimales HRESULT oder eine Dezimalzahl.9
certutil -error 0x80070005
certutil -error 5
net helpmsg 5
net helpmsg ist nur für einen Win32-Fehlercode in Dezimal, aber in einer deutschen Umgebung kommt die Nachricht auf Deutsch zurück, daher können Sie sie so für eine Erklärung an den Benutzer verwenden.
flowchart TB
accTitle: Wie unter den Standardbefehlen zu wählen
accDescr: Ein dezimaler Win32-Fehlercode lässt sich mit net helpmsg nachschlagen; ein Code, der Hex enthält, einschließlich HRESULT, mit der Option -error von certutil
q{"Der Code, den Sie haben, ist?"} -->|Dezimal-Win32| net["net helpmsg"]
q -->|Enthält Hex| cert["certutil -error"]
net -.-> jp["Eine deutsche Nachricht kommt zurück"]
cert -.-> any["Akzeptiert Hex und Dezimal"]
Abbildung 18: Wie unter den Standardbefehlen zu wählen. Ein dezimaler Win32-Fehler ist net helpmsg; wenn Hex enthalten ist, certutil -error.
7.3. Eine Sammlung von PowerShell-Einzeilern
# Win32 error code → OS message string
[System.ComponentModel.Win32Exception]::new(5).Message
# → Access is denied.
# Negative decimal → hex notation (confirm the identity of an HRESULT)
'0x{0:X8}' -f -2147467259 # 0x80004005
# 0x8007xxxx → the Win32 error code in the low 16 bits
0x80070005 -band 0xFFFF # 5
# HRESULT → confirm the exception .NET maps
[System.Runtime.InteropServices.Marshal]::GetExceptionForHR(-2147024891)
# → UnauthorizedAccessException (0x80070005)
# Win32 error code → HRESULT (reproduce the wrap)
'0x{0:X8}' -f (0x80070000 -bor 32) # 0x80070020
7.4. WinDbgs !error
Um während der Abbildanalyse einen Code nachzuschlagen, ist die Erweiterung !error von WinDbg schnell. Standardmäßig interpretiert sie als Win32-Fehlercode; übergeben Sie 1 als zweites Argument und sie interpretiert als NTSTATUS.10
0:000> !error 5
Error code: (Win32) 0x5 (5) - Access is denied.
0:000> !error 0xc0000005 1
Error code: (NTSTATUS) 0xc0000005 - <Access violation>
In einem Absturzabbild zeigt !analyze -v automatisch den Ausnahme-Code (NTSTATUS) an, daher ist der Fluss, die Bedeutung von dort mit !error <code> 1 zu bestätigen.
flowchart TB
accTitle: Fluss, einen Ausnahme-Code in WinDbg zu bestätigen
accDescr: In einem Absturzabbild zeigt der analyze-Befehl automatisch den Ausnahme-Code an; übergeben Sie diesen Code an die error-Erweiterung mit einem zweiten Argument 1 und bestätigen Sie die Bedeutung als NTSTATUS
dump["Das Absturzabbild öffnen"] --> an["!analyze -v ausführen"]
an --> exc["Der Ausnahme-Code wird angezeigt"]
exc --> chk["Die Bedeutung mit !error code 1 bestätigen"]
Abbildung 19: In der Abbildanalyse schlagen Sie den Ausnahme-Code, den !analyze -v angezeigt hat, mit !error und Flag 1 nach.
8. Ein Untersuchungsverfahren — Vom Beurteilen der Schicht zum Abgleichen gegen den Kontext
Stellen Sie das Wissen bisher zu einem Verfahren zusammen, um einen Fehlercode tatsächlich zu untersuchen.
- Normalisieren Sie die Schreibweise. Wenn es eine negative Dezimalzahl ist, wandeln Sie sie in 8-stelliges Hex um. Füllen Sie Hex kürzer als 8 Stellen mit Nullen auf und lesen Sie es.
- Beurteilen Sie, welche Schicht der Code ist. Wie in der Beurteilungstabelle unten entscheiden die führenden wenigen Stellen es fast.
- Zerlegen und den wesentlichen Code herausnehmen. Eine mechanische Operation: die unteren 16 Bit wenn 0x8007xxxx, das N-Bit entfernen wenn 0xDxxxxxxx.
- Schlagen Sie den Namen und die Definition mit einem Werkzeug nach. Bestätigen Sie den Symbolnamen und die Nachricht mit err.exe, certutil oder
!error. - Gleichen Sie ihn gegen den Kontext ab. Identifizieren Sie, welche App, welcher Vorgang, welche API, gegen was fehlgeschlagen ist, aus dem App-Protokoll, dem Ereignisprotokoll und Procmon. Der Code ist „die Art des Fehlschlags“; der Kontext ist „der Ort der Ursache“.
flowchart TB
accTitle: Verfahren zur Untersuchung eines Fehlercodes
accDescr: Das Untersuchungsmuster, die Schreibweise auf Hex auszurichten, die Schicht aus den führenden wenigen Stellen zu beurteilen, zu zerlegen und den wesentlichen Code herauszunehmen, Name und Definition mit einem Werkzeug nachzuschlagen, dann gegen den Kontext abzugleichen
fix["Auf Hex normalisieren"] --> judge{"Führende Stellen?"}
judge -->|Dezimal| d1["Als Win32-Fehler lesen"]
judge -->|0x8007| d2["Untere 16 Bit → Dezimal"]
judge -->|0xC| d3["Als NTSTATUS lesen"]
judge -->|0xD| d4["N-Bit entfernen und lesen"]
d1 --> tool["Name/Definition nachschlagen"]
d2 --> tool
d3 --> tool
d4 --> tool
tool --> ctx["Kontext abgleichen(Procmon)"]
Abbildung 20: Das Untersuchungsmuster. Normalisieren Sie die Schreibweise, beurteilen Sie die Schicht und zerlegen Sie, schlagen Sie den Namen nach, dann gleichen Sie gegen den Kontext ab.
| Erscheinung | Erster Kandidat | Wie zerlegen und umwandeln |
|---|---|---|
| Eine 1- bis 5-stellige Dezimalzahl (5, 1223 und Ähnliches) | Win32-Fehlercode | So an net helpmsg oder err.exe |
| Eine negative Dezimalzahl (-2147024891 und Ähnliches) | HRESULT | In 8-stelliges Hex umwandeln, dann die Beurteilung der Zeilen darunter |
| 0x8007xxxx | HRESULT (FACILITY_WIN32) | Die unteren 16 Bit ins Dezimal umwandeln und als Win32 lesen |
| 0x8004xxxx | HRESULT (FACILITY_ITF) | In der Dokumentation der zurückgebenden Komponente nachschlagen |
| 0x8000xxxx | HRESULT (FACILITY_NULL) | Ein generischer Code wie E_FAIL. Das Gewicht auf eine Kontextuntersuchung verschieben |
| 0xCxxxxxxx | NTSTATUS (Fehler) | !error <code> 1; bei Bedarf in Win32 umwandeln und lesen |
| 0xDxxxxxxx | Eine NTSTATUS-HRESULT-Abbildung | Das N-Bit (0x10000000) entfernen und als NTSTATUS lesen |
| Eine eigene Facility wie 0x8024xxxx | Ein HRESULT, das einem Funktionsbereich eigen ist | Den Bereich aus dem Facility-Wert identifizieren und zu eigenen Unterlagen gehen (0x8024… ist Windows Update)2 |
Was in Schritt 5 „gegen den Kontext abgleichen“ besonders wirksam ist, ist die Result-Spalte von Process Monitor. Selbst wenn die App nur „0x80070002“ zeigt, sagt Procmon Ihnen in einer Zeile „welcher Prozess, gegen welchen Pfad, NAME NOT FOUND zurückerhalten hat“. Für das Nachschlagen auf der Ereignisprotokollseite siehe auch „Einführung in Windows-Ereignisprotokoll und ETW“.
9. Häufige Fehllektüren — Muster, die eine Untersuchung den langen Weg schicken
Schließlich Fehllektürmuster, die in tatsächlichen Beratungen auftauchen.
Fehllektüre 1: Denken, 0x80004005 sei „ein Code, der eine bestimmte Ursache anzeigt“
E_FAIL ist „Unspecified failure“, und derselbe Wert erscheint in Windows Update, Netzwerk und Datenbanken. Jede Abhilfe zu versuchen, die erscheint, wenn Sie nach diesem Code suchen, ist fast sicher der lange Weg. Engen Sie nicht vom Code ein, sondern von „welche App, welcher Vorgang, andere Protokolle zur gleichen Zeit“.6
Fehllektüre 2: Nicht bemerken, dass eine negative Dezimalzahl ein HRESULT ist
Ein Fall, ein Protokoll, das „Error -2147467259 occurred“ sagt, so zu suchen, oder von „ein Minus-Fehler?“ verwirrt zu sein. Wenn Sie ein Negatives sehen, wandeln Sie es ins Hexadezimal um. Das allein sagt Ihnen, dass es 0x80004005 (E_FAIL) ist, und verbindet sich mit dem Wissen von Fehllektüre 1.
Fehllektüre 3: Die ganzen 8 Stellen von 0x8007xxxx nachzuschlagen und den darunterliegenden Win32-Fehler nicht anzusehen
Das Wesen von 0x80070005 ist „5 = Zugriff verweigert“. Nach dem Herausnehmen der unteren 16 Bit über „was Win32-Fehler 5 im Kontext dieses Vorgangs bedeutet“ nachzudenken erreicht den Kern schneller als die ganzen 8 Stellen zu suchen.
Fehllektüre 4: Anzunehmen „derselbe Code = dieselbe Ursache“
Wenn Sie einmal „Fehler 5 wurde durch Antivirus verursacht“ hatten, neigen Sie dazu, beim nächsten Fehler 5 zur selben Abhilfe zu springen. Selbst mit demselben Code, wenn die API, die fehlgeschlagen ist, und die Zielressource sich unterscheiden, ist die Ursache ein anderes Ding. Die Bedeutung des Codes zu bestätigen und das Ziel mit Procmon oder ähnlichem zu identifizieren sind ein Satz, jedes Mal.
Fehllektüre 5: Win32-Fehler 5 mit 0xC0000005 und einen STOP-Code mit NTSTATUS zu verwechseln
ERROR_ACCESS_DENIED und STATUS_ACCESS_VIOLATION wegen der „5“-Verbindung als dieselben zu behandeln schickt die Untersuchung in völlig verschiedene Richtungen — ein Berechtigungsproblem gegen einen Programmfehler. Auch ist ein Bluescreen-STOP-Code ein anderes System als NTSTATUS, daher erzeugt 0x9F in einer NTSTATUS-Tabelle nachzuschlagen keine sinnvolle Antwort.14
flowchart TB
accTitle: Fehler 5 und 0xC0000005 haben verschiedene Untersuchungsrichtungen
accDescr: Win32-Fehler 5 sollte als Berechtigungsproblem untersucht werden und NTSTATUS 0xC0000005 als Programmfehler; sie als dieselben zu behandeln schickt die Untersuchung in eine andere Richtung
a["Win32-Fehler 5"] --> ad["Ein Berechtigungsproblem untersuchen"]
b["NTSTATUS 0xC0000005"] --> bd["Einen Programmfehler untersuchen"]
a -.-> memo["Unzusammenhängende Codes verschiedener Systeme"]
b -.-> memo
Abbildung 21: Behandeln Sie sie nicht als dieselben wegen der „5“-Verbindung. Fehler 5 geht zu einem Berechtigungsproblem; 0xC0000005 zu einem Programmfehler.
10. Zusammenfassung
- Windows-Fehlercodes sind eine Dreischichtstruktur aus Win32-Fehlercodes, HRESULT und NTSTATUS. Beurteilen Sie zuerst, welche Schicht, welche Partei den Code zurückgegeben hat.
- Schreibweisenschwankungen (Dezimal / Hex / negativ) lassen sich mechanisch ausrichten. Wandeln Sie ein Negatives in 8-stelliges Hex um und lesen Sie es dann.
- HRESULT ist eine Struktur aus S/R/C/N/X-Bits + Facility (11 Bit) + Code (16 Bit), und 0x8007xxxx ist das wichtigste Muster, ein verpackter Win32-Fehler. Wandeln Sie die unteren 16 Bit ins Dezimal um und nehmen Sie den wesentlichen Code heraus.
- Ein generischer Code wie 0x80004005 (E_FAIL) zeigt keine Ursache an. Die Beurteilung, aufzuhören, in den Code zu graben, und zu einer Kontextuntersuchung zu wechseln, ist gerade deshalb möglich, weil Sie die Struktur kennen.
- Sie begegnen NTSTATUS als Absturz-Ausnahme-Code oder in der Result-Spalte von Procmon. 0xC0000005 ist eine Zugriffsverletzung, unzusammenhängend mit Win32-Fehler 5. Ein STOP-Code ist noch ein anderes System.
- In .NET wird ein HRESULT auf einen Ausnahmetyp abgebildet, und der ursprüngliche Wert bleibt in Exception.HResult. In P/Invoke verwenden Sie SetLastError=true und Marshal.GetLastWin32Error als Satz.
- Die Nachschlagewerkzeuge sind certutil -error und net helpmsg (Standard), err.exe (eine Entwicklungsmaschine), PowerShell-Einzeiler und WinDbgs !error.
- Das Verfahren ist „die Schreibweise normalisieren → die Schicht beurteilen → zerlegen → den Namen nachschlagen → gegen den Kontext abgleichen“. Was ein Code Ihnen sagt, ist die Art des Fehlschlags; der Ort der Ursache ist, was der Kontext Ihnen sagt.
Das nächste Mal, wenn Sie einem unvertrauten Fehlercode begegnen, schauen Sie auf die führenden wenigen Stellen, bevor Sie ihn in das Suchfeld einfügen. Die unteren 4 Stellen wenn 0x8007, NTSTATUS wenn 0xC, ins Hex umwandeln wenn negativ — diese 10-Sekunden-Zerlegung entscheidet weitgehend die Untersuchungszeit, die folgt.
Weiterführende Artikel
- Absturz-Dumps mit WinDbg + SOS lesen — Ein praxistauglicher Leitfaden zur Analyse nach der Sammlung
- Einführung in das Sammeln von Windows-Absturzabbildern - WER/ProcDump/WinDbg
- Wo sollten catch und Logging in der Ausnahmebehandlung stehen?
- Process Monitor (ProcMon) Praxisleitfaden — „Konfiguration wird nicht gelesen“ und ACCESS DENIED in 10 Minuten identifizieren
- Einführung in Windows-Ereignisprotokoll und ETW ── Protokolle von Geschäftsanwendungen in die Standardmechanismen des Betriebssystems einordnen
Zugehörige Beratungsfelder
KomuraSoft LLC übernimmt Störungsuntersuchungen, die von einem Fehlercode ausgehen — „ich weiß nicht, was dieser Fehlercode bedeutet“, „0x80070005 erscheint nur in einer bestimmten Umgebung“ —, den Entwurf der Fehlerbehandlung für Apps, die Win32 API, COM und .NET mischen, und das Identifizieren von Ursachen mit Absturzabbildern und Process Monitor. Eine Beratung von einem einzelnen Screenshot eines Fehlerdialogs ist in Ordnung.
- Windows-Anwendungsentwicklung
- Fehleruntersuchung und Ursachenanalyse
- Technische Beratung und Design-Review
- Kontakt
Quellen
-
Microsoft Learn, Debug system error codes. Ein Index zur Liste der Win32-Systemfehlercodes (0–15999); das Holen der Nachricht für einen Code, den
GetLastErrorzurückgibt, mit FormatMessage und dem Flag FORMAT_MESSAGE_FROM_SYSTEM; dass WinINet/WinHTTP-Fehler (die 12000er) in diesem Raum definiert sind; und Untersuchungsmethoden mit Microsoft Error Lookup Tool und dem Befehl !err. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Open Specifications, [MS-ERREF]: HRESULT. Das HRESULT-Bit-Layout (S-, R-, C-, N- und X-Bits, 11-Bit-Facility, 16-Bit-Code); dass das N-Bit einen in den HRESULT-Raum abgebildeten NTSTATUS-Wert anzeigt; und die Liste der Facility-Codes einschließlich FACILITY_WINDOWS_UPDATE (36). ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Open Specifications, [MS-ERREF]: NTSTATUS. Das NTSTATUS-Bit-Layout (2-Bit-Sev, C-Bit, N-Bit, 12-Bit-Facility, 16-Bit-Code); und dass Severity sich in vier Arten teilt: Erfolg (00), Information (01), Warnung (10) und Fehler (11). ↩ ↩2 ↩3
-
Microsoft Learn, HRESULT_FROM_WIN32 macro. Die Definition des winerror.h-Makros, das einen Win32-Systemfehlercode auf einen HRESULT-Wert abbildet. ↩ ↩2 ↩3
-
Microsoft Learn, Structure of COM Error Codes. Die Rolle des HRESULT-Severity-Bits und des Facility-Felds; die Werte von FACILITY_NULL, FACILITY_RPC, FACILITY_ITF, FACILITY_WIN32 und FACILITY_WINDOWS; und dass ein FACILITY_ITF-Code seine Bedeutung pro Schnittstelle definiert hat und derselbe Wert etwas anderes bedeuten kann. ↩ ↩2 ↩3
-
Microsoft Learn, Common HRESULT values. Dass E_FAIL (0x80004005) „Unspecified failure“ ist; und Definitionen häufig gesehener HRESULT-Werte wie E_ACCESSDENIED (0x80070005), E_INVALIDARG (0x80070057) und E_OUTOFMEMORY (0x8007000E). ↩ ↩2 ↩3 ↩4
-
Microsoft Open Specifications, [MS-ERREF]: NTSTATUS values. Die Liste der NTSTATUS-Werte einschließlich STATUS_ACCESS_VIOLATION (0xC0000005), STATUS_DLL_NOT_FOUND (0xC0000135), STATUS_STACK_OVERFLOW (0xC00000FD), STATUS_HEAP_CORRUPTION (0xC0000374) und STATUS_BREAKPOINT (0x80000003). ↩ ↩2 ↩3
-
Microsoft Learn, The Microsoft Error Lookup Tool. Dass es ein eigenständiges Werkzeug ist, das den Nachrichtentext anzeigt, der einem hexadezimalen Statuscode zugeordnet ist, über verschiedene Header-Dateien wie Winerror.h; dass der Download-Dateiname Err_6.4.5.exe ist; und dass Sie beachten müssen, dass die gebündelten Definitionen zum Kompilierzeitpunkt gelten. ↩ ↩2 ↩3
-
Microsoft Learn, certutil. Dass die Option -error von certutil den Nachrichtentext anzeigt, der einem Fehlercode zugeordnet ist, und dass eine Fehlerschreibweise, die einen Symbolnamen enthält, in einer Form wie 0x80070002 (WIN32: 2 ERROR_FILE_NOT_FOUND) verwendet wird. ↩ ↩2
-
Microsoft Learn, !error. Dass die !error-Erweiterung von WinDbg Win32-, Winsock-, NTSTATUS- und NetAPI-Fehlerwerte dekodiert und anzeigt; und dass das Angeben von 1 als Flag als NTSTATUS interpretiert. ↩ ↩2
-
Microsoft Learn, How to: Map HRESULTs and exceptions. Der gegenseitige Abbildungsmechanismus zwischen COM-HRESULT und .NET-Ausnahmen; die Entsprechungstabelle wie E_NOTIMPL → NotImplementedException; dass ein HRESULT ohne explizite Abbildung in COMException umgewandelt wird; und dass Ausnahme-Message, Source und Ähnliches aus IErrorInfo-Informationen initialisiert werden. ↩ ↩2
-
Microsoft Learn, RtlNtStatusToDosError function (winternl.h). Dass es eine Funktion ist, die einen NTSTATUS-Code in den entsprechenden Win32-Systemfehlercode umwandelt; dass ERROR_MR_MID_NOT_FOUND zurückgegeben wird, wenn keine Entsprechung definiert ist; und dass eine Funktion, die die Umkehrung durchführt, nicht existiert. ↩ ↩2
-
Microsoft Learn, Last-Error Code. Dass der Last-Error-Code pro Thread gehalten wird; dass er mit GetLastError unmittelbar nach dem Fehlschlag geholt werden sollte; dass APIs, die den Code bei Erfolg mit 0 überschreiben, und APIs, die ihn nicht anrühren, gemischt sind; und dass Bit 29 für anwendungsdefinierte Codes reserviert ist. ↩ ↩2
-
Microsoft Learn, Bug check code reference. Die Liste der Bug-Check-Codes (STOP-Codes), die auf einem Bluescreen angezeigt werden, und wie Informationen über einen Code mit der !analyze-Erweiterung von WinDbg anzuzeigen sind. Dass es ein eigenes Nummerierungssystem ist, getrennt von NTSTATUS, lässt sich aus der Liste bestätigen. ↩ ↩2
-
Microsoft Learn, Marshal.GetLastWin32Error Method. Dass es ein Weg ist, den Last-Error-Code eines P/Invoke-Aufrufs zu holen, der das Flag SetLastError gesetzt hat; dass GetLastError direkt zu P/Invoken nicht vertrauenswürdig ist wegen Überschreibung durch einen API-Aufruf innerhalb der Laufzeit; und dass ab .NET 6 GetLastPInvokeError empfohlen wird. ↩
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
Was bedeutet Windows' „Speicherauslastung“ eigentlich? — Working Set, Private Bytes, Commit und die Auslagerungsdatei richtig lesen
Der Speicherwert im Task-Manager, Working Set, Private Bytes und Commit sind nicht dasselbe. Dieser Artikel erklärt das Verhältnis von vi...
Die Win32-Thread-Pool-API — Nebenläufigkeit ohne eigene Threads, über CreateThreadpoolWork
Verstreuen Sie CreateThread-Aufrufe über Ihren nativen Code? Dieser Artikel erklärt die in Vista neu entworfene Win32-Thread-Pool-API — d...
Named Pipes in der Praxis — Windows' Standard-IPC von Entwurf bis Sicherheit
Ein Praxisleitfaden zu Named Pipes, der Standard-Prozesskommunikation unter Windows. Der Artikel ordnet anhand von Primärquellen die Wahl...
Apps, die nach dem Fortsetzen aus dem Schlaf kaputtgehen — Wie Windows-Energieereignisse funktionieren und wie Sie Geschäftsanwendungen bauen, die sie überstehen
Sie haben den Laptop aufgeklappt, und die Verbindungen der Geschäftsanwendung waren tot — die Ursache ist ein Entwurf, der Schlaf nie ein...
DllMain und die Ladersperre — Der wahre Grund, warum man Ihnen sagt, „in der DLL-Initialisierung nichts zu tun“
Warum Sie aus DllMain weder LoadLibrary aufrufen noch mit anderen Threads synchronisieren dürfen. Anhand von Primärquellen erklärt dieser...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Fehleranalyse und Langzeitprobleme
Sporadische Fehler, Kommunikationsdiagnose, Langzeitabstürze und Tests von Fehlerpfaden.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
Geschäftsanwendungen, Geräteintegration und Kommunikationstools von den Anforderungen bis zur Umsetzung.
Fehleruntersuchung und Ursachenanalyse
Wir untersuchen schwer reproduzierbare Fehler, Langzeitprobleme, Lecks und Kommunikationsabbrüche.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Was bedeutet der Fehler 0x80004005?
- 0x80004005 ist der HRESULT E_FAIL und bedeutet "Unspecified failure" (nicht näher bezeichnetes Fehlschlagen). Er zeigt nur an, dass "ein Fehlschlagen aufgetreten ist, das keinen detaillierten Grund berichten kann"; er ist kein Code, der die Ursache selbst darstellt. Derselbe 0x80004005 erscheint deshalb an unzusammenhängenden Stellen — Netzwerk, Windows Update, VBA, ein Datenbanktreiber. Wenn Sie diesen Code sehen, graben Sie nicht in der Bedeutung des Codes; engen Sie die Ursache anhand des Kontexts ein, welche App und welcher Vorgang ihn erzeugt hat, und anhand anderer Fehlerinformationen im Ereignisprotokoll oder in einem detaillierten Protokoll.
- Was ist ein negativer Fehlercode wie -2147467259?
- Es ist ein 32-Bit-HRESULT, angezeigt als vorzeichenbehaftete Dezimalzahl. Ein HRESULT setzt bei Fehlschlag das höchstwertige Bit, daher ist er als vorzeichenbehaftete Ganzzahl immer negativ. In PowerShell wandelt '0x{0:X8}' -f -2147467259 ihn zurück ins Hexadezimal (in diesem Beispiel 0x80004005 = E_FAIL). Wenn Sie in einem Protokoll oder einer Skriptfehlermeldung eine negative Zahl sehen, die mit -214… beginnt, ist der Standarderstschritt, sie ins Hexadezimal umzuwandeln und dann nachzuschlagen.
- Was ist der einfachste Weg, die Bedeutung eines Fehlercodes nachzuschlagen?
- Ohne Extra-Installation können Sie an der Eingabeaufforderung net helpmsg 5 (für einen Win32-Fehler in Dezimal) und certutil -error 0x80070005 verwenden. certutil akzeptiert auch ein hexadezimales HRESULT und zeigt den Symbolnamen und den Nachrichtentext. In PowerShell holt [System.ComponentModel.Win32Exception]::new(5).Message die lokalisierte Nachricht. Auf einer Entwicklungsmaschine behalten Sie Microsofts offizielles Nachschlagewerkzeug err.exe (Microsoft Error Lookup Tool); es kann über Win32, HRESULT und NTSTATUS suchen und passende Definitionen auf einmal auflisten.
- Was für ein Fehler ist 0xC0000005?
- Es ist der NTSTATUS STATUS_ACCESS_VIOLATION, also eine Zugriffsverletzung (ein unzulässiger Speicherzugriff). Es ist der Code, den Sie beim Absturz einer Anwendung am häufigsten als "Exception code" im Ereignisprotokoll oder in einem Absturzabbild sehen, und er zeigt einen Programmfehler an, etwa die Dereferenzierung eines ungültigen Zeigers oder den Zugriff auf bereits freigegebenen Speicher. Er ähnelt namentlich dem Win32-Fehler 5 (ERROR_ACCESS_DENIED = Zugriff verweigert), ist aber ein unzusammenhängender Code eines anderen Systems; verwechseln Sie sie nicht. Der zuverlässige Weg, die Ursache zu identifizieren, ist, ein Absturzabbild zu erfassen und es in WinDbg zu analysieren.
- Warum ist die Ursache jedes Mal anders, selbst beim gleichen Fehlercode?
- Weil ein Fehlercode nur "welche Art von Fehlschlag" darstellt, und "was fehlgeschlagen ist und warum" vom Aufrufkontext entschieden wird. Fehler 5 (Zugriff verweigert) ist zum Beispiel derselbe Code für völlig verschiedene Ursachen — unzureichende NTFS-Berechtigungen, fehlende Administratorrechte, eine Antivirus-Sperre und so weiter. In einer ähnlichen Lage, wenn ein anderer Prozess die Datei noch offen hat, erhalten Sie einen anderen Code (Fehler 32 = Freigabeverletzung), und den Code richtig zu lesen ändert, wo Sie suchen. Fehler 2 (Datei nicht gefunden) ist oft auch nicht die Hauptdatei, sondern eine abhängige DLL oder eine Einstellungsdatei. Sobald Sie die Bedeutung des Codes nachgeschlagen haben, mit Process Monitor oder ähnlichem zu bestätigen, welche API gegen welche Ressource fehlgeschlagen ist, ist der kurze Weg zur Ursache.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.