【问题标题】:Delphi: App initialization - best practices / approachDelphi:应用程序初始化 - 最佳实践/方法
【发布时间】:2008-12-19 23:13:05
【问题描述】:

我经常遇到这种情况,我只是在寻找最佳实践/方法。我有一个包含数据库/数据模块的应用程序,并希望在启动时启动数据库/数据集,而在设计时将“运行时活动”设置为 true(数据库位置不同)。当应用程序启动时,还要运行一个网络“检查更新”例程。

鉴于 TForm 事件序列,以及各种反复试验的结果,我目前正在使用这种方法:

我使用在主窗体中设置的“全局”记录来存储所有全局变量,其中有一个元素称为 Globals.AppInitialized(布尔值),并在主窗体的初始化部分将其设置为 False。

在主窗体的 OnShow 事件中(所有窗体都由那时创建),我测试 Globals.AppInitialized;如果它是假的,我运行我的“初始化”东西,然后通过设置 Globals.AppInitialized := True 来完成。

这似乎工作得很好,但它是最好的方法吗?从他人的经验、想法和意见中寻找洞察力。 TIA..

【问题讨论】:

标签: forms delphi initialization datamodule


【解决方案1】:

我通常总是关闭所有表单的自动创建,除了主表单和可能的主数据模块。

我了解到您可以做的一个技巧是将您的数据模块添加到您的项目中,允许它在您的主表单之前自动创建和创建。然后,当您的主窗体创建时,数据模块的 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);

我想不出有一次这个消息从未被处理过,但调用的本质是它被添加到消息队列中,如果队列已满,则它被“丢弃”。请注意存在边缘情况。

【讨论】:

  • +1 用于禁用表单自动创建的提示。这是 Delphi 应用程序启动速度比它们必须要慢的原因之一。用于延迟处理的 Windows 消息也值得研究。
  • wmAppStarted 过程的主体应该是什么?
  • @Wojtas 考虑到此时应用程序已启动并运行并处理消息,您想要执行的任何逻辑。
【解决方案2】:

您可能希望在表单创建调用之后和 Application.Run 之前直接干预项目源(.dpr 文件)。 (或者甚至更早,以防万一。)

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

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

【讨论】:

  • 是的!重要的是要知道“主窗体”不是程序的入口点,OnShow 不是窗体的入口点。
  • 您在程序 (TApplication) 有机会进入消息循环之前执行代码! skamradt 的答案更好/更安全!
  • 更糟糕的是,你搞砸了 Delphi 的默认异常处理机制:docwiki.embarcadero.com/RADStudio/Rio/en/… 我引用:“在某些情况下不会调用 HandleException。在执行之前或之后发生的异常HandleException 没有捕获和处理应用程序的 Run 方法"
【解决方案3】:

我不知道这是否有帮助,但我的一些应用程序没有自动创建任何表单,即它们在 IDE 中没有主表单。

以 Application 对象为其所有者创建的第一个表单将自动成为主表单。因此,我只自动创建一个数据模块作为加载程序,并让这个数据模块决定何时创建哪些数据模块以及以什么顺序创建哪些表单。这个数据模块有一个 StartUp 和 ShutDown 方法,它们在 dpr 中被称为 Application.Run 周围的“括号”。 ShutDown 方法对关闭过程提供了更多控制。

当您为应用程序的不同用例设计了不同的“主窗体”或者您可以使用一些配置文件来选择不同的主窗体时,这会很有用。

【讨论】:

  • 这很有创意,我能想到一些有用的例子。感谢发布!
  • 你能描述一下“括号”吗:“这个数据模块有一个 StartUp 和 ShutDown 方法,它们在 dpr 中被称为 Application.Run 周围的“括号””
  • @Gravity Application.CreateForm(TdmLoader, dmLoader); dmLoader.启动;应用程序。运行; dmLoader.ShutDown;
【解决方案4】:

Delphi 中实际上没有“全局变量”这样的概念。所有变量的范围都限于它们所在的单位和使用该单位的其他单位。

只需将 AppInitializedInitialization 内容作为数据模块的一部分。基本上有一个类(或数据模块)来统治所有非 UI 的东西(有点像 One-Ring,除了不是所有的邪恶之类的东西。)

您也可以:

  • 从您的初始屏幕调用它。
  • 在登录时进行
  • 在后台线程中运行“检查更新” - 不要强迫他们立即更新。像 Firefox 那样做。

【讨论】:

  • 谢谢吉姆 - 有帮助。点注太重:“全局变量”的区别。在时间方面,您是否看到从主窗体的 OnShow 事件中“触发”初始化内容的任何问题? (不过,我喜欢您将实际 代码 存放在数据模块中的想法)。
  • 从 OnShow 执行此操作的问题是显示表单与您想要执行的操作无关。并非所有事情都需要在表单的上下文中发生。
【解决方案5】:

我不确定我是否理解您为什么需要全局变量?如今,我编写所有的 Delphi 应用程序时都没有一个全局变量。即使我确实使用了它们,每个应用程序也从来没有超过几个。

所以也许你需要先想想你为什么真的需要它们。

【讨论】:

  • 一般来说,我会避开它们,但在某些情况下,我发现有一些变量似乎最适合属于“应用程序本身”。这是一个例子。有时我会在以另一种形式运行一些例程之前检查它。它也可以设置为表单或 DM 的“属性”。
【解决方案6】:

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

Application.CreateForm(TDmMain, DmMain);

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

  Application.Run;

【讨论】:

    【解决方案7】:

    我使用的一个技巧是在主窗体上放置一个 TTimer,将时间设置为 300 毫秒,然后执行任何初始化(数据库登录、网络文件复制等)。启动应用程序会立即打开主窗体并允许任何初始化“东西”发生。用户不会在启动多个实例时想“哦……我没有 dbl-click……我会再做一次……”

    【讨论】:

    • 如果你的计时器不够长?进行初始化,然后发送您的主要内容以获取 Windows 消息。
    • @Rigel。便宜是对的。使用这个技巧让应用程序运行了近 20 年(从 Delphi 2 一直到 Delphi 10),没有任何可靠性问题。我见过的唯一问题是那些试图在线程等方面变得聪明的开发人员。
    • 那么,聪明对你来说是个问题吗?你会建议所有的德尔福程序员愚蠢吗?这是现在的新智能吗?
    • 而@skamradt 是我们当中最愚蠢的,因为他提供了最好的解决方案?
    • 至于@Mawg Delphi 的 Timer 事件是单线程的,因此一旦事件触发,无论从开始...结束之间花费多长时间,都可以做任何事情。没有线程,没有晦涩的低级窗口消息处理。代码成瘾是一种严重的疾病。正如伟大的 Marco Cantu 所说/所说:“如果您编写大量代码来做某事,那么您可能做错了。”
    猜你喜欢
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多