Delphi:应用程序初始化-最佳实践/方法

3
我经常遇到这样的问题,只是在寻找最佳实践/方法。我有一个包含数据库/数据模块的应用程序,并且想要在启动时启动数据库/数据集,而不需要在设计时将"运行时激活"设置为true(数据库位置会变化)。同时,在应用程序启动时运行一个网页的“检查更新”程序。
鉴于TForm事件序列和各种试验和误差的结果,我目前采用以下方法:
我在主窗体中设置了一个“Globals”记录来存储所有全局变量,其中有一个名为Globals.AppInitialized的元素(布尔值),并在主窗体的初始化部分将其设置为False。
在主窗体的OnShow事件中(此时所有窗体都已创建),我测试Globals.AppInitialized;如果它为false,则运行我的“初始化”内容,然后通过设置Globals.AppInitialized:=True结束。
这个方法似乎效果很好,但是这是最好的方法吗?期待从他人的经验、想法和意见中获得洞见。谢谢。

1
10年前的视频:https://youtu.be/_PJdZjM2oTw,其中有一些相关内容。 - Alister
1
https://stackoverflow.com/questions/2075839/how-do-do-things-during-delphi-form-startup/2095224 - Gabriel
1
https://dev59.com/x2rXa4cB1Zd3GeqPEO14 - Gabriel
7个回答

11

通常情况下,我会关闭除主窗体和可能的主数据模块之外的所有表单的自动创建。

我学到的一个技巧是将数据模块添加到项目中,并允许它自动创建并在主窗体之前创建。这样,在主窗体被创建时,数据模块的onCreate已经运行过了。

如果您的应用程序有一些代码需要设置控件的焦点(因为此时该控件“还不可见”),则可以在onCreate中创建一个用户消息并将其发布到窗体中。该消息应该(但不保证)在窗体消息循环被处理时立即被处理。例如:

const
  wm_AppStarted = wm_User + 101;


type
  Form1 = class(tForm)
    :
    procedure wmAppStarted(var Msg:tMessage); message wm_AppStarted;
  end; 

// in your oncreate event add the following, which should result in your wmAppStarted event firing.
PostMessage(handle,wm_AppStarted,0,0);

我想不出有任何一次这个消息没有被处理,但是调用的性质是将它添加到消息队列中,如果队列已满,则会“丢弃”它。只需注意这种边缘情况的存在即可。


3
感谢提供禁用表单自动创建的提示,这是 Delphi 应用程序启动较慢的原因之一。另外,值得研究的还有延迟处理的 Windows 消息。 - mghie
wmAppStarted过程的主体应该包含什么? - Wojtas
@Wojtas 无论你想执行什么逻辑,考虑到此时应用程序已经启动并正在处理消息。 - skamradt

6

在表单创建调用和Application.Run之前,您可能希望直接干预项目源代码(.dpr文件)。 (甚至更早的情况下。)

这是我通常处理此类初始化问题的方式:

...
Application.CreateForm(TMainForm, MainForm);    
...
MainForm.ApplicationLoaded; // loads options, etc..
Application.Run;
...

3
是的!重要的是要知道“主窗体”不是程序的入口点,OnShow 也不是窗体的入口点。 - Rob Kennedy
你正在程序(TApplication)进入消息循环之前执行代码!skamradt的答案更好/更安全! - Gabriel
更糟糕的是,您可能会搞乱Delphi的默认异常处理机制:http://docwiki.embarcadero.com/RADStudio/Rio/en/Default_Exception_Handling_in_VCL。我引用一下:“在某些情况下,HandleException不会被调用。在应用程序的Run方法执行之前或之后发生的异常不会被HandleException捕获和处理。” - Gabriel

3
我不知道这是否有帮助,但我的一些应用程序没有自动创建任何表单,即它们在IDE中没有主窗体。第一个使用Application对象作为其所有者创建的窗体将自动成为主窗体。因此,我只自动创建一个数据模块作为加载器,并让它决定何时创建哪些数据模块以及以什么顺序创建哪些窗体。此数据模块具有StartUp和ShutDown方法,在dpr中的Application.Run周围调用它们作为“括号”。ShutDown方法可以更好地控制关闭过程。
当您为应用程序的不同用例设计了不同的“主窗体”或者您可以使用一些配置文件来选择不同的主窗体时,这可能非常有用。

这很有创意,我可以想到一些有用的情况。感谢您的发布! - Jamo
你能描述一下“括号”吗?“这个数据模块有一个StartUp和ShutDown方法,它们被称为在dpr中Application.Run周围的‘括号’。” - Gabriel
@Gravity Application.CreateForm(TdmLoader, dmLoader); dmLoader.StartUp; Application.Run; dmLoader.ShutDown; 应用程序.创建窗体(TdmLoader, dmLoader); dmLoader.启动; 应用程序.运行; dmLoader.关闭; - Uwe Raabe

2
实际上,在Delphi中并没有“全局变量”的概念。所有变量都作用于它们所在的单元和使用该单元的其他单元。
只需将AppInitializedInitialization作为数据模块的一部分即可。基本上,拥有一个类(或数据模块)来管理所有非UI相关的内容(有点像“魔戒”,但并不邪恶之类的)。
或者你可以:
  • 从闪屏界面调用它。
  • 在登录时执行。
  • 在后台线程中运行“检查更新”-不要强制用户立即更新。像Firefox一样。

谢谢Jim提供的帮助,我也注意到了关于“全局变量”区别的问题。就时间安排而言,您是否认为从主表单的OnShow事件中“启动”初始化工作会有任何问题?(尽管我喜欢您的想法将实际的代码放在数据模块中)。 - Jamo
在OnShow中执行此操作的问题在于,显示窗体与您想要执行的操作没有关系。并非所有事情都需要在窗体的上下文中发生。 - Rob Kennedy

1

我不确定你为什么需要全局变量?现在我编写的所有Delphi应用程序都没有使用任何全局变量。即使我曾经使用过它们,每个应用程序也从未超过几个。

因此,也许你需要先思考一下为什么实际上需要它们。


2
一般情况下,我会避免使用它们,但在某些情况下,我发现有一些变量似乎最好属于“应用程序本身”。这是一个例子。有时,在运行另一个表单中的一些例行程序之前,我会检查它。它也可以设置为表单或DM的“属性”。 - Jamo

1

我使用一个主数据模块来检查数据库连接是否正常,如果不正常,则显示一个自定义组件表单来设置数据库连接,然后加载主表单:

Application.CreateForm(TDmMain, DmMain);

  if DmMain.isDBConnected then
    begin
      Application.CreateForm(TDmVisualUtils, DmVisualUtils);
      Application.CreateForm(TfrmMain, frmMain);
    end;

  Application.Run;

-1

我使用的一个技巧是在主表单上放置一个TTimer,将时间设置为大约300毫秒,并执行任何初始化(数据库登录,网络文件复制等)。 启动应用程序会立即显示主表单并允许进行任何初始化“工作”。用户不会启动多个实例,并想“哦..我没有双击...我再试一次..”


1
如果你的计时器不够长怎么办?进行初始化,然后将主程序发送到Windows消息。 - Mawg says reinstate Monica
1
那么,你认为聪明反被聪明误吗?你会建议所有的Delphi程序员变得愚蠢吗?这现在是新的聪明之道吗? - Gabriel
1
@skamradt是我们中最聪明的,因为他提供了最好的解决方案? - Gabriel
@ Rigal 喔,看来有人起床气不好。在乱扔“愚蠢”这个词汇前,最好先了解一下 Stack Overflow 的新行为准则。使用这种言辞具有煽动性,而且不会让你看起来比其他人更聪明。 我们没有问题与机智,只是对那些仅为开发者自我服务而不是为人服务的机智感到困惑。这就是我们写代码并解决问题为人服务的原因所在。 - KevinRF
1
关于@Mawg Delphi的计时器事件是单线程的,因此一旦事件触发,在begin...end之间需要做任何事情和花费多长时间。没有线程,没有晦涩的低级窗口消息处理。编码成瘾是一种严重的疾病。正如伟大的Marco Cantu所说/说过:“如果你要写很多代码来完成某件事,那么你可能做错了。” - KevinRF
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接