摘 要:本文通過一個實用例子完整示範了如何使用C#在ASP.NET裡調用Word、自動化Word,並且總結了實際應用中發現的問題,最後提出了有效解決方案。
關鍵詞:ASP.NET; C#; Word; 自動化; VBA; COM; 死進程;
原文地址:http://hi.baidu.com/officeworkspace/blog/item/3a673109582badd763d98667.html
1. 建立工程
在ASP.NET裡操作Word的第一步就是添加COM引用到你的工程裡,通過右鍵點擊“方案總管”的“引用”,添加引用。選擇COM選項卡,添加Microsoft Word 12.0 Object Library(其中12.0是Word版本號碼,根據當前電腦上安裝的Word版本確定)。 ASP.Net會自動產生Word的COM封裝類程式集添加到應用程式目錄裡。
2. 代碼邏輯
在伺服器端訪問本地存在的Word檔案,並根據他建立一個檔案,利用Word的標籤定位賦值。用戶端瀏覽器通過檔案連結訪問到這個新產生的Word檔案。
具體代碼如下:
private void Page_Load(object sender, System.EventArgs e) { // 在此處放置使用者代碼以初始化頁面 object Missing = Type.Missing; //取得Word檔案路徑 string strTemp = "doc/test.doc"; //新Word檔案儲存路徑 string newFileName = "doc/test2.doc"; //建立一個名為WordApp的組件對象 Application WordApp = new ApplicationClass(); //必須設定為不可見 WordApp.Visible = false; try { //建立以strTemp為模板的文檔 object oTemplate = Server.MapPath(strTemp); Document WordDoc = WordApp.Documents.Add(ref oTemplate, ref Missing,ref Missing, ref Missing); WordDoc.Activate(); //對標籤"Title"進行填充 string strBM = "Title"; object objBM = strBM; if(WordApp.ActiveDocument.Bookmarks.Exists(strBM) == true) { WordApp.ActiveDocument.Bookmarks.get_Item(ref objBM).Select(); WordApp.Selection.TypeText("公文標題"); } //儲存為新檔案 object oNewFileName = Server.MapPath(newFileName); WordDoc.SaveAs(ref oNewFileName, 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); WordDoc.Close(ref Missing, ref Missing, ref Missing); WordApp.Quit(ref Missing, ref Missing, ref Missing); } catch(Exception Ex) { throw new Exception(Ex.Message); } //瀏覽器彈出下載框 Page.RegisterStartupScript("", "<script>window.open('"+newFileName+"')</script>"); } |
運行前,工程目錄下建檔案夾doc, doc裡建立一個test.doc,內容自己定,手動插入一個名為“Title”的標籤。
此代碼在 VS.Net2003+WinXP+Office2007 下運行通過。
如果你在運行時出現下面的調試錯誤:
拒絕訪問。
說明: 執行當前 Web 請求期間,出現未處理的異常。請檢查堆疊追蹤資訊,以瞭解有關該錯誤以及代碼中導致錯誤的出處的詳細資料。
異常詳細資料: System.UnauthorizedAccessException: 拒絕訪問。
ASP.NET 未被授權訪問所請求的資源。請考慮授予 ASP.NET 請求標識訪問此資源的許可權。ASP.NET 有一個在應用程式沒有類比時使用的基進程標識(通常,在 IIS 5 上為 {MACHINE}\ASPNET,在 IIS 6 上為網路服務)。如果應用程式正在通過 <identity impersonate="true"/> 類比,則標識將為匿名使用者(通常為 IUSR_MACHINENAME)或經過身分識別驗證的請求使用者。
若要授予 ASP.NET 對檔案的寫訪問權,請在資源管理員中右擊該檔案,選擇“屬性”,然後選擇“安全”選項卡。單擊“添加”添加適當的使用者或組。反白 ASP.NET 帳戶,選中所需存取權限對應的框。
出現以上錯誤時,表明ASP.NET進程無法對具有使用者介面的Word進行自動化調用,必須由一個擁有案頭的使用者角色來啟動ASP.NET進程。解決方案:在Web.config檔案的System.Web節裡添加<identity impersonate="true" userName="*" password="*" />,其中userName和password是你電腦裡的Windows登入賬戶。
3. 方案總結
Web伺服器端自動化調用Word在實際應用中發現的問題:
l 開發難易度:
Word自動化中的調用都基於VBA文法,需要開發人員對VBA很熟悉。VBA中Word對象眾多、邏輯複雜,COM調用方式難於理解。一般開發人員很少接觸VBA和COM,因此開發起來比較麻煩。
l 代碼安全性:
上述運行錯誤“拒絕訪問”的最佳解決方案就是添加<identity impersonate="true" userName="*" password="*" />,不過缺點是在Web.config裡可以看到你的賬戶密碼,儘管Web.config不會輕易被人下載到,但還是具有一定的危險性。另外也可以運行Dcomcnfg.exe工具提升ASPNET賬戶許可權為互動式使用者,當然這樣也會增加伺服器的風險。網上搜尋發現有網友的解決方案是:在.net 安裝根目錄下找到config檔案夾下的machine.config檔案將processModel 中的username屬性改為SYSTEM。還有網友的解決方案是:將IIS預設的賬戶改為管理員賬戶。這兩個方法更加危險,一旦駭客獲得了ASP.Net進程的許可權,他就能完全控制你的伺服器。
l 運行穩定性:
微軟Office是主要針對普通使用者開發的案頭辦公應用軟體,它具有豐富的UI(使用者介面)元素,是一套純粹的本地運行軟體或者說是用戶端軟體。Word自動化介面主要是為了方便視窗應用程式調用而設計的。例如Delphi、VB、C# Winform等開發的本地應用程式。雖然可以強制Visible為false,Word可以運行在伺服器端代碼裡,但畢竟還是會帶來許多棘手問題。1. ASP.NET是基於B/S架構的。B/S架構下使用者訪問都是並發的,也就是說經常會出現同時N個使用者對一個伺服器頁面發出請求。在這種情況下Word自動化調用會時常出現死進程。2. 由於隱藏介面運行,一些涉及介面的可以在視窗程序裡成功調用的介面,在伺服器端調用就會失敗,甚至崩潰,這種情況也會經常導致死進程。3. 由於Word是複雜的傳統型程式,並不符合一般Web服務程式簡潔高效的標準,所以在伺服器端運行時速度慢,並且還會消耗大量資源(CPU、記憶體),尤其不能支援大量使用者同時訪問,資源會很快耗盡。
l 絕大部分開發人員對COM技術比較陌生,在編程調用Word介面時經常存在一些代碼錯誤,而又很難檢查到問題所在,這又是導致死進程的經常因素。
Word死進程不僅會消耗伺服器資源,還經常會導致伺服器頁面不能建立新的WordAutomation 物件而無法繼續工作。有網友提出死進程解決方案:編程Kill掉Word死進程,這樣是治標不治本的做法,Word死進程是不在了,可是Word非正常關閉會導致很多資源無法及時釋放。這樣的Web伺服器能持續工作多久恐怕就很難說了。
既然在Web伺服器端自動化調用Word存在這麼多問題,那麼能不能在用戶端瀏覽器裡調用Word呢?用JavaScript肯定可以,不過要想運行就得把瀏覽器的安全性降到最低,呵呵,恐怕沒有幾個使用者願意這麼做啊。即使不存在安全問題,本來寫在伺服器端的代碼邏輯要寫在JavaScript裡,由此帶來的大量麻煩(開啟、傳值、取值、儲存到伺服器等)也會讓人難以容忍。
4. 解決方案
為瞭解決這些問題,筆者經過全面研究比較,發現網上有一款軟體SOAOffice(微軟Office專用Web中介軟體),完全消除了以上問題,推薦給大家分享。
經研究發現,SOAOffice是一套由伺服器端組件和用戶端控制項構成的中介軟體系統。伺服器端組件是標準.NET組件,提供簡潔高效的Word、Excel簡化介面;用戶端控制項在瀏覽器網頁裡運行。伺服器端調用SOAWord.WebOpen開啟文檔後,瀏覽器頁面裡用戶端控制項會啟動客戶機上的Word並且運行在網頁裡而不是本地開啟。伺服器端無需安裝Office軟體。
SOAOffice的架構很巧妙,開發人員只需關注伺服器端編程邏輯,用戶端如何工作都交由控制項自動完成。SOAOffice充分利用了分散式運算的思想,把本來要在伺服器端啟動並執行Word運算量交給了客戶機。也就是說,原來採用伺服器端自動化技術的網頁同時要處理N個Word任務現在交給了N個客戶機,每個客戶機運行一個Word。伺服器只需處理需要伺服器處理的商務邏輯,一切與介面有關、與Word程式本身有關的工作由客戶機運行,當然這也是客戶機的強項。
SOAOffice的架構消除了伺服器端運行Word、Excel的風險,又充分利用了客戶機閑置的計算資源,這種架構不但解決了ASP、ASP.NET等Windows web服務調用Word、Excel的問題,而且還給Java寫的Web服務調用Word、Excel提供瞭解決方案(Unix、linux等無法自動化Word、Excel)。
SOAOffice能夠讓使用者直接在網頁裡看到word檔案內容,並且可以直接編輯、儲存回Web伺服器,給使用者省去了先下載下來,修改完後再上傳的麻煩。
SOAOffice還有其他更多自動化調用Word無法做到的強悍功能,比如唯讀、防下載、防複製等,你就下載一個慢慢琢磨吧。
附上利用 SOAOffice 完成本執行個體相同功能 + 唯讀防下載功能的代碼:
private void Page_Load(object sender, System.EventArgs e) { // 在此處放置使用者代碼以初始化頁面 SOAOfficeX.WordResponse SOAWord = new SOAOfficeX.WordResponse(); //對資料區域"Title"進行填充 SOAWord.OpenDataRegion("Title").Value = "公文標題"; SOAOfficeX.SOAOfficeCtrl SOACtrl = new SOAOfficeX.SOAOfficeCtrl(); // 設定介面樣式 SOACtrl.MainStyle = SOAOfficeX.soaMainStyle.VistaBlue; SOACtrl.Caption = "動態產生文檔"; SOACtrl.Menubar = false; SOACtrl.Toolbars = false; SOACtrl.CanCopy = false;//禁止下載、複製粘貼等 SOACtrl.Assign(SOAWord);// 綁定資料 // 唯讀開啟產生的文檔 SOACtrl.WebOpen("doc/test.doc", SOAOfficeX.soaWorkMode.docReadOnly, "SomeBody", "Word.Document"); } |