# ets **Repository Path**: ets-ddui/ets ## Basic Information - **Project Name**: ets - **Description**: 一个可以用js、python扩展程序功能的小工具 - **Primary Language**: C++ - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-30 - **Last Updated**: 2023-10-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [TOC] # ETS 可扩展工具集,全称叫`Extended Tool Set`(可扩展工具集),最初是个人为了做单元测试及测试一些感兴趣的技术,而写的一个小工具。 后来在里面集成了脚本语言,打通了Delphi和C++的交互,将功能进行了模块化。 此工具还在继续完善中,现阶段相关功能并不稳定。 ## 整体结构 从整体上来看,程序分为三个部分: 1. 功能插件,使用VC开发,实现了脚本引擎和进程管理模块,网络通信模块正在开发中。 2. 脚本,目前支持JScript和Python,但对Python的支持还不完善,计划还要引入一个自定义类型,VBA理论上也支持,但未验证过。 3. UI界面,使用Delphi实现,之前开放出来的[ets-ddui](https://gitee.com/ets-ddui/ets-ddui.git),算是其中最重要的一个模块。 UI和功能插件,都是以DLL的形式对外提供服务,供主程序使用。 UI导出`GetFrame`函数,功能插件导出`GetModule`函数。 脚本在其中扮演粘合剂的角色,将不同功能串联起来,组织成一个完整的功能。 同时,因为脚本本身的特性,为程序功能的扩展提供了极大的灵活性。 ### IDispatch和RTTI 程序最初引入的是JScript引擎,因此,在接口设计上,保留了COM程序的影子。 所有功能插件,都被包装成IDispatch接口,在JScript中,天然就能调用。 但在Python中,由于所有要导入的功能,都需要先使用`PyMethodDef`类型的数组,进行预定义,这和IDispatch的设计(实际调用时才会解析)存在一定的冲突,目前还在研究有没有完美融合的方法。 提到IDispatch,就不得不提实现这个接口的方式: 1. Delphi中有RTTI机制,为实现IDispatch提供了天然的基础,可以简单的用`TObjectDispatch`做个包装就行了。 不过ETS并没有直接使用TObjectDispatch,而是Copy了其代码,加了一点自定义的逻辑,可以在RTTI之外,注册一些自定义的功能进去。 2. VC中没有RTTI(严格说是功能太弱,没法用),所以,借用了微软在COM中引入的类型库`tlb`来实现。 要生成类型库,需要使用微软的`MIDL`编译器,但这个程序并没找到Linux版,所以,这也成了ETS无法跨平台的最大障碍。 也考虑过借鉴Python的实现,但Python要求为每个导出功能,再重新包装一个导出函数,和IDispatch的实现相比,显得有点笨重。 不过最近无意中发现,Chrome中有个Blink机制,也是编译IDL,生成V8引擎的接口代码,感觉应该有不少可以借鉴的地方,后续计划抽空研究研究。 ### 用脚本驱动窗口的执行 用脚本驱动窗口的概念,对于前端开发的朋友应该并不陌生,用HTML搭界面,用JS操作DOM,就是这个概念的最佳实践。 在ETS中,既然脚本可以通过IDispatch对宿主程序中的功能进行调用,那直接用脚本创建窗口,技术上也是可行的。 在实现过程中,遇到的技术要点主要有: 1. Delphi默认使用`.dfm`文件来创建窗口,`.dfm`会被编译为二进制文件,不方便修改和维护。 ETS引入了JSON格式(如果改用XML,可能和HTML更像一点)来定义窗口框架,并重写了组件创建的函数。 不过最近在考虑精简这块的逻辑,Delphi中仅保留创建控件实例的功能,对控件属性的初始化、事件绑定、子控件创建等逻辑,统统移到脚本中处理。 2. Delphi中设计的窗口,会自动包含控件的RTTI,窗口创建时,用这些RTTI创建控件的实例。 但在脚本中创建时,因为没有自定义的窗口类,因此,也就无法获取到控件的RTTI。 解决方案是复用控件开发时,`Register`中注册控件的逻辑。 设计时,调用`RegisterComponents`,将控件注册到IDE。 运行时,调用`RegisterClasses`,将控件的RTTI注册到程序中的内部数组,之后再用`FindClass`从数组中获取。 3. 对回调事件的处理,包括对入参的解析、JS回调函数的调用、函数的堆栈平衡。 这里要提一点,Delphi为回调事件生成的RTTI信息非常有限,远没有函数上的RTTI丰富,目前仅能支持对基本类型的处理。 ## 程序构建 ### 环境准备 * **操作系统** Windows 10家庭版(Win7理论上也支持,不过我本机电脑已经换了Win10,Win7未验证过)。 * **编译器** 需要安装VC2010和Delphi2007。 构建脚本会调用MSBuild构建程序,但因为Delphi不是微软亲生的,与MSBuild配合不是太好。 构建前,脚本会先读取注册表“HKLM\SOFTWARE\Borland\BDS\5.0”,查找Delphi的安装目录,VC没有此问题。 * **Git** 本项目中使用了boost这类项目,由于文件较大,因此,被单独存放到[build-tools](https://gitee.com/ets-ddui/build-tools.git)库中,编译时,会调用Git下载相关代码。 建议将Git的安装目录添加到环境变量`PATH`中。 ### 编译 执行`build.bat`即可,如果一切正常,会在`Out`目录中生成编译结果。 ```txt RootPath |__ets # ETS源码根目录 |__Out |__Boost # boost的源码及编译后的库文件,功能开发时使用 |__Component # 设计时控件组件,可在Delphi IDE中安装 |__ETS # 编译出来的程序文件都放在这个目录中,可将整个目录拷贝到其他地方使用 |__Temp # 程序编译时产生的中间文件,可删除 ``` ## 后续计划 1. 对Delphi这块,我会慢慢弱化,最后可能仅会保留DDUI控件的实现。 窗口功能实现,会慢慢用脚本替代。 后续也有计划研究Chromium、libcef、electron等功能的实现,说不定哪一天,界面就改成Web了,Delphi就会完全废弃。 2. 目前正在做一个小巧的项目管理界面,使用前面提到的脚本驱动窗口的方案,计划会支持gn、make的目标查看、程序编译功能。 3. ComboBox、Edit控件的DUI化打算重新加入开发日程中,因为项目管理的功能中会用到,计划去Duilib库中取取经。 ## 联系作者 * 作者邮箱: xinghun87@163.com * 作者博客:[https://blog.csdn.net/xinghun61](https://blog.csdn.net/xinghun61)