C#调用DELPHI webservice 的DLL 在退出程序是出现异常处理 runtime error 216
1.修改注册表:
开始菜单-运行-输入regedit-点确定-进入注册表, 在:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks 下,应该只有一个正常的键值"{AEB6717E-7E19-11d0-97EE-00C04FD91972}, 将其他的删除。 2.Delphi中利用SEH屏蔽退出时的Runtime ErrorDelphi
写的程序,如果在单元的finalization里出现了一些异常操作会导致退出时抛出Runtime Error ,规范的处理办法当然是解决这些异常,但是有些特殊的情况下,比如用了很多的第三方控件,实在没办法解决问题时,只有把他屏蔽掉,这样给客户的时候就不至于看到满天的Runtime Error 了。前些日子同事正好碰到了这个问题,他写的一个ACTIVEX控件,在客户的IE里关闭的时候就会抛出很多错误导致IE死掉,但是在本机又模拟不出来,问题不能解决,又得应付客户。找我讨论解决方案,我提议可以用SEH解决,于是写了一段代码,效果不错,不敢独享,贴出来希望能够帮大家应付遇到的问题。我的做法其实很简单,就是在 END.之前手工调用Halt释放,并且将Halt抛出的错误屏蔽掉,这样做和正常的DELPHI释放过程没有任何区别,因为End.编译后其实就是一句话 Call Halt0,只是VCL自己没有屏蔽Halt0里抛出的错误,而是跳出个Runtime Error 来;首先就是位置的问题,如果是EXE的话,直接在END.之前就行了,如果是Dll的话就麻烦点,需要挂上DllProc,当wReason = DLL_PROCESS_DETACH时处理。然后就是如何屏蔽错误的问题了,第一个最容易想到的做法就是直接Tryhaltexceptend;
新窗口查看复制到剪贴板打印? Try halt except end;
Try haltexceptend;但是这样是不行的,因为try…except end捕获的错误都会放到System单元的_HandleOnException中处理,函数检查错误类型是否是DelphiException,如果不是就不处理,这个时候就会被DELPHI的顶层异常机制捕获,并抛出Runtime error,halt里抛出来的错误恰恰就是非DelphiException,代码如下:
新窗口查看复制到剪贴板打印? procedure _HandleOnException; … CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException JE @@DelphiException CLD CALL _FpuInit MOV EDX,ExceptClsProc TEST EDX,EDX JE @@exit CALL EDX TEST EAX,EAX JNE @@common JMP @@exit … End;
procedure _HandleOnException;…CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiExceptionJE @@DelphiExceptionCLDCALL _FpuInitMOV EDX,ExceptClsProcTEST EDX,EDXJE @@exitCALL EDXTEST EAX,EAXJNE @@commonJMP @@exit…End;所以,需要借助SHE机制来处理这个问题(哈哈,不然就得改标题了),代码如下(关于如何挂SHE我就不介绍了,我在另外一篇文章《Delphi异常机制与SEH》详细介绍了): 新窗口查看复制到剪贴板打印? asm //挂上SEH xor edx, edx push ebp push OFFSET @@safecode push dword ptr fs:[edx] mov fs:[edx],esp //调用Halt0 call Halt0 jmp @@exit; @@safecode: //如果出现异常继续调用Halt0退出 call Halt0; @@exit: end;
asm//挂上SEHxor edx, edxpush ebppush OFFSET @@safecodepush dword ptr fs:[edx]mov fs:[edx],esp//调用Halt0call Halt0jmp @@exit;@@safecode://如果出现异常继续调用Halt0退出call Halt0;@@exit:end;这个做法的好处就是,不会对DELPHI正常释放过程产生影响,所有的释放操作都是和VCL一致的,只是不会把错误显示出来。
以下是完整代码:
一、EXE的情况,把代码放在工程文件
二、DLL的情况,把代码放在工程文件里
明生注:Exe的 在工程文件PER里面写入此过程
新窗口查看复制到剪贴板打印? procedure Halt0; begin Halt; end;
procedure Halt0;begin Halt;end;
然后在BEGIN和END.之间写上此汇编
新窗口查看复制到剪贴板打印? asm xor edx, edx push ebp push OFFSET @@safecode push dword ptr fs:[edx] mov fs:[edx],esp call Halt0 jmp @@exit; @@safecode: call Halt0; @@exit: end;
asm xor edx, edx push ebp push OFFSET @@safecode push dword ptr fs:[edx] mov fs:[edx],esp call Halt0 jmp @@exit; @@safecode: call Halt0; @@exit: end;DLL处写入:
新窗口查看复制到剪贴板打印? procedure Halt0; begin Halt; end; var OldProc: Pointer; procedure DLLEntryPoint(dwReason: DWord); begin if (dwReason = DLL_PROCESS_DETACH) Then Begin asm xor edx, edx push ebp push OFFSET @@safecode push dword ptr fs:[edx] mov fs:[edx],esp call Halt0 jmp @@exit; @@safecode: call Halt0; @@exit: end; end; end; begin DllProc := @DLLEntryPoint; DllProcEX := @DLLEntryPoint; end.
例子
library HouseAgent;
{ Important note about DLL memory management: ShareMem must be the first unit in your library's USES clause AND your project's (select Project-View Source) USES clause if your DLL exports any procedures or functions that pass strings as parameters or function results. This applies to all strings passed to and from your DLL--even those that are nested in records and classes. ShareMem is the interface unit to the BORLNDMM.DLL shared memory manager, which must be deployed along with your DLL. To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters. }
uses SysUtils, Forms, Windows, Messages, Classes, Dialogs, cell in 'cell.pas' {cellfrm}, ManageUser in 'ManageUser.pas' {ManageUserfrm};
{$R *.res} procedure showBook(hdc:Thandle); stdcall ; {接口函数} var frm :Tcellfrm; begin frm := Tcellfrm.Create(Application); frm.ParentWindow := hdc; frm.Show; end;
procedure showManagerUser(hdc:Thandle;UserId:integer;Soap_Mode:integer); stdcall ; {接口函数 UserId:登录店ID Soap_Mode是否SOAP模式} var frm :TManageUserfrm; begin frm := TManageUserfrm.Create(Application); frm.ParentWindow := hdc; frm.UserId:=UserId; frm.Soap_Mode:=Soap_Mode; frm.Show; end;
//异常处理 procedure Halt0; begin Halt; end;
procedure DLLEntryPoint(dwReason: DWord); begin if (dwReason = DLL_PROCESS_DETACH) Then Begin asm xor edx, edx push ebp push OFFSET @@safecode push dword ptr fs:[edx] mov fs:[edx],esp call Halt0 jmp @@exit; @@safecode: call Halt0; @@exit: end; end; end;
|