קריאת קודי שגיאה של Windows — מבנה שלוש השכבות של Win32, HRESULT ו-NTSTATUS
· Go Komura · Windows, קודי שגיאה, HRESULT, NTSTATUS, Win32 API, פתרון תקלות, דיבוג, פיתוח Windows
“על מסך האפליקציה הופיעה השגיאה 0x80004005. מה זה אומר?” — בייעוצי חקירת תקלות, שאלה כזו היא קלאסיקה. מי שהדביק את המספר מתיבת דו-שיח של שגיאה ישר במנוע חיפוש וקיבל הצפה של מאמרים לא קשורים — כשל Windows Update, תיקייה משותפת שלא מתחברת, שגיאת זמן ריצה של VBA, כשל חיבור למסד נתונים — והתבלבל עוד יותר, יש לו חברה רבה.
זה קורה כי 0x80004005 (E_FAIL) הוא קוד גנרי שמשמעותו היחידה היא “כשל לא מוגדר”. אותו קוד משמש באין-ספור מצבים, ולכן חיפוש לפי הקוד בלבד לא מגיע לסיבה. לעומת זאת, קוד כמו 0x80070005, אם מכירים את המבנה, אפשר לפרק בשניות לפני החיפוש ל”מספר שגיאת Win32 5 = הגישה נדחתה, עטוף כ-HRESULT”.
קודי שגיאה של Windows, מסיבות היסטוריות, יוצרים שלוש שכבות — קודי שגיאת Win32, HRESULT ו-NTSTATUS — והם מומרים בין השכבות. ברגע שהמבנה הזה בראש, אפשר לשפוט בעצמכם “איזו שכבה, איזה צד החזיר את הקוד” ו”מה הקוד המהותי”, ומהלך הפתיחה של חקירה נעשה מהיר הרבה יותר.
מיועד לאנשי IT בעסקים קטנים ובינוניים ולמפתחי אפליקציות Windows, המאמר מסדר איך להבחין ולפרק את שלוש מערכות קודי השגיאה, את הקשר לחריגות .NET, וחיפוש מעשי עם err.exe ו-PowerShell — על בסיס Microsoft Learn והמפרט שפורסם [MS-ERREF] נכון לאוגוסט 2026.
1. השורה התחתונה קודם
- קודי שגיאה של Windows הם בעיקר שלוש מערכות. קודי שגיאת Win32 (העשרוני הקטן ש-
GetLastErrorמחזיר), HRESULT (הקוד בן 32 הסיביות מאז COM, הקס שמתחיל ב-0x8 או עשרוני שלילי), ו-NTSTATUS (קודי שכבת הליבה; שגיאות מתחילות ב-0xC).123 - עשרוני והקסדצימלי הם סימונים שונים לאותו קוד. “שגיאה 5”, “0x5” ו”16 הסיביות הנמוכות של 0x80070005” כולם מציינים ERROR_ACCESS_DENIED (הגישה נדחתה).1
- 0x8007xxxx הוא “שגיאת Win32 עטופה”. זה קוד שגיאת Win32 שמאוחסן ב-HRESULT FACILITY_WIN32 (7); המירו את 16 הסיביות הנמוכות לעשרוני וקיבלתם את הקוד המהותי. זו התבנית החשובה ביותר בקריאת קודי שגיאה.45
- 0x80004005 (E_FAIL) אינו קוד סיבה. הוא אומר “Unspecified failure” ואינו נושא מידע נוסף. במקום לחפור בקוד הזה, חפשו את הקשר המקור ואת היומנים הנלווים.6
- עשרוני שלילי (-2147467259 וכדומה) הוא HRESULT. הסיבית הכי משמעותית של 32 הסיביות (סיבית הכשל) מוצבת, ולכן תצוגה מסומנת שלילית. המירו להקס ואז קראו.2
- ערך בן 8 ספרות שמתחיל ב-0xC הוא NTSTATUS. 0xC0000005 (הפרת גישה) ו-0xC0000135 (DLL לא נמצאה) מופיעים כל הזמן ביומן האירועים ובדאמפים ברגע קריסה. אין להם קשר למספר שגיאת Win32 5.7
- אותו קוד משנה משמעות עם ההקשר. סיבת שגיאה 5 נעה בין ACL, העלאת הרשאות, אנטי-וירוס, קובץ מוחזק ועוד, ו”הקובץ לא נמצא” של שגיאה 2 לעיתים קרובות DLL תלויה. תמיד קראו את משמעות הקוד יחד עם איזה API נכשל מול מה.1
- כלי המרה וחיפוש הם סטנדרט.
certutil -errorו-net helpmsgמובנים ב-Windows;Win32Exceptionשל PowerShell מביא את ההודעה; במכונת פיתוח err.exe (Microsoft Error Lookup Tool); בניתוח דאמפ!errorשל WinDbg.8910 - ב-.NET, HRESULT ממופה לסוג חריגה. HRESULT מוכר הולך לסוג המתאים (E_ACCESSDENIED → UnauthorizedAccessException וכדומה); לא מוכר הופך ל-COMException; הערך המקורי נשאר ב-
Exception.HResult.11
במשפט אחד, תבנית חקירת קוד שגיאה של Windows היא “יישור הסימון להקס → שיפוט באיזו שכבה הקוד → פירוק והוצאת הקוד המהותי → קריאה יחד עם ההקשר”.
2. ל-Windows יש שלוש מערכות קודי שגיאה
קודם, המפה הכוללת. קודי שגיאה של Windows מתפצלים בעיקר לשלוש המערכות הבאות, לפי השכבה שמחזירה אותם.
| מערכת | הצד העיקרי שמחזיר | מראה טיפוסי | דוגמה מייצגת |
|---|---|---|---|
| קוד שגיאת Win32 | API של Win32 (GetLastError), קוד יציאה של פקודה |
עשרוני קטן (0–15999) | 5 = ERROR_ACCESS_DENIED |
| HRESULT | רכיב COM, המעטפת, מתקין, מסגרות רבות | הקס בן 8 ספרות שמתחיל ב-0x8, או עשרוני שלילי | 0x80004005 = E_FAIL |
| NTSTATUS | הליבה, מנהל התקן, API מקורי (ntdll) | שגיאות הן הקס בן 8 ספרות שמתחיל ב-0xC | 0xC0000005 = STATUS_ACCESS_VIOLATION |
היסטורית הן נערמו בסדר הזה: קודי שגיאת Win32 שירשו מספרי שגיאה של MS-DOS, NTSTATUS שהליבה NT משתמשת בו פנימית, ו-HRESULT שתוכנן בהכנסת COM כדי “לארוז הצלחה/כשל ומקור ב-32 סיביות”. ב-Windows הנוכחי זרימת המרה היא יומיומית: הליבה מחזירה NTSTATUS, תת-מערכת Win32 ממירה אותו לקוד שגיאת Win32, ושכבת COM עוטפת אותו הלאה כ-HRESULT.124
flowchart TB
accTitle: זרימת המרה בין שלוש המערכות
accDescr: תת-מערכת Win32 ממירה NTSTATUS שהליבה החזירה לקוד שגיאת Win32, ושכבת COM עוטפת אותו הלאה כ-HRESULT
kernel["ליבה ומנהלי התקן"] --> nt["NTSTATUS(שגיאות 0xC…)"]
nt -->|תת-מערכת Win32 ממירה| win["קוד שגיאת Win32(5 וכדומה)"]
win -->|שכבת COM עוטפת| hr["HRESULT(0x8007xxxx)"]
איור 1: זרימת ההמרה בין שכבות. NTSTATUS של הליבה הופך לשגיאת Win32, ואז נעטף הלאה כ-HRESULT.
2.1. התרגלו לקרוא עשרוני והקסדצימלי לסירוגין
לפני שמבחינים בין שלוש המערכות צריך לספוג את תנודת הסימון. אותו קוד מוצג כעשרוני או כהקס לפי המצב.
- “שגיאה 5”, “קוד שגיאה: 0x5” → אותו ERROR_ACCESS_DENIED
- “שגיאה 1223”, “0x4C1” → אותו ERROR_CANCELLED
- “0x80070005”, “-2147024891” → אותו HRESULT
ב-PowerShell ההמרה היא שורה אחת.
# Decimal → hex
'0x{0:X8}' -f 1223 # 0x000004C1
'0x{0:X8}' -f -2147024891 # 0x80070005 (negative = HRESULT to hex)
# Hex → decimal
0x4C1 # 1223
כשרואים עשרוני שלילי שמתחיל ב-“-214…”, המירו בהחזר להקס. זה לבדו חותך הרבה תעייה בכניסה לחקירה.
flowchart TB
accTitle: שלושה מראים של אותו קוד
accDescr: שגיאה עשרונית 5, הקס 0x5 ו-16 הסיביות הנמוכות של 0x80070005 כולם מציינים את אותו ERROR_ACCESS_DENIED
d["סימון עשרוני: שגיאה 5"] --> same["ERROR_ACCESS_DENIED"]
h["סימון הקס: 0x5"] --> same
l["16 הסיביות הנמוכות של 0x80070005"] --> same
same -.-> memo["סימון שונה, אותו קוד"]
איור 2: עשרוני, הקס ו-16 הסיביות הנמוכות של HRESULT הם רק סימונים שונים לאותו קוד.
3. קודי שגיאת Win32 — GetLastError ו-FORMAT_MESSAGE
3.1. התנהגות בסיסית של GetLastError
API רבים של Win32 כמו CreateFile ו-RegOpenKeyEx מציינים כשל בערך החזרה (FALSE, NULL, INVALID_HANDLE_VALUE וכדומה), ומאחסנים את קוד השגיאה המפורט ב-“last-error code” שמוחזק לפי שרשור. הקורא שולף אותו ב-GetLastError מיד אחרי אישור הכשל.13
שתי אזהרות מעשיות.13
- קראו מיד אחרי הכשל. אם משחילים קריאת API אחרת (פונקציית יומן, למשל) באמצע, הקריאה הזו יכולה לדרוס את ה-last-error code.
- אל תסמכו על הערך בהצלחה. חלק מה-API מאפסים את ה-last-error code ל-0 בהצלחה; חלק לא נוגעים. הכלל הוא לאשר כשל מערך החזרה ואז לקרוא.
sequenceDiagram
accTitle: לקרוא GetLastError מיד אחרי כשל
accDescr: אחרי אישור כשל מערך החזרה, לשלוף את ה-last-error code ב-GetLastError מיד, בלי להשחיל קריאת API אחרת
participant app as App
participant api as Win32 API
app->>api: קריאת CreateFile
api-->>app: ערך החזרה של כשל
app->>api: GetLastError
api-->>app: קוד 5
Note over app: השחלת API אחר באמצע יכולה לדרוס
איור 3: קראו את ה-last-error code מיד אחרי כשל. השחלת קריאת API אחרת באמצע יכולה לדרוס אותו.
כדי לקבל מחרוזת הודעה מקוד, השתמשו ב-FormatMessage עם הדגל 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);
}
השארת גם עשרוני וגם הקס וגם טקסט ההודעה ביומן האפליקציה שלכם, כמו כאן, מאיצה חקירה מאוחרת בצעד אחד.
flowchart TB
accTitle: לחפש הודעה מקוד ולהשאיר ביומן
accDescr: להעביר את הדגל FORMAT_MESSAGE_FROM_SYSTEM ל-FormatMessage כדי לקבל את מחרוזת ההודעה של קוד השגיאה, ולהשאיר עשרוני, הקס וטקסט ביומן
code["קוד שגיאה(דוגמה: 5)"] --> fm["קבלת המחרוזת ב-FormatMessage"]
fm --> msg["טקסט ההודעה"]
msg --> log["רישום ביומן"]
log -.-> both["לכתוב עשרוני, הקס וטקסט יחד"]
איור 4: המירו קוד שגיאה למחרוזת הודעה ב-FormatMessage, והשאירו עשרוני, הקס וטקסט יחד ביומן.
3.2. קודים מייצגים שעולים כל הזמן בשטח
קודי שגיאת Win32 מוגדרים בטווח 0–15999, ול-Microsoft Learn יש רשימה מלאה.1 ביניהם, הפנים שפוגשים שוב ושוב בחקירת תקלות הם אלה.
| עשרוני | הקס | סמל | משמעות |
|---|---|---|---|
| 2 | 0x2 | ERROR_FILE_NOT_FOUND | הקובץ שצוין לא נמצא |
| 3 | 0x3 | ERROR_PATH_NOT_FOUND | הנתיב שצוין לא נמצא |
| 5 | 0x5 | ERROR_ACCESS_DENIED | הגישה נדחתה |
| 32 | 0x20 | ERROR_SHARING_VIOLATION | תהליך אחר משתמש, הגישה אינה אפשרית |
| 87 | 0x57 | ERROR_INVALID_PARAMETER | הפרמטר שגוי |
| 122 | 0x7A | ERROR_INSUFFICIENT_BUFFER | החוצץ שהועבר קטן מדי |
| 998 | 0x3E6 | ERROR_NOACCESS | גישה לא תקפה למיקום זיכרון |
| 1223 | 0x4C1 | ERROR_CANCELLED | הפעולה בוטלה בידי המשתמש |
מתוכם, 998 (ERROR_NOACCESS) אינו “הגישה נדחתה” אלא הביטוי של Win32 להפרת גישה לזיכרון, הצורה של NTSTATUS STATUS_ACCESS_VIOLATION שיידון בהמשך אחרי המרה לשכבת Win32. הזהרו מבלבול עם המספר 5. גם 1223 (ERROR_CANCELLED) הוא קוד שמופיע כשהמשתמש בוחר “לא” בתיבת העלאת הרשאות UAC, למשל — יותר “בוטל” מאשר שגיאה.
flowchart TB
accTitle: שגיאות 998 ו-5 הן דברים שונים
accDescr: 998 היא הפרת גישה לזיכרון, הפרת גישה של NTSTATUS שהומרה לשכבת Win32, ושונה במשמעות מ-5 שמייצגת הגישה נדחתה
nt["NTSTATUS 0xC0000005"] -->|הומר לשכבת Win32| e998["שגיאה 998(ERROR_NOACCESS)"]
e998 -.-> m1["המשמעות היא הפרת גישה לזיכרון"]
e5["שגיאה 5(הגישה נדחתה)"] -.-> m2["בעיית הרשאות. דבר אחר מ-998"]
איור 5: שגיאה 998 היא הפרת גישה של NTSTATUS שהומרה לשכבת Win32, דבר אחר מדחיית-גישה 5.
3.3. אותו קוד משנה משמעות עם ההקשר
חשוב יותר משינון טבלת הקודים המייצגים הוא התחושה שקוד שגיאה אומר רק “סוג הכשל”.
- שגיאה 5 (הגישה נדחתה): מועמדי הסיבה רחבים — ACL NTFS חסר, כתיבה לאזור מוגן בלי הרשאות מנהל, חסימה באנטי-וירוס או AppLocker, הרשאות חשבון שירות חסרות וכן הלאה.
- שגיאה 2 (הקובץ לא נמצא): לא בהכרח הקובץ שהמשתמש ציין. DLL תלויה שה-EXE ניסה לטעון במרומז, קובץ הגדרות שנראה במקום הלא נכון בגלל הפניית רישום (32 סיביות/64 סיביות), נתיב שהרחבת משתנה הסביבה שלו נכשלה — “איזה קובץ” לא נמצא אינו נראה מהקוד.
- שגיאה 32 (הפרת שיתוף): “איזה תהליך מחזיק” היא השאלה האמיתית, אבל הקוד לא אומר זאת.
flowchart TB
accTitle: סיבת שגיאה 5 נקבעת לפי ההקשר
accDescr: גם אותה דחיית גישה יש לה כמה מועמדי סיבה כמו ACL חסר או היעדר הרשאות מנהל, וצריך לזהות איזה API נכשל מול מה
e5["שגיאה 5(הגישה נדחתה)"] --> c1["ACL חסר"]
e5 --> c2["אין הרשאות מנהל"]
e5 --> c3["חסימת מוצר אבטחה"]
e5 --> c4["הרשאות שירות נמוכות"]
c1 --> next["Procmon: יעד שנכשל"]
c2 --> next
c3 --> next
c4 --> next
איור 6: קוד אומר רק “סוג הכשל”. לשגיאה 5 יש כמה מועמדי סיבה, וזיהוי היעד נדרש.
הכלי שמודד “איזה API, מול איזה שם אובייקט, החזיר איזו תוצאה” הוא Process Monitor. השימוש בו מפורט ב”מדריך מעשי ל-Process Monitor (ProcMon)”. חיפוש משמעות קוד שגיאה וזיהוי היעד שנכשל הם שני גלגלי אותה עגלה.
4. HRESULT — קריאת המבנה הארוז ב-32 סיביות
4.1. פריסת סיביות
HRESULT הוא פורמט שאוחז הצלחה/כשל, מקור וקוד פירוט בערך אחד בן 32 סיביות. המפרט שפורסם [MS-ERREF] מגדיר אותו בפריסה הבאה.2
| מיקום סיבית | שם | משמעות |
|---|---|---|
| 31 | S | Severity. 0 = הצלחה, 1 = כשל |
| 30 | R | שמור (חלק מ-severity במיפוי NTSTATUS) |
| 29 | C | סיבית Customer. 1 פירושו קוד שהוגדר בידי מי שאינו Microsoft |
| 28 | N | 1 פירושו ערך NTSTATUS שממופה למרחב HRESULT |
| 27 | X | שמור (0) |
| 26–16 | Facility | קוד facility שמציין את המקור (11 סיביות) |
| 15–0 | Code | קוד פירוט בתוך ה-facility (16 סיביות) |
סיבית S הכי משמעותית היא 1, כלומר HRESULT שסימון ההקס שלו מתחיל ב-0x8 ומעלה הוא כשל. הצגה כשלם מסומן בן 32 סיביות הופכת אותו לשלילי — זו זהות ה-“-214…” שהוזכר קודם.
flowchart TB
accTitle: הקשר בין סיבית S לתצוגה שלילית
accDescr: HRESULT של כשל מציב את סיבית S הכי משמעותית ל-1, ולכן בהקס הוא מתחיל ב-0x8 ומעלה, וכשלם מסומן בן 32 סיביות הוא שלילי
s["סיבית S = 1(כשל)"] --> hex["ההקס מתחיל ב-0x8 ומעלה"]
hex --> neg["תצוגה מסומנת שלילית"]
neg --> back["כשרואים שלילי, להמיר להקס ולקרוא"]
איור 7: HRESULT של כשל מתחיל ב-0x8 ומעלה כי סיבית S היא 1, ותצוגה מסומנת שלילית.
ערכי Facility מייצגים הם אלה.5
| Facility | ערך | מראה הקס | משמעות |
|---|---|---|---|
| FACILITY_NULL | 0 | 0x8000xxxx | קודים נפוצים רחבים (E_FAIL, E_UNEXPECTED וכדומה) |
| FACILITY_RPC | 1 | 0x8001xxxx | מקור RPC |
| FACILITY_ITF | 4 | 0x8004xxxx | שגיאה מוגדרת-ממשק (המשמעות תלויה בממשק) |
| FACILITY_WIN32 | 7 | 0x8007xxxx | קוד שגיאת Win32 עטוף |
| FACILITY_WINDOWS | 8 | 0x8008xxxx | ממשקים נוספים שהוגדרו בידי Microsoft |
4.2. פירוק 0x80004005 ו-0x80070005
נפרק אותם בפועל.
עבור 0x80004005: S=1 (כשל), Facility=(0x80004005 » 16) & 0x7FF = 0 (FACILITY_NULL), Code=0x4005. קוד FACILITY_NULL גנרי, מוגדר כ-E_FAIL “Unspecified failure”.6 כלומר הקוד הזה נושא רק את המשמעות “כשל שאינו יכול לדווח פירוט”. כשרואים 0x80004005, הפסיקו לחפור בקוד עצמו שם, והעבירו את משקל החקירה ל”איזה רכיב החזיר” ו”יש פירוט ביומן האירועים או ביומן האפליקציה באותו זמן”.
עבור 0x80070005: S=1, Facility=7 (FACILITY_WIN32), Code=0x0005=5. רואים שזה מספר שגיאת Win32 5 (ERROR_ACCESS_DENIED) עטוף כ-HRESULT. הכינוי E_ACCESSDENIED הוא במהותו ערך זה.6
גם לאותה “הגישה נדחתה”, 0x80070005 הוא עטיפה של כשל קונקרטי שאירע בשכבת Win32, וכמות המידע שונה לגמרי מ-0x80004005.
flowchart TB
accTitle: פירוק 0x80004005 ו-0x80070005
accDescr: 0x80004005 הוא קוד FACILITY_NULL הגנרי E_FAIL, בלי פירוט, ויש לעבור לחקירת הקשר; 0x80070005 הוא FACILITY_WIN32 וניתן לקריאה כעטיפה של מספר שגיאת Win32 5, הגישה נדחתה
a["0x80004005"] --> af["Facility=0(FACILITY_NULL)"]
af --> ac["Code=0x4005 → E_FAIL"]
ac --> ax["כשל לא מוגדר. הלאה לחקירת הקשר"]
b["0x80070005"] --> bf["Facility=7(FACILITY_WIN32)"]
bf --> bc["Code=0x0005 → 5"]
bc --> bx["ERROR_ACCESS_DENIED"]
איור 8: גם אותו “כשל” יש לו כמות מידע שונה אחרי פירוק. 0x80070005 אפשר ללכת איתו למספר שגיאת Win32 5.
4.3. התבנית החשובה ביותר: 0x8007xxxx = HRESULT_FROM_WIN32
כדי להעביר כשל משכבה תחתונה שיכולה להחזיר רק קוד שגיאת Win32 לשכבה עליונה שמחזירה HRESULT (שיטת COM או זמן הריצה של .NET), winerror.h מספק את המאקרו HRESULT_FROM_WIN32.4 ההתנהגות היא “לאחסן את קוד שגיאת Win32 ב-16 הסיביות הנמוכות, להציב Facility ל-FACILITY_WIN32 (7), ולהציב את סיבית S ל-1”.
flowchart TB
accTitle: איך HRESULT_FROM_WIN32 עובד
accDescr: לאחסן את קוד שגיאת Win32 ב-16 הסיביות הנמוכות, להציב Facility ל-7 ואת סיבית S ל-1, ולהרכיב HRESULT 0x8007xxxx
win["קוד שגיאת Win32(דוגמה: 5)"] --> low["אחסון ב-16 הסיביות הנמוכות"]
low --> fac["הצבת Facility ל-7"]
fac --> sbit["הצבת סיבית S ל-1"]
sbit --> hr["0x80070005"]
איור 9: HRESULT_FROM_WIN32 מאחסן את שגיאת Win32 ב-16 הסיביות הנמוכות ומציב Facility=7 ואת סיבית 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)
כדי לקרוא לכיוון השני, קחו את 16 הסיביות הנמוכות ב-PowerShell.
0x80070005 -band 0xFFFF # 5 → ERROR_ACCESS_DENIED
0x80072EE7 -band 0xFFFF # 12007 → ERROR_INTERNET_NAME_NOT_RESOLVED (WinINet)
כמו בדוגמה השנייה, שגיאות WinINet ו-WinHTTP (טווח הקודים 12000) מוגדרות גם הן במרחב קודי שגיאת Win321, ולכן 0x8007xxxx רשתי אפשר לפרק באותו הליך. להכניס לזיכרון השריר “כשרואים 0x8007, המירו את 4 הספרות הנמוכות לעשרוני” הוא הכישור המעשי מספר אחת שהמאמר רוצה שתיקחו.
יש אזהרה הפוכה ל-0x8004xxxx (FACILITY_ITF). לקוד FACILITY_ITF יש צד שמגדיר את המשמעות שונה לפי ממשק, ולכן אותו ערך בן 32 סיביות יכול לומר משהו אחר אם הצד שהחזיר שונה.5 ל-0x8004xxxx לא מוכר חפשו לא בחיפוש גנרי אלא בתיעוד הרכיב שהחזיר (ספרייה, SDK של מנהל התקן, מוצר שרת).
flowchart TB
accTitle: איך מחפשים משתנה בין 0x8007 ל-0x8004
accDescr: FACILITY_WIN32 0x8007xxxx נקרא בפירוק מכני של 16 הסיביות הנמוכות, אבל FACILITY_ITF 0x8004xxxx יש לו צד שמגדיר משמעות שונה לפי ממשק, לכן חפשו בחומרי הרכיב המחזיר
hr{"Facility הוא?"} -->|7, WIN32| w["המרת 16 הסיביות הנמוכות לעשרוני"]
hr -->|4, ITF| i["המשמעות שונה לפי הצד שהחזיר"]
w --> ww["לקרוא כשגיאת Win32"]
i --> ii["לחפש בחומרי הצד שהחזיר"]
איור 10: 0x8007xxxx אפשר לפרק מכנית; 0x8004xxxx מחפשים בחומרי הרכיב המחזיר.
5. NTSTATUS — קודי שכבת הליבה ועולם הקריסות
5.1. פריסה ו-Severity
NTSTATUS הוא קוד בן 32 סיביות שהליבה, מנהלי התקן וממשקי ntdll מקוריים משתמשים בו, ופריסתו דומה ל-HRESULT בלי להיות אותה.3
| מיקום סיבית | שם | משמעות |
|---|---|---|
| 31–30 | Sev | Severity. 00 = הצלחה, 01 = מידע, 10 = אזהרה, 11 = שגיאה |
| 29 | C | סיבית Customer |
| 28 | N | שמור (0, כדי שמיפוי ל-HRESULT יהיה אפשרי) |
| 27–16 | Facility | Facility (12 סיביות) |
| 15–0 | Code | קוד פירוט |
כי severity הוא 2 סיביות, אפשר לקרוא את הסוג מהספרה ההקסדצימלית המובילה. 0xC… הוא שגיאה (11), 0x8… אזהרה (10), 0x4… מידע (01), 0x0–0x3… הצלחה. חריגת נקודת עצירה 0x80000003 (STATUS_BREAKPOINT) היא דוגמה מייצגת ל”אזהרה, לא שגיאה”.37
flowchart TB
accTitle: NTSTATUS נקרא לפי סוג מהספרה המובילה
accDescr: כי severity הוא 2 סיביות, NTSTATUS נקרא כשגיאה אם הספרה ההקסדצימלית המובילה היא 0xC, אזהרה אם 0x8, מידע אם 0x4, והצלחה אם 0x0 עד 0x3
head{"הספרה ההקסדצימלית המובילה?"} -->|0xC| e["שגיאה"]
head -->|0x8| w["אזהרה"]
head -->|0x4| i["מידע"]
head -->|0x0–0x3| s["הצלחה"]
w -.-> ex["דוגמה: 0x80000003 היא אזהרה"]
איור 11: NTSTATUS נקרא לפי סוג מהספרה ההקסדצימלית המובילה. 0x80000003 הוא “אזהרה, לא שגיאה”.
5.2. איפה פוגשים אותו — קודי חריגה, קודי STOP ויומן האירועים
המצבים שבהם אנשי IT ומפתחים פוגשים NTSTATUS קשורים בעיקר לקריסות.
- קוד חריגת קריסת אפליקציה: “Exception code: 0xc0000005” שנרשם ביומן האירועים תחת “Application Error (event ID 1000)” הוא NTSTATUS. ערכים מייצגים הם אלה.7
| ערך | סמל | משמעות |
|---|---|---|
| 0xC0000005 | STATUS_ACCESS_VIOLATION | הפרת גישה (גישה לא חוקית לזיכרון) |
| 0xC0000135 | STATUS_DLL_NOT_FOUND | DLL נדרשת לא נמצאה ואי אפשר להתחיל |
| 0xC00000FD | STATUS_STACK_OVERFLOW | גלישת מחסנית |
| 0xC0000374 | STATUS_HEAP_CORRUPTION | השחתת ערימה |
- קוד STOP של מסך כחול: נראים דומים במבט ראשון, אבל קוד STOP (קוד bug check) הוא מערכת מספור משלו, נפרדת מ-NTSTATUS, כמו 0x0000009F (DRIVER_POWER_STATE_FAILURE), ויש לו מקור ייעודי.14 לזכור רק את ההבחנה “0xC0000005 הוא NTSTATUS; STOP 0x9F הוא קוד bug check ואסור לחפש אותו בטבלת NTSTATUS” מספיק.
- עמודת Result של Process Monitor: NAME NOT FOUND ו-ACCESS DENIED בעמודת Result של Procmon הם שמות תצוגה של ה-NTSTATUS שהליבה החזירה (STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED). זה גם מקום שבו מרגישים את התאמת השכבות: לצפות בכשל קלט/פלט קובץ באוצר המילים של NTSTATUS ושהכשל הזה מומר לשגיאת Win32 ומגיע לאפליקציה.
flowchart TB
accTitle: הבחנה בין קוד חריגה לקוד STOP
accDescr: לקרוא קוד חריגה של יומן אירועים כ-NTSTATUS; לחפש קוד STOP של מסך כחול במקור קודי ה-bug-check הייעודי, מערכת אחרת
q{"איפה הופיע הקוד?"} -->|קוד חריגה| nt["לקרוא כ-NTSTATUS"]
q -->|קוד STOP| bc["לחפש בטבלת קודי bug-check"]
nt -.-> n1["דוגמה: 0xC0000005"]
bc -.-> b1["דוגמה: 0x0000009F"]
איור 12: קוד חריגה של יומן אירועים הוא NTSTATUS; קוד STOP של מסך כחול הוא מערכת אחרת. אל תחפשו בטבלה הלא נכונה.
חקירה מעבר לקוד החריגה, כלומר לכידה וניתוח של crash dump, מכוסה ב”מבוא לאיסוף crash dump ב-Windows” וב”קריאת crash dumps עם WinDbg + SOS”.
5.3. הקשר ל-HRESULT — סיבית N ו-RtlNtStatusToDosError
הגשר בין NTSTATUS לשתי השכבות האחרות יש לו שני נתיבים.
- מיפוי למרחב HRESULT: הצבת סיבית N של HRESULT (0x10000000) מכניסה ערך NTSTATUS למרחב HRESULT כמות שהוא (המאקרו HRESULT_FROM_NT ב-winerror.h). מיפוי 0xC0000005 הופך למשל ל-0xD0000005. כשרואים HRESULT שמתחיל ב-0xD, ההליך הנכון הוא להסיר את סיבית N ולקרוא כ-NTSTATUS.2
- המרה לקוד שגיאת Win32:
RtlNtStatusToDosErrorשל ntdll ממיר NTSTATUS לקוד שגיאת Win32 המתאים. ערך בלי התאמה מוגדרת הופך ל-ERROR_MR_MID_NOT_FOUND.12 למשל STATUS_ACCESS_VIOLATION (0xC0000005) מומר ל-ERROR_NOACCESS (998), ו-STATUS_OBJECT_NAME_NOT_FOUND (0xC0000034) ל-ERROR_FILE_NOT_FOUND (2). שימושי גם לזכור שאוצר המילים העשיר של הליבה מעוגל לפעמים בשכבת Win32 להבחנה גסה יותר.
flowchart TB
accTitle: שני גשרים מ-NTSTATUS לשכבות האחרות
accDescr: NTSTATUS מועבר לשכבות האחרות בשתי דרכים: מיפוי למרחב HRESULT בהצבת סיבית N, והמרה לקוד שגיאת Win32 בידי RtlNtStatusToDosError
nt["NTSTATUS(0xC0000005)"] -->|הצבת סיבית N| hr["HRESULT(0xD0000005)"]
nt -->|RtlNtStatusToDosError| win["שגיאת Win32 998(ERROR_NOACCESS)"]
win -.-> memo["ERROR_MR_MID_NOT_FOUND אם אין התאמה מוגדרת"]
איור 13: יש שני גשרי NTSTATUS. התחלה 0xD נקראת כ-NTSTATUS אחרי הסרת סיבית N.
6. COM ו-.NET — איך קוד שגיאה ממופה לחריגה
6.1. סגנון COM — HRESULT + IErrorInfo
שיטת COM מחזירה ביסוד HRESULT, אבל יש גבול למה שאפשר לארוז ב-32 סיביות, ולכן כהשלמה מנגנון IErrorInfo יכול להעביר מחרוזת תיאור שגיאה ומקור בנפרד. ב-C++, המחלקה _com_error הנתמכת במקמפל מטפלת ב-HRESULT וב-IErrorInfo יחד. אפליקציה שתיבת השגיאה שלה מציגה “קוד + תיאור” לעיתים קרובות נושאת את התיאור במנגנון הזה.
flowchart TB
accTitle: IErrorInfo שמשלים HRESULT
accDescr: יש גבול למה שאפשר לארוז ב-HRESULT בן 32 סיביות, לכן מחרוזת תיאור שגיאה ומקור מועברים בנפרד ב-IErrorInfo, וב-C++ המחלקה _com_error מטפלת בשניהם יחד
hr["HRESULT(רק 32 סיביות)"] --> lim["יש גבול למה שנארז"]
lim --> ei["IErrorInfo נושא את התיאור"]
ei --> ce["_com_error מטפל בהם יחד"]
ce -.-> dlg["קוד + תיאור של התיבה"]
איור 14: מחרוזת תיאור שלא נכנסת ל-HRESULT בן 32 סיביות נישאת בנפרד בידי IErrorInfo.
6.2. סגנון .NET — מ-HRESULT לסוג חריגה
כשזמן הריצה של .NET מקבל כשל HRESULT ב-COM interop, הוא ממיר אותו לחריגה. HRESULT מוכר ממופה לסוג החריגה המתאים; לא מוכר הופך ל-COMException.11
flowchart TB
accTitle: מיפוי מ-HRESULT לחריגת .NET
accDescr: HRESULT כשל שמתקבל ב-COM interop מומר לסוג החריגה המתאים אם מוכר, או ל-COMException אם לא, ובכל מקרה הערך המקורי נשמר ב-Exception.HResult
hr["HRESULT כשל"] --> known{"מיפוי מוכר?"}
known -->|Yes| typed["המרה לסוג החריגה המתאים"]
known -->|No| comex["המרה ל-COMException"]
typed --> keep["הערך המקורי נשמר ב-Exception.HResult"]
comex --> keep
איור 15: .NET ממפה HRESULT לסוג חריגה, והערך המקורי נשאר ב-Exception.HResult בכל חריגה.
| HRESULT | סוג חריגת .NET |
|---|---|
| E_ACCESSDENIED (0x80070005) | UnauthorizedAccessException |
| E_OUTOFMEMORY (0x8007000E) | OutOfMemoryException |
| E_INVALIDARG (0x80070057) | ArgumentException |
| E_NOTIMPL (0x80004001) | NotImplementedException |
| ערך בלי מיפוי מוגדר | COMException (הערך המקורי במאפיין ErrorCode) |
בכל חריגה, ה-HRESULT המקורי נשמר במאפיין Exception.HResult. הסתעפות בטיפול בחריגות קלט/פלט קבצים כמו “לנסות שוב רק בהפרת שיתוף” אפשר לכתוב בערך הזה.
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 ו-GetLastError
כשקוראים ל-API של Win32 ישירות דרך P/Invoke, ציינו SetLastError = true ב-DllImport (או LibraryImport), ואז שלפו ב-Marshal.GetLastWin32Error (מ-.NET 6, המקביל GetLastPInvokeError). להגדיר את GetLastError עצמו כ-P/Invoke ולקרוא לו אינו מדויק, כי קריאת API בתוך זמן הריצה יכולה לדרוס את הערך.15
flowchart TB
accTitle: שליפת השגיאה האחרונה ב-P/Invoke
accDescr: לציין SetLastError כ-true ולשלוף ב-Marshal.GetLastWin32Error נכון; לקרוא ל-GetLastError ישירות ב-P/Invoke אינו מדויק בגלל דריסה של זמן הריצה
pi["קריאה ל-API של Win32 דרך P/Invoke"] --> ok["ציון SetLastError=true"]
ok --> get["שליפה ב-GetLastWin32Error"]
pi --> ng["הגדרה שקוראת ל-GetLastError ישירות"]
ng --> bad["זמן הריצה דורס וזה לא מדויק"]
איור 16: ב-P/Invoke השתמשו ב-SetLastError=true וב-Marshal.GetLastWin32Error כזוג. קריאה ישירה ל-GetLastError אינה מדויקת.
[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 מחפש את מחרוזת הודעת מערכת ההפעלה מקוד שגיאת Win32, ולכן אפשר להשתמש בו כמות שהוא להשארת גם הקוד וגם ההודעה ביומן. שאלת התכנון באיזו שכבה לתפוס חריגה ואיך להשאיר אותה ביומן מכוסה ב”איפה צריכים catch ורישום להיות בטיפול בחריגות?”.
7. כלי המרה וחקירה בפועל — עזר חיפוש להעתקה-הדבקה
7.1. err.exe (Microsoft Error Lookup Tool)
כלי חיפוש שגיאות עצמאי ש-Microsoft מפיצה. הוא עובר על מספר גדול של קבצי כותרת כמו winerror.h ו-ntstatus.h ומפרט הגדרות והודעות שתואמות לקוד שצוין.8
err 0x80070005
err 5
err 0xC0000005
מספר אחד יכול לפגוע בכמה כותרות (למשל “5” תואם הגדרות במקומות שונים מלבד Win32 ERROR_ACCESS_DENIED), ולכן איזה מהמועמדים סביר צריך לבחור לפי הקשר. שם קובץ ההורדה מגרסא (Err_6.4.5.exe בעת הכתיבה), ושימו לב גם שהגדרות הקוד מבוססות על הכותרות בעת האריזה.8
flowchart TB
accTitle: לבחור תוצאות חיפוש err.exe לפי הקשר
accDescr: err.exe עובר על מספר גדול של קבצי כותרת ומפרט הגדרות תואמות, לכן כשכמה מועמדים עולים לאותו מספר, בחרו את הסביר לפי הקשר
in["הזנת err 5"] --> scan["מעבר על מספר גדול של כותרות"]
scan --> hits["כמה הגדרות פגעו"]
hits --> pick["בחירת מועמד סביר לפי הקשר"]
איור 17: err.exe הוא חיפוש חוצה-כותרות, ולכן כמה מועמדים יכולים לעלות, והסביר נבחר לפי הקשר.
7.2. פקודות מובנות ב-Windows
מה שאפשר להשתמש בו בלי התקנה נוספת הם certutil ו-net helpmsg. אפשרות -error של certutil מציגה את טקסט ההודעה שמתאים לקוד שגיאה, ומקבלת HRESULT הקסדצימלי או עשרוני.9
certutil -error 0x80070005
certutil -error 5
net helpmsg 5
net helpmsg הוא לקוד שגיאת Win32 בעשרוני בלבד, אבל בסביבה עברית ההודעה חוזרת בעברית, ולכן אפשר להשתמש בה כמות שהיא להסבר למשתמש.
flowchart TB
accTitle: איך לבחור בין הפקודות הסטנדרטיות
accDescr: קוד שגיאת Win32 עשרוני מחפשים ב-net helpmsg; קוד שכולל הקס, כולל HRESULT, מחפשים באפשרות -error של certutil
q{"הקוד שיש לכם הוא?"} -->|Win32 עשרוני| net["net helpmsg"]
q -->|כולל הקס| cert["certutil -error"]
net -.-> jp["הודעה עברית חוזרת"]
cert -.-> any["מקבל הקס ועשרוני"]
איור 18: איך לבחור בין הפקודות הסטנדרטיות. שגיאת Win32 עשרונית היא net helpmsg; אם יש הקס, certutil -error.
7.3. אוסף שורות-אחת של PowerShell
# 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. !error של WinDbg
כדי לחפש קוד במהלך ניתוח דאמפ, הרחבת !error של WinDbg מהירה. כברירת מחדל היא מפרשת כקוד שגיאת Win32; העבירו 1 כארגומנט שני והיא מפרשת כ-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>
ב-crash dump, !analyze -v מציג אוטומטית את קוד החריגה (NTSTATUS), ולכן הזרימה היא לאשר את המשמעות משם ב-!error <code> 1.
flowchart TB
accTitle: זרימת אישור קוד חריגה ב-WinDbg
accDescr: ב-crash dump פקודת analyze מציגה אוטומטית את קוד החריגה; העבירו את הקוד להרחבת error עם ארגומנט שני 1 ואשרו את המשמעות כ-NTSTATUS
dump["פתיחת ה-crash dump"] --> an["הרצת !analyze -v"]
an --> exc["קוד החריגה מוצג"]
exc --> chk["אישור המשמעות ב-!error code 1"]
איור 19: בניתוח דאמפ חפשו את קוד החריגה ש-!analyze -v הציג ב-!error עם דגל 1.
8. הליך חקירה — משיפוט השכבה להתאמה מול ההקשר
הרכיבו את הידע עד כאן להליך לחקירת קוד שגיאה בפועל.
- נרמלו את הסימון. אם זה עשרוני שלילי, המירו להקס בן 8 ספרות. מלאו אפסים להקס קצר מ-8 ספרות וקראו.
- שפטו באיזו שכבה הקוד. כמו בטבלת השיפוט למטה, הספרות המובילות כמעט מחליטות.
- פרקו והוציאו את הקוד המהותי. פעולה מכנית: 16 הסיביות הנמוכות אם 0x8007xxxx, הסרת סיבית N אם 0xDxxxxxxx.
- חפשו שם והגדרה בכלי. אשרו את שם הסמל ואת ההודעה ב-err.exe, certutil או
!error. - התאימו מול ההקשר. זהו איזו אפליקציה, איזו פעולה, איזה API, נכשל מול מה, מיומן האפליקציה, יומן האירועים ו-Procmon. הקוד הוא “סוג הכשל”; ההקשר הוא “מקום הסיבה”.
flowchart TB
accTitle: הליך חקירת קוד שגיאה
accDescr: תבנית החקירה של יישור הסימון להקס, שיפוט השכבה מהספרות המובילות, פירוק והוצאת הקוד המהותי, חיפוש שם והגדרה בכלי, ואז התאמה מול ההקשר
fix["נרמול להקס"] --> judge{"ספרות מובילות?"}
judge -->|עשרוני| d1["קריאה כשגיאת Win32"]
judge -->|0x8007| d2["16 הסיביות הנמוכות → עשרוני"]
judge -->|0xC| d3["קריאה כ-NTSTATUS"]
judge -->|0xD| d4["הסרת סיבית N וקריאה"]
d1 --> tool["חיפוש שם/הגדרה"]
d2 --> tool
d3 --> tool
d4 --> tool
tool --> ctx["התאמת הקשר(Procmon)"]
איור 20: תבנית החקירה. נרמלו את הסימון, שפטו שכבה ופרקו, חפשו שם, ואז התאימו מול ההקשר.
| מראה | מועמד ראשון | איך לפרק ולהמיר |
|---|---|---|
| עשרוני בן 1 עד 5 ספרות (5, 1223 וכדומה) | קוד שגיאת Win32 | כמות שהוא ל-net helpmsg או err.exe |
| עשרוני שלילי (-2147024891 וכדומה) | HRESULT | המרה להקס בן 8 ספרות, ואז שיפוט השורות למטה |
| 0x8007xxxx | HRESULT (FACILITY_WIN32) | המרת 16 הסיביות הנמוכות לעשרוני וקריאה כ-Win32 |
| 0x8004xxxx | HRESULT (FACILITY_ITF) | חיפוש בתיעוד הרכיב המחזיר |
| 0x8000xxxx | HRESULT (FACILITY_NULL) | קוד גנרי כמו E_FAIL. העברת המשקל לחקירת הקשר |
| 0xCxxxxxxx | NTSTATUS (שגיאה) | !error <code> 1; המרה ל-Win32 וקריאה אם צריך |
| 0xDxxxxxxx | מיפוי HRESULT של NTSTATUS | הסרת סיבית N (0x10000000) וקריאה כ-NTSTATUS |
| Facility משלו כמו 0x8024xxxx | HRESULT ייחודי לאזור יכולת | זיהוי האזור מערך Facility ומעבר לחומרים ייעודיים (0x8024… הוא Windows Update)2 |
מה שיעיל במיוחד בצעד 5 “התאמה מול ההקשר” הוא עמודת Result של Process Monitor. גם אם האפליקציה מציגה רק “0x80070002”, Procmon אומר בשורה אחת “איזה תהליך, מול איזה נתיב, קיבל NAME NOT FOUND”. לחיפוש מצד יומן האירועים ראו גם “מבוא ליומן האירועים של Windows ול-ETW”.
9. קריאות שגויות נפוצות — תבניות ששולחות חקירה בדרך הארוכה
לבסוף, תבניות קריאה שגויה שמופיעות בייעוצים אמיתיים.
קריאה שגויה 1: לחשוב ש-0x80004005 הוא “קוד שמציין סיבה ספציפית”
E_FAIL הוא “Unspecified failure”, ואותו ערך מופיע ב-Windows Update, ברשת ובמסדי נתונים. לנסות כל תיקון שעולה כשמחפשים את הקוד הזה הוא כמעט בוודאות הדרך הארוכה. צמצמו לא מהקוד אלא מ”איזו אפליקציה, איזו פעולה, יומנים אחרים באותו זמן”.6
קריאה שגויה 2: לא להבחין שעשרוני שלילי הוא HRESULT
מקרה של חיפוש יומן שאומר “Error -2147467259 occurred” כמות שהוא, או בלבול מ”שגיאה עם מינוס?”. כשרואים שלילי, המירו להקס. זה לבדו אומר שזה 0x80004005 (E_FAIL), ומתחבר לידע של קריאה שגויה 1.
קריאה שגויה 3: לחפש את כל 8 הספרות של 0x8007xxxx ולא להסתכל על שגיאת Win32 שמתחת
מהות 0x80070005 היא “5 = הגישה נדחתה”. לחשוב אחרי הוצאת 16 הסיביות הנמוכות על “מה שגיאת Win32 5 אומרת בהקשר הפעולה הזו” מגיע לליבה מהר יותר מחיפוש כל 8 הספרות.
קריאה שגויה 4: להניח “אותו קוד = אותה סיבה”
אם פעם היה “שגיאה 5 נגרמה מאנטי-וירוס”, נוטים לקפוץ לאותו תיקון בשגיאה 5 הבאה. גם באותו קוד, אם ה-API שנכשל ומשאב היעד שונים, הסיבה היא דבר אחר. אישור משמעות הקוד וזיהוי היעד ב-Procmon או דומה הם זוג, בכל פעם.
קריאה שגויה 5: לבלבל שגיאת Win32 5 עם 0xC0000005, וקוד STOP עם NTSTATUS
להתייחס ל-ERROR_ACCESS_DENIED ול-STATUS_ACCESS_VIOLATION כאותו דבר בגלל הקשר “5” שולח את החקירה לכיוונים שונים לגמרי — בעיית הרשאות מול באג תוכנית. גם קוד STOP של מסך כחול הוא מערכת אחרת מ-NTSTATUS, ולכן חיפוש 0x9F בטבלת NTSTATUS לא מניב תשובה משמעותית.14
flowchart TB
accTitle: לשגיאה 5 ול-0xC0000005 יש כיווני חקירה שונים
accDescr: שגיאת Win32 5 יש לחקור כבעיית הרשאות, ו-NTSTATUS 0xC0000005 כבאג תוכנית; להתייחס אליהם כאותו דבר שולח את החקירה לכיוון אחר
a["שגיאת Win32 5"] --> ad["חקירת בעיית הרשאות"]
b["NTSTATUS 0xC0000005"] --> bd["חקירת באג תוכנית"]
a -.-> memo["קודים לא קשורים ממערכות שונות"]
b -.-> memo
איור 21: אל תתייחסו אליהם כאותו דבר בגלל הקשר “5”. שגיאה 5 הולכת לבעיית הרשאות; 0xC0000005 לבאג תוכנית.
10. סיכום
- קודי שגיאה של Windows הם מבנה שלוש שכבות של קודי שגיאת Win32, HRESULT ו-NTSTATUS. שפטו קודם איזו שכבה, איזה צד החזיר את הקוד.
- תנודת סימון (עשרוני / הקס / שלילי) אפשר ליישר מכנית. המירו שלילי להקס בן 8 ספרות ואז קראו.
- HRESULT הוא מבנה סיביות S/R/C/N/X + Facility (11 סיביות) + Code (16 סיביות), ו-0x8007xxxx הוא התבנית החשובה ביותר, שגיאת Win32 עטופה. המירו את 16 הסיביות הנמוכות לעשרוני והוציאו את הקוד המהותי.
- קוד גנרי כמו 0x80004005 (E_FAIL) אינו מציין סיבה. השיפוט להפסיק לחפור בקוד ולעבור לחקירת הקשר אפשרי דווקא כי מכירים את המבנה.
- פוגשים NTSTATUS כקוד חריגת קריסה או בעמודת Result של Procmon. 0xC0000005 הוא הפרת גישה, לא קשור לשגיאת Win32 5. קוד STOP הוא עוד מערכת.
- ב-.NET, HRESULT ממופה לסוג חריגה, והערך המקורי נשאר ב-Exception.HResult. ב-P/Invoke השתמשו ב-SetLastError=true וב-Marshal.GetLastWin32Error כזוג.
- כלי החיפוש הם certutil -error ו-net helpmsg (סטנדרט), err.exe (מכונת פיתוח), שורות-אחת של PowerShell, ו-!error של WinDbg.
- ההליך הוא “נרמול הסימון → שיפוט השכבה → פירוק → חיפוש שם → התאמה מול ההקשר”. מה שקוד אומר הוא סוג הכשל; מקום הסיבה הוא מה שההקשר אומר.
בפעם הבאה שתפגשו קוד שגיאה לא מוכר, הסתכלו על הספרות המובילות לפני שמדביקים בתיבת החיפוש. 4 הספרות הנמוכות אם 0x8007, NTSTATUS אם 0xC, המרה להקס אם שלילי — הפירוק של 10 השניות הזה מחליט במידה רבה את זמן החקירה שאחרי.
מאמרים קשורים
- קריאת crash dumps עם WinDbg + SOS — מדריך מעשי לניתוח אחרי איסוף
- מבוא לאיסוף crash dump ב-Windows - WER, ProcDump, WinDbg
- איפה צריכים catch ורישום להיות בטיפול בחריגות?
- מדריך מעשי ל-Process Monitor (ProcMon) — לאתר «הגדרות לא הוחלו» ו-«ACCESS DENIED» ב-10 דקות
- מבוא ליומן האירועים של Windows ול-ETW — הנחת יומני היישום העסקי על מנגנוני הסטנדרט של מערכת ההפעלה
תחומי ייעוץ קשורים
KomuraSoft LLC מטפלת בחקירת תקלות שמתחילה מקוד שגיאה — “לא יודע מה הקוד הזה אומר”, “0x80070005 מופיע רק בסביבה מסוימת” —, תכנון טיפול בשגיאות לאפליקציות שמערבבות Win32 API, COM ו-.NET, וזיהוי סיבות עם crash dump ו-Process Monitor. ייעוץ מצילום מסך אחד של תיבת שגיאה מתאים.
קישורים
-
Microsoft Learn, Debug system error codes. מפתח לרשימת קודי שגיאת מערכת Win32 (0–15999); קבלת ההודעה לקוד ש-
GetLastErrorמחזיר ב-FormatMessage ובדגל FORMAT_MESSAGE_FROM_SYSTEM; ששגיאות WinINet/WinHTTP (טווח הקודים 12000) מוגדרות במרחב הזה; ושיטות חקירה עם Microsoft Error Lookup Tool ופקודת !err. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Open Specifications, [MS-ERREF]: HRESULT. פריסת סיביות HRESULT (סיביות S, R, C, N ו-X, Facility בן 11 סיביות, Code בן 16 סיביות); שסיבית N מציינת ערך NTSTATUS שממופה למרחב HRESULT; ורשימת קודי facility כולל FACILITY_WINDOWS_UPDATE (36). ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Open Specifications, [MS-ERREF]: NTSTATUS. פריסת סיביות NTSTATUS (Sev בן 2 סיביות, סיבית C, סיבית N, Facility בן 12 סיביות, Code בן 16 סיביות); וש-severity מתפצל לארבעה סוגים: הצלחה (00), מידע (01), אזהרה (10) ושגיאה (11). ↩ ↩2 ↩3
-
Microsoft Learn, HRESULT_FROM_WIN32 macro. הגדרת מאקרו winerror.h שממפה קוד שגיאת מערכת Win32 לערך HRESULT. ↩ ↩2 ↩3
-
Microsoft Learn, Structure of COM Error Codes. תפקיד סיבית ה-severity של HRESULT ושדה ה-facility; ערכי FACILITY_NULL, FACILITY_RPC, FACILITY_ITF, FACILITY_WIN32 ו-FACILITY_WINDOWS; ושקוד FACILITY_ITF משמעותו מוגדרת לפי ממשק ואותו ערך יכול לומר משהו אחר. ↩ ↩2 ↩3
-
Microsoft Learn, Common HRESULT values. ש-E_FAIL (0x80004005) הוא “Unspecified failure”; והגדרות ערכי HRESULT נפוצות כמו E_ACCESSDENIED (0x80070005), E_INVALIDARG (0x80070057) ו-E_OUTOFMEMORY (0x8007000E). ↩ ↩2 ↩3 ↩4
-
Microsoft Open Specifications, [MS-ERREF]: NTSTATUS values. רשימת ערכי NTSTATUS כולל STATUS_ACCESS_VIOLATION (0xC0000005), STATUS_DLL_NOT_FOUND (0xC0000135), STATUS_STACK_OVERFLOW (0xC00000FD), STATUS_HEAP_CORRUPTION (0xC0000374) ו-STATUS_BREAKPOINT (0x80000003). ↩ ↩2 ↩3
-
Microsoft Learn, The Microsoft Error Lookup Tool. שזה כלי עצמאי שמציג את טקסט ההודעה הקשור לקוד מצב הקסדצימלי על פני קבצי כותרת שונים כמו Winerror.h; ששם קובץ ההורדה הוא Err_6.4.5.exe; ושצריך לשים לב שההגדרות הארוזות הן נכון לזמן הקימפול. ↩ ↩2 ↩3
-
Microsoft Learn, certutil. שאפשרות -error של certutil מציגה את טקסט ההודעה הקשור לקוד שגיאה, ושסימון שגיאה שכולל שם סמל משמש בצורה כמו 0x80070002 (WIN32: 2 ERROR_FILE_NOT_FOUND). ↩ ↩2
-
Microsoft Learn, !error. שהרחבת !error של WinDbg מפענחת ומציגה ערכי שגיאה של Win32, Winsock, NTSTATUS ו-NetAPI; ושקביעת 1 כדגל מפרשת כ-NTSTATUS. ↩ ↩2
-
Microsoft Learn, How to: Map HRESULTs and exceptions. מנגנון המיפוי ההדדי בין HRESULT של COM לחריגות .NET; טבלת ההתאמה כמו E_NOTIMPL → NotImplementedException; ש-HRESULT בלי מיפוי מפורש מומר ל-COMException; וש-Message, Source וכדומה של החריגה מאותחלים ממידע IErrorInfo. ↩ ↩2
-
Microsoft Learn, RtlNtStatusToDosError function (winternl.h). שזו פונקציה שממירה קוד NTSTATUS לקוד שגיאת מערכת Win32 המתאים; ש-ERROR_MR_MID_NOT_FOUND מוחזר כשאין התאמה מוגדרת; ושפונקציה שמבצעת המרה הפוכה אינה קיימת. ↩ ↩2
-
Microsoft Learn, Last-Error Code. שה-last-error code מוחזק לפי שרשור; שיש לשלוף אותו ב-GetLastError מיד אחרי כשל; ש-API שדורסים את הקוד ב-0 בהצלחה ו-API שלא נוגעים מעורבים; ושסיבית 29 שמורה לקודים שמוגדרים בידי האפליקציה. ↩ ↩2
-
Microsoft Learn, Bug check code reference. רשימת קודי bug check (קודי STOP) שמוצגים במסך כחול, ואיך להציג מידע על קוד בהרחבת !analyze של WinDbg. שזו מערכת מספור משלה, נפרדת מ-NTSTATUS, אפשר לאשר מהרשימה. ↩ ↩2
-
Microsoft Learn, Marshal.GetLastWin32Error Method. שזו דרך לשלוף את ה-last-error code של קריאת P/Invoke שהציבה את דגל SetLastError; שקריאה ישירה ל-GetLastError ב-P/Invoke אינה אמינה בגלל דריסה בקריאת API בתוך זמן הריצה; ושמ-.NET 6 מומלץ GetLastPInvokeError. ↩
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
Win32 Thread Pool API — מקביליות בלי ליצור תהליכונים, דרך CreateThreadpoolWork
מפזרים קריאות CreateThread בכל הקוד הנייטיב? המאמר מסביר את ה-API של מאגר התהליכונים של Win32 שעוצב מחדש ב-Vista — ארבעת האובייקטים work,...
Named Pipes בפועל — ה-IPC הסטנדרטי של Windows, מתכנון ועד אבטחה
מדריך מעשי ל-Named Pipes, התקשורת הסטנדרטית בין תהליכים ב-Windows. המאמר מסדר ממקורות ראשוניים את הבחירה בין מצב בתים למצב הודעות, תכנון ...
יישומים שנשברים בחזרה משינה — איך אירועי צריכת החשמל של Windows עובדים ואיך לבנות יישומים עסקיים ששורדים אותם
פתחתם את המחשב הנייד והחיבורים של היישום העסקי היו מתים — הסיבה היא תכנון שלא חשב על שינה. המאמר מכסה את זרימת ההודעות WM_POWERBROADCAST,...
DllMain ונעילת הטוען — הסיבה האמיתית שאומרים לכם "לא לעשות כלום באתחול DLL"
למה אסור לקרוא ל-LoadLibrary או להסתנכרן עם תהליכונים אחרים מתוך DllMain. המאמר מסביר ממקורות ראשוניים איך נעילת הטוען מסדרת כל הודעת DLL...
מה "לא מגיב" באמת — איך Windows מחליט שיישום נתקע, ואיך לתכנן יישומים שלא
"לא מגיב" של Windows הוא מנגנון שבו מערכת ההפעלה קובעת שחלון לא שלף הודעה במשך 5 שניות ומחליפה אותו בחלון רפאים. המאמר מכסה את פנים השיפו...
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
פיתוח יישומי Windows
יישומים עסקיים, חיבור התקנים וכלי תקשורת, מהגדרת הדרישות ועד הפיתוח.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- מה משמעות השגיאה 0x80004005?
- 0x80004005 הוא HRESULT E_FAIL, והוא אומר "Unspecified failure" (כשל לא מוגדר). כלומר הוא מציין רק ש"אירע כשל שאינו יכול לדווח סיבה מפורטת"; זה אינו קוד שמייצג את הסיבה עצמה. אותו 0x80004005 מופיע במקומות לא קשורים — רשת, Windows Update, VBA, מנהל התקן של מסד נתונים — מסיבה זו. כשרואים את הקוד הזה, אל תחפרו במשמעות הקוד; צמצמו את הסיבה מההקשר של איזו אפליקציה ואיזו פעולה יצרו אותו, וממידע שגיאה אחר ביומן האירועים או ביומן מפורט.
- מהו קוד שגיאה שלילי כמו -2147467259?
- זה HRESULT בן 32 סיביות המוצג כעשרוני מסומן. HRESULT מציב את הסיבית הכי משמעותית בכשל, ולכן כשלם מסומן הוא תמיד שלילי. ב-PowerShell, '0x{0:X8}' -f -2147467259 ממיר אותו בחזרה להקסדצימלי (בדוגמה זו 0x80004005 = E_FAIL). כשרואים מספר שלילי שמתחיל ב--214… ביומן או בהודעת שגיאת סקריפט, הצעד הראשון הסטנדרטי הוא להמיר להקס ולחפש.
- מה הדרך הקלה ביותר לחפש את משמעות קוד שגיאה?
- בלי התקנה נוספת אפשר להשתמש ב-net helpmsg 5 בשורת הפקודה (לשגיאת Win32 בעשרוני) וב-certutil -error 0x80070005. certutil מקבל גם HRESULT הקסדצימלי ומציג את שם הסמל ואת טקסט ההודעה. ב-PowerShell, [System.ComponentModel.Win32Exception]::new(5).Message מביא את ההודעה המקומית. במכונת פיתוח שמרו את כלי החיפוש הרשמי של Microsoft, err.exe (Microsoft Error Lookup Tool); הוא מחפש ב-Win32, HRESULT ו-NTSTATUS ומפרט הגדרות תואמות בבת אחת.
- איזה סוג שגיאה הוא 0xC0000005?
- זה NTSTATUS STATUS_ACCESS_VIOLATION, כלומר הפרת גישה (גישה לא חוקית לזיכרון). זה הקוד שרואים הכי הרבה כ-"Exception code" ביומן האירועים או ב-crash dump כשאפליקציה קורסת, והוא מציין באג תוכנית כמו פירוק מצביע לא תקף או גישה לזיכרון שכבר שוחרר. השם דומה לשגיאת Win32 5 (ERROR_ACCESS_DENIED = הגישה נדחתה), אבל זה קוד לא קשור ממערכת אחרת; אל תבלבלו. הדרך האמינה לזהות את הסיבה היא ללכוד crash dump ולנתח אותו ב-WinDbg.
- למה הסיבה שונה בכל פעם, גם באותו קוד שגיאה?
- כי קוד שגיאה מייצג רק "איזה סוג כשל", ו"מה נכשל ולמה" נקבע לפי הקשר הקריאה. שגיאה 5 (הגישה נדחתה), למשל, היא אותו קוד לסיבות שונות לגמרי — הרשאות NTFS חסרות, הרשאות מנהל חסרות, חסימת אנטי-וירוס וכן הלאה. במצב דומה, אם תהליך אחר עדיין מחזיק את הקובץ פתוח מקבלים קוד אחר (שגיאה 32 = הפרת שיתוף), וקריאה נכונה של הקוד משנה לאן מחפשים. שגיאה 2 (הקובץ לא נמצא) גם לעיתים קרובות אינה הקובץ הראשי אלא DLL תלויה או קובץ הגדרות. אחרי שחיפשתם את משמעות הקוד, אישור איזה API נכשל מול איזה משאב עם Process Monitor או דומה הוא הדרך הקצרה לסיבה.