客户端应用程序第一次从部件的类中请求对象时,该部件启动。接着创建所请求类的一个新实例,客户端得到对新创建对象的引用。
在请求第一个对象时,如果该部件有 Sub Main 过程,则执行该过程。Sub Main 过程完成后才会创建对象。
重点 不要在 Sub Main 执行过长的初始化动作,这会使创建对象超时并失败。
在 Sub Main 中不要显示窗体或用户信息,因为这可能导致类似初始化任务过长时的问题。正是由于这个原因,Visual Basic 不允许在 ActiveX EXE 和 ActiveX DLL 工程中使用窗体作为启动对象。
提供对象的桌面应用程序要检测 App StartMode,只有在单独启动时才显示主窗体。在这种情况下,Sub Main 会在请求第一个对象之前很早就已执行。
注意 如果您已经在“工程属性”对话框的“部件”选项卡中,为“启动模式”选项选择了 ActiveX 部件,那么当您的部件成为运行模式时,Sub Main 将不会立即执行。Visual Basic 这样做是为了使您可以精确地调试启动代码。一旦您的部件已经完成了编译,直到第一次客户请求对象,Sub Main 才会执行,所以开发环境完全继承了这个特性。
可以在创建第一个对象时在其 Class_Initialize 事件中执行初始化任务。完成初始化任务后设置一个全局标志,从而保证该类的后续实例不再执行初始化任务。
或者可以推迟某些初始化任务,直到真正需要使用该服务时。在这种情形下,每一个对象必须在使用服务前进行测试,确保它已被初始化─ 如果没有则进行初始化。如果初始化工作由很多小而独立的任务组成时,这种技巧是最适合的。
若初始化任务很大且不能拆开时,可以使用一个回调计时器在后台执行。可以在 Sub Main 的结束处启动计时器,这在“建立代码部件”中阐述。在计时器的 Tick 事件处理程序中,关闭该计时器并执行该初始化任务。在 Tick 事件处理程序的结束处设置一个全局标志,标识该部件已完成初始化。这样就可以通知那些在初始化期间创建的任意对象。
重点 不要为此使用 Timer 控件,因为 Timer 控件需要加载一个窗体。
在使用该技巧时,要确保每一个对象在其初始化事件中检测该全局标志。如果该部件尚未初始化,所有对象均应插入到一个全局集合,以便后台的初始化完成后得到通知。
可以为每一个类添加一个通知方法─ 例如 NotifyInitComplete。将该方法作为一个 Friend 方法,使之只能在该部件中可见。
详细信息 Friend 方法在本章后面的“对象之间的私有通信”中介绍。
当所有客户端都已释放对部件所提供的对象的引用时,应该关闭该部件(提供对象的桌面程序是该规则的一个例外)。
为了能够简便地关闭部件,Visual Basic 做了大量的事情。其中最重要的是跟踪对公共对象的引用,因为只要客户端仍在引用对象,部件就不能关闭。
重点 只有存在对公共对象的引用才能使部件保持运行状态。对私有对象(由 Visual Basic 提供的对象、或者标记为 Private 的类产生的对象)的引用,不能阻止部件关闭以及随后的客户端灾难性失败。对私有对象的引用一定不要传递给客户端。
对于进程内和进程外部件,Visual Basic 关闭部件的规则是不同的。
用 Visual Basic 编写的进程外部件的关闭条件是:
加载窗体将使在所有引用均被释放后部件也继续运行。当创建窗体的对象终止时应将该窗体卸载。
如果客户端释放了所有的引用,公共对象的内部引用不能使部件继续运行。进程外部件正在使用的进程内部件持有的引用,也不起作用。
最终,进程内部件的生命期由其客户端程序控制,因为该部件在客户端的进程中运行。一旦客户端关闭,该部件也将卸载,而不管那些未完成的对象引用、打开的窗体等。
当客户端不再使用某个部件提供的对象时,可能要求卸载该部件。Visual Basic 的进程内部件在响应客户端请求时是否卸载该部件,规则如下:
可见的窗体会使该部件保留在内存中,即使没有对象引用;而不可见的窗体则不会。
如果进程内部件在引用其自己的对象,Visual Basic 不会将其卸载。Visual Basic 无法识别一个引用是内部的还是外部的。
注意 使用 Visual Basic 编写的客户端应用程序可能不会在释放最后一个引用后立即试图卸载进程内部件。这种尝试的频繁程度取决于是否频繁出现空闲时间,通常大约两分钟。
详细信息 对关闭部件的规则的更完整论述位于“ActiveX 部件的标准及指南”的“ ActiveX 部件关闭”。