อ่านรหัสข้อผิดพลาด Windows — โครงสร้างสามชั้นของ Win32, HRESULT และ NTSTATUS

· · Windows, รหัสข้อผิดพลาด, HRESULT, NTSTATUS, Win32 API, แก้ปัญหา, ดีบัก, พัฒนา Windows

“หน้าจอแอปแสดงข้อผิดพลาด 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

ไหลการแปลงข้ามสามระบบระบบย่อย Win32 แปลง NTSTATUS ที่เคอร์เนลคืนเป็นรหัสข้อผิดพลาด Win32 และชั้น COM ห่อต่อเป็น HRESULTระบบย่อย Win32 แปลงชั้น COM ห่อเคอร์เนลและไดรเวอร์NTSTATUS(ข้อผิดพลาด 0xC…)รหัสข้อผิดพลาด Win32(5 เป็นต้น)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…” ให้แปลงเป็นเฮกซ์โดยรีเฟล็กซ์ แค่นั้นตัดการหลงทางที่ปากทางสอบสวนได้มาก

สามลักษณะของรหัสเดียวกันข้อผิดพลาดทศนิยม 5 เฮกซ์ 0x5 และ 16 บิตล่างของ 0x80070005 ล้วนชี้ ERROR_ACCESS_DENIED เดียวกันสัญกรณ์ทศนิยม: ข้อผิดพลาด 5ERROR_ACCESS_DENIEDสัญกรณ์เฮกซ์: 0x516 บิตล่างของ 0x80070005สัญกรณ์ต่าง รหัสเดียวกัน

ภาพ 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

  1. อ่านทันทีหลังล้มเหลว หากแทรกการเรียก API อื่น (ฟังก์ชันบันทึก เช่น) คั่น การเรียกนั้นอาจเขียนทับ last-error code
  2. อย่าพึ่งค่าเมื่อสำเร็จ บาง API ล้าง last-error code เป็น 0 เมื่อสำเร็จ บางตัวไม่แตะ กฎคือยืนยันความล้มเหลวจากค่าคืนแล้วอ่าน
อ่าน GetLastError ทันทีหลังล้มเหลวหลังยืนยันความล้มเหลวจากค่าคืน ให้ดึง last-error code ด้วย GetLastError ทันที โดยไม่แทรกการเรียก API อื่นWin32 APIAppWin32 APIAppแทรก API อื่นคั่นอาจเขียนทับเรียก CreateFileค่าคืนล้มเหลวGetLastErrorรหัส 5

ภาพ 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);
}

การทิ้ง ทั้งทศนิยม ทั้งเฮกซ์ และข้อความ ในบันทึกแอปของคุณอย่างนี้ ทำให้การสอบสวนภายหลังเร็วขึ้นหนึ่งก้าว

ค้นข้อความจากรหัสแล้วทิ้งในบันทึกส่งธง FORMAT_MESSAGE_FROM_SYSTEM ให้ FormatMessage เพื่อได้สตริงข้อความของรหัสข้อผิดพลาด และทิ้งทศนิยม เฮกซ์ และข้อความในบันทึกรหัสข้อผิดพลาด(ตัวอย่าง: 5)ได้สตริงด้วย FormatMessageข้อความบันทึกในล็อกเขียนทศนิยม เฮกซ์ และข้อความด้วยกัน

ภาพ 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 เช่น — ใกล้ “ถูกยกเลิก” มากกว่าข้อผิดพลาด

ข้อผิดพลาด 998 กับ 5 เป็นคนละเรื่อง998 คือการละเมิดการเข้าถึงหน่วยความจำ การละเมิดการเข้าถึง NTSTATUS ที่แปลงสู่ชั้น Win32 และต่างความหมายจาก 5 ที่แทนการปฏิเสธการเข้าถึงแปลงสู่ชั้น Win32NTSTATUS 0xC0000005ข้อผิดพลาด 998(ERROR_NOACCESS)ความหมายคือละเมิดการเข้าถึงหน่วยความจำข้อผิดพลาด 5(ปฏิเสธการเข้าถึง)ปัญหาสิทธิ์ คนละเรื่องกับ 998

ภาพ 5: ข้อผิดพลาด 998 คือการละเมิดการเข้าถึง NTSTATUS ที่แปลงสู่ชั้น Win32 คนละเรื่องกับปฏิเสธการเข้าถึง 5

3.3. รหัสเดียวกันเปลี่ยนความหมายตามบริบท

สำคัญกว่าท่องตารางรหัสตัวแทนคือความรู้สึกว่า รหัสข้อผิดพลาดบอกเพียง “ชนิดของความล้มเหลว”

  • ข้อผิดพลาด 5 (ปฏิเสธการเข้าถึง): ผู้สมัครสาเหตุกว้าง — ACL NTFS ไม่พอ เขียนพื้นที่ป้องกันโดยไม่มีสิทธิ์ผู้ดูแล บล็อกแอนติไวรัสหรือ AppLocker สิทธิ์บัญชีบริการไม่พอ เป็นต้น
  • ข้อผิดพลาด 2 (ไม่พบไฟล์): ไม่จำเป็นต้องเป็นไฟล์ที่ผู้ใช้ระบุ DLL ที่พึ่งซึ่ง EXE พยายามโหลดโดยนัย ไฟล์ตั้งค่าที่เห็นผิดที่เพราะการเปลี่ยนทิศทางรีจิสทรี (32 บิต/64 บิต) เส้นทางที่การขยายตัวแปรสภาพแวดล้อมล้มเหลว — “ไฟล์ใด” ไม่พบ มองไม่เห็นจากรหัส
  • ข้อผิดพลาด 32 (ละเมิดการแชร์): “โปรเซสใดถืออยู่” คือคำถามจริง แต่รหัสไม่บอก
สาเหตุข้อผิดพลาด 5 ถูกตัดสินโดยบริบทแม้ปฏิเสธการเข้าถึงเดียวกันก็มีผู้สมัครสาเหตุหลายอย่างเช่น ACL ไม่พอหรือไม่มีสิทธิ์ผู้ดูแล และต้องระบุว่า API ใดล้มเหลวต่ออะไรข้อผิดพลาด 5(ปฏิเสธการเข้าถึง)ACL ไม่พอไม่มีสิทธิ์ผู้ดูแลบล็อกผลิตภัณฑ์ความปลอดภัยสิทธิ์บริการต่ำProcmon: เป้าหมายที่ล้มเหลว

ภาพ 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…” ที่กล่าวก่อน

ความสัมพันธ์ของบิต S กับการแสดงติดลบHRESULT ที่ล้มเหลวตั้งบิต S ที่มีนัยสำคัญสูงสุดเป็น 1 จึงเริ่มที่ 0x8 ขึ้นไปในเฮกซ์ และเป็นลบเมื่อเป็นจำนวนเต็ม 32 บิตมีเครื่องหมายบิต S = 1(ล้มเหลว)เฮกซ์เริ่มที่ 0x8 ขึ้นไปการแสดงมีเครื่องหมายเป็นลบเมื่อเห็นลบ แปลงเป็นเฮกซ์แล้วอ่าน

ภาพ 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 โดยสิ้นเชิง

การแยก 0x80004005 และ 0x800700050x80004005 คือรหัส FACILITY_NULL ทั่วไป E_FAIL ไม่ถือรายละเอียด และควรไปสอบสวนบริบท 0x80070005 คือ FACILITY_WIN32 และอ่านได้เป็นการห่อหมายเลขข้อผิดพลาด Win32 5 ปฏิเสธการเข้าถึง0x80004005Facility=0(FACILITY_NULL)Code=0x4005 → E_FAILความล้มเหลวที่ไม่ระบุ ต่อไปสอบสวนบริบท0x80070005Facility=7(FACILITY_WIN32)Code=0x0005 → 5ERROR_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”

HRESULT_FROM_WIN32 ทำงานอย่างไรเก็บรหัสข้อผิดพลาด Win32 ใน 16 บิตล่าง ตั้ง Facility เป็น 7 และบิต S เป็น 1 แล้วประกอบ HRESULT 0x8007xxxxรหัสข้อผิดพลาด Win32(ตัวอย่าง: 5)เก็บใน 16 บิตล่างตั้ง Facility เป็น 7ตั้งบิต S เป็น 10x80070005

ภาพ 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 ไดรเวอร์ ผลิตภัณฑ์เซิร์ฟเวอร์)

วิธีค้นเปลี่ยนระหว่าง 0x8007 กับ 0x8004FACILITY_WIN32 0x8007xxxx อ่านได้ด้วยการแยก 16 บิตล่างเชิงกล แต่ FACILITY_ITF 0x8004xxxx มีฝ่ายนิยามความหมายต่างต่ออินเทอร์เฟซ จึงค้นในเอกสารคอมโพเนนต์ที่คืน7, WIN324, ITFFacility คือ?แปลง 16 บิตล่างเป็นทศนิยมความหมายต่างตามฝ่ายที่คืนอ่านเป็นข้อผิดพลาด Win32ค้นในเอกสารฝ่ายที่คืน

ภาพ 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

NTSTATUS อ่านชนิดจากหลักนำได้เพราะ severity เป็น 2 บิต NTSTATUS อ่านเป็นข้อผิดพลาดถ้าหลักเฮกซ์นำเป็น 0xC คำเตือนถ้า 0x8 ข้อมูลถ้า 0x4 และสำเร็จถ้า 0x0 ถึง 0x30xC0x80x40x0–0x3หลักเฮกซ์นำคือ?ข้อผิดพลาดคำเตือนข้อมูลสำเร็จตัวอย่าง: 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 และถึงแอป
แยกรหัสข้อยกเว้นจากรหัส STOPอ่านรหัสข้อยกเว้นบันทึกเหตุการณ์เป็น NTSTATUS ค้นรหัส STOP หน้าจอฟ้าในเอกสารรหัสบั๊กเช็กเฉพาะ ระบบอื่นรหัสข้อยกเว้นรหัส STOPรหัสปรากฏที่ไหน?อ่านเป็น NTSTATUSค้นในตารางรหัสบั๊กเช็กตัวอย่าง: 0xC0000005ตัวอย่าง: 0x0000009F

ภาพ 12: รหัสข้อยกเว้นบันทึกเหตุการณ์คือ NTSTATUS รหัส STOP หน้าจอฟ้าเป็นระบบอื่น อย่าค้นผิดตาราง

การสอบสวนนอกรหัสข้อยกเว้น คือการจับและวิเคราะห์ดัมป์พัง ถูกครอบใน “บทนำการเก็บ Windows crash dump” และ “อ่าน Crash Dump ด้วย WinDbg + SOS

5.3. ความสัมพันธ์กับ HRESULT — บิต N และ RtlNtStatusToDosError

สะพานระหว่าง NTSTATUS กับอีกสองชั้นมีสองทาง

  1. แมปเข้าพื้นที่ HRESULT: การตั้งบิต N ของ HRESULT (0x10000000) พาค่า NTSTATUS เข้าพื้นที่ HRESULT ตามที่เป็น (แมโคร HRESULT_FROM_NT ใน winerror.h) การแมป 0xC0000005 กลายเป็น 0xD0000005 เช่น เมื่อเห็น HRESULT ที่ขึ้นต้น 0xD ขั้นตอนที่ถูกคือถอดบิต N แล้วอ่านเป็น NTSTATUS2
  2. แปลงเป็นรหัสข้อผิดพลาด 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
สองสะพานจาก NTSTATUS ไปชั้นอื่นNTSTATUS ถูกส่งไปชั้นอื่นสองทาง แมปเข้าพื้นที่ HRESULT โดยตั้งบิต N และแปลงเป็นรหัสข้อผิดพลาด Win32 โดย RtlNtStatusToDosErrorตั้งบิต NRtlNtStatusToDosErrorNTSTATUS(0xC0000005)HRESULT(0xD0000005)ข้อผิดพลาด Win32 998(ERROR_NOACCESS)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 ด้วยกัน แอปที่กล่องข้อผิดพลาดแสดง “รหัส + คำอธิบาย” มักพาคำอธิบายผ่านกลไกนี้

IErrorInfo ที่เติม HRESULTมีขีดจำกัดว่าห่ออะไรใน HRESULT 32 บิตได้ จึงส่งสตริงคำอธิบายข้อผิดพลาดและต้นทางแยกด้วย IErrorInfo และใน C++ คลาส _com_error จัดการทั้งคู่ด้วยกันHRESULT(เพียง 32 บิต)มีขีดจำกัดว่าห่ออะไรได้IErrorInfo พาคำอธิบาย_com_error จัดการด้วยกันรหัส + คำอธิบายของกล่อง

ภาพ 14: สตริงคำอธิบายที่ไม่พอใน HRESULT 32 บิตถูก IErrorInfo พาแยก

6.2. สไตล์ .NET — จาก HRESULT สู่ชนิดข้อยกเว้น

เมื่อรันไทม์ .NET รับความล้มเหลว HRESULT ใน COM interop มันแปลงเป็นข้อยกเว้น HRESULT ที่รู้จักถูกแมปเป็นชนิดข้อยกเว้นที่ตรง ที่ไม่รู้จักเป็น COMException11

การแมปจาก HRESULT สู่ข้อยกเว้น .NETHRESULT ที่ล้มเหลวที่รับใน COM interop ถูกแปลงเป็นชนิดข้อยกเว้นที่ตรงหากรู้จัก หรือ COMException หากไม่ และในทั้งสองกรณีค่าต้นอยู่ใน Exception.HResultYesNoHRESULT ที่ล้มเหลวแมปที่รู้จัก?แปลงเป็นชนิดข้อยกเว้นที่ตรงแปลงเป็น COMExceptionค่าต้นอยู่ใน Exception.HResult

ภาพ 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

ดึงข้อผิดพลาดล่าสุดใน P/Invokeระบุ SetLastError เป็น true แล้วดึงด้วย Marshal.GetLastWin32Error ถูกต้อง การ P/Invoke GetLastError โดยตรงไม่แม่นเพราะรันไทม์เขียนทับเรียก Win32 API ผ่าน P/Invokeระบุ SetLastError=trueดึงด้วย GetLastWin32Errorนิยามที่เรียก GetLastError โดยตรงรันไทม์เขียนทับและไม่แม่น

ภาพ 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

เลือกผลค้น err.exe ตามบริบทerr.exe เดินผ่านไฟล์หัวจำนวนมากและรายการนิยามที่ตรง จึงเมื่อมีผู้สมัครหลายตัวสำหรับตัวเลขเดียวกัน ให้เลือกที่น่าเชื่อตามบริบทใส่ err 5เดินผ่านหัวจำนวนมากนิยามหลายตัวโดนเลือกผู้สมัครที่น่าเชื่อตามบริบท

ภาพ 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 เป็นทศนิยมเท่านั้น แต่ในสภาพแวดล้อมไทยข้อความกลับเป็นภาษาที่ระบบแปลไว้ จึงใช้อธิบายให้ผู้ใช้ตามที่เป็นได้

วิธีเลือกในคำสั่งมาตรฐานรหัสข้อผิดพลาด Win32 ทศนิยมค้นด้วย net helpmsg รหัสที่มีเฮกซ์รวม HRESULT ค้นด้วยตัวเลือก -error ของ certutilWin32 ทศนิยมมีเฮกซ์รหัสที่คุณมีคือ?net helpmsgcertutil -errorข้อความที่แปลตามภาษากลับมารับทั้งเฮกซ์และทศนิยม

ภาพ 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

ไหลยืนยันรหัสข้อยกเว้นใน WinDbgในดัมป์พัง คำสั่ง analyze แสดงรหัสข้อยกเว้นอัตโนมัติ ส่งรหัสนั้นให้ส่วนขยาย error ด้วยอาร์กิวเมนต์ที่สอง 1 แล้วยืนยันความหมายเป็น NTSTATUSเปิดดัมป์พังรัน !analyze -vรหัสข้อยกเว้นถูกแสดงยืนยันความหมายด้วย !error code 1

ภาพ 19: ในการวิเคราะห์ดัมป์ ค้นรหัสข้อยกเว้นที่ !analyze -v แสดงด้วย !error และธง 1

8. ขั้นตอนสอบสวน — จากการตัดสินชั้นสู่การจับคู่บริบท

ประกอบความรู้จนถึงนี้เป็นขั้นตอนสอบสวนรหัสข้อผิดพลาดจริง

  1. ปรับสัญกรณ์ให้ปกติ หากเป็นทศนิยมติดลบ แปลงเป็นเฮกซ์ 8 หลัก เติมศูนย์ให้เฮกซ์สั้นกว่า 8 หลักแล้วอ่าน
  2. ตัดสินว่ารหัสอยู่ชั้นใด ตามตารางตัดสินด้านล่าง หลักนำไม่กี่ตัวเกือบตัดสิน
  3. แยกแล้วดึงรหัสสาระ การดำเนินการเชิงกล: 16 บิตล่างหาก 0x8007xxxx ถอดบิต N หาก 0xDxxxxxxx
  4. ค้นชื่อและนิยามด้วยเครื่องมือ ยืนยันชื่อสัญลักษณ์และข้อความด้วย err.exe, certutil หรือ !error
  5. จับคู่กับบริบท ระบุแอปใด การดำเนินการใด API ใด ล้มเหลวต่ออะไร จากบันทึกแอป บันทึกเหตุการณ์ และ Procmon รหัสคือ “ชนิดของความล้มเหลว” บริบทคือ “ที่ของสาเหตุ”
ขั้นตอนสอบสวนรหัสข้อผิดพลาดรูปแบบสอบสวนจัดสัญกรณ์ให้เป็นเฮกซ์ ตัดสินชั้นจากหลักนำ แยกแล้วดึงรหัสสาระ ค้นชื่อและนิยามด้วยเครื่องมือ แล้วจับคู่บริบททศนิยม0x80070xC0xDปรับเป็นเฮกซ์หลักนำ?อ่านเป็นข้อผิดพลาด Win3216 บิตล่าง → ทศนิยมอ่านเป็น NTSTATUSถอดบิต N แล้วอ่านค้นชื่อ/นิยามจับคู่บริบท(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

ข้อผิดพลาด 5 กับ 0xC0000005 มีทิศสอบสวนต่างกันข้อผิดพลาด Win32 5 ควรสอบสวนเป็นปัญหาสิทธิ์ และ NTSTATUS 0xC0000005 เป็นบั๊กโปรแกรม การถือเป็นอันเดียวกันส่งสอบสวนไปทิศอื่นข้อผิดพลาด Win32 5สอบสวนปัญหาสิทธิ์NTSTATUS 0xC0000005สอบสวนบั๊กโปรแกรมรหัสไม่เกี่ยวข้องจากระบบต่าง

ภาพ 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 วินาทีนี้ตัดสินเวลาสอบสวนที่ตามมาเป็นส่วนใหญ่

บทความที่เกี่ยวข้อง

ด้านให้คำปรึกษาที่เกี่ยวข้อง

KomuraSoft LLC รับสอบสวนเหตุการณ์ที่เริ่มจากรหัสข้อผิดพลาด — “ไม่รู้รหัสข้อผิดพลาดนี้หมายถึงอะไร” “0x80070005 ปรากฏเฉพาะในสภาพแวดล้อมหนึ่ง” — การออกแบบจัดการข้อผิดพลาดสำหรับแอปที่ผสม Win32 API, COM และ .NET และการระบุสาเหตุด้วยดัมป์พังและ Process Monitor คำปรึกษาจากภาพหน้าจอเดียวของกล่องข้อผิดพลาดใช้ได้

ลิงก์อ้างอิง

  1. 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

  2. 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

  3. Microsoft Open Specifications, [MS-ERREF]: NTSTATUS เลย์เอาต์บิต NTSTATUS (Sev 2 บิต บิต C บิต N Facility 12 บิต Code 16 บิต) และว่า severity แยกสี่ชนิด สำเร็จ (00) ข้อมูล (01) คำเตือน (10) และข้อผิดพลาด (11)  2 3

  4. Microsoft Learn, HRESULT_FROM_WIN32 macro นิยามแมโคร winerror.h ที่แมปรหัสข้อผิดพลาดระบบ Win32 เป็นค่า HRESULT  2 3

  5. Microsoft Learn, Structure of COM Error Codes บทบาทของบิต severity HRESULT และฟิลด์ facility ค่า FACILITY_NULL, FACILITY_RPC, FACILITY_ITF, FACILITY_WIN32 และ FACILITY_WINDOWS และว่ารหัส FACILITY_ITF มีความหมายนิยามต่ออินเทอร์เฟซ และค่าเดียวกันอาจหมายอย่างอื่น  2 3

  6. Microsoft Learn, Common HRESULT values ว่า E_FAIL (0x80004005) คือ “Unspecified failure” และนิยามค่า HRESULT ที่เห็นบ่อยเช่น E_ACCESSDENIED (0x80070005), E_INVALIDARG (0x80070057) และ E_OUTOFMEMORY (0x8007000E)  2 3 4

  7. 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

  8. Microsoft Learn, The Microsoft Error Lookup Tool ว่าเป็นเครื่องมืออิสระที่แสดงข้อความที่ผูกกับรหัสสถานะฐานสิบหกข้ามไฟล์หัวต่าง ๆ เช่น Winerror.h ว่าชื่อไฟล์ดาวน์โหลดคือ Err_6.4.5.exe และว่าต้องทราบว่านิยามที่มัดเป็น ณ เวลาคอมไพล์  2 3

  9. Microsoft Learn, certutil ว่าตัวเลือก -error ของ certutil แสดงข้อความที่ผูกกับรหัสข้อผิดพลาด และว่าสัญกรณ์ข้อผิดพลาดที่มีชื่อสัญลักษณ์ใช้ในรูปเช่น 0x80070002 (WIN32: 2 ERROR_FILE_NOT_FOUND)  2

  10. Microsoft Learn, !error ว่าส่วนขยาย !error ของ WinDbg ถอดรหัสและแสดงค่าข้อผิดพลาด Win32, Winsock, NTSTATUS และ NetAPI และว่าการระบุ 1 เป็นธงตีความเป็น NTSTATUS  2

  11. Microsoft Learn, How to: Map HRESULTs and exceptions กลไกแมปสองทางระหว่าง HRESULT ของ COM กับข้อยกเว้น .NET ตารางคู่เช่น E_NOTIMPL → NotImplementedException ว่า HRESULT ที่ไม่มีแมปชัดถูกแปลงเป็น COMException และว่า Message, Source เป็นต้น ของข้อยกเว้นถูกเริ่มจากข้อมูล IErrorInfo  2

  12. Microsoft Learn, RtlNtStatusToDosError function (winternl.h) ว่าเป็นฟังก์ชันที่แปลงรหัส NTSTATUS เป็นรหัสข้อผิดพลาดระบบ Win32 ที่ตรง ว่า ERROR_MR_MID_NOT_FOUND ถูกคืนเมื่อไม่นิยามคู่ และว่าฟังก์ชันแปลงกลับไม่มี  2

  13. Microsoft Learn, Last-Error Code ว่า last-error code ถูกถือต่อเธรด ว่าควรดึงด้วย GetLastError ทันทีหลังล้มเหลว ว่า API ที่เขียนทับรหัสด้วย 0 เมื่อสำเร็จและ API ที่ไม่แตะปนกัน และว่าบิต 29 สำรองสำหรับรหัสที่แอปนิยาม  2

  14. Microsoft Learn, Bug check code reference รายการรหัสบั๊กเช็ก (รหัส STOP) ที่แสดงบนหน้าจอฟ้า และวิธีแสดงข้อมูลเกี่ยวกับรหัสด้วยส่วนขยาย !analyze ของ WinDbg ว่าเป็นระบบเลขของตัวเองแยกจาก NTSTATUS ยืนยันจากรายการได้  2

  15. 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 วินาทีแล้วแทนที่ด้วยหน้าต่างโกสต์ บทความนี้ครอบคลุมภาย...

หน้าเหล่านี้วางหัวข้อไว้ในบริบทที่กว้างขึ้นของบริการและการตัดสินใจ

บทความนี้เกี่ยวข้องโดยตรงกับบริการต่อไปนี้

คำถามที่พบบ่อย

คำถามที่มักพบในการปรึกษาเกี่ยวกับหัวข้อของบทความนี้

ข้อผิดพลาด 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 หรือคล้ายกันคือทางสั้นสู่สาเหตุ

โปรไฟล์ผู้เขียน

หน้าแนะนำผู้เขียนบทความ

Go Komura

ผู้แทนของ KomuraSoft LLC

เชี่ยวชาญด้านการพัฒนาซอฟต์แวร์ Windows ที่ปรึกษาเทคนิค และการตรวจสอบบั๊ก โดยเฉพาะโปรเจกต์ที่มีระบบเดิมและบั๊กที่ทำซ้ำได้ยาก

ลิงก์สาธารณะ

กลับไปยังบล็อก