為了說明這個過程,我們必須寫一個示例程序,如下:
#include "stdafx.h" #include <tchar.h> #include <stdio.h> #include <Windows.h> #pragma comment(lib, "user32") WNDPROC oldproc = NULL; LRESULT CALLBACK newproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { throw 0; return oldproc(hwnd, uMsg, wParam, lParam); } int _tmain(int argc, TCHAR *argv[]) { HWND hWnd = CreateWindowEx(0, TEXT("STATIC"), TEXT("Name"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); oldproc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)newproc); UpdateWindow(hWnd); system("pause"); }
將上面的程序在vs建立工程編譯運行,得到如下結果
退出,在vs里按下快捷建F11,程序中斷后,給函數ZwRaiseException下斷點
按F5運行程序,程序中斷,觀察到程序停在了_NtRaiseException@12
看下調用棧
可以看到此時正在拋出第一個異常,也就是真正的異常 throw 0;
按F5 繼續運行程序,程序又中斷了,可以看到此時又中斷在_NtRaiseException@12,看下棧
這是第二次拋出異常,也就是拋出0xc000041d,我們觀察下函數ntdll.dll!_LdrpLogFatalUserCallbackException@8()
在這里里,填充了EXCEPTION_RECORD結構,緊接着調用了_NtRaiseException@12拋出異常。
簡單來說,當程序產生了一個異常,首先走的還是正常的異常分發流程,當沒有得到處理,又是在Windows系統的用戶回調里發生的,會分發給_KiUserCallbackExceptionHandler去處理,_KiUserCallbackExceptionHandler里會調用_LdrpLogFatalUserCallbackException,_LdrpLogFatalUserCallbackException里會重新填充EXCEPTION_RECORD結構拋出異常0xc000041d。