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;
联系客服