1、 毕业设计 ( 论文 ) Word试题库设计 论文作者姓名: 申请学位专业: 申请学位类别: 指导教师姓名(职称): 论文提交日期: Word试题库设计 摘 要 编写试题、排版试卷一直以来都是一项繁重的工作。手工录入试题然后排版试卷工作量大且容易出错,试题的收集保存也是以文件的形式,这使得查找试题变得相当困难。改用数据库存储试题将使试题的存储和查找得到优化,这是大部分试题库软件的优势,但是这些试题库软件都使用软件自身的文本编辑器,功能十分有限,在数据的存储方面也只 能存储纯文本的试题,不能存储图片、表格等。这使得用户在编辑试题时十分不便,在数据的存储方面也有很大限制。如果结合数据库的存储功能、
2、结构化查询语言的查询功能和 Word 的文本编辑功能,利用Word 进行试题编写、试卷排版,利用数据库存储试题,利用结构化查询语言查找试题、过滤显示同类试题,那么编写试题、排版试卷等工作将变得方便快捷。为了实现 Word 与数据库之间的试题存取 (能够支持图片、表格、公式的存取 )以及其他一些功能编写的 Word 试题库工具插件以 COM 组件的形式嵌入 Word 程序中,通过它将 Word 与试题数据库有效 的结合到一起,使用户既能够方便的编辑试题又能够将试题存入数据库或从数据库中取出,还能够通过结构化查询语句搜索试题。 关键词 : COM 组件; Word 插件;二进制字段;结构化查询语言
3、 The Design of an Word for the Test Question Bank Abstract Editing test questions and typesetting examination papers is always an arduous work, which is tedious and easy to make mistake. Besides, the collection of the test questions is in the form of files that makes it difficult to search proper te
4、st questions. Using the database to store test questions can improve the searching function for the test questions, but such softwares almost use their own text editors with limited format control functions. At the aspect of their storage form, they commonly only save test questions in plain text, w
5、hich means they can hardly store the pictures or tables that may be nesscesary in the test question. Considering that large part of test questions are edited in MSWord, if we can utilize the powerful format control function combined with the binary data storage function of the database to provide a
6、localized test question management tool, the work of editing test questions and typesetting examination papers would become more effective and convinent. With the help of COM component technology, such a tool can be implemented in the form of Word Addin that can integrate MSWord and the test questio
7、n bank seamlessly and facilitate the users to store the test question into the bank and retrieve test questions selectively by the Structural Query Language (SQL). Key words: COM; Word Addin; Binary Field; SQL 目 录 论文总页数: 33 页 1 引言 . 1 1.1 课题背景 . 1 1.2 国内外研究现状 . 1 1.3 本课题研究的意义 . 1 2 理论基础 . 1 2.1 COM
8、组件技术 . 1 2.1.1 什么是 COM 组件 . 1 2.1.2 COM 组件的基本元素 . 2 2.1.3 关键接口 . 3 2.1.4 如何创建 COM 对象 . 4 2.1.5 如何删除 COM 对象 . 5 2.2 Word 对象模型 . 6 2.2.1 什么是 Word 对象模型 . 6 2.2.2 Word 对象模型抽象图 . 6 2.2.3 主要对象模型介绍 . 7 2.3 数据库技术 . 7 2.4 ODL 文件语法 . 8 2.4.1 ODL 文件语法结构 . 8 2.4.2 类型库 TLB 的使用方法 . 10 2.5 ADO 技术 .11 2.5.1 什么是 ADO
9、 技术 .11 2.5.2 如何在 VB 中使用 ADO . 12 2.5.3 ADO 技术主要应用举例 . 15 2.6 插件技术 . 20 2.6.1 什么是插件 . 20 2.6.2 主流插件简 介 . 20 3 需求分析与方案设计 . 21 3.1 什么是 Word 试题库工具插件 . 21 3.2 Word 试题库工具插件的功能需求分析 . 21 3.2.1 创建题库功能 . 22 3.2.2 打开试题库功能 . 22 3.2.3 存储试题功能 . 22 3.2.4 提取试题功能 . 22 3.2.5 删除试题功能 . 22 3.2.6 预览试题功能 . 22 3.2.7 搜索试题功
10、能 . 22 3.2.8 过滤试题功能 . 23 3.3 Word 试题库工具插件的结构设计 . 23 3.3.1 数据库结构 . 23 3.3.2 主程序结构 . 23 4 详细设计与实现 . 23 4.1 已实现的功能 . 23 4.2 在 Word 中添加按钮的实现过程 . 24 4.2.1 实现原理 . 24 4.2.2 关键代码 . 24 4.2.3 相关技术的运用 . 25 4.3 试题存储的实现过程 . 25 4.3.1 实现原理 . 25 4.3.2 关键代码 . 26 4.3.3 相关技术的运用 . 27 4.4 试题提取的实现过程 . 28 4.4.1 实现原理 . 28
11、4.4.2 关键代码 . 28 4.4.3 相关技术的运用 . 30 5 测试和分析 . 30 结 论 . 30 参考文献 . 31 致 谢 . 32 声 明 . 错误 !未定义书签。第 1 页 共 33 页 1 引言 1.1 课题背景 目前大量试题使用 Word 编写,在 Word 中可以很方便地编写公式、插入图片和表格。一些独立的试题库软件使用简单的文本编辑功能,在试题编辑方面没有Word 方便。但是 Word 仅能编辑试题,不能存储收集试题。 Word 试题库工具插件以 COM 组件的形式嵌入 Word 程序中,通过它将 Word 与数据库有效的结合到一起,从而使用户既能够方便的编辑试题
12、又能够将试题存入数据库或从数据库中取出。 1.2 国内外研究现状 大多数试题库软件都使用软件自身的文本编辑器,功能十分有限;在数据的存储方面只能存储纯文本的试题,不能存储图片、表格等,这使得用户在编辑试题时十分不便,在数据的存储方面也有很大限制。 1.3 本课题研究的意义 通过综合运用 COM 组件技术、插件技术、数据库技术开发的 Word 试题库工具插件能嵌入 Word 程序内,从而将 Word 与数据库有效的结合到一起。使用户在编辑试题时既能用 Word 方便快捷的编辑试题又能方便的将试题存 入数据库或从数据库提取试题。使试题编辑工作更加方便快捷。 2 理论基础 2.1 COM 组件技术
13、2.1.1 什么是 COM 组件 COM 即组件对象模型,是 Component Object Model 的缩写。 COM 是一种跨应用和语言共享二进制代码的方法。与 C+不同,它提倡源代码重用。 ATL 便是一个很好的例证。源码级重用虽然好,但只能用于 C+。它还带来了名字冲突的可能性,更不用说不断拷贝重用代码而导致工程膨胀和臃肿。 Windows 使用 DLLs 在二进制级共享代码。这也是 Windows 程序运行的关键 重用 kernel32.dll, user32.dll 等。但 DLLs 是针对 C 接口而写的,它们只能被 C 或理解 C 调用规范的语言使用。由编程语言来负责实现共
14、享代码,而不是由 DLLs 本身。这样的话 DLLs 的使用受到限制。 MFC 引入了另外一种 MFC 扩展 DLLs 二进制共享机制。但它的使用仍受限制 只能在 MFC 程序中使用。 COM 通过定义二进制标准解决了这些问题,即 COM 明确指出二进制模块( DLLs 和 EXEs)必须被编译成与指定的结构匹配。这个标准也确切规定了在内存中如何组织 COM 对象。 COM 定 义的二进制标准还必须独立于任何编程语言(如 C+中的命名修饰)。一旦满足了这些条件,就可以轻松地从任何编程语言中存取这些模块。由编译器负责所产生的二进制代码与标准兼容。这样使后来的人就能更容易地使用这些二进制代码。第
15、2 页 共 33 页 在内存中, COM 对象的这种标准形式在 C+虚函数中偶尔用到,所以这就是为什么许多 COM 代码使用 C+的原因。但是记住,编写模块所用的语言是无关的,因为结果二进制代码为所有语言可用。 2.1.2 COM 组件的基本元素 coclass(简称组件对象类 component object class)被包含在 DLL 或EXE 中,并且包含着一个或者多个接口的代码。组件对象类( coclasss)实现这些接口。 COM 对象在内存中表现为组件对象类( coclasss)的一个实例。注意COM“ 类 ” 和 C+“ 类 ” 是不相同的,尽管常常 COM 类实现的就是一个
16、C+类。 COM 服务器是包含了一个或多个 coclass 的二进制 DLL 或 EXE。 注册( Registration)是创建注册表入口的一个过程,告诉 Windows 操作系统 COM 服务器放在什么位置。取消注册( Unregistration)则相反 从注册 表删除这些注册入口。 GUID(谐音为 “fluid” ,意思是全球唯一标示符 globally unique identifier)是个 128 位的数字。它是一种独立于 COM 编程语言的标示方法。每一个接口和 coclass 有一个 GUID。因为每一个 GUID 都是全球唯一的,所以避免了名字冲突(只要用 COM AP
17、I 创建它们)。有时还会碰到另一个术语 UUID(意思也是全球唯一标示符 universally iqueidentifier)。 UUIDs 和 GUIDs 在实际使用时的用途是一样的。类 ID 或者 CLSID 是命名 coclass 的 GUID。接口 ID 或者 IID 是命名接口的 GUID。 在 COM 中广泛地使用 GUID 有两个理由: 1、 GUIDs 只是简单的数字,任何编程语言都可以对之进行处理。 2、 GUIDs 可以在任何机器上被任何人创建,一旦完成创建,它就是唯一的。因此, COM 开发人员可以创建自己特有的 GUIDs 而不会与其它开发人员所创建的GUIDs 有冲
18、突。这样就消除了集中授权发布 GUIDs 的必要。 HRESULT 是 COM 用来返回错误和成功代码的整型数字。除此之外,别无它意,虽然以 H 作前缀 ,但没有句柄之意。 最后, COM 库是在使用 COM 时与交互的操作系统的一部分,它常常指的就是 COM 本身。但是为了避免混淆才分开描述的。 使用和处理 COM 对象 每一种语言都有其自己处理对象的方式。例如, C+是在栈中创建对象,或者用 new 动态分配。因为 COM 必须独立于语言,所以 COM 库为自己提供对象管理例程。下面是对 COM 对象管理和 C+对象管理所做的一个比较: 创建一个新对象 C+中,用 new 操作符,或者在栈
19、中创建对象。 第 3 页 共 33 页 COM 中,调用 COM 库中的 API。 删除对象 C+中,用 delete 操作符,或将栈对象踢出。 COM 中,所有的对象保持它们自己的引用计数。调用者必须通知对象什么时候用完这个对象。当引用计数为零时, COM 对象将自己从内存中释放。 由此可见,对象处理的两个阶段:创建和销毁,缺一不可。当创建 COM 对象时要通知 COM 库使用哪一个接口。如果这个对象创建成功, COM 库返回所请求接口的指针。然后通过这个指针调用方法,就像使用常规 C+对象指针一样。 2.1.3 关键接口 IUnknown 接口 每一个 COM 接口都派生于 IUnknow
20、n。这个名字有点误导人,其中没有未知( Unknown)接口的意思。它的原意是如果有一个指向某 COM 对象的 IUnknown 指针,就不用知道潜在的对象是什么,因为每个 COM 对象都实现 IUnknown。 IUnknown 有三个方法: AddRef() 通知 COM 对象增加它的引用计数。如果你进行了一次接口指针的拷贝,就必须调用一次这个方法,并且原始的值和拷贝的值两者都要用到。在本文的例子中没有用到 AddRef()方法。 Release() 通知 COM 对象减少它的引用计数。参见前面的 Release()示例代码段。 QueryInterface() 从 COM 对象请求一个接
21、口指针。当 coclass 实现一个以上的接口时,就要用到这个方法。 IClassFactory 接口 IClassFactory 的作用是创建 COM 组件。创建组件的责任交给了一个单独的对象,这个对象就是类厂。每个组件都必须有一个与之相关的类厂,这个类厂知道怎么样创建组件,当客户请求一个组件对象的实例时,实际上这个请求交给了类厂,由类厂创建组件实例,然后把实例指针交给客户程序。这个过程在跨进程及远程创建组件时特别有用,因为这时就不是一个简单的 new 操作就可以的了,它必须要 经过调度,而这些复杂的操作都交给类厂对象去做了。 IClassFactory 最重要的一个函数就是 CreateI
22、nstance,顾名思议就是创建组件实例,一般情况下我们不会直接调用它, API 函数都为我们封装好它了,只有某些特殊情况下才会由我们自己来调用它。 IDispatch 接口 IDispatch 叫做调度接口。调度接口把每一个函数每一个属性都编上号,客户程序要调用这些函数属性的时侯就把这些编号传给 IDispatch 接口就行了,第 4 页 共 33 页 IDispatch 再根据这些编号调用相应的函数。 IDispatch 接口 的主要函数是Invoke,客户程序都调用它,然后 Invoke 再调用相应的函数。 2.1.4 如何创建 COM 对象 为了创建 COM 对象并从这个对象获得接口,
23、 必须调用 COM 库的 API 函数,CoCreateInstance()。其原型如下: HRESULT CoCreateInstance (REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv ); 以下是参数解释: rclsid coclass 的 CLSID,例如,可以传递 CLSID_ShellLink 创建一个 COM 对象来建立快捷方式。 pUnkOuter 这个参数只用于 COM 对象的聚合,利用它向现有的 coclass 添加新方法。参数值为 null 表示不使用聚
24、合。 dwClsContext 表示所使用 COM 服务器的种类。本文使用的是最简单的 COM 服务器,一个进程内( in-process) DLL,所以传递的参数值为 CLSCTX_INPROC_SERVER。注意这里不要随意使用 CLSCTX_ALL(在 ATL 中,它是个缺省值),因为在没有安 装DCOM 的 Windows95 系统上会导致失败。 riid 请求接口的 IID。例如,可以传递 IID_IShellLink 获得 IShellLink 接口指针。 ppv 接口指针的地址。 COM 库通过这个参数返回请求的接口。当调用CoCreateInstance()时,它负责在注册表中
25、查找 COM 服务器的位置,将服务器加载到内存,并创建所请求的 coclass 实例。 以下是一个调用的例子,创建一个 CLSID_ShellLink 对象的实例并请求指向这个对象 IShellLink 接口指针。 HRESULT hr; IShellLink* pISL; hr = CoCreateInstance ( CLSID_ShellLink, / coclass 的 CLSID 第 5 页 共 33 页 NULL, / 不是用聚合 CLSCTX_INPROC_SERVER, / 服务器类型 IID_IShellLink, / 接口的 IID (void*) / 指向接口的指针 if
26、 ( SUCCEEDED ( hr ) ) / 用 pISL 调用方法 else / 不能创建 COM 对象, hr 为出错代码 首 先声明一个接受 CoCreateInstance()返回值的 HRESULT 和 IShellLink 指针。调用 CoCreateInstance()来创建新的 COM 对象。如果 hr 接受到一个表示成功的代码,则 SUCCEEDED 宏返回 TRUE,否则返回 FALSE。 FAILED 是一个与SUCCEEDED 对应的宏用来检查失败代码。 2.1.5 如何删除 COM 对象 前面说过,不用释放 COM 对象,只要告诉它们已经用完对象。 IUnknown
27、 是每一个 COM 对象必须实现的接口,它有一个方法, Release()。调用这 个方法通知 COM 对象不再需要对象。一旦调用了这个方法之后,就不能再次使用这个接口,因为这个 COM 对象可能从此就从内存中消失了。如果的应用程序使用许多不同的 COM 对象,因此在用完某个接口后调用 Release()就显得非常重要。如果不释放接口,这个 COM 对象(包含代码的 DLLs)将保留在内存中,这会增加不必要的开销。如果的应用程序要长时间运行,就应该在应用程序处于空闲期间调用 CoFreeUnusedLibraries() API。这个 API 将卸载任何没有明显引用的 COM 服务器,因此这也降低了应用程序 使用的内存开销。继续用上面的例子来说明如何使用 Release(): / 像上面一样创建 COM 对象, 然后, if ( SUCCEEDED ( hr ) ) / 用 pISL 调用方法 / 通知 COM 对象不再使用它 pISL-Release();