用VC6调试CGI程序

问题的起因

前几天为了调试gsoap生成的server端代码(实际是一个cgi程序),一直没有找到好的调试方法,网上搜也没有找到一个切实可行的方法,于是分析了一下cgi的原理,终于找到一个调试CGI的办法。

CGI程序的本质

其实cgi就是一个没有界面的exe程序,cgi程序从stdin读取消息,从 stdout输出结果,一个cgi程序往往是部署在一个http server上,比如iis或apache,http server在接到cgi请求以后会创建一个线程,在线程里用CreatePipe创建一个匿名管道:

The CreatePipe function creates an anonymous pipe, and returns handles to the read and write ends of the pipe.

BOOL CreatePipe(
PHANDLE
hReadPipe, // read handle
PHANDLE hWritePipe, // write handle
LPSECURITY_ATTRIBUTES lpPipeAttributes, // security attributes
DWORD nSize // pipe size
);

该函数在创建匿名管道的同时返回两个句柄:管道读句柄hReadPipe和管道写句柄hWritePipe,通过hReadPipe和hWritePipe所指向的句柄可分别以只读、只写的方式去访问管道。

然后,创建出cgi进程,并替换cgi进程的标准输入、标准输出和标准错误句柄。cgi进程运行以后从stdin(读管道句柄)读取http server中穿过来的消息,然后根据请求执行响应的操作,最后将结果通过stdout(实际已经被与客户端对应的socket句柄替换)输出到http server或直接输出给客户端,之后cgi进程就结束了。

下面的是从msdn里的http server例子HTTPSVR中摘出来的,显示了启动一个CGI程序的过程:

UINT CGIThread( LPVOID pvParam )
{
    ......
    HANDLE hWritePipe, hReadPipe;
    // create a pipe we'll use for STDIN....
    CreatePipe( &hReadPipe, &hWritePipe, &g_sa, 0 ) )
    ......
    //创建CGI进程
    PROCESS_INFORMATION pi;
    STARTUPINFO si = {0};
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.wShowWindow = SW_HIDE;
    si.hStdInput = hReadPipe;
    si.hStdOutput = pReqSock->m_hFile;
    si.hStdError = pReqSock->m_hFile;
    bOk = CreateProcess( NULL, strCmdLine.GetBuffer(1),
                         NULL, NULL, TRUE,
                         dwCreateFlags, pEnv,
                         strDir, &si, &pi );
    ......
    // send the body of the post to the stdin....
    WriteFile( hWritePipe, pRequest->m_baBody.GetData(),
    pRequest->m_cbBody, &dwWritten, NULL );
    ......
    //结果输出
    //由于直接将与客户端连接的socket句柄赋值给了si.hStdOutput
    //所以cgi程序输出的时候直接将结果写回给客户端了
    //所以http server在这里不需要再读取cgi的输出了
}

调试CGI程序

了解了CGI的本质,调试的思路就有了,就是调试一个事先执行起来的exe程序嘛,用VC6中的“Build|Start Debug|Attach to process”方法调试就搞定了。需要注意的就是cgi运行的很快,往往是当你Attach这个进程的时候,它往往已经执行完毕退出了,即便不退出可能您所关心的代码部分已经执行过了,这个问题可以通过在cgi的main函数开头添加一个死循环来解决,如下:
   
    int main(int argc, char **argv)
    {
        while(true)
 {
     Sleep(2000);
 }

 //真正的cgi代码,比如:
 int m, s; /* master and slave sockets */
        struct soap soap;
        soap_init(&soap);

 ......

    }

这样当CGI进程起来以后,就会进入while(true)循环,这时你就可以很从容的用VC调试器 Attach到cgi进程,然后打开对应的源码,在Sleep(2000)处设置一个断点,就可以看到cgi进程乖乖进入断点了,在soap_init(&soap)处使用“Set Next Statement”命令就可以跳转到soap_init行向下执行了。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值