Show/Hide Toolbars

XSharp

Navigation: X# 文档 > X# 提示和技巧

启动代码中的编译器魔法

Scroll Prev Top Next More

X# 编译器还会在启动代码中做一些额外的 "手脚"。

 

XBase 代码可能有所谓的 INIT 过程,其中包含启动时将执行的代码。例如,在 VO GUI 类中有一个过程

PROCEDURE __WCInitCriticalSections() _INIT1

为确保在启动时调用这些程序,编译器会在函数类中生成一至三个特殊方法,名称分别为 $Init1、$Init2、$Init3 和 $Exit。VOGuiclasses 程序集中有两个这样的方法($Init1 和 $Init3)。在 ILSpy 中查看该方法的内容,可以看到如下内容(使用 C# 反编译)

// VOGUIClasses.Functions
using System.Runtime.CompilerServices;
 
[CompilerGenerated]
public static void $Init1()
{
  __WCInitCriticalSections();
}

在某些程序集中,你会发现 $Init1() 方法是存在的,但却是空的。例如,在 VOSystemClasses:

// VOSystemClasses.Functions
using System.Runtime.CompilerServices;
 
[CompilerGenerated]
public static void $Init1()
{
}

我们创建这些空初始化器的原因如下:

我们的许多 VO 客户都是通过调用 CreateInstance() 来间接实例化类的。

要做到这一点,类必须在运行时可用。

我们使用的 Roslyn 编译器非常聪明。当 exe 不引用外部程序集中的任何类型或方法时,编译器不会将外部程序集的引用包含到 exe 中。

因此,如果您在应用程序中调用 CreateInstance(#DbServer),但从未声明 DbServer 类型的变量(例如,仅声明 DataServer 类型的变量),那么即使您在编译时包含对 VORDDClasses 程序集的引用,您的主应用程序中也不会包含对 RDDClasses 的引用。

这就是我们生成空 $Init() 方法的原因。

 

在生成主程序时,X# 编译器会检查所有引用的程序集,并查找所有 $Init1、$Init2、$Init3 和 $Exit 方法,然后生成一些代码来调用所有这些方法。因此,所有引用的程序集都将在启动时加载。您可以在编译器生成的 <module> 类中的特殊编译器生成方法中找到这段启动代码:

// <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);
  }
}

正如您所看到的,这段代码不仅调用了几个 $Init1() 方法,还在 X# 运行时中设置了一些属性。您还可以看到,上面的代码是在 VO Dialect 中调用的。

如果查看 Start 函数的生成代码,它看起来是这样的:

// 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();
  }
}

编译器创建了一个 try finally 块,并调用 $AppInit() 来初始化运行时状态并调用每个引用程序集中的 init 过程。

您还可以看到,在 finally 子句中调用了 $AppExit(),其中调用了 EXIT 程序(我们在 X# 中添加了这些程序),垃圾回收器会清除所有引用,并在应用程序结束前等待所有最终程序结束。

 

遗憾的是,这并不意味着所有打开的服务器都会关闭。如果您打开了一个 DbServer 并将该对象赋值给一个全局变量,那么它并不会自动关闭服务器。

运行时中有一些代码会处理这个问题(但这是另一个主题)......