一个项目要求用 Java 调用 Word 的 Wildcards 搜索功能,并将搜索的全部结果存入数据库。该项目使用 JACOB 作为操作的 Word 的接口,JACOB 是 Java 和 COM 的“桥”。
我对 Java 不熟,也没时间研究如何使用 JACOB,于是我使用 C# 调用 Office Component,反正 JACOB 是基于 JNI 调用 COM,我只要找到解决问题的方法即可。让项目组熟悉 Java 和 JACOB 的同事改写一份即可,这个主意我不错,开工吧。
1、如何调用 Wildcards 搜索
Application 和 Document 接口没有提供 Find 或 Serach 方法,要实现搜索的功能,我们需要使用 Selection 或 Range 接口,这两个接口提供了 Find 属性(Property)。
实际上,Word 提供了一个名称叫 Find 接口,Range 对象的 Find 属性就是该类型的,搜索功能主要由该接口实现。针对我们要实现的功能,主要设置 Forward、MatchWildcards、Text 三个属性,然后调用 Execute 方法,如果找到了匹配的结果则返回 true,否则返回 false。
2、如何取得搜索结果
Wildcards 搜索的“表达式”有些类似于“正则表达式”,因此如何取得搜索到的结果成了新问题,这也是同事迟迟没有搞定的。
花了很多时间写测试代码,搜索 MSDN 中关于 Range 对象和 Find 对象的帮助,最后终于找到了下面这段话。真相大白于天下!!!
当我们使用 Range 对象进行搜索时,找到第一个匹配的字符串时,“源 Range 对象”将被 Redefined,也就是说,搜索的结果是存储在“用来搜索用的 Range 对象中”。
3、如何实现搜索下一条(Find Next)
解决了搜索结果的问题,我们又遇到了新的问题。既然用来搜索用的 Range 对象已经改变,我们如何实现 Find Next 功能???
很简单,从匹配字符串的“下一个字符”继续搜索。说起来容易,如何实现呢?
我们是使用 Document 对象的 Range 方法来取得 Range 对象的,那么,只需看看 Range 的原型即可:
需要注意的是,省略 Start 参数时,默认从 Document 的第一个字符开始;省略 End 参数时,默认到 Document 的最后一个字符结束。
4、C# 调用 COM 的注意内容
Office Component 对象中的方法,通常提供 N 个参数,基本上都是 Object 的 ref 类型,给开发带来很多不便。还好,在 .Net 中提供了 Type.Missing 字段(Field),不想传递值的参数(Argument)使用该字段即可。
5、VB.NET 和 C# 的选择
我对 C# 熟悉一些,使用 .Net 之后几乎所有的测试代码都用 C# 完成。在历经了 N 次操作 Office Component 的痛苦之后,我强烈推荐大家使用 VB.NET 操作 Office Component。反正 .Net 支持多语言,我们完全可以使用 VB.NET 写好 Class Library 在 C# 中调用。
以下是我使用 Reflector 对 VB.NET 代码的分析,编译器帮我们做了很多工作(请注意反编译后的第 5~7 行代码),真的很方便!!!
VB.NET 的测试代码
1Private Function Search()Function Search(ByVal key As String) As String
2 Dim sbResult As New StringBuilder()
3 Dim isFind As Boolean
4 Dim fileName As String
5
6 fileName = ""
7 isFind = True
8
9 Dim app As New Application()
10
11 app.Documents.Open(fileName)
12
13 Return sbResult.ToString()
14End Function
Reflector 反编译的代码
1Private Function Search()Function Search(ByVal key As String) As String
2 Dim sbResult As New StringBuilder
3 Dim fileName As String = ""
4 Dim app As Application = New ApplicationClass
5 Dim VB$t_ref$S0 As Object = fileName
6 app.get_Documents.Open((VB$t_ref$S0), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value), (Missing.Value))
7 fileName = Conversions.ToString(VB$t_ref$S0)
8 Return sbResult.ToString
9End Function
6、参考资料
我发现长期培养的直觉,面对 Office 对象模型几乎没有任何作用,看来,要想做 Office 开发首先是静下心来熟悉 Office 对象模型,经验主义在这儿是行不通的,呵呵。
我机器使用的是 MSDN for Visual Studio 2005,下面的两行文字是查找 Office 帮助的路径,希望能有所帮助。
- Office Solutions Development/Microsoft Office 2003 Documentation/Office 2003/VBA Language Reference/Microsoft Word Visual Basic Reference
- Development Tools and Lanauge/Visual Studio/Visual Studio Tools for Office/Reference/Microsoft Office Word Primary Interop
7、参考代码
C# 的全部测试代码
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Text;
7using System.Windows.Forms;
8using Microsoft.Office.Interop.Word;
9
10namespace WordSearch {
11 public partial class Form1 : Form {
12 public Form1() {
13 InitializeComponent();
14 }
15
16 private void btnSearch_Click(object sender, EventArgs e) {
17 bool isFind = true;
18 object fileName = txtFile.Text;
19 StringBuilder sbResult = new StringBuilder();
20
21 ApplicationClass app = new ApplicationClass();
22 DocumentClass doc = app.Documents.Open(ref fileName, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing)
23 as DocumentClass;
24 Range rng = doc.Content;
25
26 while (isFind) {
27 // 查询条件
28 rng.Find.Forward = true;
29 rng.Find.MatchWildcards = true;
30 rng.Find.Text = txtKey.Text;
31 isFind = rng.Find.Execute(ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing, ref _missing);
32
33 // 如果找到,则输出查询结果,并且生成新的 Range 对象
34 if (isFind) {
35 // 找到对应的结果后,将更新 rng 对象,可以使用 rng.Text 取得匹配的文本
36 sbResult.Append(rng.Text);
37 sbResult.Append(Environment.NewLine);
38
39 // 下一次的查询,需要从当前位置的下一个位置开始,因此使用 rng.End + 1
40 object cnt = rng.End + 1;
41 // 第一个参数为起始位置,第二个参数忽略,则默认到文章最后一个字符
42 rng = doc.Range(ref cnt, ref _missing);
43 }
44 }
45
46 app.Quit(ref _missing, ref _missing, ref _missing);
47
48 txtResult.Text = sbResult.ToString();
49 }
50
51 private static object _missing = Type.Missing;
52 }
53}