处理关键的调试事件

必知

可以先阅读一下[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_CONTINUEDBG_EXCEPTION_NOT_HANDLED来影响调试器的行为。
所以,若我们希望调试器仅仅在程序没有异常处理器时才对接管异常,就可以在第一次接收到异常调试事件时以DBG_EXCEPTION_NOT_HANDLED继续执行,在第二次接收到异常调试事件时才对其进行处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值