代码改变世界

PowerShell2.0之与COM对象交互(二)Word自动化

2010-12-22 01:26 by @天行健中国元素, ... 阅读, ... 评论, 收藏, 编辑

上一篇文章主要描述了COM模型的基础知识,以及在powershell中如何调用COM,本篇文章中将会着重介绍powershell中通过COM操作Word达到自动化管理的目的。

COM主要用于与其他程序的自动化,是高级用户、管理员,以及开发人员的不二选择。软件产品通常会以COM对象的形式向外提供应用服务,这样即可从其他语言中调用该项功能。作为COM对象最佳的实例莫过于Microsoft Office工具,在这个工具集中的所有程序都以COM对象的形式向外提供程序对象、文档,以及其他服务,用户可以使用这些对象创建和操作文档和各类数据对象。

Word通过word.Application唯一地标识ProgID,Word应用通过自动化界面向外提供文档和文字的核心操作。可以打开一个文档,读取和修改其中的文字,并保存更改。在大多数情况下操作的是Range对象,熟悉Word的VBA(Visual Basic For Application)编程的用户可能会对它比较熟悉,这种对象针对的是可单独处理文字单元。用户可以添加删除文字、设置不同的字体,切换文字或背景的颜色、从Document对象提供的属性创建或获取文字区域对象,并从当前选取的文字中获取内容。无论是以何种方式获取的文字区域对象(range),均可以相同的方法来处理。

本文将针对Micosoft Word 2003为模板来介绍,而Word 2007支持讲述的内容。微软的程序向来以向后继承公用对象著称,在Word 2003和Word 2007提供的变量方法和属性基本相同,代码可以在不做或略加修改的情况下直接用于Word 2007上。

(1)打开文档并提取内容

首先从简单的word相关的操作开始,即从文档中读取文字。在典型的企业环境下很多时候需要生成文档递交给其他用户审批,并最终保存在存储节点中。所以从文档中提取内容是个很重要的功能,如果能实现这个功能,则可创建脚本用于搜索关键字、从文档中提取相关信息,创建报告并合并文档等很多场合。

文件名为“Sample.doc”的Word文档中包含两行内容,如图1所示。

为了通过Shell读取出文档的内容,需要获取一个Word.Application对象的实例,它允许打开文档,并获取该文档对象的引用。获取后即可通过Document的Content属性来获取完整的内容,并返回一个Range对象,这个对象可以被用于提取内容。把这个脚本保存为Get-DocumentText.ps1,其代码如下:

$wordApp = New-Object -COM Word.Application

$file = (dir Sample.doc).FullName

$doc = $wordApp.Documents.Open($file)

$text = $doc.Content.Text

$text

$wordApp.Exit

image

图1 用word Sample.doc文档中包含两行内容

执行这个脚本,结果如图2所示,可以看到已经获取Range对象的Text属性;另外需要强调的一点是,需要传递要读取文件的绝对地址。

image

图2 通过Word.Application读取的Word文档

【注意】

Documents集合的Open方法需要一个完整路径,相对路径无效,因为Word在运行时的路径可以不同于当前路径。如很多情况下启动的Shell的当前目录有可能是C:\Windows\System32,而所有文档保存在另外的目录中。

【强调】

Get-DocumentText.ps1脚本在使用COM之后,必须销毁先前创建的COM实例;否则将会挂起Winword.exe进程。尽管不会有太明显的问题,但是会占用物理内存。甚至在没有成功退出对象的情况下保留一个指向打开文档的引用并锁定文档,这样其他用户将无法正常访问这个文件。

能够看到所有的文字是挤在同一行中,这是由于将富格式文本转换为无格式字符串带来的副作用。如果需要显示段落结构,则需要使用DocumentPargraphs集合。它会返回一系列Paragraph对象,每个对象在文档中都以一个段落的形式存在。这些对象中的每一个都包含文字区域对象(range),可以用其获取真实的文本内容。为了能够从Word文档中读取出原始的段落结构和内容,创建一个名为“Get-DocumentParagraph.ps1”的脚本,代码如下:

$wordApp = New-Object -COM Word.Application

$file = (dir Sample.doc).FullName

$doc = $wordApp.Documents.Open($file)

$paragraphs = $doc.Paragraphs

foreach ($paragraph in $Paragraphs)

{

Write-Host "PARAGRAPH:"

Write-Host $paragraph.Range.Text

}

$wordApp.Quit()

执行结果如图3所示,前述代码中用foreach语句通过索引访问每个文字区域的内容并打印。可以看到共有3个段落,其中中间一个只包含回车,无法在屏幕上打印而只输出了paragraph:字样。可以猜想这里段落的分割通过回车来区分,由于空行中存在回车,所以输出时尽管没有文字,还是有段落存在,因而出现了空行。

image 图3 执行结果

当然我们也可以定位到更小的单元;Document对象向外提供Sentences集合,允许用户获取由Application识别的指向sentence对象的文字区域。下面创建一个名为“Get-DocumentSentences.ps1”的脚本用于逐句读取文档内容,其代码如下:

$wordApp = New-Object -COM Word.Application

$file = (dir Sample.doc).FullName

$doc = $wordApp.Documents.Open($file)

$sentences = $doc.Sentences

foreach ($sentence in $sentences)

{

Write-Host "Sentence:"

Write-Host $sentence.Text

}

上述代码和前面关于段落的脚本很相似,唯一不同是Sentences集合包含原始的Range对象,所以不需要像Paragraph那样采用单独的Range对象存储。脚本执行结果如图4所示,能够看到之前的文档中在两段文字之间有个单独的回车符,可以理解为一段。在逐句读取时并没有看到有空白的sentence输出,即没有输出。可以猜想这里句子的分割通过句点(.)来实现。由于空行中不包含句点,所以再按照句子输出时该行被忽略。感兴趣的读者可以在Word文档的空行中添加句点,可以预料在原有空行的位置会有单独的句点以句子内容的形式出现。

 

 

image

图4 执行结果

在拆分到句子的层次后,文字为能够从Word中读取的最小单元。在这里需要读取Words集合,创建一个名为“Get-DocumentWords.ps1”的脚本,代码如下:

$wordApp = New-Object -COM Word.Application

$file = (dir Sample.doc).FullName

$doc = $wordApp.Documents.Open($file)

$words = $doc.Words

$words | select Text

$wordApp.Quit()

Words集合返回一系列Range对象,前面脚本返回的文字区域对象是单词的集合,该脚本的执行结果如图5所示。

image

图5 执行结果

能够看到按照单词拆分包含的文字,拆分使用空格字符。使用3个句点(…)来代表回车的位置,这样可以通过输出的文字看出整个单词构成的句子和段落的形式,可以尝试在其中的空行中添加一个到多个不等的句点和空格的混合字符串来验证是否是按照空格来拆分单词;另外上述以句点代表句子结束,以3个句点代表段落回车的方式来通过拆分后的单词构成反向读出句子的断句,以及段落的拆分方式并不绝对正确。因为如果在句子中连续出现用空格分隔的3个句点,则可能被误判为两个回车符。

(2)创建和修改文档

文字区域对象可以用来获取只读状态下的文档内容,同时也可用来写入并修改文档内容及格式。这里使用Document.Content获取文字区域内容,并使用通过设置Text属性控制内容的格式。首先创建一个名为“New-Document.ps1”的脚本,该脚本会接受两个参数。其中path用于保存文档的位置,text用于保存文件内容。这个脚本的代码如下:

param ($path, $text)

$wordApp = New-Object -COM Word.Application

#delete the file if it already exists

if (Test-Path $path)

{

del $path

}

$doc = $wordApp.Documents.Add()

$doc.Content.Text = $text

$doc.SaveAs($path)

$wordApp.Quit()

上述代码调用Document.Add()方法通知Word创建心的文档对象。一旦text的内容被赋给Content文本序列,将会调用SaveAs()方法将文档写入到磁盘中。脚本的执行结果如图6所示。

image

图6 执行结果

写入文档打开后的结果如图7所示。

image

 

图7 通过COM写入文档打开后的结果

为打开并修改现有文档,需要打开文档并定位到需要修改的字符区域,然后修改其中的内容和格式并保存。下面创建一个名为“Insert-RedText.ps1”的脚本用于打开现有文档,添加一些文字并将字体颜色改为红色,代码如下:

$wordApp = New-Object -COM Word.Application

$file = (dir NewDocTest.doc).FullName

$doc = $wordApp.Documents.Open($file)

$documentFront = $doc.Content

$documentFront.End = $start.Start

$documentFront.Text ="IMPROTANT!!!please review!`n"

$documentFront.Font.Name="Times new roman"

$documentFront.Font.ColorIndex = 6

$doc.save()

$wordApp.Quit()

其中首先获取文档内容的文字区域,并将 End属性设定为Start所包含的内容。类似在文档开头创建了一个零长度的字符区域,这时可以将光标放置在文档开头。然后设置Text属性在文档开头插入文字,使用文字区域对象改变文字的字体并设置文字颜色,其中索引值为6代表红色。最后通过Save()方法保存文件。上述脚本的执行结果如图8所示。

image

图8 执行结果

写入文档打开后的结果如图9所示。

image

图9 写入文档打开后的结果

调试时显示Word窗口

在脚本中调用Word的自动化操作不显示程序窗口,用户看不到任何窗口。所有任务均在后台完成,这样在调试代码时非常不方便。为了能够有效地解决这个问题,用户可以设置Word在执行时显示窗口。这样即可够在执行过程中既能看到代码连续执行,又能看到执行的过程。

为了强制主程序窗口在运行时可见,只需要设置Application对象的Visible属性为$true。通过之前在脚本中设定的变量名,可以在代码中的任意位置插入以下代码:

$wordApp.Visible = $true

在代码调试正常后务必要删除这条代码,使程序正确执行。

(3)使用拼写检查

Word的拼写检查(spell-checker)支持通过COM自动化调用,在使用时只需添加一些文字并获取Document的SpellingErrors属性值,这个属性会收集包含错误文本段的文字区域对象集合。一旦一个文字区域包含错误的单词,可以通过调用Range对象的GetSpellingSuggestions()方法获取更正建议。这个方法的返回值是SpellingSuggestion对象,其Name属性值包含更正建议。

为了演示拼写检查功能,在这里创建一个名为“SpellCheck-Text.ps1”的脚本,在发现拼写错误后提出更正建议。代码如下:

$wordApp = New-Object -COM Word.Application

$doc = $wordApp.Documents.Add()

$doc.Content.Text = "this is some text with mistakn and errros"

$errors = $doc.SpellingErrors

foreach ($errorRange in $errors)

{

Write-Host "Error: $($errorRange.Text)"

$suggestions = ($errorRange.GetSpellingSuggestions())

$suggestedWords =$suggestions | foreach {$_.Name}

Write-Host "Suggestions :"

Write-Host $suggestedWords

}

$wordApp.Quit()

上述代码中通过获取每个文字区域中错误的SpellingSuggestion对象,并将其存入数组中。然后将集合用管道传递给foreach cmdlet,分别将集合中包含的Name属性的值,执行结果如图10所示。

image

图10 执行结果

 

 

(4)获取帮助

本节中所有的涉及Word的COM接口的内容都以浅尝辄止的形式进行,因为Microsoft Office自动化的功能非常强大远非一本书可以写完。这里所涉及的内容都是为广大读者打开知识的大门,其他更具体的内容还需要读者在学习和工作中掌握。有Microsoft Office自动化编程背景经验的脚本作者和用户对于脚本中提到的对象应该比较熟悉,因为这些对象和VBA编程环境针对的COM接口完全相同。VBA编程语言的语法结构完全不同于PowerShell,但是绝大多数情况下将VBA的程序翻译为PowerShell的脚本非常容易,因为所有的方法、属性和操作语法基本相同。如果感兴趣,可以使用《Microsoft Word Visual Basic Reference》中的Word部分的帮助来获取自动化对象和属性的详细介绍。

Microsoft Office自动化还有一个强大的工具可以用来快速生成代码,这就是宏(macro)记录工具。用户可以通过录制自己的操作来生成宏,并用Visual Basic 代码编辑器打开宏。Word将会根据之前录制的宏生成Visual Basic代码,将这些代码翻译为PowerShell的脚本。

本节中我们曾经通过构造特定字符串来窥视PowerShell的一些底层操作的思路,对于编程人员来说很重要。很多时候一些系统需要黑盒方式分析,需要通过输入/输出来校验自己的一些思路。进而一步步将软件系统按照自己的理解搭建起来。也许其中的代码并不能和系统本身的代码完全吻合,但具体的功能却可以由此模仿出来。

 

作者: 付海军
出处:http://fuhj02.cnblogs.com
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】,谢谢
要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
个人网站: http://txj.shell.tor.hu/