如何最好地将插件功能添加到Delphi程序中

时间:2008-12-14 00:02:27

标签: delphi plugins

我希望为用户添加将插件编写到我在Delphi中开发的程序的功能。该程序是单个可执行文件,没有使用DLL。

这将允许用户社区编写我的程序的扩展来访问内部数据并添加他们可能觉得有用的功能。

我在Suggestions for Adding Plugin Capability?看过帖子,但它的答案似乎无法转移到Delphi程序。

如果可能的话,我希望添加此功能并将我的应用程序保留为单个可执行文件,而不需要任何DLL或其他模块。

您是否知道任何建议如何在Delphi中做到最好的资源,组件或文章,或者您有自己的建议吗?

9 个答案:

答案 0 :(得分:10)

我要问的第一个问题是,您是否需要插件来访问主机应用程序的UI,或者添加自己的任何UI元素?或者插件是否仅限于查询和/或向主机应用程序提供数据?

后者更容易,并开辟了两种可能性。其他人已经提到了DLL,这是第一种方法。某些警告适用 - 通常,您应该仅使用Windows API中使用的数据类型与dll连接。这样你可以确定插件DLL将理解你的数据类型,无论它们是在什么语言/编译器中创建的。(例如,使用PChars,而不是字符串。不要通常将Delphi类如TStream传递给这在某些情况下似乎有效,但一般来说不安全,因为即使DLL是在Delphi中编译的,它也可能是编译器的不同版本,对TStream的概念略有不同)。谷歌在Delphi中使用DLL,你会发现更多的提示。

尚未提及的另一种方法是在应用程序本身中启用脚本。有几个非常强大的第三方脚本引擎,包括商业和免费,其中大多数允许您使用脚本交换Delphi对象。其中一些库仅支持Pascal作为脚本语言,其他库将允许您使用Basic(可能更适合初学者用户)或其他语言。请参阅http://www.remobjects.com/free.aspx上的RemObjects Pascal Script(免费)。

我最喜欢的脚本解决方案是来自http://mmm-experts.com/Products.aspx?ProductID=3的Python for Delphi(P4D,也是免费的)它可以通过RTTI与您的类完美地接口,并允许您在Delphi应用程序中执行Python代码,以及在Python脚本中使用Delphi类。鉴于Python的流行,如果您想吸引开发人员加入您的项目,这可能是一个可行的解决方案。但是,每个用户都需要安装Python发行版。

在我看来,从潜在插件编写者的角度来看,如果你使用脚本而不是选择DLL,那么进入的门槛就会降低。

现在,回到我最初的问题:如果您需要插件与您的用户界面进行交互,情况会变得复杂得多,例如:通过对其进行控制。通常,DLL不能用于执行此操作。 Borland / CodeGear认可的方法是使用包(BPL)。使用BPL,您可以访问和实例化插件提供的类,就像它们在主机应用程序中声明一样。问题是,所有BPL必须使用与主应用程序相同的Delphi版本和版本进行编译。在我看来,这使得包完全不切实际,因为很难期望全世界所有潜在的插件编写者都会使用相同版本的Delphi。一个主要的陷阱。

为了解决这个问题,我尝试了不同的方法:继续使用DLL,并为插件开发语法来描述它所需的UI,然后在主机应用程序中自己创建UI。 (XML是一种表达UI的便捷方式,因为您可以免费获得父母/嵌套的概念。)UI描述语法可以包括在控件的内容或状态发生变化时触发的DLL回调。但是,此方法会将插件限制为应用程序已使用或已注册的VCL控件集。而且这不是一个更蠢的工作,而BPL肯定是。

谷歌的“Delphi插件框架”也是如此。有一些现成的解决方案,但据我所知,他们通常使用BPL,但实用性有限。

答案 1 :(得分:9)

实际上,the accepted answer to the question you cite也非常适合Delphi。您的插件将是DLL,您可以指示他们应该导出具有特定名称和签名的函数。然后,您的程序将加载DLL(带LoadLibrary)并获取函数的地址(使用GetProcAddress)。如果DLL没有加载,或者函数不存在,则DLL不是应用程序的插件。

获得DLL的地址后,可以调用它。您可以将函数接口传递给代表您希望公开的应用程序部分的内容。您还可以要求该函数返回一个接口,其中包含应用程序将在不同时间调用的方法。

在测试插件系统时,最好用Delphi以外的语言编写插件。这样,您就可以更自信地不会无意中要求每个人都使用Delphi。

答案 2 :(得分:7)

起初我去了BPL和DLL基础插件。并发现他们难以保持。

如果您使用BPL系统,则需要将BPL版本与EXE版本匹配。这包括可能破坏某些东西的Delphi更新。我发现(困难的方法)如果我需要在每个版本中包含我的所有插件,那么根本就没有插件。

然后我切换到普通的DLL插件。但该系统只是复杂的代码库。这不是一件好事。

在破坏网络的同时,我发现了Lua嵌入式脚本语言,并随之提供。 Lua是150K DLL,嵌入字节码编译器,解释器以及非常简单和智能的动态编程语言。

我的插件是简单的lua脚本。容易保持和制作。有预制的Delphi示例,因此您可以将任何类或过程导出为Lua表或函数。 GUI与否。例如,我在我的应用程序中使用TurboPower Abbrevia进行压缩。我将我的压缩类导出到lua,所有插件现在都可以调用zip('','dir.zip')和unzip()。然后我切换到7zip并且只实现了旧类来使用7zip。所有插件都可以正常工作,支持新的zip('','dir.7z')。

我创建了TLuaAction,它从脚本中调用Execute(),Update(),Hint()过程。

Lua allso拥有自己的插件系统,可以轻松添加功能。例如,luacom make易于使用COM自动化,luainterface允许从lua调用.net。 有关详情,请参阅luaforge。在Delphi中有Lua IDE,有源代码。

答案 3 :(得分:6)

前段时间我试图制作overview of all such options。我和读者/评论者一起建立了这个清单:

  • 从程序加载的DLL / BPL。
  • 从沙箱加载的DLL / BPL(可以是程序的另一个副本或专用的“服务器”应用程序,并通过messages / sockets / msmq / named pipes / mailslots / memory mapped files与主程序通信)。
  • COM(任何变体)。
  • DDE(请不要)。
  • 通过标准输入/输出进行通信的外部程序。
  • 通过文件进行通信的外部程序(文件名是程序的参数)。
  • 通过drop文件夹工作的外部程序(对批处理很有用)。
  • 与Windows消息/ windows sockets / msmq /命名管道/邮件地址/内存映射文件/数据库发布 - 订阅进行通信的外部程序。

答案 4 :(得分:4)

我正在使用插件来实现我正在构建的游戏引擎中的大部分功能。主要的EXE由脚本引擎,插件管理器,一些基本的图形例程组成,而不是其他。我正在使用JEDI VCL库中的TJvPluginManager。这是一个非常好的经理,它可以为你的程序添加任何你想要的东西。查看包中包含的演示,了解它的工作原理。唯一的缺点是它为您的程序添加了大量JCL / JVCL代码,但如果您已经在使用其他JVCL组件,那么这并不是真正的问题。

答案 5 :(得分:4)

添加插件功能的最通用方法是使用COM。埃里克·哈蒙(Eric Harmon)Delphi Com Programming是一本很好的书,可以让你开始上路。虽然它最初是为Delphi版本3到5编写的,但书籍内容仍然适用于最新版本的Delphi。

就个人而言,我已将此技术与活动脚本一起使用,以允许最终用户自定义。

答案 6 :(得分:2)

如果将在Delphi或C ++构建器中开发插件,请使用packages + interfaces。 Delphi OTA就是一个很好的例子。 如果插件与语言无关,那么COM是一个很好的方法。

补充:如果您不使用COM,则可能需要为每种语言提供SDK。并且不同语言之间的数据类型处理可能很痛苦(例如delphi字符串类型)。 Delphi COM支持非常好,你不需要担心细节问题。这些都是Delphi COM支持的主要内容。不要试图再次发明轮子。我很惊讶为什么人们不倾向于提及它。

答案 7 :(得分:2)

我找到了Tim Sullivan的一篇文章:
Implementing Plug-Ins for Your Delphi Applications

答案 8 :(得分:1)

您可以查看Remobjects中的Hydra。这不仅可以让你添加插件,还可以混合使用win32和.net。