用COM和ASP创建动态Word文档(转)

发表于:2007-06-30来源:作者:点击数: 标签:
大多数公司由于意识到无文档的工作过程会成为前进的绊脚石,因此都 开发 了定义详细的文档程序。每个公司都为不同的过程定义自己的一套文档模板,使它们随时可被职员使用,用于进行购买请求或申请度假等。 但是,随着Inte .net 逐渐为大家熟悉和逐渐普及,越
  大多数公司由于意识到无文档的工作过程会成为前进的绊脚石,因此都开发了定义详细的文档程序。每个公司都为不同的过程定义自己的一套文档模板,使它们随时可被职员使用,用于进行购买请求或申请度假等。
  但是,随着Inte.net 逐渐为大家熟悉和逐渐普及,越来越多的功能被移植到“开放空间”,以实现更好的可视性和更有效的通讯。比如说,一个人也许要问:“我可以登录到Internet / Intranet,填写一张休假申请表,然后以公司标准模板样式将它作为Word 文档发送给我的经理吗?”答案是肯定的,下面会为你演示如何实现。

关于这个应用程序
  我们的应用程序有一个样本表格,由访问web 站点的用户填写。一旦提交,ASP文件收集其中信息,使用web 服务器上存放的预先定义的模板,以其内容创建一个Word 文档。然后显示一个链接,允许用户查看或者下载这个文档。

  我们创建一个Visual Basic COM 组件(ActiveX DLL), 通过ASP应用程序调用它,给它传递必要的参数。组件从本质上是使用Microsoft Word 对象库,创建一个对将要传递参数的 Word 文档的引用。所有这些都是在服务器上完成的,因为这种方法有许多优势。

  其中最重要的是在程序内运行的since.dll (与网络服务器在同样的内存中),它们比程序外运行的(如CGI或Perl 脚本)运行更快,使用的资源更少,后者在运行中每次被调用时,都将创建自己本身的实例(如复制)作为单独的程序,因此要用掉大量的服务器内存。这还意味着为了使程序外组件在服务器上运行(关键字:ASPAllowOutOfProcComponents),你不需要修改Metabase (一个存储Internet信息服务器配置设置的结构,与Windows 注册相同,但是使用较少的磁盘空间)。

  另一方面,这种方法最明显的缺点也许是因为它与网络服务器在同样的内存空间中运行,任何DLL的问题都有可能使服务器出故障。因此,在开发和执行程序内应用程序时需要十分小心。

程序要求和优点

完成本文所说的功能,需要具备以下条件:
● Visual Basic 5 或 6
● 具备IIS 4的NT服务器 或 工作站,或者有PWS 的任何 Windows 9.x
● MS Word 97 ( Office 97 套装的一部分)

  本例还可以和MS Word 2000一起执行,但是会有一些问题,在文章最后要提到。其它额外的软件是不必要的,只需要保证默认站点http://localhost/ 是有效的(点击这个超链接会把你带到个人的web 服务器或Windows NT 的主页)。
  我们将把创建的所有文档都存储在C:\Inetpub\scripts\documents 文件夹中, 所以一旦文档被创建之后,我们提供到它的链接是很容易的(可以根据需要修改)。一定要创建这个路径,否则我们的例子就不能工作。所有其它的文件都位于我们的脚本路径 ( C:\Inetpub\scripts )。我们的dll 将尽可能地灵活,使任何模板的修改都只需要最少的代码修改。

更深一层的技术

  要设计的模板可以基于一个公司希望在他们的文档出现的内容:登录、适当的页眉和页脚信息、基本文本等等。另外文档创建之后,我们希望在其中看到用户特殊信息的地方还要加入标记(这就使这个应用程序是动态的)。在我们回顾代码时还会仔细看这些部分。我们的.dll 将包含一个称为GenerateDocument 的函数(在类文件内部),它要求向它传递4个参数,分别是:
● 一个为所有标记用的分界字符串(来自文本模板)
● 一个为所有相应值用的分界字符串(来自 web浏览器上用户填充的表格)
● 模板在服务器上的位置
● 生成的文档在服务器上被存储的位置

现在我们可以往下进行了。

组合在一起
文档模板
  首先创建一个word 模板的样本,假设它就是我们公司的标准文档模板。我们要为这个例子获取职员的信息,我们希望文档中包含以下的特定信息:名字、地址、Email Id。现在基于这些信息创建模板,一定要在文档中将要显示用户信息的地方包含适当的标记(如. < Name >, < Address >)。

  将文件命名为EmployeeTemplate.dot(记住,在Save As 对话框的文件类型列表中选择文档模板,并将其存入C:\Inetpub\Scripts\Templates\。看看可下载材料部分包含的文档模板样本,以便对它有个更好的理解。


COM组件
  现在用Visual Basic创建COM组件,按照以下的步骤:
● 启动 Visual Basic, 选择 ActiveX DLL 作为工程文件类型。
● 将类名改为DocumentObject, 工程文件名改为 MyDocumen(这是我们在ASP页中创建COM组件的一个例示时要使用的信息)
● 接着,点击工程文件菜单选项,到 References。
● 向下滚动直到看见 "Microsoft Word n.0 Object Library " (n 是一个识别服务器上安装的word对象库版本的数字).选中这个选项,点击click OK。

  请参阅本文结尾处可下载文件中的类模块代码。GenerateDocument()函数要用到从ASP文件向它传递的4个参数。
它返回一个字符串类型,在后面可以看到:
Option Explicit
‘’ Declare a New word application Object
Dim wdApp As New Word.Application
Public Function GenerateDocument(sTags, sValues, sSourcePath, sDestPath) _
As String
On Error GoTo ErrHandler
Dim arrTags() As String, arrValues() As String, iLoop As Integer

  此函数执行的第一个任务是从指定的源路径(作为参数从ASP文件传递过来)打开模板文件。基于在服务器上创建的模板,引用一个新的Word 文档:
wdApp.Documents.Open sSourcePath

  然后,将从HTML表单中获取的所有标记,用Split 函数放入arrTags数列中。逗号是一个分界符,在ASP文件中分隔开标记的值:
arrTags = Split(sTags, ", ")

  我们将相应的用户输入值存入arrValues数列。pipe 字符( | ) 是分界符,来分隔开这些值:arrValues = Split(sValues, " | ") 代码在arrTags 中循环 ,用查找和代替操作(用应用程序脚本的Visual Basic)从标记数列中找到标记,在创建的Word文档中,用arrValues 数列中的相应值代替它们:

For iLoop = 0 To UBound(arrTags) wdApp.ActiveDocument.Content.Find.Execute arrTags(iLoop), , True, , _
, , , , , arrValues(iLoop), 2
Next iLoop

  你看到的一串逗号是Find-Execute 方法的不同属性,我们没有设置。我们只对 MatchWholeWord, ReplaceWith和ReplaceAll(用数字常量2代表)的设置选择感兴趣。然后,我们将文档存入指定的目的路径和文件名中,退出和释放之前关闭这个word文档对象:

wdApp.ActiveDocument.SaveAs sDestPath
wdApp.ActiveDocument.Close
wdApp.Quit
Set wdApp = Nothing
退出函数之前,返回一个‘’Suclearcase/" target="_blank" >ccess‘’ 标志:
GenerateDocument = "Success"
Exit Function
这是一个错误处理程序。如果在上面的应用程序执行中遇到错误的话,它返回一个错误信息。
ErrHandler:
‘’ Quit and release the word document object
wdApp.Quit
Set wdApp = Nothing
‘’ Build the Error Message, and pass it back
Dim ErrMsg As String
ErrMsg = "Error Number: " & Err.Number & "< BR >< BR >"
ErrMsg = ErrMsg & "Error Source: " & Err.Source & "< BR >< BR >"
ErrMsg = ErrMsg & "Error Description: " & Err.Description & "< BR >< BR >"
GenerateDocument = ErrMsg
Exit Function
End Function
Private Sub Class_Terminate()
‘’ Release the reference
Set wdApp = Nothing
End Sub

  在Visual Basic中,保存应用程序(保留类和工程文件名),编译,看是否产生了什么错误。然后,打开File 菜单, 点击Make MyDocument.dll。保留它的名字,在工程文件所在的文件夹中保存它。

  然后,在web 服务器上注册dll,如下: ● 将 MyDocument.dll 复制到windows\system 或 winnt\system32路径(取决于使用的操作系统).
● 在命令提示符下执行以下命令 C:\winnt\system32 >regsvr32 MyDocument.dll
● 你将看到一个成功的信息:DllRegisterServer in MyDocument.dll 。
上面完成了这个应用程序的主要部分。现在进入下一步。

HTML 页面

  现在需要创建HTML页面( EmployeeForm.html ),用户将在其中输入填充到新Word 文档中的值。下面是我们使用的HTML页样本:
< HTML >
< TITLE > Employee Registration Page < /TITLE >
< BODY BGCOLOR=#ECECEC >
< CENTER >< FONT FACE=arial SIZE=-1 >
< FONT SIZE=5 > Employee Registration Page < /FONT >
< HR ALIGN=center COLOR=black >< BR >
Please complete your details as per the Registration Form...< BR >< BR >
< TABLE CELLSPACING=1 CELLPADDING=5 BGCOLOR="#000000" BORDER=0 ALIGN=center >
< FORM ACTION=CustomDoc.asp METHOD=post >
< TR > < TD BGCOLOR=#00BCA8 COLSPAN=2 ALIGN=center >
< B >
< FONT FACE=arial SIZE=-1 COLOR=black > Employee Details
< /TD >
< /TR >
< TR > < TD BGCOLOR=#C4C2C2 >
< B >< FONT FACE=arial SIZE=-1 COLOR=black >Your Name:
< /TD >
< TD BGCOLOR=#E3E1E1 > < INPUT TYPE=Text NAME="Name" SIZE=20 MAXLENGTH=25 > < /TD >
< /TR >
< TR > < TD BGCOLOR=#C4C2C2 >
< B >< FONT FACE=arial SIZE=-1 COLOR=black >Address: < /TD >
< TD BGCOLOR=#E3E1E1 >
< INPUT TYPE=Text NAME="Address" SIZE=40 MAXLENGTH=40 >
< /TD > < /TR > < TR >
< TD BGCOLOR=#C4C2C2 >
< B >< FONT FACE=arial SIZE=-1 COLOR=black >City, State: < /TD >
< TD BGCOLOR=#E3E1E1 > < INPUT TYPE=Text NAME="City" SIZE=20 MAXLENGTH=20>
< INPUT TYPE=Text NAME="State" SIZE=20 MAXLENGTH=20 >
< /TD > < /TR >
< TR > < TD BGCOLOR=#C4C2C2 >
< B >< FONT FACE=arial SIZE=-1 COLOR=black >Zip, Country: < /TD >
< TD BGCOLOR=#E3E1E1 >
< INPUT TYPE=Text NAME="Zip" SIZE=20 MAXLENGTH=20 >
< INPUT TYPE=Text NAME="Country" SIZE=20 MAXLENGTH=20 >
< /TD > < /TR > < TR > < TD BGCOLOR=#C4C2C2 >
< B >< FONT FACE=arial SIZE=-1 COLOR=black >Email: < /TD >
< TD BGCOLOR=#E3E1E1 > < INPUT TYPE=Text NAME="Email" SIZE=40 MAXLENGTH=40>
< /TD > < /TR > < TR >
< TD BGCOLOR=#00BCA8 COLSPAN=2 >
< INPUT TYPE=Submit VALUE="Save and Generate Profile Document" >
< INPUT TYPE=Reset VALUE="Clear Fields" >
< /TD > < /TR >
< /FORM > < /TABLE >
< /BODY >< /HTML >
  将文件存入C:\Inetpub\scripts\ 中,要确定脚本路径在个人Web服务器或IIS中有读取并执行权限。


ASP页面
ASP页( CustomDoc.asp ) 实际完成以下任务:取得用户输入、用COM组件创建Word 文档、
将它递交回用户。下面是带注释的代码:
< %
Dim sTags, sValues, sDestPath, sSourcePath, resValue
‘’ Get all the User Input values from the Form
sName = Request("Name")
sAddress = Request("Address")
sCity = Request("City")
sState = Request("State")
sZip = Request("Zip")
sCountry = Request("Country")
sEmail = Request("Email")
sDate = Now() ‘’ Create a list of all the tags as defined in the Word Template by You
sTags = "< Name >, < Address >, < City >, < State >, < Zip >, < Country >, " & _
"< Email >, < Date >"
‘’ Gather up all the User Input values into one delimited string
sValues = sName & " | " & sAddress & " | " & sCity & " | " & sState & _
" | " & sZip & " | " & sCountry & " | " & sEmail & " | " & sDate

  完成之后,需要识别源文件(Template) 和目标文件(Document)的位置。APPL_PHYSICAL_PATH 返回CustomDoc.asp 文件所在的路径。我们将文档文件夹和文件名附加在一起,这是用户负空间的名字。

sSourcePath = Request.ServerVariables("APPL_PHYSICAL_PATH") & _
"Templates\" & "EmployeeTemplate.dot"
sDestPath = Request.ServerVariables("APPL_PHYSICAL_PATH") & _
"Documents\" & Replace(sName, " ","") & ".doc"

  现在完成了常见MyDocument对象的例示和调用GenerateDocument.doc,在返回值的基础上进行适当的操作。
Set myDocObj = Server.CreateObject("MyDocument.DocumentObject")
‘’ Call the GenerateDocument function while passing the required
‘’ Parameters retValue = myDocObj.GenerateDocument(sTags, sValues, sSourcePath, _
sDestPath)
‘’ Take appropriate action based on the returned value
If retValue = "Success" Then
Msg = "Successfully generated your Document : " & Replace(sName, _
" ", "") & ".doc"
Response.Write("< font face=arial size=-1 color=Green >< br >" & Msg)
Response.Write("< br >< br >< a href=Documents/" & _
Replace(sName, " ","") & ".doc" & " >Here it is!< /a >")
Else Response.Write("< font face=arial size=-1 color=Red >< br >" & retValue) End If
% > 将文件存入C:\Inetpub\scripts\。就行了。现在在浏览器中键入
http://localhost/scripts/EmployeeForm.html,执行应用程序,输入所有的域值,点击
Save and Generate Profile Document。现在可以看到我们的方法是多么有效和美观了。

对于Office 2000 的问题

  如果你的机器安装了Word 2000,并且用Microsoft word 9.0 对象库创建dll 的话,就会遇到一些问题。组件本身好象工作得绝对良好,没有任何错误,但是从ASP页调用会操作超时,或产生一个ActiveX 不能创建对象的错误。这也许是Office 2k设计上的特色,或者也许是一个错误(Bug)。下面解释一下为什么会发生这些问题。大致来说,这是因为安全信用不能在所有的例示子对象中持续。当调用一个.asp 页时它作为IUSR 帐号(web 服务器上的默认internet 客户帐号)持续。当调用我们创建的COM组件时,IUSR 信用也被传递。

  这个COM反过来产生对office COM (子对象)的调用,但是却不能传递信用。为了克服这个问题,我们需要这样做:
● 打开组件服务控制台, 然后在COM+ Applications点击右键。
● 选择 New Application,跟随 COM 应用程序向导, 命名一个空的app。

● 打开新的应用程序,在 Components上点击右键。
● 选择创建一个新组件,选择置入已经注册的组件。

● 在下一个对话框的组件列表中,选择MyDocument.DocumentObject,点击finish。应用程序现在就
很可能工作了。
  通过将COM 投入MTS ,我们允许COM持续为特定帐号,将它传递给调用的COM(在这个操作以前好象没有发生过)。我猜想这是因为Microsoft只允许一级安全信用持续, 我怀疑不需要这样做也可以把信用持续下去。

观察和结论 我们已经基本上完成了。开发应用程序时还有一些需要注意。
● 如果你仔细观察组件的设计,你会观察到即使你已经从模板修改、增加、删除了标记或文本,也根本不需要修改代码。它是可以再利用的。只需要通过ASP文件传递一些参数,你就可以快速创建符合公司口味的Word 文档了。
● 你可以根据需要定制它。只需要保证在模板文件中,你的标记是唯一的(在两个不同的上下文中不要 使用相同的标记)。
● 还可以加强应用程序的功能,一旦创建,就可以根据输入的Email ID (或从数据库中)直接将文档email给用户,以实现交互式的改进。
● 在ASP文件中的标记和值字符串,应该彼此同步,尽管在模板文件中它们不一定会同步出现。

原文转自:http://www.ltesting.net