ArWen@csdn 2008-11-28
今天,有客户反映,说程序运行出错了。我问故障现象能重现吗?客户很肯定地说,能。
暗喜,最是那不能重现的BUG让人头疼,而这能重现的,无疑是开发者有信心去处理的。
于是请客户说明了一下问题的出现过程与现象。即,点击设置服务器地址按钮后,程序将弹出对话窗口让用户进行选择,但此时,用户却不进行选择,取消退出该对话窗口,以后,再点击设置服务器地址按钮,系统就会弹出提示:A component named WebServiceAddressForm already exists.
我照此操作,果然,该提示还在那里等着我。
这个问题简单嘛,很快地,我就定位到出错源代码:
- void __fastcall TMainForm::SetWSAddr(bool promptFlag, AnsiString newAddr)
- { // 功能: 设置服务地址
- // promptFlag: 是否希望显式设置
- // newAddr: 新的服务地址
- TWebServiceAddressForm * wsForm = new TWebServiceAddressForm(this);
- try
- { // 处理异常
- try
- { // 准备工作
- if(newAddr.Length())
- WebServiceAddressForm->Edit2->Text = newAddr;
- AnsiString address = wsForm->WebServiceAddress; // 服务地址
- if(promptFlag || address.Length() == 0)
- { // 显示设置
- if(wsForm->ShowModal() != mrOk)
- return; // 用户不想进行设置,简单退出
- address = wsForm->WebServiceAddress; // 取得用户设置的地址
- }
- ServiceType = CbwServiceType(wsForm->RadioGroup1->ItemIndex); // 接口类型
- if(GlobalGraphInterface) // 重新创建通信接口
- delete GlobalGraphInterface;
- GlobalGraphInterface = NULL;
- if(address.Length())
- { // 设置服务地址
- GlobalGraphInterface = new TGraphInterface(address, true);
- if(!OnlyForLocal) // 更新网络资源
- FileModule->RefreshApplicationSettings(address, true);
- }
- if(GlobalGraphInterface) // 更新客户端访问服务地址
- FClientInterface->WebServiceAddr = GlobalGraphInterface->Address;
- }
- catch(Exception& ex)
- { // 异常处理
- DEBUGMSG;
- DebugInfoOfTime(CBWDEBUGMSG + ex.Message);
- }
- }
- __finally
- { // 确保退出时,释放wsForm
- wsForm->Free();
- }
- }
最终的原因找到了,在于用户不想进行设置,简单退出后,没有调用wsForm->Free()。
这点发现让我很吃惊,我当初设计时,考虑到该过程中将进行网络通信,所以必须处理异常,即需要try…catch,但为确保分配的对象能释放,于是在其外面再包了一个try…__finally处理。
难道这也有问题?还是程序中的其它逻辑处理不对?
幸亏问题很明确,我就重新做一个小程序来进行检验。
1. 检验在try中return后,是否调用__finally
- void __fastcall TForm1::Button1Click(TObject *Sender)
- {
- static int count = 0;
- try
- {
- if(count++ % 2) return;
- }
- __finally
- {
- ShowMessage("调用 __finally");
- }
- }
结果:每次均调用。
2. 检验在try…__finally中嵌套加入try…catch,其中return后是否调用__finally
- void __fastcall TForm1::Button1Click(TObject *Sender)
- {
- static int count = 0;
- try
- {
- try
- {
- if(count++ % 2) return;
- }
- catch(...)
- {
- }
- }
- __finally
- {
- ShowMessage("调用 __finally");
- }
- }
结果:调用return后,不再调用__finally
3. 再试一下,看在try…__finally中嵌套加入try…catch,其中catch后是否调用__finally
- void __fastcall TForm1::Button1Click(TObject *Sender)
- {
- static int count = 0;
- try
- {
- try
- {
- if(count++ % 2)
- throw Exception("Exception");
- }
- catch(Exception& ex)
- {
- ShowMessage(ex.Message);
- }
- }
- __finally
- {
- ShowMessage("调用 __finally");
- }
- }
结果:调用。
4. 再试一下,看在try…__finally中嵌套加入try…catch,在catch中return后是否调用__finally
- void __fastcall TForm1::Button1Click(TObject *Sender)
- {
- static int count = 0;
- try
- {
- try
- {
- if(count++ % 2)
- throw Exception("Exception");
- }
- catch(Exception& ex)
- {
- ShowMessage(ex.Message);
- return;
- }
- }
- __finally
- {
- ShowMessage("调用 __finally");
- }
- }
结果:不调用。
看来我的基础不牢啊,狂汗一个…
回头把程序中的return前加上一个wsFrom->Free(),问题解决了。
赶紧检查程序中所有的__finally,花了10分钟,再次搞定。
结论:在try…__finally中嵌套try…catch时,若在try…catch中直接return,则不会调用外层的__finally。
呵呵,一点收获,不知道书上是如何写的,也懒得去翻书了。