The X# compiler also does some extra "tricks" in the startup code.
XBase code may have so called INIT procedures, which contain code that will be executed at startup. For example inside the VO GUI Classes there is a procedure
PROCEDURE __WCInitCriticalSections() _INIT1
To ensure that these procedures are called at start the compiler generates one to three special methods in the functions class with the names $Init1, $Init2, $Init3 and $Exit. The VOGuiclasses assembly has two of these ($Init1 and $Init3). When you look at the contents of this method in ILSpy you see the following (using C# decompilation)
// VOGUIClasses.Functions
using System.Runtime.CompilerServices;
[CompilerGenerated]
public static void $Init1()
{
__WCInitCriticalSections();
}
In some assemblies you will see that the $Init1() method is there but it is empty. For example in the VOSystemClasses:
// VOSystemClasses.Functions
using System.Runtime.CompilerServices;
[CompilerGenerated]
public static void $Init1()
{
}
The reason why we are creating these empty initializers is the following:
many of our VO customers are instantiating classes indirectly, by calling CreateInstance().
To be able to do so the classes have to be available at runtime.
The Roslyn compiler that we use is very smart. It does not include references to external assemblies into the exe when the exe does not reference any types or methods in that assembly.
As a result if you were calling CreateInstance(#DbServer) in your app but you were never declaring variables of type DbServer (but only of type DataServer for example) then even when you include a reference to the VORDDClasses assembly at compile time you would not have a reference to the RDDClasses in your main app.
That is why we are generating the empty $Init() methods.
When building the main application, the X# compiler checks all referenced assemblies and looks for all $Init1, $Init2, $Init3 and $Exit methods and build some code to call all these methods. As a result all referenced assemblies will be loaded at startup. You will find this startup code in a special compiler generated method in the compiler generated <module> class:
// <Module>
using Application1.Exe;
using System;
using System.Runtime.CompilerServices;
using VOWin32APILibrary;
using XSharp;
using XSharp.RT;
[CompilerGenerated]
internal static void $AppInit()
{
try
{
RuntimeState.AppModule = typeof(Application1.Exe.Functions).Module;
RuntimeState.CompilerOptionOVF = false;
RuntimeState.CompilerOptionVO11 = false;
RuntimeState.CompilerOptionVO13 = false;
RuntimeState.Dialect = XSharpDialect.VO;
VOWin32APILibrary.Functions.$Init1();
XSharp.RT.Functions.$Init1();
Application1.Exe.Functions.$Init1();
}
catch (Exception innerException)
{
throw new Exception("Error when executing code in INIT procedure(s)", innerException);
}
}
As you can see this code not only calls several $Init1() methods, but it also sets some properties in the X# runtime. You can also see that the code above was called in the VO Dialect.
And if you look at the generated code for the Start function this looks like this:
// Application1.Exe.Functions
using System;
using XSharp.RT;
public static void Start()
{
try
{
<Module>.$AppInit();
XSharp.RT.Functions.QOut("Function Start");
}
finally
{
<Module>.$AppExit();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
The compiler has created a try finally block and calls $AppInit() to initialize the runtime state and call the init procedures in every referenced assembly.
You also see that in the finally clause there is a call to $AppExit() from which EXIT procedures are called (we have added these in X#) and where the garbage collector cleans all references and waits for all finalizers to finish before your app finished.
Unfortunately this does not mean that for example all open servers will be closed. If you have opened a DbServer and assigned the object to a global variable then this does not automatically close the server.
There is some code in the runtime that takes care of this (but that is the subject of another topic)..