“หน้าจอแอปแสดงข้อผิดพลาด 0x80004005 หมายความว่าอะไร?” — ในคำปรึกษาสอบสวนเหตุการณ์ คำถามแบบนี้เป็นคลาสสิก ผู้ที่วางตัวเลขจากกล่องโต้ตอบข้อผิดพลาดตรงเข้าเครื่องมือค้นหาแล้วเจอคลื่นบทความที่ไม่เกี่ยวข้อง — ความล้มเหลว Windows Update, โฟลเดอร์แชร์ที่ต่อไม่ได้, ข้อผิดพลาดรันไทม์ VBA, ความล้มเหลวเชื่อมฐานข้อมูล — แล้วยิ่งงง มีเพื่อนมาก
นั่นเกิดเพราะ 0x80004005 (E_FAIL) เป็นรหัสทั่วไปที่ความหมายเดียวคือ “ความล้มเหลวที่ไม่ระบุ” รหัสเดียวกันใช้ในสถานการณ์นับไม่ถ้วน จึงค้นด้วยรหัสอย่างเดียวไม่ถึงสาเหตุ ในทางกลับกัน รหัสอย่าง 0x80070005 หากรู้โครงสร้าง สามารถแยกในไม่กี่วินาทีก่อนค้นเป็น “หมายเลขข้อผิดพลาด Win32 5 = ปฏิเสธการเข้าถึง ห่อเป็น HRESULT”
รหัสข้อผิดพลาด Windows ด้วยเหตุประวัติศาสตร์สร้างสามชั้น — รหัสข้อผิดพลาด Win32, HRESULT และ NTSTATUS — และถูกแปลงข้ามชั้น เมื่อโครงสร้างนี้อยู่ในหัว คุณตัดสินเองได้ว่า “ชั้นใด ฝ่ายใดคืนรหัสนี้” และ “รหัสที่เป็นสาระคืออะไร” และการเปิดสอบสวนเร็วขึ้นมาก
มุ่งที่พนักงานไอทีของธุรกิจขนาดเล็กและกลางกับนักพัฒนาแอป 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 57
- รหัสเดียวกันเปลี่ยนความหมายตามบริบท สาเหตุข้อผิดพลาด 5 กว้างจาก ACL การยกสิทธิ์ แอนติไวรัส ไฟล์ที่ถูกถือ และอื่น และ “ไม่พบไฟล์” ของข้อผิดพลาด 2 มักเป็น DLL ที่พึ่ง อ่านความหมายของรหัสพร้อมว่า API ใดล้มเหลวต่ออะไรเสมอ1
- เครื่องมือแปลงและค้นเป็นมาตรฐาน
certutil -errorและnet helpmsgอยู่ใน Windows;Win32Exceptionของ PowerShell ได้ข้อความ; บนเครื่องพัฒนา err.exe (Microsoft Error Lookup Tool); ในวิเคราะห์ดัมป์!errorของ WinDbg8910 - ใน .NET HRESULT ถูกแมปเป็นชนิดข้อยกเว้น HRESULT ที่รู้จักไปชนิดที่ตรง (E_ACCESSDENIED → UnauthorizedAccessException เป็นต้น) ที่ไม่รู้จักเป็น COMException ค่าต้นยังอยู่ใน
Exception.HResult11
ในประโยคเดียว รูปแบบสอบสวนรหัสข้อผิดพลาด Windows คือ “จัดสัญกรณ์ให้เป็นเฮกซ์ → ตัดสินว่ารหัสอยู่ชั้นใด → แยกแล้วดึงรหัสสาระ → อ่านพร้อมบริบท”
2. Windows มีสามระบบรหัสข้อผิดพลาด
ก่อนอื่น แผนรวม รหัสข้อผิดพลาด Windows แยกเป็นสามระบบต่อไปนี้เป็นหลัก ตามชั้นที่คืนมัน
| ระบบ | ฝ่ายหลักที่คืน | ลักษณะทั่วไป | ตัวอย่างตัวแทน |
|---|---|---|---|
| รหัสข้อผิดพลาด Win32 | Win32 API (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 ห่อต่อเป็น HRESULT124
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
Win32 API หลายตัวเช่น 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_SYSTEM1
#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_WIN324 พฤติกรรมคือ “เก็บรหัสข้อผิดพลาด 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 (หมื่นสองพัน) ก็ถูกนิยามในพื้นที่รหัสข้อผิดพลาด 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 บิตที่เคอร์เนล ไดรเวอร์อุปกรณ์ และ API เนทีฟ 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 และบันทึกเหตุการณ์
สถานการณ์ที่พนักงานไอทีและนักพัฒนาเจอ 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 (รหัสบั๊กเช็ก) เป็น ระบบเลขของตัวเอง แยกจาก NTSTATUS เช่น 0x0000009F (DRIVER_POWER_STATE_FAILURE) และมีเอกสารเฉพาะ14 จำเพียงข้อแยก “0xC0000005 คือ NTSTATUS STOP 0x9F คือรหัสบั๊กเช็กและห้ามค้นในตาราง NTSTATUS” ก็พอ
- คอลัมน์ Result ของ Process Monitor: NAME NOT FOUND และ ACCESS DENIED ในคอลัมน์ Result ของ Procmon คือชื่อแสดงของ NTSTATUS ที่เคอร์เนลคืน (STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED) เป็นที่ที่รู้สึกความสอดคล้องชั้นด้วย: สังเกตความล้มเหลว I/O ไฟล์ด้วยคำศัพท์ NTSTATUS แล้วเห็นความล้มเหลวนั้นถูกแปลงเป็นข้อผิดพลาด Win32 และถึงแอป
flowchart TB
accTitle: แยกรหัสข้อยกเว้นจากรหัส STOP
accDescr: อ่านรหัสข้อยกเว้นบันทึกเหตุการณ์เป็น NTSTATUS ค้นรหัส STOP หน้าจอฟ้าในเอกสารรหัสบั๊กเช็กเฉพาะ ระบบอื่น
q{"รหัสปรากฏที่ไหน?"} -->|รหัสข้อยกเว้น| nt["อ่านเป็น NTSTATUS"]
q -->|รหัส STOP| bc["ค้นในตารางรหัสบั๊กเช็ก"]
nt -.-> n1["ตัวอย่าง: 0xC0000005"]
bc -.-> b1["ตัวอย่าง: 0x0000009F"]
ภาพ 12: รหัสข้อยกเว้นบันทึกเหตุการณ์คือ NTSTATUS รหัส STOP หน้าจอฟ้าเป็นระบบอื่น อย่าค้นผิดตาราง
การสอบสวนนอกรหัสข้อยกเว้น คือการจับและวิเคราะห์ดัมป์พัง ถูกครอบใน “บทนำการเก็บ Windows crash dump” และ “อ่าน Crash Dump ด้วย WinDbg + SOS”
5.3. ความสัมพันธ์กับ HRESULT — บิต N และ RtlNtStatusToDosError
สะพานระหว่าง NTSTATUS กับอีกสองชั้นมีสองทาง
- แมปเข้าพื้นที่ HRESULT: การตั้งบิต N ของ HRESULT (0x10000000) พาค่า NTSTATUS เข้าพื้นที่ HRESULT ตามที่เป็น (แมโคร HRESULT_FROM_NT ใน winerror.h) การแมป 0xC0000005 กลายเป็น 0xD0000005 เช่น เมื่อเห็น HRESULT ที่ขึ้นต้น 0xD ขั้นตอนที่ถูกคือถอดบิต N แล้วอ่านเป็น NTSTATUS2
- แปลงเป็นรหัสข้อผิดพลาด Win32:
RtlNtStatusToDosErrorของ ntdll แปลง NTSTATUS เป็นรหัสข้อผิดพลาด Win32 ที่ตรง ค่าที่ไม่มีคู่ที่นิยามกลายเป็น ERROR_MR_MID_NOT_FOUND12 เช่น 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 ที่รู้จักถูกแมปเป็นชนิดข้อยกเว้นที่ตรง ที่ไม่รู้จักเป็น COMException11
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 สาขาในจัดการข้อยกเว้น I/O ไฟล์เช่น “ลองใหม่เฉพาะเมื่อละเมิดการแชร์” เขียนด้วยค่านี้ได้
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
เมื่อเรียก Win32 API โดยตรงผ่าน 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 ถูกต้อง การ P/Invoke GetLastError โดยตรงไม่แม่นเพราะรันไทม์เขียนทับ
pi["เรียก Win32 API ผ่าน 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 จึงใช้ตามที่เป็นเพื่อทิ้งทั้งรหัสและข้อความในบันทึก คำถามออกแบบว่าจับข้อยกเว้นที่ชั้นใดและทิ้งในบันทึกอย่างไรถูกครอบใน “ใน exception handling ควรวาง 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 เป็นอาร์กิวเมนต์ที่สองแล้วตีความเป็น NTSTATUS10
0:000> !error 5
Error code: (Win32) 0x5 (5) - Access is denied.
0:000> !error 0xc0000005 1
Error code: (NTSTATUS) 0xc0000005 - <Access violation>
ในดัมป์พัง !analyze -v แสดงรหัสข้อยกเว้น (NTSTATUS) อัตโนมัติ จึงไหลคือยืนยันความหมายจากที่นั่นด้วย !error <code> 1
flowchart TB
accTitle: ไหลยืนยันรหัสข้อยกเว้นใน WinDbg
accDescr: ในดัมป์พัง คำสั่ง analyze แสดงรหัสข้อยกเว้นอัตโนมัติ ส่งรหัสนั้นให้ส่วนขยาย error ด้วยอาร์กิวเมนต์ที่สอง 1 แล้วยืนยันความหมายเป็น NTSTATUS
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 Event Log และ 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 Dump ด้วย WinDbg + SOS — คู่มือวิเคราะห์เชิงปฏิบัติหลังการเก็บ
- บทนำการเก็บ Windows crash dump - WER/ProcDump/WinDbg
- ใน exception handling ควรวาง catch และการบันทึกไว้ที่ไหน?
- คู่มือปฏิบัติ Process Monitor (ProcMon) — ตรึง “การตั้งค่าไม่ถูกใช้” และ “ACCESS DENIED” ใน 10 นาที
- บทนำ Windows Event Log และ ETW — วางล็อกของแอปธุรกิจบนกลไกมาตรฐานของ OS
ด้านให้คำปรึกษาที่เกี่ยวข้อง
KomuraSoft LLC รับสอบสวนเหตุการณ์ที่เริ่มจากรหัสข้อผิดพลาด — “ไม่รู้รหัสข้อผิดพลาดนี้หมายถึงอะไร” “0x80070005 ปรากฏเฉพาะในสภาพแวดล้อมหนึ่ง” — การออกแบบจัดการข้อผิดพลาดสำหรับแอปที่ผสม Win32 API, COM และ .NET และการระบุสาเหตุด้วยดัมป์พังและ Process Monitor คำปรึกษาจากภาพหน้าจอเดียวของกล่องข้อผิดพลาดใช้ได้
- การพัฒนาแอปพลิเคชัน Windows
- การตรวจสอบบั๊กและวิเคราะห์หาสาเหตุที่แท้จริง
- ที่ปรึกษาด้านเทคนิคและการตรวจทานการออกแบบ
- ติดต่อเรา
ลิงก์อ้างอิง
-
Microsoft Learn, Debug system error codes ดัชนีรายการรหัสข้อผิดพลาดระบบ Win32 (0–15999) การได้ข้อความของรหัสที่
GetLastErrorคืนด้วย FormatMessage และธง FORMAT_MESSAGE_FROM_SYSTEM ว่าข้อผิดพลาด WinINet/WinHTTP (หมื่นสองพัน) ถูกนิยามในพื้นที่นี้ และวิธีสอบสวนด้วย 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 รายการรหัสบั๊กเช็ก (รหัส STOP) ที่แสดงบนหน้าจอฟ้า และวิธีแสดงข้อมูลเกี่ยวกับรหัสด้วยส่วนขยาย !analyze ของ WinDbg ว่าเป็นระบบเลขของตัวเองแยกจาก NTSTATUS ยืนยันจากรายการได้ ↩ ↩2
-
Microsoft Learn, Marshal.GetLastWin32Error Method ว่าเป็นวิธีดึง last-error code ของการเรียก P/Invoke ที่ตั้งธง SetLastError ว่าการ P/Invoke GetLastError โดยตรงไม่น่าเชื่อเพราะเขียนทับโดยการเรียก API ในรันไทม์ และว่าจาก .NET 6 แนะนำ GetLastPInvokeError ↩
บทความที่เกี่ยวข้อง
บทความล่าสุดที่มีแท็กเดียวกัน เพื่อเจาะลึกหัวข้อใกล้เคียง
Win32 Thread Pool API — คอนเคอร์เรนซีโดยไม่สร้างเธรดเอง ด้วย CreateThreadpoolWork
โค้ดเนทีฟของคุณกระจายการเรียก CreateThread ไปทั่วหรือไม่ บทความนี้อธิบาย Win32 thread pool API ที่ออกแบบใหม่ใน Vista — ออบเจ็กต์สี่ชนิด w...
Named Pipe ในทางปฏิบัติ — IPC มาตรฐานของ Windows ตั้งแต่การออกแบบถึงความปลอดภัย
คู่มือเชิงปฏิบัติเรื่อง named pipe ซึ่งเป็นการสื่อสารระหว่างโปรเซสมาตรฐานของ Windows บทความนี้จัดระเบียบจากแหล่งปฐมภูมิ ทั้งการเลือกระหว่...
แอปที่พังเมื่อรีซูมจากสลีป — อีเวนต์พลังงานของ Windows ทำงานอย่างไร และสร้างแอปธุรกิจที่รอดได้อย่างไร
เปิดแล็ปท็อปแล้วการเชื่อมต่อของแอปธุรกิจตาย — สาเหตุคือการออกแบบที่ไม่เคยคิดถึงสลีป บทความนี้ครอบคลุมลำดับการแจ้ง WM_POWERBROADCAST พฤติก...
DllMain และ Loader Lock — เหตุผลจริงที่ถูกบอกให้ "อย่าทำอะไรในการเริ่มต้น DLL"
ทำไมคุณต้องไม่เรียก LoadLibrary หรือซิงโครไนซ์กับเธรดอื่นจาก DllMain บทความนี้อธิบายจากแหล่งปฐมภูมิว่า loader lock ทำให้การแจ้ง DLL ทุกคร...
"ไม่ตอบสนอง" คืออะไรจริง ๆ — Windows ตัดสินว่าแอปแฮงอย่างไร และออกแบบอย่างไรให้ไม่แฮง
"ไม่ตอบสนอง" ของ Windows คือกลไกที่ OS ตัดสินว่าหน้าต่างไม่ได้ดึงข้อความเป็นเวลา 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" ในบันทึกเหตุการณ์หรือดัมป์เมื่อแอปพัง และชี้บั๊กโปรแกรมเช่นการอ้างอิงพอยน์เตอร์ที่ไม่ถูกต้องหรือเข้าถึงหน่วยความจำที่ปล่อยแล้ว ชื่อคล้ายข้อผิดพลาด Win32 5 (ERROR_ACCESS_DENIED = ปฏิเสธการเข้าถึง) แต่เป็นรหัสไม่เกี่ยวข้องจากระบบอื่น อย่าสับสน วิธีที่เชื่อถือได้ในการระบุสาเหตุคือจับดัมป์พังแล้ววิเคราะห์ใน WinDbg
- ทำไมสาเหตุต่างทุกครั้ง แม้รหัสข้อผิดพลาดเดียวกัน?
- เพราะรหัสข้อผิดพลาดแทนเพียง "ชนิดของความล้มเหลว" และ "อะไรล้มเหลวและทำไม" ถูกตัดสินโดยบริบทการเรียก ข้อผิดพลาด 5 (ปฏิเสธการเข้าถึง) ตัวอย่างเช่น เป็นรหัสเดียวกันสำหรับสาเหตุที่ต่างสิ้นเชิง — สิทธิ์ NTFS ไม่พอ, ไม่มีสิทธิ์ผู้ดูแล, บล็อกแอนติไวรัส เป็นต้น ในสถานการณ์คล้ายกัน หากโปรเซสอื่นยังเปิดไฟล์อยู่ คุณได้รหัสอื่น (ข้อผิดพลาด 32 = ละเมิดการแชร์) และการอ่านรหัสถูกจะเปลี่ยนที่ที่คุณมอง ข้อผิดพลาด 2 (ไม่พบไฟล์) ก็มักไม่ใช่ไฟล์หลักแต่เป็น DLL ที่พึ่งหรือไฟล์ตั้งค่า เมื่อค้นความหมายของรหัสแล้ว การยืนยันว่า API ใดล้มเหลวต่อทรัพยากรใดด้วย Process Monitor หรือคล้ายกันคือทางสั้นสู่สาเหตุ