众所周知,Freebasic和VBA一样也是使用On errror方式进行错误处理的,没有在语法上实现try catch finally的错误处理机制。那么如何自己实现Try catch finally呢,使用下面这个exceptions.bi的宏文件。
源代码如下:
#Ifndef EXCEPTION_RECORD
#Error Please include windows.bi first!
#EndIf
#Ifndef __FB_WIN32__
#Error Operating system not supported!
#EndIf
#Macro __trycheck(_name_)
#Ifndef __eh_##_name_##_try_set
#Error Undefined Try block name: _name_
#EndIf
#EndMacro
#Macro Try(_name_)
#Ifdef __eh_##_name_##_try_set2
#Error Duplicated definition: _name_
#EndIf
#Define __eh_##_name_##_try_set
#Define __eh_##_name_##_try_set2
Scope
Dim As Byte __eh_##_name_##_doret = 0
Dim As Any Ptr __eh_##_name_##_ehesp = 0
Dim As Byte __eh_##_name_##_ehandled = 0
Dim As Byte __eh_##_name_##_wasincatch = 0
Dim As EXCEPTION_RECORD Ptr __eh_##_name_##_erec = 0
Dim As Byte __eh_##_name_##_erecarr(SizeOf(EXCEPTION_RECORD))
Dim As Byte __eh_##_name_##_econarr(SizeOf(CONTEXT))
Asm
__eh_##_name_##_try:
pushad
mov esi, offset __eh_##_name_##_except
push esi
push dword ptr fs:[0]
mov dword ptr fs:[0], esp
End Asm
Scope
#EndMacro
#Macro __catch_codestart(_name_)
End Scope
#Define __eh_##_name_##_except_set
Asm
jmp __eh_##_name_##_finally_continue
__eh_##_name_##_except:
mov eax, [esp + 4]
mov eax, [eax + 4]
cmp eax, 2
jne __eh_##_name_##_nounwind
mov byte ptr [__eh_##_name_##_doret], 1
call __eh_##_name_##_finally
mov eax, 1
ret
__eh_##_name_##_nounwind:
mov eax, esp
mov esp, [esp + 8]
pop dword ptr fs:[0]
mov [esp], eax
add esp, 4
popad
mov eax, [esp - 36]
mov [__eh_##_name_##_ehesp], eax
mov edx, eax
End Asm
Scope
Dim As EXCEPTION_RECORD Ptr ERecord
Dim As CONTEXT Ptr EContext
#Define ECode (ERecord->ExceptionCode)
#Define EAddress (ERecord->ExceptionAddress)
#Define ENumParams (ERecord->NumberParameters)
#Define EParamsPtr CPtr(Integer Ptr, (@ERecord->ExceptionInformation(0)))
Asm
push edx
mov edx, [edx + 8]
mov [EContext], edx
pop edx
mov edx, [edx + 4]
mov [ERecord], edx
End Asm
#Define __ersize (SizeOf(EXCEPTION_RECORD) + ERecord->NumberParameters * 4 - 4)
#Define __erarrsize (UBound(__eh_##_name_##_erecarr) + 1)
CopyMemory(Cast(Any Ptr, @__eh_##_name_##_erecarr(0)), Cast(Any Ptr, ERecord), IIf(__ersize <= __erarrsize, __ersize, __erarrsize))
#Undef __erarrsize
#Undef __ersize
ERecord = Cast(EXCEPTION_RECORD Ptr, @__eh_##_name_##_erecarr(0))
__eh_##_name_##_erec = ERecord
CopyMemory(Cast(Any Ptr, @__eh_##_name_##_econarr(0)), Cast(Any Ptr, EContext), UBound(__eh_##_name_##_econarr) + 1)
EContext = Cast(CONTEXT Ptr, @__eh_##_name_##_econarr(0))
Select Case ECode
#EndMacro
#Define EParams(_i_) IIf(EParamsPtr <> 0 AndAlso _i_ < ENumParams, (EParamsPtr)[_i_], 0)
#Macro __catch_next(_name_, _ecode_)
#Define __ ,
Case _ecode_
#Undef __
Asm mov byte ptr [__eh_##_name_##_ehandled], 1
Asm mov byte ptr [__eh_##_name_##_wasincatch], 1
#EndMacro
#Macro Catch(_name_, _ecode_)
__trycheck(_name_)
#Ifdef __eh_##_name_##_finally_set
#Error Using Catch after Finally not allowed!
#EndIf
#Ifndef __eh_##_name_##_except_set
__catch_codestart(_name_)
#EndIf
__catch_next(_name_, _ecode_)
#EndMacro
#Macro Finally(_name_)
#Ifdef __eh_##_name_##_finally_set
#Error Multiple Finally statements not allowed!
#EndIf
__trycheck(_name_)
#Ifndef __eh_##_name_##_except_set
Catch(_name_, 0)
__eh_##_name_##_ehandled = 0
#EndIf
End Select
End Scope
#Define __eh_##_name_##_finally_set
Asm
mov byte ptr [__eh_##_name_##_doret], 0
__eh_##_name_##_finally:
End Asm
Scope
Select Case 0
Case 0
#EndMacro
#Macro Leave(_name_)
__trycheck(_name_)
Asm
mov byte ptr [__eh_##_name_##_doret], 1
call __eh_##_name_##_finally
End Asm
#EndMacro
#Macro EndTry(_name_)
__trycheck(_name_)
#Ifndef __eh_##_name_##_except_set
Catch(_name_, 0)
__eh_##_name_##_ehandled = 0
#EndIf
#Ifndef __eh_##_name_##_finally_set
Finally(_name_)
#EndIf
End Select
End Scope
Asm
cmp byte ptr [__eh_##_name_##_doret], 0
je __eh_##_name_##_noret
ret
__eh_##_name_##_noret:
cmp byte ptr [__eh_##_name_##_ehandled], 0
jne __eh_##_name_##_continue
mov esp, [__eh_##_name_##_ehesp]
End Asm
RaiseException(__eh_##_name_##_erec->ExceptionCode, __eh_##_name_##_erec->ExceptionFlags, __eh_##_name_##_erec->NumberParameters, @__eh_##_name_##_erec->ExceptionInformation(0))
Asm
int3
__eh_##_name_##_finally_continue:
mov byte ptr [__eh_##_name_##_doret], 1
call __eh_##_name_##_finally
pop dword ptr fs:[0]
add esp, 36
jmp __eh_##_name_##_end_try
__eh_##_name_##_continue:
cmp byte ptr [__eh_##_name_##_wasincatch], 1
je __eh_##_name_##_end_try
pop dword ptr fs:[0]
add esp, 36
__eh_##_name_##_end_try:
End Asm
End Scope
#Undef __eh_##_name_##_try_set
#EndMacro
#Macro ExitTry(_name_)
__trycheck(_name_)
Leave(_name_)
Asm jmp __eh_##_name_##_end_try
#EndMacro
#Macro RaiseParam(_ecode_, _nump_, _params_)
Scope
#Define __ ,
Dim __params(_nump_ - 1) As Integer = {_params_}
#Undef __
RaiseException(_ecode_, 0, _nump_, @__params(0))
End Scope
#EndMacro
#Macro Raise(_ecode_)
RaiseException(_ecode_, 0, 0, 0)
#EndMacro
这个其实不好理解。有时间再继续深入了解一下。