当然,这是因为M$的__try, __expect,__final只限于处理进程内部,同一个线程内,位于 __try {}block内部产生的异常。而我们需要的是启动的新进程~!
不知道你有没有注意过,使用vc的时候。。调试模式运行程序,它会在runtime error的时候提前弹出一个对话框提示出现访问异常~哦!我们不正是需要这个效果吗?nice,我们就仿照vc做一个调试器好了~~显然VC也是通过调用Windows API函数完成调试器的任务,而且,这些函数显然可以实现我们的要求。 我们 需要作的事情就是自己利用这些API函数,写一个简单的调试器。
如何编写实现我们的程序?需要哪些调试函数?有两种方法。。。。
对于一个已经启动的进程而言,利用DebugActiveProcess函数就可以捕获目标进程,将目标进程进入被调试状态。
BOOL DebugActiveProcess(DWORD dwProcessId);
参数dwProcessId是目标进程的进程ID。如何通过ToolHelp系列函数或Psapi库函数获得一个运行程序的进程ID在很多文章中介 绍过,这里就不再重复。对于服务器程序而言,由于没有权限无法捕获目标进程,可以通过提升监视程序的权限得到调试权限进行捕获目标进程(用户必须拥有调试 权限)。
而另外一种就是我们做oj所需要的,CreateProcess函数,设置必要的参数就可以将目标程序进入被调试状态。
BOOL CreateProcess(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES
lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID
lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation );
该函数的具体说明请参考MSDN,在这 里我仅介绍我们感兴趣的参数。这里和一般的用法不同,作为被调试程序dwCreationFlags必须设置为DEBUG_PROCESS或 DEBUG_ONLY_THIS_PROCESS。这样启动的目标程序就会进入被调试状态。这里说明一下DEBUG_PROCESS和 DEBUG_ONLY_THIS_PROCESS。DEBUG_ONLY_THIS_PROCESS就是只调试目标进程,而DEBUG_PROCESS参 数则不仅调试目标进程,而且调试由目标进程启动的所有子进程。比如:在A.exe中启动B.exe,如果用 DEBUG_ONLY_THIS_PROCESS启动,监视进程只调试A.exe不会调试B.exe,如果是DEBUG_PROCESS就会调试 A.exe和B.exe。 当然,我们是做oj,不允许用户使用多进程,所以我们只讨论启动参数为DEBUG_ONLY_THIS_PROCESS的情况。
一个小例子:
STARTUPINFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW |STARTF_USESTDHANDLES;
char *Dir = "d://1.exe"; //这个程序是一个除零的程序。
if(!CreateProcess(NULL,Dir,NULL,NULL,TRUE,DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi))
{
cout<<"Error on CreateProcess()";
return;
}
如果你直接运行上面的代码,毫无疑问还是会崩掉。。。。我们需要的是捕获异常~~~
当目标进程进入了被调试状态,调试程序 (这里调试程序就是我们的主程序)就负责对被调试的程序进行调试操作的调度。调试程序通过WaitForDebugEvent函数获得来 自被调试程序的调试消息,调试程序根据得到的调试消息进行处理,被调试进程将暂停操作,直到调试程序通过ContinueDebugEvent函数通知被 调试程序继续运行。当然,我们不需要continue了,因为这时候直接给用户返回RE,然后kill掉它就好了。。。。
BOOL WaitForDebugEvent(
LPDEBUG_EVENT lpDebugEvent, // debug event information
DWORD dwMilliseconds // time-out value
);
在参数lpDebugEvent中可以获得调试消息,需要注意的是该函数必须和让目标程序进入调试状态的线程是同一线程。也就是说和CreateProcess调用的线程是一个线程。
DEBUG_EVENT会保存子进程的调试信息。
typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode;
DWORD dwProcessId;
DWORD dwThreadId;
union {
EXCEPTION_DEBUG_INFO Exception;
CREATE_THRECheck the input
System.out.println("/nREQUEST:/n");
message.writeTo(System.out);
System.out.println();
//Close the connection
connection.close();
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
其中我们关心的是union中的 Exception,这里保存这异常调试信息~~~它的ExceptionCode常用的以下取值:
EXCEPTION_INT_DIVIDE_BY_ZERO
EXCEPTION_INT_OVERFLOW
EXCEPTION_ACCESS_VIOLATION
EXCEPTION_DATATYPE_MISALIGNMENT
EXCEPTION_FLT_STACK_CHECK
EXCEPTION_INVALID_DISPOSITION
EXCEPTION_STACK_OVERFLOW
是不是感觉很熟悉啊~~~呵呵~~~~~~~
于是有了上面的这些东西,我们似乎就可以完成我们的程序了~~!!!之前还需要注意一点,无论子进程正确与否,总是要先抛出EXCEPTION_BREAKPOINT 异常,然后再弹出其他异常
所以,我们不能在抛出EXCEPTION_BREAKPOINT 异常的时候结束进程,还需要等待抛出第二个异常的时候,得到真正的异常信息。
下面给出测试代码:
这个程序可以捕获子进程的任何异常,并提示,而且子进程不会出现讨厌的对话框。。。。太久不写代码了。。。。写得很难看。。将就一下。。。
//by c4pt0r~~~~任意转载~~随意转载~~
#include <iostream>
#include<windows.h>
using namespace std;
int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW |STARTF_USESTDHANDLES;
char *Dir = "d://1.exe";
if(!CreateProcess(NULL,Dir,NULL,NULL,TRUE,DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi))
{
printf("Error on CreateProcess()");
return;
}
DEBUG_EVENT de;
while (WaitForDebugEvent(&de,INFINITE)!=0)
{
if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
printf("create/n");
if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
switch (de.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_INT_DIVIDE_BY_ZERO:
printf("zero/n");
TerminateProcess(pi.hProcess,1);
break;
case EXCEPTION_INT_OVERFLOW:
printf("INT/n");
TerminateProcess(pi.hProcess,1);
break;
case EXCEPTION_ACCESS_VIOLATION:
printf("ACCESS/n");
TerminateProcess(pi.hProcess,1);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
printf("DATATYPE/n");
TerminateProcess(pi.hProcess,1);
break;
case EXCEPTION_FLT_STACK_CHECK:
printf("DATATYPE/n");
TerminateProcess(pi.hProcess,1);
break;
case EXCEPTION_INVALID_DISPOSITION:
printf("DISPOSITION/n");
TerminateProcess(pi.hProcess,1);
break;
case EXCEPTION_STACK_OVERFLOW:
printf("OVERFLOW/n");
TerminateProcess(pi.hProcess,1);
break;
default:
printf("exception/n");
}
}
if (de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
{
printf("exit/n");
break;
}
ContinueDebugEvent(pi.dwProcessId,pi.dwThreadId,DBG_CONTINUE);
}
}