Excel框架概述-培训大纲
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
ET框架概述-培训大纲
一、要素申明
(1)培训对象(以工龄,工种,或从事领域分类)
A.工龄和领域:
a.从事ET开发工作1年以内的同事
b.或其他对ET感兴趣的同事
B.工种:
a.原始目标:程序开发人员
b.其他:总监可以根据实际情况考虑其他工种的同事参训
(2)培训课时:2小时
(3)培训目的:(期望产生的成果)
A.让参加培训的同事对ET的整体框架有初步的了解
B.增长框架设计的知识
C.有助于在今后工作中定位BUG
D.有助于开发新功能时能尽可能考虑得全面些,不会盲目地破坏框架
(4)培训形式:宣讲
二、自我介绍
(一)姓名:
(二)工作经历:
三、培训内容
(一)ET的整体架构
(1)给出ET的框架图,并进行简要说明
Shell
AppLogic
AppCore
UILogic ETCore
ExcelRW
Render Chart Dap
Persist
Compiler FuncLib
(2)上图是ET 的基本框架结构。
A.注1:并没有按照上图严格限定各个模块之间的调用关系
B.注2:有一些模块并没有绘入到上图中 (3)启动顺序:
A.Shell -> AppLogic -> UILogic, Render, ETCore,... (4)响应用户界面消息和命令
A.Shell -> UILgoic -> Render...
(5)对外提供API ,用于二次开发:
A.对外提供的API :是指基于Windows COM 机制实现的跨进程调用接口。
这是windows 操作系统实现的基于接口技术(顺便问问有多少人了解IUnknown ,IDispatch ),并实现了通用的存根代理机制的操作系统一级的服务。
只要遵循windows 提供的实现规范,任何应用程序都可以让其他应用程序可以通过接口对象来操作自己。
B.二次开发:嵌入IE ,被其他应用程序调用
C.Shell 实现的API :对界面进行定制的API
D.AppLogic 实现的API :其他所有API (二)Shell 的概要说明
(1)Shell 是我们的可执行程序:
A.Shell 就是WPS.EXE, ET.EXE, WPP.EXE 。
简单来说,他们像一般可执行程序那样工作。
B.Shell 最主要的内容是:菜单,工具条,对话框,主窗口和文档窗口。
他们接受windows 消息,并派发到内核,有内核处理。
有时候派发之前需要在Shell 这一层做一些逻辑判断或效果预览(比如设置字体等)。
有时候需要将内核返回的结果做一些封装,用合适的方式和用户互动(比如查找替换对话框等)。
(2)Shell 和内核的对应(以ET 为例):
A.启动时,shell会通过windows api 加载applogic这个dll,并从这个dll的导出函数中获取内核的Application对象。
Shell自己有一个application对象,仅代表shell程序。
B.Shell创建文档窗口时,从内核的Application对象中获得Document对象,获得Window对象。
将这些对象和Shell内的对象关联起来,从而能够将消息发送到正确的内核对象实例上去。
C.其他类似,不描述。
(3)多语言支持
A.需要为多语言支持做一些特殊的工作,才能让应用程序在不同语言的操作系统下不出现乱码。
B.简述:
a.我们在界面看到的字符串是从et.lng加载上来的。
b.中文、日文、繁体等各有一个et.lng,他们分别放置在office6目录下的2052,1041等目录下。
c.Et.lng大概可以看作是一个大表,每一条记录的内容是:(字符串id,字符串值)
d.Et.exe里面需要使用字符串的地方实际上都是使用了字符串id,运行时根据id从et.lng中获取字符串值并显示出来。
C.内核的多语言也采取了同样的原理,但是实现的手段和使用的字符串管理工具都大不相同。
(4)Shell使用Delphi的理由:
A.认为相对于使用VC,编写界面程序比较容易,做出漂亮的效果比较容易。
实际上,当存在较多的Delphi程序员时,这是正确的。
缺点在于,界面程序员和内核程序员的技术不通用,当Delphi程序员较少时(比如当前2009年和2010年),做界面和界面效果就是一件很困难的事情了。
B.认为内核和界面使用不同的语言,有助于降低界面程序和内核的耦合度。
实际上在降低耦合度方面起到的作用有限。
原打算Shell仅仅调用Apploigc提供的API即可,但实际上不能满足需求。
在UILogic中,内核向Shell传递了很多自定义接口。
缺点在于:增加了开发难度(同步api接口文件描述和资源文件管理出了很多事情),增加了调试难度,增加了人员沟通成本(原本一个程序员可以做的事情,必须分为两个程序员来做),增加了配置管理成本(需要购买多种开发工具,每日构建也会复杂一些)。
C.总的来说,我个人认为当初使用Delphi开发界面程序是一个不完全成功的尝试。
到目前为止仍然有新的影响出现:比如移植wps到linux下。
界面程序的代码必须运行在wine下面,或重写,而其他模块(除了一个模块)都可以移植成原生代码。
运行在wine 下,对如何实现linux下的二次开发是一个重大考验,目前刘斌正在研究。
D.但是我们必须接受这个现实。
因为重做一遍界面程序,也许需要在座所有人花上几年时间。
E.最后需要感谢我们的第一辈shell程序员,他们当初都是应届毕业生,而现在都是大名鼎鼎的人物了:晁云曈,盘善君,曾有贵,卓洪涛。
据我所知,他们成功地实现了当今复杂度最高的delphi桌面程序。
还有现在仍然在shell中艰苦奋斗的同事们。
KActionTarget KViewActionTarget
KMainWndEventSink KWindowEventSink KPaneEventSink
......
菜单命令
窗口消息
(1)接收Shell 转送的用户命令和操作系统消息,并处理 A.自己实现一些代码
B.调用Applogic, AppCore, ETCore 等模块的方法 (2)最主要的东西: A.KActionTarget
a.接收所有菜单命令
b.除了响应命令的函数,还有很多工具类,用来标记当前动作进行时,内核所处的
状态。
这是细节,也是难点。
B.KViewActionTarget
a.应shell 设计,原意是要接收文档窗口菜单命令。
但实际上直接转调了
KActionTarget 的所有方法。
因此只是一个空壳。
C.KMainWndEventSink
a.接收主窗口消息:比如鼠标键盘消息,应用程序关闭,移动,改变大小,等 D.KWindowEventSink
a.接收文档窗口消息:比如鼠标键盘消息,子窗口关闭,移动,改变大小,等
E.KPaneEventSink
a.接收文档窗口内的pane 相关的消息:比如鼠标键盘消息,窗口的上下左右滚动消
息,焦点获得、丢失消息。
Application
Workbooks
Workbook
Windows
Window
Worksheets
Worksheet
...
...
(1)Applogic的设计目的:
A.对外提供的API从这里暴露出去,供二次开发使用,供shell等其他模块使用。
a.“从这里暴露出去”:涉及到COM实现原理。
简单来说,就是windows需要从注册表中找到应该从哪一个模块中创建出一个Application对象。
我们的设计是:在注册表里面告诉windows,请从applogic这个dll里面创建出Application对象。
创建时,由applogic 负责启动et.exe(这个过程和双击et.exe启动应用程序正好是相反的)
b.从常规来说,会选在在exe中实现并对外暴露api。
但是由于选择了delphi做界面,为了降低界面和内核的耦合度,所以将API实现在内核中。
c.Shell实现的界面定制相关的api,也是通过applogic传递给外界的。
B.实现应用程序的API对象模型,并维护对象的生命周期
a.Application-Workbooks-Workbook-Windows-Window-Range。
C.Applogic是et的中枢模块:(此处再次回顾一下开始的总框架图)
KAppCoreRange
KETDefinedNames
...
...
(1)AppCore的设计目的:
A.有些API对象的实现较为复杂,为避免Applogic过于庞大,将这些对象做一个双层的实现:
a.在Applogic中对这些进行壳实现,主要实现符合API标准接口定义要求的部分
b.在Appcore中对这些对象进行实际功能实现
c.比如:Range与KAppCoreRange配对,Names,Name与KETDefinedNames配对。
B.理论上来说,Appcore的全部实现移动到Applogic中,是不会造成框架上的影响的。
a.但是,目前的框架可以起到如下限制作用以降低模块间耦合的复杂度:
b.Applogic会使用Appcore,ETcore提供的对象和方法,Appcore会使用ETCore等底层模块的方法,但是不会使用Applogic,UILogic等上层模块提供的对象和方法。
KWorkSpace
KBook
KVarManager
...
KSheet
ExtData
KBookOp
KSupbooks
ISupbook
ExtData
...
(1)ETCore是底层模块的核心
A.它实现了ET的基本数据结构
B.它是电子表格计算功能的中枢
C.可以说,前面介绍的那些模块,都是对ETCore的封装
(2)ET的基本数据结构的实现:
A.KWorkSpace:KBook的合集
B.KBook:工作簿相关的实现,比如文件名,sheet集合,undo/redo控制,等等
a.KVarManager:可以简单地认为,它代表了整个计算引擎体系。
C.KSheet:工作表相关的实现,比如sheet名,行列数据等等
D.KBookOp:对工作簿和工作表的一些操作的封装。
目的是将操作实现和数据实现分离,让book和sheet的实现重点在数据本身的保存和组织。
E.KSupbooks,ISupBook:跨book公式的数据组织实现。
F.Set/GetExtDataItem:
a.其实现是一个IUknown指针数组。
b.Book和Sheet都实现了这个2方法。
c.用于保存一些ETCore 不需要关心的,但是又必须存盘的数据。
比如:对象层,数
据透视表对象集合,批注,超链接等等。
查看枚举“SHEETEXTENDDATA ”可见详细内容
G.其他:ETCore 中还有很多其他的、很重要的东西,比如计算机制,undo/redo 机制等等,很重要,很复杂,不详述。
(七)编译器和函数库在ET 中如何工作
CompileFormula(...)...
Compiler
FuncLib
IFunction {
CallFunction(...) ...}
(1)编译器Compiler 和函数库Funclib 作为辅助ET 运行的重要工具,为了提高et 实现的灵活性,降低耦合度,将它们作为独立的工程实现。
(2)编译器
A.作用:将字符串编译成为:数字,日期,时间,公式执行序列等等。
无法成功编译的就会被看作成真正的文本。
a.如“1.234”和1.234,是不同的东西
b.“1月2日”,是一个数字
c.=a1+a2,是一个公式,会被编译成为一个“公式执行序列”,比如课本上说的一个
“逆波兰表达式”就是一个简单的公式执行序列。
(此处不详细解释公式执行序列)
d.还有“反编译”的功能:即,将编译后的结果反编译成人类可以阅读的字符串 B.怎么工作:
a.对外暴露“CompileFormula ”等方法
b.用户在shell 敲击键盘,uilogic 记录下最终输入的字符串值,etcore 调用编译器对
外暴露的方法,对传入的字符串值进行编译,并将结果存储在单元格数据中。
(3)函数库
A.作用:执行公式执行序列,并将执行结果返回 a.常见的加减乘除运算符都属于函数
b.单参数函数,双参数函数,多参数函数等等 B.怎么用:
a.对外暴露“IFunction”等接口
b.Etcore发现需要计算时,将执行序列传递给IFunction::CallFunction(...)方法,获得计算值。
c.当然,实际情况要稍微复杂一些,但其原理如此。
C.编译器和函数库的功能非常专一,他们根本不了解ET的任何业务逻辑。
如果愿意,可以将它们用作他处,比如在WPS文字处理中,就用到了他们。
(八)Persist和excelrw的概要说明
Persist
Copy,Past,Cut
Open,Save
ExcelRw
Xls File Format
Lib html Lib Xml
(1)Persist的设计目的是成为所有IO操作的管理者和实现者
A.目标达到了,但是非常实现得非常糟糕
(2)所谓的所有IO操作其实只有两种操作:
A.剪贴板相关的操作:复制,粘贴,剪切
a.实现在persist工程中
B.文件相关的操作:打开,保存
a.Xls文件格式的读写实现在excelrw工程中
b.Htm,xml,ooxml文件格式的读写实现在独立的lib中,由excelrw链接进来,可以看作是实现在excelrw中
c.uof文件格式的读写独立的uofssrw.dll中,当excelrw发现当前文件是uof文件时,调用uofssrw.dll的import,export方法进行读写。
(3)为什么这么乱?
A.不断地补充功能,没有在合适的时机重构,导致新的实现将就旧的框架,从而出现混乱
(九)Render的概要说明
HDC
背景层
文本层
对象层
批注层
...
(1)简单地说,就是接受hdc参数和当前需要绘制的工作表参数,进行分层绘制
A.HDC:Handle of Device Context,代表了当前计算机的显示设备
B.背景层,文本层,对象层,批注层等20多个层
a.后绘制的层会叠在先绘制的层之上
(2)实际上Render是一个非常精细的实现,涉及到很多的显示、绘制方面的技术和技巧。
需要考虑:
A.内容的显示效果
B.兼容效果
C.绘制效率
D.区域选择效果
E.点击测试
F.窗口拆分和冻结
G.等等,不详述。
(十)串起来:打开文件
(1)Shell:用户选择“File-Open”,发送“Action_FileOpen”命令到UILogic
(2)UILogic:ActionTarget接收到消息,调用Shell的OpenFileDialog
(3)用户在对话框中选择文件,Dialog会记下用户选择了什么文件
(4)对话框关闭,UILogic开始继续控制:从Dialog返回参数中拿到文件路径名称(5)UILogic调用Applogic:Application.Workbooks.Open
(6)Workbooks.Open:new一个Workbook对象,将文件名传递给Workbook处理。
(7)Workbook:ConstructBook:
A.调用ETCore的Workspace->CreateBook,作为成员保留(m_pBook)。
a.ETCore:book,sheet的创建
B.创建Windows,Window等必要的Api对象
(8)Workbook:Persist->Open(m_pBook,...)
(9)Persist:判断文件类型:xls文件格式,启动excelrw。
(这一段实际上比较隐晦,采用了一种叫做“Filter”的机制,个人很讨厌这个玩意儿,非常不利于代码的维护,这里不细说)。
调用IETXlsRW::ImportFrom(acceptor,。
)
(10)Excelrw:读取文件,将文件填充到Acceptor中,完成。
(11)逐层返回到UILogic,命令处理完毕,返回S_OK给Shell,控制权重新交还给Shell
(12)Window会刷新,PAINT消息触发对UILogic的PaneEventSink::OnPaint的调用(13)UILogic会触发Render::IGridDraw::Draw
(14)Render:通过分层绘制,显示出刚刚打开的文件的活动工作表。
(十一)最初的约定
(1)工程目录结构的约定:
A.必须按照代码库中的目录结构获取到文件本地,否则编译不过。
B.所有工程的结果输出在指定的位置:Product目录,lib目录
C.V6之前的WPS,没有对工程目录结构的组织做约定,各项目按照自己的规则对自己的工程进行组织,导致产品集成非常困难。
没有办法进行每日构建和自动打包
D.刚开始时大家认为这个约定的缺点是:获取文件到本地时不够自由。
但事实证明这并不能成为一个缺点。
(2)共享的头文件的约定:
A.同一个枚举定义、数据结构定义、接口定义、类定义不允许在不同的文件中重复定义多次,如果发现有重复定义,必须重新组织头文件
B.同一个文件不允许被整体复制到另一个目录中供不同的工程包含(也不允许使用某些版本管理工具的link功能),如果发现这样的情况,必须重新设定工程的头文件包含策略,并删除重复的文件。
C.V6之前的WPS没有做这样的约定,导致无法保证枚举、数据结构、接口、类定义的唯一性,当需要修改这些定义时,需要在整个代码库中搜索,在不同的文件中修改这些定义。
经常出现遗漏,导致bug产生。
D.2007年,章庆元支援毒霸开发时,做的第一件事情就是整理毒霸的代码库,让它符合上述2大规则。
(3)Undo/Redo和数据层
A.此部分不是讲述数据层的实现,而是介绍undo/redo机制的概念
B.一般来说,有命令式机制和数据式机制
C.命令式较直观:
a.优点:设计和实现简单
b.缺点:需要为每一个动作实现一个undo动作封装,一个redo动作封装。
在设计期、开发期难以确定有多少个动作需要封装,开发过程中会导致实现混乱。
D.数据式较强大:
a.优点:只需设定一个数据层,在入口处统一封装undo和redo动作。
其他工程中
不需要要关心细节,随时增删需要undo和redo的数据
b.缺点:设计和实现较复杂,不便于维护和调试
E.V6之前的wps采用的是命令式,v6开始,是采用了数据式,在开发过程中根据实际情况需要,做了很薄的命令式封装。
(4)ET中的类指针、接口
A.2000年左右,接口和COM技术引入到WPS的开发中。
由于接口具有较强的自我生命周期管理能力和方便的跨模块调用能力,在团队中得到了迅速普及,直至滥用
B.接口:指以IUnknown为基类的纯虚基类。
a.优点1:通过AddRef和Release来自我管理,解决了复杂逻辑中无法知晓何时应该销毁对象的问题,解决了跨模块的对象传递问题。
b.优点2:因为对接口的描述定义是与开发语言无关的,所以可以方便地在不同语言实现的模块中传递对象。
c.缺点:调用接口方法的速度相对于直接调用类对象要慢。
C.规则:
a.模块内需要传递对象的,尽量传递类指针
b.模块间需要传递对象的,必须传递接口指针
四、答疑时间。