天天看點

C#生成CHM檔案(中級篇)

在上篇《C#生成CHM檔案(入門篇)》中,我們利用微軟自帶的hhc.exe以程式設計的方式建立一個CHM檔案,而且調用的是一個靜态的HMTL檔案。

在中篇中,實作以下幾個目标

 1.将線上的網頁儲存為CHM檔案

 2.我們将對我們進行編譯的CHM檔案進行反編譯,使用的還是微軟自帶的一個exe(hh.exe)。

 3.以程式設計的方式将CHM檔案轉換為Word

在中篇中,把界面稍微調整了下,如下圖

<a target="_blank" href="http://blog.51cto.com/attachment/201105/225129887.jpg"></a>

一、将線上的網頁儲存為CHM檔案

曾嘗試直接使用網址來編譯html檔案,結果一直報錯,于是就放棄了。現在實作的方法的思想是這樣的:先将輸入的url位址的網頁儲存到本地,然後利用上一篇中的方法生成CHM檔案。不過經測試,這樣的效率還是比較低的,主要的花費在将htm檔案下載下傳到本地,如果帶寬不夠的話,将會很慢,不過總歸是種方法,大家如果有更好的解決方案,希望能告訴我。

HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url);  

 HttpWebResponse myResp = (HttpWebResponse)myReq.GetResponse();  

 StreamReader respStream = new StreamReader(myResp.GetResponseStream(), Encoding.Default);  

 string respStr = respStream.ReadToEnd();  

 respStream.Close();  

 FileStream fs = new FileStream(startPath+@"\test.htm", FileMode.Create, FileAccess.Write);  

 StreamWriter sw = new StreamWriter(fs, Encoding.Default);  

 sw.Write(respStr);  

 sw.Close(); 

<a target="_blank" href="http://blog.51cto.com/attachment/201105/225313584.jpg"></a>

二、反編譯CHM

反編譯CHM的方法同初篇中的利用Process類來進行。

/// &lt;summary&gt;  

        /// 反編譯CHM檔案  

        /// &lt;/summary&gt;  

        /// &lt;param name="CHMFile"&gt;CHM檔案名&lt;/param&gt;  

        /// &lt;returns&gt;傳回hhc檔案名&lt;/returns&gt;  

        /// &lt;remarks&gt;uses the &lt;see cref="DecompileChm"&gt;&lt;/see&gt;&lt;/remarks&gt;  

        public string DecompileChm(string CHMFile)  

        {  

            string pathDir = Path.GetDirectoryName(CHMFile);//得到chm檔案的絕對路徑  

            pathDir = Path.Combine(pathDir, Path.GetFileNameWithoutExtension(CHMFile));  

            return DecompileChm(CHMFile, ref pathDir);  

        }  

        /// &lt;summary&gt;  

        /// &lt;param name="FolderToPut"&gt;反編譯後的檔案存放路徑&lt;/param&gt;  

        /// &lt;returns&gt;傳回反編譯後的hhc檔案名&lt;/returns&gt;  

        /// &lt;remarks&gt;使用hh.exe反編譯&lt;/remarks&gt;  

        public string DecompileChm(string CHMFile, ref string FolderToPut)  

            if ((!System.IO.File.Exists(CHMFile)))  

            {  

                throw new ArgumentException(CHMFile+"檔案不存在");  

            }  

            if ((!Directory.Exists(FolderToPut)))  

                FolderToPut = FolderToPut.Replace(" ", "_");  

                Directory.CreateDirectory(FolderToPut);  

            DirectoryInfo di = new DirectoryInfo(FolderToPut);  

            if ((di.Name.Contains(" ")))  

                throw new ArgumentException("反編譯的檔案夾名不能包含空格");  

            string strD = null;  

            strD = " -decompile " + di.FullName + " " + CHMFile;//反編譯指令  

            Console.WriteLine(strD);  

            Process p = Process.Start("hh.exe", strD);//調用hh.exe進行反編譯  

            p.WaitForExit();  

            return Directory.GetFiles(FolderToPut, "*.HHC")[0];  

        } 

三、CHM檔案轉換為Word

接下來,我們來延伸下,利用反編譯的檔案,将CHM轉換成Word檔案。思路是這樣的:利用反編譯,得到hhc檔案(hhc檔案中包含htm或html檔案的檔案名)和一大堆web頁面(如果一開始編譯進去的是一大堆的話,呵呵),建立一個word檔案,将html檔案插入到word中,下面以執行個體的方式來實作。

為了友善代碼管理,我建立了一個類庫項目,命名為CHM2Word,裡面主要實作将CHM檔案反編譯并将反編譯的檔案整合為Word。在CreateCHM項目中調用代碼即可,另需要你的機器安裝Office2003(對應,添加引用 -&gt;COM-&gt;Microsoft Word 11.0 Object Library)或2007(對應,添加引用-&gt;COM-&gt;Microsoft Word 12.0 Object Library)。

        /// 添加到word中  

        /// &lt;param name="pathFileHHC"&gt;&lt;/param&gt;  

        /// &lt;param name="saveAs"&gt;&lt;/param&gt;  

        public void AddToWord(string pathFileHHC, string saveAs)  

            if (File.Exists(saveAs))  

                throw new Exception("word檔案已經存在!");  

            Object Nothing = System.Reflection.Missing.Value;  

            Microsoft.Office.Interop.Word.Application wApp = (Microsoft.Office.Interop.Word.Application)this.Word();  

            Document wDoc = wApp.Documents.Add(ref  Nothing, ref  Nothing, ref  Nothing, ref  Nothing);  

            if (wApp == null)  

                throw new Exception("轉換失敗");  

            try 

                string dirfile = "";//目錄位置  

                dirfile = Path.GetDirectoryName(pathFileHHC);//目錄的絕對路徑  

                string[] lines = File.ReadAllLines(pathFileHHC);//讀取hhc所有的行,這是為了找出裡面的htm或html檔案  

                string quote = "" + (char)34;  

                long filenumber = 0;  

                //周遊每一行  

                foreach (string TextLine in lines)  

                {  

                    string htmFile = null;  

                    if (TextLine.IndexOf(".html", 0) &gt; 0 || TextLine.IndexOf(".htm", 0) &gt; 0)//如果這一行裡面有.htm或者html.的字元串  

                    {  

                        #region 以下代碼是擷取htm或者html檔案名  

                        int endQuote = 0;  

                        if (TextLine.IndexOf(".html", 0) &gt; 0)  

                        {  

                            endQuote = TextLine.IndexOf(quote, TextLine.IndexOf(".html", 0));  

                        }  

                        else 

                            endQuote = TextLine.IndexOf(quote, TextLine.IndexOf(".htm", 0));  

                        int quoteLoop = 0;  

                        quoteLoop = endQuote - 1;  

                        while (TextLine.Substring(quoteLoop, 1) != quote)  

                            quoteLoop = quoteLoop - 1;  

                        htmFile = TextLine.Substring(quoteLoop + 1, endQuote - quoteLoop - 1);//擷取html檔案的名字  

                        #endregion  

                        htmFile = dirfile + "\\" + htmFile;  

                        bool b = false;//是否存在html檔案  

                        try 

                            b = File.Exists(htmFile);  

                        catch (Exception ex)  

                        if ((!b))  

                            continue;  

                        //将檔案插入到word中  

                        wApp.Selection.InsertParagraphAfter();  

                        filenumber += 1;  

                        if (ProcessFile != null)  

                            ProcessFile(this, new ProcessFileEventArgs(htmFile, filenumber));  

                        //InsertFile參數說明  

                        //檔案名: 必選的 String. 要被插入的檔案名和路徑。如果沒有指定路徑,Word預設為目前檔案夾  

                        //Range: 可選的 Object. 如果指定的檔案時word, 參數為bookmark(書簽). 如果檔案為其他類型(如Excel工作表), 參數為指定的一個單元或區域,如 R1C1:R3C4  

                        //确定是否轉換 可選 Object.如果值為 True,則 word 應用程式将在插入非“ Word 文檔”格式的文檔時提示對轉換進行确認。.  

                        //連結:  可選 Object. 如果值為 True,則可用 INCLUDETEXT 域插入該文檔。  

                        //附件: 可選 Object. 為 True 時将該檔案作為附件插入電子郵件消息中。  

                        wApp.Selection.InsertFile(htmFile, ref Nothing, ref Nothing, ref Nothing, ref Nothing);  

                        if ((filenumber % 10 == 0))  

                            wDoc.Save();  

                    }  

                }  

                wDoc.Save();//儲存word  

                wDoc.Close(ref Nothing, ref Nothing, ref Nothing);//關閉  

                wApp.Quit(ref Nothing, ref Nothing, ref Nothing);//釋放  

            catch (Exception ex)  

            finally//釋放對象  

                if ((wDoc != null))  

                    Marshal.ReleaseComObject(wDoc);  

                    wDoc = null;  

                Marshal.ReleaseComObject(wApp);  

                wApp = null;  

建立word對象

        /// 建立word對象  

        /// &lt;returns&gt;&lt;/returns&gt;  

        public object Word()  

            Microsoft.Office.Interop.Word.Application WordApp;  

                //WordApp = new Microsoft.Office.Interop.Word.ApplicationClass();//如果是office2003和office2007用這樣方法  

                WordApp = new Microsoft.Office.Interop.Word.Application();//如果是office2010,使用這個方法  

            catch (Exception e)  

                WordApp = null;  

            return WordApp;  

反編譯導出類主要方法

        /// feedback about processing   

        public event EventHandler&lt;ProcessFileEventArgs&gt; ProcessFileIntoWord;//定義一個事件屬性  

        private WordClass withEventsField_w = new WordClass();  

        /// 通過這個類,我們可以轉換為word,并且把事件傳給調用者  

        /// &lt;/summary&gt;      

        public WordClass w  

            get { return withEventsField_w; }  

            set 

                if (withEventsField_w != null)//如果不為null,撤銷事件  

                    withEventsField_w.ProcessFile -= w_ProcessFile;  

                withEventsField_w = value;  

                if (withEventsField_w != null)//如果不為null,注冊  

                    withEventsField_w.ProcessFile += w_ProcessFile;  

        /// 主要函數:反編譯、導出  

        /// &lt;param name="ChmFile"&gt;待反編譯的CHM檔案&lt;/param&gt;  

        /// &lt;param name="DocFile"&gt;word檔案名&lt;/param&gt;  

        /// &lt;remarks&gt;word檔案一定不存在&lt;/remarks&gt;  

        public void DecompileAndExport(string ChmFile, string DocFile)  

                Decompile d = new Decompile();//執行個體化一個反編譯類  

                string strHHC = d.DecompileChm(ChmFile);//擷取hhc檔案  

                w.AddToWord(strHHC, DocFile);//調用word類的添加到word中方法  

            catch (System.Runtime.InteropServices.COMException ex)  

                //throw new clsError("Com exception:" + ex.Message, ErrorsOcurred.ComError);  

我利用剛剛生成的baidu的CHM導出的word如圖:

<a target="_blank" href="http://blog.51cto.com/attachment/201105/225628425.jpg"></a>

效果還是不錯的,呵呵。如果你的CHM檔案大的話,導出的時間可能會比較長一些。

 PS:

1.如果你使用的是office2003或者office2007,需要修改類庫項目下的WordClass類下Word方法,因為office2010的

Microsoft.Office.Interop.Word.ApplicationClass不再提供構造方法,而是提供Microsoft.Office.Interop.Word.Application()接口

2.如果在轉換的工程中,始終沒有反應,可以調試下,如果出現這樣的錯誤,“因為沒有打開的文檔,是以這一指令無效”。

調試中不會彈出異常,但是将滑鼠放到wApp對象中,檢視的會發現那樣的錯誤,原因是因為權限不夠,可以采用如下方法解決:

運作dcomcnfg打開元件服務,依次展開"元件服務"-&gt;"計算機"-&gt;"我的電腦"-&gt;"DCOM配置"

找到"Microsoft Word應用程式",右鍵打開屬性對話框, 

點選"辨別"頁籤,點選"辨別"标簽,選擇"互動式使用者"(此設定可能對計算機安全存在威脅,如不設定可以解決問題就不設定,點"下列使用者",把管理者的使用者administrator密碼....正确填寫進去也行)

點選"安全"頁籤,依次把"啟動和激活權限","通路權限","配置權限",都選擇為自定義,然後依次點選它們的編輯,把everyone添加進去,并加入所有的權限...

OK,解決此問題!

如果你的office是2010或者你的系統版本較高的話,很有可能遇到這樣的問題。我的電腦是windows7+office2010,就遇到了這樣的問題。

在下篇(應用篇)中,我将說說如何将這些技術運用到實際中。

<a href="http://down.51cto.com/data/2358199" target="_blank">附件:http://down.51cto.com/data/2358199</a>

    本文轉自xshf12345 51CTO部落格,原文連結:http://blog.51cto.com/alexis/573999,如需轉載請自行聯系原作者