我一直有一个问题总是不能解决,希望大家讨论一下:
我在做报表的时候如果吧报表的form在工程文件里面创建之后经常会碰到form会被莫名其妙的释放,而且还不是nil,报表form的所有对象都变成了nil,结果在程序中赋值得时候就会碰到非法操作,起初我以为是某一个地方不小心吧form释放了,但是我反复检查了程序,而且跟踪程序的全部过程,都没有碰到什么地方释放了空间,结果到了某一个地方后form还是会被释放,但是当我把机器重起了之后就没事了,程序运行几次之后又出现问题,始终没有解决,只好把form放在必要的地方创建,然后关闭的时候在释放,但是这个报表是经常要使用的,感觉还是放在工程文件里面创建好一些,不知道星星们有什么高见呢?
欢迎大家讨论,也许是delphi的一个自身的问题,或者是我对delphi的理解有问题,希望星星们解惑!
我的可用分也不多,只是想给大家一个讨论的机会,欢迎兄弟们进来发言!

解决方案 »

  1.   

    尽量不要用现成的报表软件(如QuickRep);
    还是自己编来的放心
      

  2.   

    既然在工程文件中创建,那么肯定是一直要到Application结束的时候才被释放另外,你在工程文件中创建窗体,那么只有在指定窗体的Owner为Application的时候才能保证窗体在整个程序运行期间存在于内存中。所以我认为你的问题,要么是指定Owner不对,要不就是在哪里释放了窗体,只是你没有跟踪到(因为没有代码,所以我不知道哪里出的错误!)再者,频繁使用的窗体其实也不一定非要放在Project里面进行创建,也完全可以随时用随时创建,关闭的时候立刻释放(最好用FreeAndNil进行释放!)楼主最好是给个代码,要不无法知道你哪里出错!
      

  3.   

    procedure TFrm_xzkh.printreport;
    var
      sqlwhere:String;
      account:String;
      str:String;
      i:integer;
      postcode,yhmc,lxr,lxdh,yhdz:String;
    begin
      if Listbox1.Items.Count>0 then
        begin
          str:=Listbox1.Items[Listbox1.itemindex];
          i:=pos(',',str);
          account:=copy(str,1,i-1);
          sqlwhere:='(ACCOUNT='''+account+''') and (KSSJ>='''+trim(edit3.text)+'000000'') and (KSSJ<='''+trim(edit4.text)+'240000'')';
    //获取客户资料
          dm_public.query_public.Close;
          dm_public.query_public.SQL.Clear;
          dm_public.query_public.SQL.Add('select * from CUSTOMER where ACCOUNT='''+account+'''');
          dm_public.query_public.Open;
          postcode:=dm_public.query_public.fieldbyname('POSTCODE').AsString;
          yhmc:=dm_public.query_public.fieldbyname('YHMC').AsString;
          lxr:=dm_public.query_public.fieldbyname('LXR').AsString;
          lxdh:=dm_public.query_public.fieldbyname('LXDH').AsString;
          yhdz:=dm_public.query_public.fieldbyname('YHDZ').AsString;
          dm_public.query_public.Close;
    //获取客户资料
    //清空临时库
          dm_public.Query_public.close;
          dm_public.Query_public.sql.Clear;
          dm_public.Query_public.SQL.Add('delete from TEMPTJ');
          dm_public.Query_public.ExecSQL;
    //清空临时库
    //写入临时库
          if (flag=2) or (flag=3) or (flag=6) then
            begin
              dm_public.Query_public.close;
              dm_public.Query_public.sql.Clear;
              dm_public.Query_public.SQL.Add('insert into TEMPTJ(ZJHM) select distinct ZJHM from THFY where'+sqlwhere);
              dm_public.Query_public.ExecSQL;
            end
          else if (flag=4) or (flag=5) then
            begin
              dm_public.Query_public.close;
              dm_public.Query_public.sql.Clear;
              dm_public.Query_public.SQL.Add('insert into TEMPTJ(PASSWORD,ZJHM) select distinct PASSWORD,ZJHM from THFY where'+sqlwhere);
              dm_public.Query_public.ExecSQL;
            end;
    //写入临时库
    //国内话费
          dm_public.Query_public.Close;
          dm_public.Query_public.SQL.clear;
          if (flag=2) or (flag=3) then
            dm_public.Query_public.sql.add('update TEMPTJ SET GNHF=(select sum(THFY) from THFY where '+sqlwhere+' and substring(BJHM,2,1)<>''0'' and THFY.ZJHM=TEMPTJ.ZJHM)')
          else if (flag=4) or (flag=5) then
            dm_public.Query_public.sql.add('update TEMPTJ SET GNHF=(select sum(THFY) from THFY where '+sqlwhere+' and substring(BJHM,2,1)<>''0'' and THFY.PASSWORD=TEMPTJ.PASSWORD and THFY.ZJHM=TEMPTJ.ZJHM)')
          else if (flag=6) then
            dm_public.Query_public.sql.add('update TEMPTJ SET GNHF=(select sum(YSSF) from THFY where '+sqlwhere+' and substring(BJHM,2,1)<>''0'' and THFY.ZJHM=TEMPTJ.ZJHM)');
          dm_public.Query_public.ExecSQL;
    //国内话费
    //国际话费
          dm_public.Query_public.Close;
          dm_public.Query_public.sql.clear;
          if (flag=2) or (flag=3) then
            dm_public.Query_public.sql.add('update TEMPTJ SET GJHF=(select sum(THFY) from THFY where '+sqlwhere+' and substring(BJHM,2,1)=''0'' and THFY.ZJHM=TEMPTJ.ZJHM)')
          else if (flag=4) or (flag=5) then
            dm_public.Query_public.sql.add('update TEMPTJ SET GJHF=(select sum(THFY) from THFY where '+sqlwhere+' and substring(BJHM,2,1)=''0'' and THFY.PASSWORD=TEMPTJ.PASSWORD and THFY.ZJHM=TEMPTJ.ZJHM)')
          else if (flag=6) then
            dm_public.Query_public.sql.add('update TEMPTJ SET GJHF=(select sum(YSSF) from THFY where '+sqlwhere+' and substring(BJHM,2,1)=''0'' and THFY.ZJHM=TEMPTJ.ZJHM)');      dm_public.Query_public.ExecSQL;
    //国际话费
    //计算总额
          dm_public.Query_public.Close;
          dm_public.Query_public.sql.clear;
          dm_public.Query_public.sql.add('update TEMPTJ SET GNHF=0 WHERE GNHF IS NULL');
          dm_public.Query_public.ExecSQL;
          dm_public.Query_public.Close;      dm_public.Query_public.Close;
          dm_public.Query_public.sql.clear;
          dm_public.Query_public.sql.add('update TEMPTJ SET GJHF=0 WHERE GJHF IS NULL');
          dm_public.Query_public.ExecSQL;
          dm_public.Query_public.Close;      dm_public.Query_public.sql.clear;
          dm_public.Query_public.sql.add('update TEMPTJ SET TOTAL=GNHF+GJHF');
          dm_public.Query_public.ExecSQL;
    //计算总额      dm_public.Query_public.Close;
          dm_public.Query_public.SQL.Clear;
          dm_public.Query_public.SQL.Add('select * from TEMPTJ order by PASSWORD,ZJHM');
          dm_public.Query_public.Open;      dm_public.Query1.Close;
          dm_public.Query1.SQL.Clear;
          dm_public.Query1.SQL.Add('select * from THFY where '+sqlwhere);
          if (flag=2) or (flag=3) OR (flag=6) then//正常
            dm_public.Query1.SQL.Add(' order by ZJHM,KSSJ')
          else if (flag=4) or (flag=5) then
            dm_public.Query1.SQL.Add(' order by PASSWORD,ZJHM,KSSJ');
          dm_public.Query1.Open;      if (flag=2) then//分钟
            begin
              frm_zjhmfz.QRLabel4.Caption:='通话时长(分)';
              frm_zjhmfz.QRExpr2.Expression:='Query1.SFSJ';
            end
          else if flag=4 then//密码分钟
            begin
              frm_zjhmfz.QRLabel4.Caption:='通话时长(分)';
              frm_mmfz.QRExpr2.Expression:='Query1.SFSJ';
            end
          else if (flag=3) then//秒
    //        frm_zjhmfz.QRExpr2.Expression:='FORMATNUMERIC(''0.00'',Query1.SFSJ - 1+(Query1.SJSJ/100))'
            begin
              frm_zjhmfz.QRLabel4.Caption:='通话时长(秒)';
              frm_zjhmfz.QRExpr2.Expression:='(query1.sfsj-1)*60+query1.sjsj';
            end
          else if flag=5 then//密码秒
    //        frm_mmfz.QRExpr2.Expression:='FORMATNUMERIC(''0.00'',Query1.SFSJ - 1+(Query1.SJSJ/100))';
            begin
              frm_mmfz.QRLabel4.Caption:='通话时长(秒)';
              frm_mmfz.QRExpr2.Expression:='(query1.sfsj-1)*60+query1.sjsj';
            end;      Frm_zjhm.QRLabel_POST.Caption:=postcode;
          frm_zjhm.QRLabel_ZH.Caption:=account;
          frm_zjhm.QRLabel_YHMC.Caption:=yhmc;
          frm_zjhm.QRLabel_LXR.Caption:=lxr;
          frm_zjhm.QRLabel_LXDH.Caption:=lxdh;
          frm_zjhm.QRLabel_YHDZ.Caption:=yhdz;
          frm_zjhm.QRLabel_start.Caption:=copy(edit3.Text,1,4)+'年'+copy(edit3.text,5,2)+'月'+copy(edit3.text,7,2)+'日';
          frm_zjhm.QRLabel_end.Caption:=copy(edit4.Text,1,4)+'年'+copy(edit4.text,5,2)+'月'+copy(edit4.text,7,2)+'日';      frm_zjhmfz.qrlabel_ZH.Caption:='账号'+account;
          frm_zjhm.QRLabel1.Caption:=frm_main.dwmc;      Frm_mmtittle.QRLabel_POST.Caption:=postcode;
          frm_mmtittle.QRLabel_ZH.Caption:=account;
          frm_mmtittle.QRLabel_YHMC.Caption:=yhmc;
          frm_mmtittle.QRLabel_LXR.Caption:=lxr;
          frm_mmtittle.QRLabel_LXDH.Caption:=lxdh;
          frm_mmtittle.QRLabel_YHDZ.Caption:=yhdz;
          Frm_mmtittle.QRLabel_start.Caption:=copy(edit3.Text,1,4)+'年'+copy(edit3.text,5,2)+'月'+copy(edit3.text,7,2)+'日';
          Frm_mmtittle.QRLabel_end.Caption:=copy(edit4.Text,1,4)+'年'+copy(edit4.text,5,2)+'月'+copy(edit4.text,7,2)+'日';      Frm_mmfz.QRLabel_ZH.Caption:='账号'+account;
          frm_mmtittle.QRLabel1.Caption:=frm_main.dwmc;      frm_true.QRLabel_ZH.Caption:='账号'+account;      QRCompositeReport1.Preview;
        end
      else
        showmessage('请重新输入客户条件!');
      dm_public.Query_public.Close;
      dm_public.Query1.Close;
    end;
      

  4.   

    或者把报表放到DLL中,这样更好,多个相同进程共享!
      

  5.   

    我想这个问题还不能上升到“也许是delphi的一个自身的问题,或者是我对delphi的理解有问题”的高度吧。跟报表也没有什么关系,并不是因为在Form上使用了报表就一定会出现这个问题。我想还是你的程序问题。
    1、“跟踪程序的全部过程”,你可以跟踪这个Form是在哪个地方被释放的,就可以找到原因。
    2、也可能不是Form本身的释放问题,而是在报表的打印/预览过程中某些代码出错。
    就我的使用经验来说,还没有发现Delphi中有明显错误的地方,当然不排除有不够好的地方。100%(对我而言)的情况下,都是因为我自己的错误。————————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    ————————————————————————————————————
      

  6.   

    应该是你的代码问题,比如对象的作用域,创建的时机等等,check一下吧。
    你这样问,无疑是让大家天方夜谭阿
      

  7.   

    第一次动态创建时不释放 if rep <> nil then ,在关闭整个程序时释放(我没这样做过,都是用的时候创建,用完就free了)
      

  8.   

    大家能不能把qq留下啊?
    代码调用的部分已经贴出来了,我反复检查过其他地方的调用情况,没有地方涉及到这些form,只有这里使用,这个地方第一次进来就报错,但是重新启动后能稳定一些。
    动态创建是很好,但是一次运行程序需要数百次创建和释放,感觉还是静态创建比较好一些,不知道大家都怎么处理这种情况?
      

  9.   

    你上面贴的这个代码肯定不会释放TFrm_xzkh的对象的!
      

  10.   

    这么长,难怪网页老打不开
    Frm_mmfz frm_true Frm_mmtittle都是些什么啊?没有初始化或者创建的代码阿
      

  11.   

    在要用到窗体之前加上这一行如何
    if not Assigned(Frm_mmtittle) then
      Frm_mmtittle:=TFrm_mmtittle.Create(Application);
      

  12.   

    上面的代码中frm###.qrlabel的地方会出错,但是不一定是什么地方错,而且不一定每次都报错,实在想不明白!其他的form我都是用玩free,nil,但是这个没这么用,结果除了问题,只是想知道为什么会这样?而且每次出现问题的时候都是机器表现的极慢,开始以为是系统的问题,让客户重新装了系统,就好了,但是今天发现我的机器也这样了,感觉好像不是系统和病毒的问题,猜想可能是内存调度的问题,不知道大家有没有这么用过,是不是大量的内存调度会出现地址错误?还是有些地方存在内存黑洞?
      

  13.   

    Frm_mmfz frm_true Frm_mmtittle这些form都是报表的form,都在application里面创建了
      

  14.   

    建议参考一下 DELPHI 的例子
    \Delphi6\Demos\QuickRpt\Qr2
      

  15.   

    就是按照demo里面的例子写得,我最开始写报表的时候就是学的demo里面的!
     TOMWLD(仰首再笑天) 没有这么用过,他和application.createform(Tform,Form)有什么区别呢?
      

  16.   

    你说的内存调度会出现内存错误是不可能的,所有的内存调度和进程空间分配都是由操作系统来完成的,除非你的系统有问题!内存黑洞就是你自己程序的问题了,出现内存泄露!你用什么方法调试的,在Watch窗口里面把你出错的(你所说的会“自动”释放的)窗体变量名称加入,然后开始单步调试,观察Watch窗口里的变量的值,不可能查不到出错的地方的!
      

  17.   

    我跟踪过这个form,通常情况下当我加入跟踪器的时候就没有问题了,但是偶尔跟踪的时候也会出现到frm_@@@.qrlable:=*****的时候突然form=nil了,我跟踪了3天,但是就是找不到规律,后来没办法就改成动态了,结果就好了。感觉在form成为nil的时候都无法跟踪内存状态了,很是奇怪!
      

  18.   

    呵呵,从这些代码中,我实在看不出来有什么不对的地方.
    不过给楼主一个建议,您的代码不太规范,一个模块不要超过50行,如果超过了,就要考虑细化模块了.
    另外您的代码中所有open和execsql的地方都没有try,这样不太好.一旦程序出错,肯定会出现一个很恶心的英文对话框.
      

  19.   

    但是偶尔跟踪的时候也会出现到frm_@@@.qrlable:=*****的时候突然form=nil了
    是不是在单步调试到这里的时候,(选中frm_@@@)然后鼠标放上去会出现form;=nil的提示?
      

  20.   

    这么长晕, 不会Delphi的问题, 应该是你代码的问题,  把一个很大的程序分成子程序是一种好的编程习惯, 看看VCL的代码,  哪个过程有超过这么多行的,  问题没给你解决发发牢骚,  请多包涵
      

  21.   

    呵呵,我也讨厌长的代码段,但是这个程序给的钱太少只好这样了是watch list中显示form 不是nil 而是他的一些成员编程了nil,很奇怪
      

  22.   

    现在机器上没有DELPHI:(....在做PHP 只好帮你UP一下
      

  23.   

    给的钱太少就这样写
    我晕!!!
    我想一个稍微自负一点的程序员会如此吗
    我看好象vfp之类的代码风格!!!
      

  24.   

    没在代码里找到什么错误!
    但是:
    无论给多少钱,编程习惯还都是一样的。不明白为什么连WITH都不用。
      

  25.   

    这大数是你的代码有问题,可能是哪个地方不小心释放了,在调试时尽量不要用try...except end;//先屏蔽掉,然后独立运行看看能不能找出错误。我们也没有你的代码,这种问题实在没有遇到过,建议你把你的代码中的功能分独立些,尽量多提取一些函数,第每函数的代码行数在50行内,最好是30行,像vcl源码一样,这样调试起来会方便些,
    不好意思,没能帮上什么忙!!
      

  26.   

    原本上述代码是一个个人行为的东西,所以写的时候对方说得很简单,然后就这样写了,结果对方不断增加条件,然后就写烦了,最终成了这样一个段代码,现在也懒得改了。
    不过改成动态创建之后就没问有问题了,很奇怪!
    流水不腐兄弟,如果别人给你的钱还不到你的成本,你会写得非常规范吗?至于with语句,我一直就不喜欢用,因为我喜欢用delphi的提示功能。这个程序原本非常简单,也就几天就搞定了,结果后来对方不断在报表上增加要求,程序一直写了3个月,最可恨的是,每天增加一些!
      

  27.   

    还是非常感谢楼上的众猩猩们,呵呵!其实这个问题我也一直怀疑是自己写程序习惯的问题,以前没怎么写过静态创建的form,所有的都是在使用过程中动态创建,释放。这次也是图一个省事,结果还是绕回来了!
    关于上面哪为兄弟说一个procedure不要超过50行,我也一直这样写,后来发现过程调用多了以后会一定程度上影响程序的速度,然后就在一些小程序中不在单独拆分功能了!估计这种情况肯定很多朋友都碰到过,一个小问题总也解决不了,然后就绕过去,然后慢慢就不再那么深究了问题的根本了!
      

  28.   

    做的时候都是动态创建的,静态的话用quickrep。destroy释放,工程中一般除了主窗体外都是动态创建的
      

  29.   

    做的时候都是动态创建的,静态的话用quickrep。destroy释放,工程中一般除了主窗体外都是动态创建的
      

  30.   

    从来没有遇到这种情况,不管是自己的还是现成的报表.你用的这几个报表是不是QRCompositeReport1这样同名的??这样的话你的其它什么qrlabel等等也是同名的是不是?为什么要把他放到那么多的FROM上去呢?好象有5到6个FORM呀,这样调来调去肯定会出现问题.如果是同一个报表,不如把他放到同一个FORM上!if not Assigned(Frm_mmtittle) then
      Frm_mmtittle:=TFrm_mmtittle.Create(Application);这样子是先判断后创建(如果没有创建),一般application时创建的窗体是不会自动NIL的,只有退出才有.你动态创建的时候,也就是当前只有一个FORM,其它的QRLABEL也只有一个,application时创建已经有多个QRlabel,而且同名,可能会有冲突,试试看,我猜的
      

  31.   

    从来没有遇到这种情况,不管是自己的还是现成的报表.你用的这几个报表是不是QRCompositeReport1这样同名的??这样的话你的其它什么qrlabel等等也是同名的是不是?为什么要把他放到那么多的FROM上去呢?好象有5到6个FORM呀,这样调来调去肯定会出现问题.如果是同一个报表,不如把他放到同一个FORM上!if not Assigned(Frm_mmtittle) then
      Frm_mmtittle:=TFrm_mmtittle.Create(Application);这样子是先判断后创建(如果没有创建),一般application时创建的窗体是不会自动NIL的,只有退出才有.你动态创建的时候,也就是当前只有一个FORM,其它的QRLABEL也只有一个,application时创建已经有多个QRlabel,而且同名,可能会有冲突,试试看,我猜的
      

  32.   

    cow8063(吴七郎) 说得很有道理,我的确是这种情况,用了CompositeReport这种报表累加得,而且qrlabel的名称也一样,就是不明白这样有什么问题啊?两个qrlabel虽然同名,但是却属于不同的类啊,按说不应该出现冲突啊。除非是一种情况:qrlabel都属于form的objects中,而不属于quickrep中,有可能compositereport中全部的qrlabel最后的归属与compositereport,从而造成了冲突,感觉这个解释还是有可能的!