前言
先前就尝试过使用Lazarus
写一个插件式的桌面应用框架,主程序只负责整体的插件管理以及为插件提供页签式的展示容器,具体的功能实现都由插件(动态库)来完成。
但是之前比较零散的几次尝试,多或少有些问题,比较有代表性的如:
-
无法以模式窗口显示插件内的子窗口
-
插件内的窗口在任务栏上显示的是和主程序分离的两个程序
-
动态库无法卸载
-
使用接口无法释放动态库里的对象
-
无法跨平台(主要针对
windows
)
近来又进行了一次系统性的尝试,以前的一些也针对性的研究了下,并找到了目前来看比较合适的解决方案,并将阶段性成果开源
了,具体地址见文末。
旧问题解决
模式窗口
这个问题其实早在Lazarus
官方的wiki
(Form in Dll)上就有解决方案了。
在此基础上也尝试进行一些魔改,过程就不多说了,经验总结如下:
-
DisableFormsCallBack
和EnableFormsCallback
两个回调必须为普通过程,改为类方法会导致模式窗口失效 -
TApplicationCallback
可以和插件动态库内的管理类合并,做为插件入口的统一管理,甚至做为插件统一接口的实现
视觉上为同一程序
Form in Dll中已经涉及,即在CreateParams
中将Params.WndParent
赋值为主程序中对应容器的句柄。
卸载/释放
对于动态库的卸载,在Lazarus的dll卸载问题中已有提到,本次直接避免。
对于对象的释放,这前的尝试犯了一个很严重的错误,即未遵守谁创建谁释放
这一原则。接口中增加专门的释放
函数,用于释放通过接口创建的对象。但,对于接口创建的对象,主程序除了释放外,也不应该(事实上也不能)进行其它操作,否则会抛内存访问异常。
共享内存管理器
如果仅使用基本数据类型的话,这就是个伪命题,但若使用高阶数据类型的话还是会方便很多,毕竟字符串
其实并不算是真正的基本类型。
文章在Lazarus中使用ShareMem解决了在Windows
平台上共享内存管理器的问题。在QQ群的交流中,群友啊D
提出使用GetMemoryManager
和SetMemoryManager
,目前来看是能解决该问题的。
跨平台一致性
Lazarus
本身就是跨平台的,只注意避免使用平台专用的api即可,或者对不同平台的api进行封装。
对于插件式的动态库,exports
导出的函数统一使用Name
关键字强制命名。同时,对于32位
CPU,导出函数统一使用cdecl
而不是stdcall
进行传参约束;对于64位
CPU,导出函数统一使用默认的传参约束,即不使用任何关键字,由编译器管理。
新的问题
-
基于有限的测试,
Linux
的gtk2
中显示模式窗口时主窗体还能进行最大化、最小化、移动等操作,但不能操作窗体内的元素,这与Windows
上的行为表现不太一致 -
插件动态库创建的窗体,嵌入主程序容器后,并不能像嵌入自身容器内那样可以方便的自适应大小和位置
TODO
-
解决新发现的问题
-
主程序页签式容器的实现
-
主程序插件的管理