必知
可以先阅读一下[Win32]一个调试器的实现(三)异常,写得非常好!
当程序中发生异常时,我们的调试器是会接收到EXCEPTION_DEBUG_EVENT调试事件的。但是这时候到底要不要处理这个调试事件,我们还必须斟酌一下。因为很可能程序里有相应的异常处理代码,而这时我们的调试器就没有什么必要接管这个异常。异常的分发过程如图:
这里是我的代码,可以拿去把玩一下,感受一下Windows的异常机制:
①myDEBUG.cpp
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
using namespace std;
DWORD continue_status = DBG_EXCEPTION_NOT_HANDLED;
//异常调试事件处理函数
void OnException(const EXCEPTION_DEBUG_INFO* excpt_debug_info){
cout<<"\tAn exception occured at "<<hex<<excpt_debug_info->ExceptionRecord.ExceptionAddress<<endl
<<"\tException code: "<<excpt_debug_info->ExceptionRecord.ExceptionCode<<dec<<endl;
if(excpt_debug_info->dwFirstChance){
cout<<"\tfirst chance"<<endl;
continue_status = DBG_EXCEPTION_NOT_HANDLED;
}else{
cout<<"\tsecond chance"<<endl;
continue_status = DBG_EXCEPTION_NOT_HANDLED;
}
}
void main(){
PROCESS_INFORMATION process_info;
STARTUPINFO startup_info;
memset(&process_info, 0, sizeof(process_info));
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(STARTUPINFO);
if(CreateProcess(TEXT("E:\\myCode\\dividedByZero\\Debug\\dividedByZero.exe"),NULL, NULL, NULL, FALSE,
DEBUG_ONLY_THIS_PROCESS | CREATE_NEW_CONSOLE,NULL, NULL, &startup_info, &process_info)){
//ResumeThread(process_info.hThread);
while(true){
DEBUG_EVENT debug_info;
if(!WaitForDebugEvent(&debug_info, INFINITE))
break;
switch (debug_info.dwDebugEventCode)
{
case CREATE_PROCESS_DEBUG_EVENT://创建进程
cout<<"CREATE_PROCESS_DEBUG_EVENT"<<endl;
break;
case CREATE_THREAD_DEBUG_EVENT://创建线程
cout<<"CREATE_THREAD_DEBUG_EVENT"<<endl;
break;
case EXIT_THREAD_DEBUG_EVENT://退出线程
cout<<"EXIT_THREAD_DEBUG_EVENT"<<endl;
break;
case EXIT_PROCESS_DEBUG_EVENT://退出进程
cout<<"EXIT_PROCESS_DEBUG_EVENT"<<endl;
break;
case EXCEPTION_DEBUG_EVENT://发生异常
cout<<"EXCEPTION_DEBUG_EVENT"<<endl;
OnException(&debug_info.u.Exception);
break;
case OUTPUT_DEBUG_STRING_EVENT://调用OutputDebugString函数
cout<<"OUTPUT_DEBUG_STRING_EVENT"<<endl;
break;
case RIP_EVENT://发生系统调试错误
cout<<"RIP_EVENT"<<endl;
break;
case LOAD_DLL_DEBUG_EVENT://加载dll
cout<<"LOAD_DLL_DEBUG_EVENT"<<endl;
break;
case UNLOAD_DLL_DEBUG_EVENT://卸载dll
cout<<"UNLOAD_DLL_DEBUG_EVENT"<<endl;
break;
}
if(debug_info.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
break;
ContinueDebugEvent(debug_info.dwProcessId, debug_info.dwThreadId, continue_status);
}
CloseHandle(process_info.hThread);
CloseHandle(process_info.hProcess);
}else{
cout<<"Can't create process."<<endl;
}
}
②dividedByZero.cpp
#include "stdafx.h"
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
OutputDebugString(TEXT("Warning! An exception will be thrown!"));
__try {
int a = 0;
int b = 10 / a;
getchar();
}
__except(EXCEPTION_EXECUTE_HANDLER) {
OutputDebugString(TEXT("Entered exception handler."));
}
return 0;
}
以上两段代码结合起来的效果如图:
思路
现在可以知道,EXCEPTION_DEBUG_EVENT调试事件是调试器和debugee交互的重要手段,通过处理EXCEPTION_DEBUG_EVENT调试事件可以完成常规的断点、单步执行等操作。而ContinueDebugEvent函数的第三个参数非常重要,我们可以使用DBG_CONTINUE或DBG_EXCEPTION_NOT_HANDLED来影响调试器的行为。
所以,若我们希望调试器仅仅在程序没有异常处理器时才对接管异常,就可以在第一次接收到异常调试事件时以DBG_EXCEPTION_NOT_HANDLED继续执行,在第二次接收到异常调试事件时才对其进行处理。