在编译语言(C#/C/C++/D)中做插件的“正常”方法是什么?我对与语言无关的方法特别感兴趣,但特定于语言的方法并非不可接受。
目前,“编译时”插件方法(仅包含代码或不包含代码,一切正常)是有效的,但可以迁移到更动态方法的东西是首选。
关于运行时类型,我更感兴趣的是加载插件的机制等等,而不是设计插件/应用程序界面
编辑:顺便说一句,插件将是奴隶而不是主人。插件的基本动作是在给定的情况下,它会被要求“做它的事情”并被赋予一个环境对象,它应该使用它来获得它需要操作的东西。
在编译语言(C#/C/C++/D)中做插件的“正常”方法是什么?我对与语言无关的方法特别感兴趣,但特定于语言的方法并非不可接受。
目前,“编译时”插件方法(仅包含代码或不包含代码,一切正常)是有效的,但可以迁移到更动态方法的东西是首选。
关于运行时类型,我更感兴趣的是加载插件的机制等等,而不是设计插件/应用程序界面
编辑:顺便说一句,插件将是奴隶而不是主人。插件的基本动作是在给定的情况下,它会被要求“做它的事情”并被赋予一个环境对象,它应该使用它来获得它需要操作的东西。
Mono.Addins似乎是 .NET 的一个很好的解决方案。我相信它包含 API,允许您从 repo 下载插件(或插件)并将其动态加载到正在运行的程序集中。
对于编译语言(编译意味着程序作为本机可执行文件运行,没有任何类型的虚拟机),您几乎需要使用某种特定于平台的共享库方法。在 Windows 中,这意味着使用 DLL。
您根据一组函数(具有特定名称、参数和调用约定)定义插件接口。然后,您在共享库中加载函数的地址并前往城镇。在 Windows 中,这意味着使用GetProcAddress(),然后将返回值转换为 C 中适当类型的函数指针,或者您正在使用的语言中的任何等价物。
另一个可能更理想也可能不更理想的选择是从您的本机应用程序中运行另一种语言的虚拟机,并让插件成为该语言的代码。例如,您可以使用 CPython 运行 Python VM 并动态加载 Python 模块。
我发现插件的难点在于:找到它们、解决它们的依赖关系以及处理版本问题。您和您的插件作者应该清楚如何处理这些问题。如果你把这些问题弄错了,那将导致无穷无尽的痛苦。我会研究使用插件的脚本语言和应用程序,以了解什么是有效的。
静态构造函数通常是坏意义上的“聪明”。由于无论如何您都必须一次加载一个插件(C/C++:dlopen 和 Linux 下的朋友)(在动态情况下),您也可以在这样做时明确地初始化它们。除此之外,这可能会让您有机会拒绝没有预期 api 的插件。
请注意,您不必为插件使用动态加载库。也可以使用其他机制:共享内存、套接字等......
这真的取决于你想做什么。在 Emacs 和 Gimp 中看到的常见 Unix 模式是编写一个程序,该程序由一个小型编译组件组成,该组件公开了解释组件用来执行所有操作的基本功能。提供可以在应用程序之上构建的新功能的插件很容易,但您需要在提供的原语中非常灵活才能实现这一点。在相反的极端想象一个可以以多种格式保存的照片编辑器。您希望允许人们编写自己的文件格式处理程序。这需要让您的代码使用一组简单的原语,然后在运行时选择一个实现。在直接(Unix)C 中使用 dlopen,在 C++ 中使用 extern C,这限制了你可以做什么和 dlopen。在 Objective-C 中,你有一个类可以为你做这件事。
对于每个插件封装一组通用函数的不同实现的从属插件,我只需将 DLL 存放在主机应用程序已知的插件文件夹中(例如“c:\program files\myapp\plugins”),并且然后通过反射从主机调用 DLL。
在安装每个 DLL 时,您可以进行某种复杂的注册过程,但我从未遇到过简单的插件在一个文件夹中的方法的任何问题。
编辑:要在 C# 中执行此操作,您只需向 DLL(名为“插件”或其他名称)添加一个公共类,并在那里实现所需的功能。然后从您的主机创建一个 Plugin 类型的对象并调用其方法(全部使用反射)。
带有 void Register(EventSource) 的接口似乎运行良好 - 有关示例,请参见 ASP.NET 的IHttpModule.Init(HttpApplication )。
这允许应用程序作者(谁控制 EventSource)根据需要添加事件,而无需扩展 IPlugin 接口(不可避免地导致 IPluginEx、IPlugin2、IPlugin2Ex 等)
我使用的一种方法(在 .NET 中)是让主机对插件进行初始调用(通过反射),启动插件并将引用传递给插件保存的主机。然后插件根据需要调用主机上的方法(也通过反射)。
我认为对于大多数插件,调用通常会在另一个方向进行(即主机会根据需要调用插件)。就我而言,插件本身具有需要使用主机功能的 UI 元素。
除了低级模块实现问题(例如windows DLL和实现问题),我使用的游戏引擎在DLL中只有一个全局注册函数,并尝试在插件目录中的每个dll上查找并调用它。注册功能执行公开功能所需的任何簿记。