Guys,
I have given this some thought and a special function name will not be the way to go.
This function will become part of the compiler generated static functions class and that means that the static constructor for this class will have to be called before the code in the special function can be executed. This static constructor contains code to initialize defines that do not have a compile time constant (such as defines that have a symbol as value, eg DEFINE SomeValue := #SomeSymbol) and the code to initialize globals that are declared with an initializer (GLOBAL aValues[0] AS ARRAY).
There is a big risk that some of this initialization code will depend on external DLLs that may or may not be correctly initialized.
However there is a better way to do this and it is already built into the compiler.
This is the compiler option -Main.
Consider the following code:
Code: Select all
GLOBAL x AS INT
GLOBAL y := 1 / x AS INT
FUNCTION Start AS VOID
? "Function Start"
RETURN
This code will generate an exception at startup:
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:XIDEProjectsDefaultApplicationsApplication1PrgStart.prg:line 4
--- End of inner exception stack trace ---
at Application1.Exe.Functions.Start()
And you cannot intercept this.
Now add the following code:
Code: Select all
CLASS MyStartupCode
STATIC METHOD Start AS VOID
TRY
// Note that in the following line the name before .Exe must
// match the file name of your EXE. In my case I am generating Application1.exe
Application1.Exe.Functions.Start()
CATCH e AS Exception
// We should probably log this to disk as well !
Console.WriteLine("An unhandled exception has occurred")
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("Press any to close the application")
Console.ReadLine()
END TRY
RETURN
END CLASS
You may have to change the call to Application1.Exe.Functions.Start() into something that matches your EXE name.
Now goto the General page in the application properties in VS and at the entry "Startup Object" set the value MyStartupCode (in XIDE add the command line option -main:MyStartupCode)
and run the code again
Of course you can also register an UnHandledException handler in the AppDomain class inside the new startup code. Change the code to:
Code: Select all
CLASS MyStartupCode
STATIC METHOD Start AS VOID
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
e := (Exception) args:ExceptionObject
// We should probably log this to disk as well !
Console.WriteLine("An unhandled exception has occurred")
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("Press any to close the application")
Console.ReadLine()
END CLASS
One remark:
Do NOT use or call any Xbase types and or functions in the exception handler, since you can't be sure that the runtime was initialized properly. If you use classes written by yourself make sure that everything is strongly typed and uses native types only. So no USUAL, FLOAT, SYMBOL etc.
I hope this helps.
Robert