有时,程序在启动时会出现运行时错误。造成这些错误的原因可能是程序集丢失或初始化代码出错。
这些错误可能很难捕获,因为错误发生在应用程序第一行代码之前执行的代码中。
举例如下:
GLOBAL x AS INT
GLOBAL y := 1 / x AS INT
FUNCTION Start AS VOID
? "Function Start"
RETURN
这段代码会在启动时产生一个异常,只有在命令行下运行应用程序时才能看到或读取该异常
Unhandled Exception: System.TypeInitializationException: The type initializer for 'Application1.Exe.Functions' threw an exception. ---> System.DivideByZeroException: Attempted to divide by zero.
at Application1.Exe.Functions..cctor() in C:\XIDE\Projects\Default\Applications\Application1\Prg\Start.prg:line 4
--- End of inner exception stack trace ---
at Application1.Exe.Functions.Start()
X# 应用程序的正常程序流程是这样的:
•启动应用程序
•初始化 DotNet Framework
•调用入口点。应用程序的正常入口点是 Start 函数,编译器会将其转换为编译器生成的 Functions 类中的 Start 方法。
该类还包含应用程序中的全局和定义。如果这些全局项或定义中包含一个在编译时无法解析的初始化表达式,那么这些代码将在 Functions 类的静态构造函数中执行(在上面的错误信息中称为 "类型初始化器")。
•上面的代码显然犯了一个错误,导致了除以零的错误。
为了拦截这种情况,我们希望在启动时运行一些其他代码,并添加一个 try ... catch 结构,以确保我们能捕捉到这种错误。
增加下面的代码:
CLASS MyStartupCode
STATIC METHOD Start AS VOID STRICT
TRY
// 请注意,在下面一行中,.Exe 前面的名称必须是
// 与 EXE 的文件名相匹配。在我的例子中,我生成的是 Application1.exe
Application1.Exe.Functions.Start()
CATCH e AS Exception
// 我们或许也应该将其记录到磁盘中 !
Console.WriteLine("发生了未处理的异常")
Console.WriteLine("===================================")
DO WHILE e != NULL
Console.WriteLine("Exception: "+e:Message)
Console.WriteLine("Callstack:")
Console.WriteLine(e:StackTrace)
Console.WriteLine()
e := e:InnerException
ENDDO
Console.WriteLine("===================================")
Console.WriteLine("按任意键关闭程序")
Console.ReadLine()
END TRY
RETURN
END CLASS
您可能需要将 Application1.Exe.Functions.Start() 的调用改为与您的 EXE 名称一致的名称。
现在进入 VS 应用程序属性中的常规页面,在 "Startup Object(启动对象)"中设置 MyStartupCode 的值。
在 XIDE 中添加命令行选项 -main:MyStartupCode:
并再次运行代码。
现在错误已被捕获并显示出来。
如果您的应用程序不是控制台应用程序,而是 Windows 应用程序,那么控制台输出可能不可见。
当然,你也可以在新启动代码中的 AppDomain 类中注册一个 UnHandledException 处理程序。
将代码更改为:
CLASS MyStartupCode
STATIC METHOD Start AS VOID STRICT
TRY
System.AppDomain.CurrentDomain:UnhandledException += ExceptionHandler
Application1.Exe.Functions.Start()
CATCH e AS Exception
ExceptionHandler(NULL, UnhandledExceptionEventArgs{e, TRUE})
END TRY
RETURN
STATIC METHOD ExceptionHandler( sender AS OBJECT, args AS UnhandledExceptionEventArgs) AS VOID
LOCAL e AS Exception
LOCAL c AS STRINGe := (Exception) args:ExceptionObject
c := "发生了未处理的异常"+crlf
c += "==================================="+crlf
DO WHILE e != NULL
c += "Exception: "+e:Message+crlf
c += "Callstack:"+crlf
c += e:StackTrace+crlf
e := e:InnerException
ENDDO
c += "==================================="+crlf
MessageBox(IntPtr.Zero, c,"Error",0x60010) // MB_OK + MB_ICONSTOP+ MB_DEFAULT_DESKTOP_ONLY + MB_TOPMOST
[DllImport("user32.dll", CharSet := CharSet.Ansi)];
STATIC METHOD MessageBox(hwnd AS IntPtr, lpText AS STRING, lpCaption AS STRING, uType AS DWORD) AS INT PASCAL
END CLASS
注意一点:
请勿在异常处理程序中使用或调用任何 Xbase 类型和函数,因为您无法确保运行时已正确初始化。
如果使用自己编写的类,请确保所有类都是强类型的,并且只使用本地类型。因此不要使用 USUAL、FLOAT、SYMBOL 等类型。
不要调用同一应用程序或 DLL 中函数内部的代码,因为这些函数所在类的类型初始化程序也会产生异常。