最近筆者一直被詢問著這樣的問題。其實在Windows 2000的時代之前,元件服務與dcomcnfg的設定是分開的,到了Windows XP的時代後,微軟將DCOM相關的設定整合在元件服務的MMC Console的畫面中,其時做此DCOM的安全性設定其實與COM+(元件服務->以下簡稱COM+)一點關係都沒有,因為該元件並未使用到COM+的功能,也未執行在COM+的容器之中...
看到這個主題許多人一定好奇,為什麼突然間會想要談這個有點冷門的內容呢,其實是因為最近幫公司內部同事解決一個在IIS 7的執行緒內部叫起EXCEL的問題,這個問題看似簡單,但還是有許多人並不了解為什麼需要在DCOM/COM+(元件服務)內做此設定。
前言
COM是(Component Object Model)的縮寫,對於COM有興趣的可以參考筆者最早寫過的從OLE到COM、Automation、ActiveX技術,而DCOM與COM有什麼不同呢?DCOM的D是Distributed分散式的意思,COM不單只負責元件的封裝,在當時.NET時代還沒來時,Win32下行程與行程間相互溝通除了使用SendMessage的Win32 API之外,就是COM了,COM是以RPC (Remote Procedure Call)使不同的行程可以相互的溝通,所謂的DCOM則是可以進行跨機器的溝通,這也是COM+的基礎。
COM與DCOM有什麼不同:
而DCOM與COM+又有什麼不同呢,而我只是要在ASP.NET裡叫起EXCEL,為什麼需要開啟元件服務,進行如此複雜的設定呢?最近筆者一直被詢問著這樣的問題。其實在Windows 2000的時代之前,元件服務與dcomcnfg的設定是分開的,到了Windows XP的時代後,微軟將DCOM相關的設定整合在元件服務的MMC Console的畫面中,其時做此DCOM的安全性設定其實與COM+(元件服務->以下簡稱COM+)一點關係都沒有,因為該元件並未使用到COM+的功能,也未執行在COM+的容器之中,筆者記得以前使用Delphi/VB6撰寫COM+元件時必須參考"COM+ Service Type Library",才可以真正使用到COM+所提供的Transaction等分散式交易的機制,否則它只是一個具備通訊的元件而已。那麼這些與EXCEL又有什麼關係呢? 現在您所使用的Office系列應用程式都是OLE Automation的應用程式,而所謂的OLE Automation也是以DCOM/OLE為基礎實做出來的,它可以以撰寫程式的方式來提供其他應用程式所提供的功能/服務。有些人會認為那使用DCOM元件不就好了!DCOM也可提供遠端呼叫,但其實不同,DCOM與ActiveX都是以元件為導向的觀念,而OLE Automation是以應用程式為導向的觀念,因為當一個應用程式要提供服務給其他的遠端的時候,它可不能像ActiveX或DCOM元件依附在別的應用程式之中再提供服務!OLE Automation它是一個獨立的應用程式,直接讓其他本地端或是遠端的應用程式存取,因此這類的應用程式當然也受DCOM的安全性所控管(DCOM的驗證&模擬的機制其實非常完整,也有些複雜,模擬的等級就包括:匿名、識別、模擬、委派、等四種,有機會筆者再詳細解說細節),即使DCOM的Server在本機端,與DCOM Server或OLE Automation溝通仍然必須透過RPC協定,也就是說,在.NET平台透過InterOp服務參考EXCEL進行呼叫的作業其實就是.NET 的RCW透過RPC資料Marshaling的一個過程,描述有點複雜,實際上,底層的運作的確有些複雜,可透過下面的圖來表示。
什麼是RCW:
所謂的RCW為(Runtime Callable Wrapper)的簡稱,因為COM與.NET物件本身無法相互溝通,所以需要建立一種包裝(Wrapper)的一種機制將介面提供出來,早期COM的Marshaling只是一種將本來該程式語言所支援的資料型別對應成COM所能支援的型別,RCW便是將COM包裝起來對應為.NET所支援的型別。另外還有一種CCW(COM Callable Wrapper),它剛好相反,它將.NET的型別輸出為COM型別的TypeLib的二進位註冊資料tlb檔案以便可以註冊至Registry中。
話說筆者一直不喜歡後來Windows[元件服務]這樣的中文的翻譯,在筆者接觸時明明就叫COM+(COM+的前身其實是MTS,有興趣可以參考何謂MTS/COM+(基礎篇)),不過這是題外話。有些人會誤以為呼叫EXCEL所出現的問題這是Windows Server 2008或是IIS 7的問題,其實並不算是,在Windows 2003也必須做相同的設定。下面我們來重現此問題。
1.問題的發生
單存的在ASP.NET的應用程式中,參考Microsoft Excel 12.0 Object Library,執行如下程式碼:
1: ApplicationClass excelApp = new ApplicationClass();
2: excelApp.Visible = false;
3: try
4: {
5: Workbook workBook = excelApp.Workbooks.Add(Missing.Value);
6: object SheetObj = excelApp.Worksheets.Add(Missing.Value, Missing.Value, Missing.Value, Missing.Value);
7: excelApp.ActiveWorkbook.SaveAs(@"D:\VisualStudioDemo\2010\WebSites\WebExcelTestSite\Output\test.xlsx");
8: }
9: catch (Exception ex)
10: {
11: throw ex;
12: }
13: finally
14: {
15: excelApp.Quit();
16: }
如圖中所發生的為一個System.UnautoriziedAccessException,這是表示RPC的SSP無法識別呼叫的Client的身分所致,若您看到這裡相信不難了解其問題發生的原因了,因為筆者非常喜歡追根究柢,剛出道時,常有人被筆者問的很煩...題外話=.=,不過當了解其原理之後程式寫起來就非常有感覺,而且不會忘記!而且了解其原理有助於Debug系統問題的。此Exception實際上是由SSP所丟出,因此必須先說明RPC的驗證模型。
RPC的驗證模型:
RPC的驗證模型其實是在外部包一層SSP (Security Support Provider)的載入可安全模組來執行的,所有的SSP都是根據一組稱為SSPI (Security Support Provider Interface)的標準API來撰寫而成的,其他廠商也可以根據此標轉來撰寫自己在Windows Server下的SSP,在Windows 2000之後提供了Windows NTLM SSP與Kerberos Version 5 SSP。
當開啟Microsoft Excel Application的DCOM設定時會看見其中有一個[驗證等級]的下拉設定:
SSP的驗證等級有下列幾種:
驗證等級 | 意義 |
(無) | 不驗證所有COM的呼叫。 |
預設值 | 表示由SSP指定的驗證等級。 |
連線 | 驗證只有在第一次連線建立時會發生(這是目前的預設值)。 |
呼叫 | 驗證是在每個函式第一次被呼叫時發生。 |
封包 | 每個函式,包含每個呼叫的封包都會被驗證。 |
封包完整性 | 除了封包的驗證等級之外,再加上對每一個收到的封包進行Cryptographic Checksum的檢查。 |
封包私密性 | 除了封包完整性的驗證等級外,再加上一些對封包私人的編碼。 |
一般來說一個COM的Client與Server彼此之間都可以有它們預設的驗證等級,但是當兩台機器設定成不同的驗證等級時,RPC的SSP層會自動使用最嚴格的那一個。且越嚴格的驗證等級對效能的影響越大。
設定前要說明一下前因後果,現在我們知道所有的Automation Server都受DCOM的RPC的SSP層所管控,且預設值為連線,也就是說第一次呼叫EXCEL時會需要驗證呼叫者的身分。我想許多人都知道在Windows 2003中ASP.NET的應用程式式執行在NETWORK SERVICE身分中且處理序是w3wp.exe、Windows XP則是ASPNET處理序是aspnet_wp.exe。因此Windows 2003只要設定給NETWORK SERVICE即可,但Windows 7/2008的IIS 7的Work Process的Model有了很大的改變,若您的ASP.NET應用程式是使用.NET Framework 2.0執行,使用預設DefaultAppPool的應用程式集區,那麼實際上執行的使用者應該是IIS AppPool\DefaultAppPool 才對,如果您是執行.NET Framework 4.0那麼會是ASP.NET v4.0,如果您使用這個集區的話。
所以在IIS 7/7.5要使應用程式可以存取Microsoft Excel Application就要賦予該集區的使用者有存取的權限,在下圖點選[啟動何啟用權限]的編輯(還記得前面我們使用的是預設值,也就是連線的驗證等級,所以只有第一次會驗證身分):
接著在[選取使用者或群組]畫面中輸入 "IIS AppPool\DefaultAppPool"如下圖:
然後再按下[檢查名稱],這時才會出現DefaultAppPool使用者:
再設定其最少具備[本機啟動]的權限,若您沒有在其它台電腦會啟動這台機器的EXCEL不需勾選[遠端啟動]。除非您的伺服器作為叢集Cluster,無法確切判別呼叫者為遠端或是本機才勾選此項。
圖、DefaultAppPool的權限:
而DefaultAppPool這個使用者您是無法在Windows的使用者、群組中找到的,因為這是IIS 7.5的新增的ApplicationPoolIdentity功能,使個別ASP.NET應用程式擁有自己的執行使用者,不會再發生如在Windows 2003下因為賦予NETWORK SERVICE某些權限之後,所有使用NETWORK SERVICE帳號執行的Service都被賦予相同權限,造成安全性問題,這就是此功能的初衷,但這必須是IIS 7.5的應用程式集區設定為"ApplicationPoolIdentity",才可以使用,如下:
到這裡其實還沒結束,按照規定還是要加入Authenticated Users帳號,並賦予與DefaultAppPool相同的權限才算完成整個設定作業。
另外,有兩個偷步的方法,不過不建議啦(話先說在前面),
1.將Microsoft Excel Application的識別身分設為[互動的使用者],此方式會以當時登入到Windows的使用者來執行Excel,比較不建議這樣使用,因為如果當時登入系統的人是Administrator的話,那麼Excel也就是以Administrator來執行,若Excel內執行一些具破壞性的巨集,那傷害可想而知,而且還有一個問題,如果當時Windows沒有登入... 那這個方式也會宣告破功。
2.直接取消[驗證等級],也就是說我不驗證身分,任何人都可以執行,但還是要賦予實際執行Excel的帳號的檔案存取與資料夾權限,否則還是會出現錯誤。
OK,冷門的內容說明完畢,不過當然產出Excel報表筆者還是會建議使用NPOI Library,可以免掉上面的一切設定。但如果您在維護一些舊的系統,多吸收些知識我想還是有幫助的。下次見!
簽名:
學習是一趟奇妙的旅程
這當中,有辛苦、有心酸、也有成果。有時也會有瓶頸。要能夠繼續勇往直前就必須保有一顆最熱誠的心。
軟體開發之路(FB 社團):https://www.facebook.com/groups/361804473860062/
Gelis 程式設計訓練營(粉絲團):https://www.facebook.com/gelis.dev.learning/
如果文章對您有用,幫我點一下讚,或是點一下『我要推薦』,這會讓我更有動力的為各位讀者撰寫下一篇文章。
非常謝謝各位的支持與愛護,小弟在此位各位說聲謝謝!!! ^_^