我们都知道,Office是基于COM组件技术开发的,属于非托管程序,然而C#使用的都是托管程序,那么如何使用非托管的COM组件就是我们操作WORD的第一个问题。所幸的是,.NET FRAMEWORK提供了一种不同种类类库的转换工具tlbimp.exe(在C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin下),通过这个转换工具,我们可以把COM组件转化为.NET FRAMEWORK可以直接调用的DLL文件。
接下来就是转化工作了,Office组件都可以在C:Program FilesMicrosoft Office目录下找到,当然安装的Office版本不同,子目录是不一样的。笔者使用的是Office 2007,可以在C:Program FilesMicrosoft OfficeOffice12目录下找到MSWORD.OLB,这个是WORD组件的类库文件,还可以找到MSACC.OLB操作ACCESS,MSPPT.OLB操作PPT,XL5CHS32.OLB操作EXCEL。载入不同的组件就可以完成对不同Office组件的操作。使用tlbimp.exe工具转化MSWORD.OLB文件后可以得到三个DLL文件,Office,dll,Visual BasicIDE.dll,Word.dll。最后在编译文件的时候,记得将这三个DLL文件载入,命令如下:
csc /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll /r:office.dll /r:vbide.dll /r:word.dll word.cs
笔者使用Visual Studio.NET 2005编译环境,通过IDE提供的功能可以大大简化我们对组件转化的工作,并且在编译时也不需要输入那么繁杂的语句,非常方便了。下面介绍一下IDE载入Office组件的方式。
在菜单栏选择“项目”-“添加引用”,弹出的窗口中我们可以选择“COM”选项卡,找到Microsoft Office 12.0 Object Library(Office 2003/2007需要使用12.0版的,如果你使用的是Office 2000或者更低的版本,只要载入10.0版的就可以了),确定后引入.也可以在“浏览”选项卡下找到我们上面提到的MSWORD.OLB文件,引入即可。引入后我们可以发现在解决方案中,引用目录下多了三个文件Microsoft.Office.Core,Microsoft.Office.Interop.Word,VSIDE。这说明引用文件成功,之后在编译程序的时候,在Debug目录下会生成两个DLL文件,Interop.Microsoft.Office.Core.dll,Interop.Microsoft.Office.Interop.Word.dll。完成组件的引入,下面就可以开始程序设计了。
先看一下Word对像模型
ApplicationClass :用来表现WORD应用程序,包含其它所有对象。他的成员经常应用于整个WORD,你可以用它的属性和方法控制WORD环境。
DocumentClass :Document对象是WORD编程的核心。当你打开一个已有的文档或创建一个新的文档时,就创建了一个新的Document对象, 新创建的Document将会被添加到Word Documents Collection。
Selection :Selection对象是描述当前选中的区域。若选择区域为空,则认为是当前光标处。
Rang :是Document的连续部分,根据起始字符和结束字符定义位置。
Bookmark:类似于Rang,但Bookmark可以有名字并在保存Document时Bookmark也被保存。
在编程中使用到的代码如下,注释比较详细,这里就不再具体的说明。
//Word程序对象
private Microsoft.Office.Interop.Word.Application WordApp = new Microsoft.Office.Interop.Word.Application();
//Word文档对象
private Microsoft.Office.Interop.Word._Document aDoc;
private void openfile_Click(object sender, EventArgs e)
{//打开Word文件
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
//定义打开文件的16个参数
object fileName = openFileDialog.FileName; //文件名称
object ConfirmConversions = false; //允许转换
object ReadOnly = false; //只读方式打开
object AddToRecentFiles = false; //添加到最近打开的文档
object PasswordDocument = System.Type.Missing;
object PasswordTemplate = System.Type.Missing;
object Revert = System.Type.Missing;
object WritePasswordDocument = System.Type.Missing;
object WritePasswordTemplate = System.Type.Missing;
object Format = System.Type.Missing; //格式
object Encoding = System.Type.Missing; //编码
object Visible = System.Type.Missing;
object OpenAndRepair = System.Type.Missing;
object DocumentDirection = System.Type.Missing;
object NoEncodingDialog = System.Type.Missing;
object XMLTransform = System.Type.Missing;
WordApp.Visible = true;
try
{
//打开文档
aDoc = WordApp.Documents.Open(ref fileName, ref ConfirmConversions, ref ReadOnly, ref AddToRecentFiles,
ref PasswordDocument, ref PasswordTemplate, ref Revert, ref WritePasswordDocument, ref WritePasswordTemplate,
ref Format, ref Encoding, ref Visible, ref OpenAndRepair, ref DocumentDirection, ref NoEncodingDialog, ref XMLTransform);
//激活文档,使文档为当前处理
aDoc.Activate();
}
catch
{
MessageBox.Show("出现错误!");
}
}
}
private void closefile_Click(object sender, EventArgs e)
{//关闭Word文件
object SaveChanges = false; //保存更改
object OriginalFormat = System.Type.Missing;
object RouteDocument = System.Type.Missing;
//关闭文档
aDoc.Close(ref SaveChanges, ref OriginalFormat, ref RouteDocument);
//退出程序
WordApp.Quit(ref SaveChanges, ref OriginalFormat, ref RouteDocument);
}
通过文档类对象aDoc还可以完成文件的保存,另存为等等操作,详细的可以参阅MSDN。
用C#动态生成Word文档并将数据填入Word表格中
刚刚实现了个功能:用C#实现动态生成Word文档,在Word文档中插入表格,并将读出的数据填入到表格中。
要使用C#操作word,首先要添加引用:
1、添加引用->COM->Microsoft Word 11.0 Object Library
2、在.cs文件中添加
using Word;
下面的例子中包括C#对Word文档的创建、插入表格、设置样式等操作:
(例子中代码有些涉及数据信息部分被省略,重要是介绍一些C#操作word文档的方法)
public string CreateWordFile(string CheckedInfo)
...{
string message = "";
try
...{
Object Nothing = System.Reflection.Missing.Value;
Directory.CreateDirectory("C:/CNSI"); //创建文件所在目录
string + DateTime.Now.ToShortString()+".doc";
object filename = "C://CNSI//" + name; //文件保存路径
//创建Word文档
Word.Application WordApp = new Word.ApplicationClass();
Word.Document WordDoc = WordApp.Documents.Add(ref Nothing, ref Nothing, ref Nothing, ref Nothing);
//添加页眉
WordApp.ActiveWindow.View.Type = WdViewType.wdOutlineView;
WordApp.ActiveWindow.View.SeekView = WdSeekView.wdSeekPrimaryHeader;
WordApp.ActiveWindow.ActivePane.Selection.InsertAfter("[页眉内容]");
WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphRight;//设置右对齐
WordApp.ActiveWindow.View.SeekView = WdSeekView.wdSeekMainDocument;//跳出页眉设置
WordApp.Selection.ParagraphFormat.LineSpacing = 15f;//设置文档的行间距
//移动焦点并换行
object count = 14;
object WdLine = Word.WdUnits.wdLine;//换一行;
WordApp.Selection.MoveDown(ref WdLine, ref count, ref Nothing);//移动焦点
WordApp.Selection.TypeParagraph();//插入段落
//文档中创建表格
Word.Table newTable = WordDoc.Tables.Add(WordApp.Selection.Range, 12, 3, ref Nothing, ref Nothing);
//设置表格样式
newTable.Borders.OutsideLineStyle = Word.WdLineStyle.wdLineStyleThickThinLargeGap;
newTable.Borders.InsideLineStyle = Word.WdLineStyle.wdLineStyleSingle;
newTable.Columns[1].Width = 100f;
newTable.Columns[2].Width = 220f;
newTable.Columns[3].Width = 105f;
//填充表格内容
newTable.Cell(1, 1).Range.Text = "产品详细信息表";
newTable.Cell(1, 1).Range.Bold = 2;//设置单元格中字体为粗体
//合并单元格
newTable.Cell(1, 1).Merge(newTable.Cell(1, 3));
WordApp.Selection.Cells.VerticalAlignment = Word.WdCellVerticalAlignment.wdCellAlignVerticalCenter;//垂直居中
WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;//水平居中
//填充表格内容
newTable.Cell(2, 1).Range.Text = "产品基本信息";
newTable.Cell(2, 1).Range.Font.Color = Word.WdColor.wdColorDarkBlue;//设置单元格内字体颜色
//合并单元格
newTable.Cell(2, 1).Merge(newTable.Cell(2, 3));
WordApp.Selection.Cells.VerticalAlignment = Word.WdCellVerticalAlignment.wdCellAlignVerticalCenter;
//填充表格内容
newTable.Cell(3, 1).Range.Text = "品牌名称:";
newTable.Cell(3, 2).Range.Text = BrandName;
//纵向合并单元格
newTable.Cell(3, 3).Select();//选中一行
object moveUnit = Word.WdUnits.wdLine;
object moveCount = 5;
object moveExtend = Word.WdMovementType.wdExtend;
WordApp.Selection.MoveDown(ref moveUnit, ref moveCount, ref moveExtend);
WordApp.Selection.Cells.Merge();
//插入图片
string FileName = Picture;//图片所在路径
object LinkToFile = false;
object SaveWithDocument = true;
object Anchor = WordDoc.Application.Selection.Range;
WordDoc.Application.ActiveDocument.InlineShapes.AddPicture(FileName, ref LinkToFile, ref SaveWithDocument, ref Anchor);
WordDoc.Application.ActiveDocument.InlineShapes[1].Width = 100f;//图片宽度
WordDoc.Application.ActiveDocument.InlineShapes[1].Height = 100f;//图片高度
//将图片设置为四周环绕型
Word.Shape s = WordDoc.Application.ActiveDocument.InlineShapes[1].ConvertToShape();
s.WrapFormat.Type = Word.WdWrapType.wdWrapSquare;
newTable.Cell(12, 1).Range.Text = "产品特殊属性";
newTable.Cell(12, 1).Merge(newTable.Cell(12, 3));
//在表格中增加行
WordDoc.Content.Tables[1].Rows.Add(ref Nothing);
WordDoc.Paragraphs.Last.Range.Text = "文档创建时间:" + DateTime.Now.ToString();//“落款”
WordDoc.Paragraphs.Last.Alignment = Word.WdParagraphAlignment.wdAlignParagraphRight;
//文件保存
WordDoc.SaveAs(ref filename, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing);
WordDoc.Close(ref Nothing, ref Nothing, ref Nothing);
WordApp.Quit(ref Nothing, ref Nothing, ref Nothing);
message=name+"文档生成成功,以保存到C:\CNSI\下";
}
catch
...{
message = "文件导出异常!";
}
return message;
}
由于考虑到代码复用,我将代码写成了一个类。此外,通过审视代码可以发现,如果要多次读取同一文件中的不同的单元格数据会造成频繁的打开、关闭Word程序;因此,我将代码进行优化。在我做优化的时候突然想起来ADO.NET的SqlConnection和SqlCommand类。这两个类我常常用做数据库操作,一般用到的方法顺序都是:打开数据库连接,执行数据库查询,关闭数据库连接。我没有使用到两个类,我将这段代码封装于一个类中。使用Open、Close控制Word文档的打开和关闭,使用WordTableRead方法读取表格中的数据。这样对于读取多个单元格中的数据,每次只需要打开、关闭一次Word程序即可,大大的节约了资源的开销和节省了时间,提高的读取效率。此外,对于代码的优化还有可以读取指定表格中数据。下图显示了这个类的结构,代码及相应注释附在图的下方:
class WordTableRead
2{
3 private string fileName;
4 private ApplicationClass cls = null;
5 private Document doc = null;
6 private Table table = null;
7 private object missing = Missing.Value;
8 //Word是否处于打开状态
9 private bool openState;
10
11
12 /**////
13 /// 自定义构造方法
14 ///
15 /// 包含路径的文件名
16 public WordTableRead(string fileName)
17 {
18 this.fileName = fileName;
19 }
20
21 /**////
22 /// 打开Word文档
23 ///
24 public void Open()
25 {
26 object path = fileName;
27 cls = new ApplicationClass();
28 try
29 {
30 doc = cls.Documents.Open
31 (ref path, ref missing, ref missing, ref missing,
32 ref missing, ref missing, ref missing, ref missing,
33 ref missing, ref missing, ref missing, ref missing,
34 ref missing, ref missing, ref missing, ref missing);
35 openState = true;
36 }
37 catch
38 {
39 openState = false;
40 }
41 }
42
43 /**////
44 /// 返回指定单元格中的数据
45 ///
46 /// 表格号
47 /// 行号
48 /// 列号
49 ///
50 public string ReadWord(int tableIndex, int rowIndex, int colIndex)
51 {
52 //Give the value to the tow Int32 params.
53
54 try
55 {
56 if (openState == true)
57 {
58 table = doc.Tables[tableIndex];
59 string text = table.Cell(rowIndex, colIndex).Range.Text.ToString();
60 text = text.Substring(0, text.Length - 2); //去除尾部的mark
61 return text;
62 }
63 else
64 {
65 return "";
66 }
67 }
68 catch
69 {
70 return "Error";
71 }
72 }
73
74 /**////
75 /// 关闭Word文档
76 ///
77 public void Close()
78 {
79 if (openState == true)
80 {
81 if (doc != null)
82 doc.Close(ref missing, ref missing, ref missing);
83 cls.Quit(ref missing, ref missing, ref missing);
84 }
85 }
86}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Office.Interop;
namespace DealDoc
{
public partial class Form1 : Form
{
string docContent = "";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnOpen_Click(object sender, EventArgs e)
{
//if (folderBrowserDialog1.ShowDialog() == DialogResult.OK && folderBrowserDialog1.SelectedPath != "")
if(openFileDialog1.ShowDialog() == DialogResult.OK && openFileDialog1.FileName != "")
{
this.textBox1.Text = openFileDialog1.FileName;
docContent = Doc2Text(this.textBox1.Text);
this.richTextBox1.Text = docContent;
this.richTextBox1.Enabled = false;
}
}
//保存
private void btnSave_Click(object sender, EventArgs e)
{
//SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.Filter = "Doc文件(*.doc)|*.doc|Txt文件(*.txt)|*.txt|所有文件(*.*)|*.*";
saveFileDialog1.Title = "保存文件";
//StreamWriter myStream = new StreamWriter(saveFileDialog1.FileName);
if ((saveFileDialog1.ShowDialog()) == DialogResult.OK)
{
if (saveFileDialog1.FileName != null)
{
//myStream.Write(this.richTextBox1.Text);
//myStream.Close();
this.richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.PlainText);
}
}
}
//关闭
private void btnClose_Click(object sender, EventArgs e)
{
this.Dispose();
Application.Exit();
}
//获得word文本
public string Doc2Text(string docFileName)
{
Microsoft.Office.Interop.Word.ApplicationClass wordApp = new Microsoft.Office.Interop.Word.ApplicationClass();
//Normal.dot 模板使用
wordApp.NormalTemplate.Saved = true;
object fileobj = docFileName;
object nullobj = System.Reflection.Missing.Value;
Microsoft.Office.Interop.Word.Document doc = wordApp.Documents.Open(ref fileobj, ref nullobj, ref nullobj,
ref nullobj, ref nullobj, ref nullobj,
ref nullobj, ref nullobj, ref nullobj,
ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj
);
string outText = doc.Content.Text;
doc.Close(ref nullobj, ref nullobj, ref nullobj);
wordApp.Quit(ref nullobj, ref nullobj, ref nullobj);
return outText;
///关闭Word时调用
//object saveOption = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges;
//object missing = System.Reflection.Missing.Value;
//wordApp.Quit(ref saveOption, ref missing, ref missing);
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
//垃圾处理
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
********************************************************************************
详细的:将 Word 用作自动化服务器时提示保存 Normal.dot
http://support.microsoft.com/kb/285885/zh-cn
同时自动化多个 Microsoft Word 实例时,用户可能收到下面的一个或多个警告:
“Normal.dot was being edited by another Word session.If you save this document with the original name, you will overwrite any changes made in the other session.Do you want to save the document using the original name anyway?”
- 或 -
This file is in use by another application or user.(C:\Documents and Settings\...\Normal.dot)
如果对 Normal.dot 模板进行了更改,就可能会出现这些警告。
回到顶端
解决方案
要解决此问题,请执行下列操作之一: 在退出 Word 或将控制权移交给用户之前,应将 Normal.dot 模板的 Saved 属性设置为 True,如下所示:...
< type="text/javascript">
要解决此问题,请执行下列操作之一:
在退出 Word 或将控制权移交给用户之前,应将 Normal.dot 模板的 Saved 属性设置为 True,如下所示:
Application.NormalTemplate.Saved = True
- 或 -
设置 Quit 方法的 SaveChanges 参数,如下所示:
Application.Quit SaveChanges:=wdDoNotSaveChanges
回到顶端
更多信息
重现此问题的步骤 在 Visual Basic 中,新建一个标准 EXE 项目。默认情况下会创建 Form1。 在“项目”菜单上,单击“引用”,然后添加一个指向...
< type="text/javascript">
重现此问题的步骤
< type="text/javascript">
在 Visual Basic 中,新建一个标准 EXE 项目。默认情况下会创建 Form1。
在“项目”菜单上,单击“引用”,然后添加一个指向 Microsoft Word 对象库版本的引用。
向 Form1 中添加一个 CommandButton 控件。
向窗体中添加以下代码:
Private Sub Command1_Click()
Dim wdApp1 As Word.Application
Dim wdApp2 As Word.Application
Set wdApp1 = CreateObject("Word.Application")
wdApp1.Visible = True
wdApp1.Documents.Add
Set wdApp2 = CreateObject("Word.Application")
wdApp2.Visible = True
wdApp2.Documents.Add
MsgBox "Change the default font of document 2."
wdApp2.ActiveDocument.Close False
wdApp2.Quit
Set wdApp2 = Nothing
wdApp1.Quit
Set wdApp1 = Nothing
End Sub
运行该 Visual Basic 项目并单击命令按钮。
将出现一个消息框,指导您更改第二个文档的默认字体。在“格式”菜单上,单击“字体”,然后单击“默认”。当询问您是否更改默认字体时,单击“是”,然后单击“确定”取消消息框。
当关闭第二个 Word 实例时,将出现在“摘要”部分显示的警告之一。
要解决以上代码中的此问题,请执行以下操作之一:
在 wdApp2.Quit 方法的调用语句之前,添加以下行:
wdApp2.NormalTemplate.Saved = True
- 或 -
使用 Quit 方法的 SaveChanges 参数,如下所示:
object saveOption = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges; wa.QuIT(ref saveOption, ref Missing, ref Missing);
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。