发现一个以前不会发现的虫子

本文通过实验发现,在特定条件下使用return时,外层的__finally块将不会被执行,导致资源未被正确释放。文章提供了详细的代码示例来验证这一结论。

 ArWen@csdn         2008-11-28

 

今天,有客户反映,说程序运行出错了。我问故障现象能重现吗?客户很肯定地说,能。

暗喜,最是那不能重现的BUG让人头疼,而这能重现的,无疑是开发者有信心去处理的。

于是请客户说明了一下问题的出现过程与现象。即,点击设置服务器地址按钮后,程序将弹出对话窗口让用户进行选择,但此时,用户却不进行选择,取消退出该对话窗口,以后,再点击设置服务器地址按钮,系统就会弹出提示:A component named WebServiceAddressForm already exists.

我照此操作,果然,该提示还在那里等着我。

这个问题简单嘛,很快地,我就定位到出错源代码:

 

  1. void __fastcall TMainForm::SetWSAddr(bool promptFlag, AnsiString newAddr)
  2. {  // 功能: 设置服务地址
  3.    //  promptFlag: 是否希望显式设置
  4.    //  newAddr: 新的服务地址
  5.    TWebServiceAddressForm * wsForm = new TWebServiceAddressForm(this);
  6.    try
  7.    {   // 处理异常
  8.      try
  9.      {   // 准备工作
  10.       if(newAddr.Length())
  11.          WebServiceAddressForm->Edit2->Text = newAddr;
  12.       AnsiString address = wsForm->WebServiceAddress;       // 服务地址
  13.       if(promptFlag || address.Length() == 0)
  14.       {  // 显示设置
  15.          if(wsForm->ShowModal() != mrOk)
  16.             return;     // 用户不想进行设置,简单退出
  17.          address = wsForm->WebServiceAddress;   // 取得用户设置的地址
  18.       }
  19.       ServiceType = CbwServiceType(wsForm->RadioGroup1->ItemIndex);  // 接口类型
  20.       if(GlobalGraphInterface)      // 重新创建通信接口
  21.          delete GlobalGraphInterface;
  22.       GlobalGraphInterface = NULL;
  23.       if(address.Length())
  24.       {  // 设置服务地址
  25.          GlobalGraphInterface = new TGraphInterface(address, true);
  26.          if(!OnlyForLocal)    // 更新网络资源
  27.             FileModule->RefreshApplicationSettings(address, true);
  28.       }
  29.       if(GlobalGraphInterface)   // 更新客户端访问服务地址
  30.          FClientInterface->WebServiceAddr = GlobalGraphInterface->Address;
  31.      }
  32.       catch(Exception& ex)
  33.       {  // 异常处理
  34.          DEBUGMSG;
  35.          DebugInfoOfTime(CBWDEBUGMSG + ex.Message);
  36.      }
  37.    }                  
  38.    __finally
  39.    {  // 确保退出时,释放wsForm
  40.       wsForm->Free();
  41.    }
  42. }

最终的原因找到了,在于用户不想进行设置,简单退出后,没有调用wsForm->Free()

这点发现让我很吃惊,我当初设计时,考虑到该过程中将进行网络通信,所以必须处理异常,即需要try…catch,但为确保分配的对象能释放,于是在其外面再包了一个try…__finally处理。

 

难道这也有问题?还是程序中的其它逻辑处理不对?

 

幸亏问题很明确,我就重新做一个小程序来进行检验。

 

1.  检验在tryreturn后,是否调用__finally

  1. void __fastcall TForm1::Button1Click(TObject *Sender)
  2. {
  3.    static int count = 0;
  4.    try
  5.    {
  6.       if(count++ % 2)   return
  7.    }
  8.    __finally
  9.    {
  10.       ShowMessage("调用 __finally");
  11.    }
  12. }

结果:每次均调用。

2.  检验在try…__finally中嵌套加入try…catch,其中return后是否调用__finally

 

  1. void __fastcall TForm1::Button1Click(TObject *Sender)
  2. {
  3.    static int count = 0;
  4.    try
  5.    {
  6.       try
  7.       {
  8.          if(count++ % 2)   return;
  9.       }
  10.       catch(...)
  11.       {
  12.       } 
  13.    }
  14.    __finally
  15.    {
  16.       ShowMessage("调用 __finally");
  17.    }
  18. }

结果:调用return后,不再调用__finally

3.  再试一下,看在try…__finally中嵌套加入try…catch,其中catch后是否调用__finally

 

  1. void __fastcall TForm1::Button1Click(TObject *Sender)
  2. {
  3.    static int count = 0;
  4.    try
  5.    {
  6.       try
  7.       {
  8.          if(count++ % 2)
  9.             throw Exception("Exception");
  10.       }
  11.       catch(Exception& ex)
  12.       {
  13.          ShowMessage(ex.Message);
  14.       } 
  15.    }
  16.    __finally
  17.    {
  18.       ShowMessage("调用 __finally");
  19.    }
  20. }

结果:调用。

4.  再试一下,看在try…__finally中嵌套加入try…catch,在catchreturn后是否调用__finally

 

  1. void __fastcall TForm1::Button1Click(TObject *Sender)
  2. {
  3.    static int count = 0;
  4.    try
  5.    {
  6.       try
  7.       {
  8.          if(count++ % 2)
  9.             throw Exception("Exception");
  10.       }
  11.       catch(Exception& ex)
  12.       {
  13.          ShowMessage(ex.Message);
  14.          return;
  15.       } 
  16.    }
  17.    __finally
  18.    {
  19.       ShowMessage("调用 __finally");
  20.    }
  21. }

结果:不调用。

 

看来我的基础不牢啊,狂汗一个

 

回头把程序中的return前加上一个wsFrom->Free(),问题解决了。

 

赶紧检查程序中所有的__finally,花了10分钟,再次搞定。

 

结论:在try…__finally中嵌套try…catch时,若在try…catch中直接return,则不会调用外层的__finally

 

呵呵,一点收获,不知道书上是如何写的,也懒得去翻书了。

 

 

 

 

 

 

 

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值