1. Extension Methods 的應用: Save DataSet as Excel file...

    好久沒寫東西了,趁著還記得 C# 怎麼寫的時後多補幾篇 =_= 要靠程式輸出Excel已經是個FAQ級的問題了,看過我之前文章的大概都知到,我很懶的寫那種FAQ級的東西,不是說寫了沒用,而是太多人寫,一定有寫的比我好的... 到最後連我自己忘了都會去查別人寫的,那我寫了還有啥用? 所以當然是有些特別的東西,才會想寫這篇...  我碰到的問題是,程式要處理的都是 DataSet 物件,不過 DataSet 物件是從 Excel 來的,處理完也要能回存回 Excel ..., 只不過為了先把 POC 作完,現在是以 DataSet 原生的 XML 格式代替。 其實以儲存的角度來看,XML很好用。不過要教會客戶編輯XML可是個大挑戰啊... 像 Excel 這樣有個表格還是比較容易上手一點。原本的程式長的像這樣:  

    原本的程式 (處理 dataset xml)[copy code]
                DataSet ds = new DataSet();
                ds.ReadXml("data.xml");
                // do something
                ds.WriteXml("data.xml");
    
  我對程式碼的要求其實還蠻龜毛的,常常光為了一個變數名字取的好不好,就要想半天... 對於程式的結構寫起來漂不漂亮也是。上面的程式,要把 XML 換成 EXCEL 其實很簡單,把 ReadXml( ) 換成對等的載入 Excel 的程式碼,WriteXml( ) 也換成對等的輸出 Excel 就可以收工了。不過看起來就是不順眼,因為被太多細節干擾了,未來回過頭來看自己寫的 code, 如果一眼望去不能馬上看出這段 code 在做什麼,那就是不及格的作品了... 所以,我想要的 code 最好能像這樣:  
期望的程式 (處理 excel 檔)[copy code]
            DataSet ds = new DataSet();
            ds.ReadExcel("data.xls");
            // do something
            ds.WriteExcel("data.xls");
  看起來酷多了,這段程式很清楚的說明他在幹嘛... load excel, processing... and save excel... 繼承的作法,想都不用想就不考慮了。因為我的程式要嘛就直接用 System.Data.DataSet, 不然就是配合 XSD 讓 visual studio 替我 generate typed dataset 的類別... 這些情況下要用到我自訂的衍生類別都不方便... 於是我就把念頭動到 C# 的 extension ... 要怎麼把 DataSet 存成 Excel, 這就是典型的 FAQ 級的問題了,請各位 GOOGLE 一下就有了,不然文後我也附了幾個參考連結...。這裡的重點是 C# extension, 它很神奇的能讓你不需要重新編譯,也不需要拿到原始碼,就能 "擴充" 原本類別的能力。靠這樣的機制,我就能夠改造 .NET Framework 內建的 DataSet, 把它改造成我想要的樣子,如上面的範例用的 ReadExcel( ) / WriteExcel( )。 C# Extension 是我慣用的說法,其實它正統的名字是 Extension Methods, 是 .NET 3.0 之後提供的功能,不只 C#,VB.NET 也支援。它能讓你在現有的 class 上 "附加" 新的 method ...  是的,沒錯,它的能力有限,只能增加 "method", 而且只能是 instance method, 不能是 class ( static ) method, 也不能是 property 或 field ... 不過這樣也足夠了,先來看看這種 code 要怎麼寫:
ExtensionMethods 示範[copy code]
    public static class NPOIExtension
    {
        public static void ReadExcel(this DataSet ds, string inputFile)
        {
            //do something
        }

        public static void WriteExcel(this DataSet ds, string outputFile)
        {
            //do something
        }
    }
  其實這又是另一種 Microsoft 提供的 Syntex Sugar 而已.. 注意到關鍵在那邊了嗎? 其實 "extension methods" 只不過是普通的 static method 而已,關鍵就在它的第一個參數,型別就是要附加的 class, 而宣告時要再額外加個 "this" modifier 來標示它,意思就是在裡面的 code, 這個參數就把它當作 "this" 來用 ... 換個角度看,其實這只是編譯器幫我們動一點手腳而已,好讓我們的 code 看起來會漂亮一點。用 extension methods 寫的 code, 其實跟這樣的寫法是完全同等的:
不用 extension methods 的寫法[copy code]
        DataSet ds = new DataSet();
        NPOIExtension.ReadXml(ds, "data.xml");
        // do something
        NPOIExtension.WriteXml(ds, "data.xml");



     public static class NPOIExtension
    {
       public static void ReadExcel(DataSet context, string inputFile)
        {
            // do something
        }
       public static void WriteExcel(DataSet context, string outputFile)
        {
            // do something
        }
    }
  少了 this 的用法,看起來就沒這麼神奇了。就是一般的 static method 而已。我喜歡 C# 就是喜歡它有很多這類的 syntax sugar, 可以滿足我寫程式想把程式碼弄的漂漂亮亮的慾望... extension methids 就是一例,還有像 yield return, attribute 等等也是個經典... 這樣的改變讓我想起,以前公司新進工程師我都會教一堂課,就是講物件是怎麼一回事? 最早是 C / C++ 版,後來改用 javascript 來講這門課。講的就是一步一步示範如何由 procedure oriented language 轉變到 object oriented language 的過程。C++ 是個最清楚的例子,它靠 C 的 function pointer + struct, 用指標指向 function, 把 data 及 function 包裝在 struct 裡,就變成一個 object. (所以 C++ 的 struct 才會跟 class 這麼像啊)。而繼承的問題,則是在 struct 裡動一點手腳,用 virtual table 的方式,讓你寫起 code 來 "好像" 真的有物件導向這麼回事。其實 C++ 的 method, 也只是個普通的 function, 只不過它第一個參數一定叫作 "this" 而已... 扯遠了,整篇的目的只是要講,用 Extension Method 可以把 code 包的很漂亮,而且也真的可以動而已... 哈哈,最後貼一下完整的 source code, 證明我沒有唬爛... 程式我調一下,我只實作了 WriteExcel( ) 的部份,從 DataSet XML 讀進來,存成 Excel, 單純的一個轉檔程式。而轉換的過程中是這樣對應的: 一個 DataSet 就代表一個 EXCEL workbook,而一個 DataTable, 則對應到 EXCEL sheet。DataTable 的 Row / Column, 當然就是對應到 EXCEL Row 跟 Cell 了:
完整的 source code[copy code]
    public class Program
    {
        static void Main(string[] args)
        {
            DataSet ds = new DataSet();
            ds.ReadXml("data.xml");
            // do something
            ds.WriteExcel("data.xls");
        }
    }
    
    public static class NPOIExtension
    {
        public static void WriteExcel(this DataSet context, string outputFile)
        {
            HSSFWorkbook workbook = new HSSFWorkbook();

            foreach (DataTable table in context.Tables)
            {
                Sheet sheet = workbook.CreateSheet(table.TableName);

                Row headerRow = sheet.CreateRow(0);
                for(int cpos = 0; cpos < table.Columns.Count; cpos++)
                {
                    Cell cell = headerRow.CreateCell(cpos);
                    cell.SetCellType(CellType.STRING);
                    cell.SetCellValue(table.Columns[cpos].ColumnName);
                }

                int rpos = 0;
                foreach (DataRow row in table.Rows)
                {
                    rpos++;
                    Row sheetRow = sheet.CreateRow(rpos);
                    for (int cpos = 0; cpos < table.Columns.Count; cpos++)
                    {
                        object value = row[cpos];
                        Cell cell = sheetRow.CreateCell(cpos);
                        cell.SetCellValue((value == null) ? (null) : (value.ToString()));
                    }
                }
            }

            if (File.Exists(outputFile)) File.Delete(outputFile);
            FileStream fs = File.OpenWrite(outputFile);
            workbook.Write(fs);
            fs.Close();
        }
    }
  對啦,這真的是全部的 code ... 開個 console project 就可以跑了。我就說我很少貼超過百行的 code ... 哈哈 順帶一提,NPOI 這個 open source project 做的真不錯,可以在 pure .net 環境下就能處理 excel ... 這個範例就是用 NPOI 寫的,有興趣的朋友可以參考看看!         ------------- Reference:
  1. MSDN:  Extension Methods (C# Programming Guide) http://msdn.microsoft.com/en-us/library/bb383977.aspx
  2. NPOI (in CodePlex.com) http://npoi.codeplex.com/
  3. MSDN 學習園地的 NPOI 介紹文章 (中譯版) 在 Server 端存取 Excel 檔案的利器:NPOI Library http://msdn.microsoft.com/zh-tw/ee818993.aspx
  4. 另一套類似NPOI 的函式庫: Koogra Koogra Excel BIFF/XLSX Reader Library http://koogra.sourceforge.net/
  5. 直接用 OpenXML format 的範例 http://www.dotblogs.com.tw/mis2000lab/archive/2008/04/24/3454.aspx
  6. Microsoft Open XML SDK 1.0 / 2.0 for Microsoft Office 1.0 (http://www.microsoft.com/downloads/en/details.aspx?FamilyId=AD0B72FB-4A1D-4C52-BDB5-7DD7E816D046&displaylang=en) 2.0 (http://www.microsoft.com/downloads/en/details.aspx?FamilyID=c6e744e5-36e9-45f5-8d8c-331df206e0d0&DisplayLang=en)
後記: 本來有寫一段,是整理到底有幾種方法,可以在 .NET 環境下輸出 EXCEL 的... 不過後來決定沒放在本文,就挪到 reference ... 簡單的比較,需要的可以看看
  1. 直接用 Excel 提供的物件 這種方式就是透過程式去操作 Excel 。優點是只要 Excel 有提供的功能,大概都做的到。不過也因為功能太多,用起來很複雜。這方法致命的缺點就是效能。它相當於你真的開啟 Excel 然後命令它做事,想像一下,要能很順暢的執行 Excel 的機器,都不會太差。單機的 windows form 程式還好,如果碰到 server side 的程式,如 ASP.NET 這種,只要同時有五個人點這個功能,你的 server 大概就跑不動了...。
  2. 透過 Jet odbc / oledb driver 這種方式沒有第一種的缺點,不過也沒有它的優點。既然是透過 odbc 這類存取資料庫的 driver 來存取,那麼你也只能把 Excel 檔當成某種資料庫來看待。處理資料還不成問題,要套用公式或是輸出畫面就辦不到了。這方法還有個大缺點,Jet driver 沒有 x64 的版本 .... windows 2008 r2 之後就沒有 x86 版了,這方法不是長久之計...
  3. 輸出 HTML, 用 Excel 開啟 這方法很簡單,只要輸出 HTML table, 再調整 content type, 讓 browser 叫 excel 出來幫你開啟就好了。老實說我一直覺的這招有點投機 XD,好處是很簡單,缺點是對 EXCEL 的掌控程度很低,大概就是幫你把資料貼進去的程度而已,不能用公式,也不能輸出多個 sheet 。
  4. Open XML Format 直接輸出 Office 2007  開始,就放棄用了十幾年的 Ole structed storage compound file 二進位檔的格式,改以 XML 為主 (當然還是支援舊版的格式啦)。只要你知道它的 schema, 不需要太複雜的步驟就可以直接輸出 .xlsx 了。 熟 XML 的話,甚至是寫寫 XSLT 就可以生的出來。不過這還蠻考驗你對 XML / Open XML 的掌握程度... 算是小有門崁的作法。
  5. 透過 NPOI / Koogra 這類 3rd party 含式庫 這些含式庫都可以不需要安裝龐大的 Excel, 就有處理 excel 檔的能力。跟 Open XML 不同的是,這些 library 能夠處理到比較頭痛的 2003 及之前版本的 binary file, 相容性比 XML 來的好。

2010/11/06 .NET C# MSDN Tips 有的沒的 技術隨筆

  • [RUN! PC] 2010 七月號 - 結合檔案及資料庫的交易處理

     

    IMG_3972

    再次感謝編輯大人 :D,Transactional NTFS  #2 也刊出來了!

    這篇是延續上一篇,進一步的介紹 TxF  如何與 TransactionScope 互動,讓你可以結合檔案系統及資料庫的異動,變成單一交易的技巧。

    由於TxF還沒有正式在.NET Framework裡支援,所以這篇最後也介紹了 AlphaFS,可以簡化應用時的障礙。AlphaFS 是一套想要取代 System.IO.* 的類別庫,它支援了許多 NTFS 的進階功能 (像是 VSS、HardLink 等) 的功能,而 TxF 也在範圍內,透過它就不用再像 上一篇 一樣,辛苦的用 P/Invoke 了。

     

    這篇提到的範例程式可以在這裡下載。有任何意見也歡迎在這裡留話給我 :D

    2010/07/09 RUN! PC 專欄文章 .NET RUN! PC Transactional NTFS 作品集 作業系統 技術隨筆 物件導向

  • [RUN! PC] 2010 五月號 - TxF讓檔案系統也能達到交易控制

     

    image 

    五月號就刊出來,還真有點意外 :D,這次稿件趕不及,晚了幾天才交出去,編輯大人還是讓我上五月號啦,真是感謝 :D

    之前執行緒的系列,是打算把各種應用執行緒的演算法都介紹一下,寫了五篇就沒靈感了。實際寫CODE的技巧倒是很多可以介紹,不過 .NET FX 4.0 出來之後,這些鎖碎的 coding 技巧又大幅簡化了,除非有特別的演算法需要 (如同之前那五篇 :D),否則還自己拿 Thread 物件硬幹已經沒什麼意義了,所以就換另一個我有興趣的主題 - Transactional NTFS 來寫。

    這系列第一篇出爐了,主要就是先介紹它的觀念及如何入門,國內這類資訊還不多,我就野人獻曝寫了一篇,試著寫看看了。較鎖碎的實作技巧 (如 P/Invoke) 我會直接貼 BLOG,而較完整的概念及實作探討等等就會整理成文章拿來投稿了。

    再次感謝各位支持啦,底下有範例程式跟一些參考資源的 LINK,需要的歡迎取用 :D

     

    範例程式:

    1. Visual Studio 2008 Project (C#) File: TransactionDemo.zip

     

    參考資訊:

    1. AlphaFS: Brining Advanced Windows FileSystem Support to .NET
      http://alphafs.codeplex.com/
    2. MSDN magazine (July 2007): Enhance Your Apps With File System Transactions
      http://msdn.microsoft.com/en-us/magazine/cc163388.aspx
    3. B# .NET BLOG: Windows Vista - Introducing TxF In C#
      Part 1: Transacted File Delete
      Part 2: Using System.Transactions and the DTC
      Part 3: CreateFileTransacted Demo
    4. Code Project: Windows Vista TxF / TxR
      http://www.codeproject.com/KB/vista/KTM.aspx
    5. BLOG: Because we can
      http://blogs.msdn.com/because_we_can/archive/2005/05/18/419809.aspx
      Discussion and explanation relating to the Transactional NTFS feature coming in Longhorn, plus any other interesting anecdotes...
    6. Performance Consoderations for Transactional NTFS
      http://msdn.microsoft.com/en-us/library/ee240893(VS.85).aspx
    7. When to Use Transactional NTFS
      http://msdn.microsoft.com/en-us/library/aa365738(VS.85).aspx

    2010/05/05 RUN! PC 專欄文章 .NET RUN! PC Transactional NTFS 作品集 作業系統 技術隨筆 物件導向

  • [RUN! PC] 2010 四月號 - 生產者vs消費者– 執行緒的供需問題

    image 

     

    隔了足足一年,趁著過年,生出第五篇了 :D,再次感謝編輯賞光啦,願意刊登我寫的題材...

    這篇 [生產者/消費者] 其實是延續上一篇 [生產線模式] 而來的,生產線模式,是利用 PIPE 的方式,把工作切成各個階段同時進行。而生產者消費者,則是去探討兩個階段之間如何做好進度的協調。這篇除了講講處理的原則之外,也實作了BlockQueue來簡化這個問題。

    其實更漂亮的用法,是我在 MSDN Magazine 上看到的 BlockingStream, 直接把它作成 System.IO.Stream 的衍生類別。像那些包裝成 Stream 的資料處理 (像是壓縮、加密、或是 Socket 這類),都很適合。不過一般情況下不是所有應用都能套上 Stream 的模式,因此就有了把它包裝成 Queue 的念頭,也就是這篇寫的 BlockQueue 的應用。

    相關的東西其實過去也寫了幾篇,只不過 BLOG 寫的都比較瑣碎,都是針對特定主題較深入的討論,而投稿的文章就會比較完整的介紹原理及作法等等,細節就沒辦法兼顧了。有興趣的讀者可以參考一下我部落格的相關文章 :D   這期的範例程式可以在底下下載,相關的 LINK 也列在底下。

    再次謝謝各位支持啦 :D


    MSDN Magazine 閱讀心得: Stream Pipeline
    RUN!PC 精選文章 - 生產線模式的多執行緒應用
    [RUN! PC] 2008 十一月號 生產線模式的多執行緒應用
    生產者 vs 消費者 - BlockQueue 實作

    2010/04/05 RUN! PC 專欄文章 .NET RUN! PC 作品集 多執行緒 技術隨筆 物件導向

  • [TxF] #2. 先作功課 - 熟悉 P/Invoke 及 Win32 檔案處理...

     

    其實這篇是多寫的,因為前一篇提到的 Transactional NTFS 官方只提供 Win32 API 而已,不提供包裝好的 managed code 用的 library... 因此現階段想始用它,P/Invoke 是逃不掉的... 這篇就先來複習一下,想要在 C# 裡呼叫 unmanaged code 該怎麼用吧。這邊的例子為了配合後面幾篇,就同樣的以檔案處理為例。

    這篇我不想去長篇大論的討論 P/Invoke 那堆規則及語法,也不想去討論那堆 Marshal 的觀念等等... 想學好 P/Invoke 就別看我這篇了,應該去 MSDN 看... 這篇我只想交待一下該如何配合 windows api 來作檔案處理而已。因為這些是往後要用到 Transactional NTFS 必要的技巧,TxF 新的 API 都是跟 win32 標準檔案處理的 API 一一對應的,弄懂了如何用 win32 api 操作檔案,你大概就學會八成的 TxF 了... 想用 TxF ...  熟悉點 P/Invoke 是應該的...

     

    先從最單純的 MoveFile 開始吧,它沒有扯到啥 pointer 或是 handle ... 算是最單純的例子.. 先來看看這 API 原生的樣子:

    BOOL WINAPI MoveFile( __in LPCTSTR lpExistingFileName, __in LPCTSTR lpNewFileName );

     

     

    這 API 做啥事就不用多說了,把 lpExistingFileName 這檔案搬到 lpNewFileName 去... 搬成功還是失敗,就用 BOOL 值把結果傳回來。這時就要透過 P/Invoke 的用法,想辦法產生一個可以對應到 unmanaged code 的 C# function ... 我先把宣告方式貼出來,再來解試 code ...

    P/Invoke: MoveFile[copy code]
       1:  [DllImport("kernel32.dll")]
       2:  static extern bool MoveFile(string lpExistingFileName, string lpNewFileName);

     

    寫過 C / C++ 的人,大概對 extern 這個 keyword 不陌生吧? extern 這修飾字代表這個 function 是外來的,C# 正好拿來用在這裡。外來的 DLL 都是 function 型態,所以一定是 static, 沒有綁著任何一個物件... 而標在上面的 Attribute: DllImport, 則是標上這個 function 是來自那個 DLL。

    在這個例子裡,這樣就足夠了,直接在程式裡試著寫一段 C# (managed code) 來試看看結果吧:

    P/Invoke Sample #1. MoveFile[copy code]
       1:  public class PInvokeTest
       2:  {
       3:      [DllImport("kernel32.dll")]
       4:      static extern bool MoveFile(string lpExistingFileName, string lpNewFileName);
       5:      public static void Main(string[] args)
       6:      {
       7:          string srcFileName = @"C:\file1.txt";
       8:          string dstFileName = @"C:\file2.txt";
       9:          Console.Write("move file: from [{0}] to [{1}] ... ", srcFileName, dstFileName);
      10:          if (MoveFile(srcFileName, dstFileName) == true)
      11:          {
      12:              Console.WriteLine("OK!");
      13:          }
      14:          else
      15:          {
      16:              Console.WriteLine("FAIL!");
      17:          }
      18:      }
      19:  }

     

    程式執行前,看一下 C:\ 的 DIR *.TXT 指令執行結果:

    image

    沒錯,有 c:\file1.txt 這檔案... 接著來執行範例程式:

    image

    執行成功。再重新看一下 C:\ 的 DIR *.TXT 指令執行結果:

    image

    看來程式很順利的呼叫了 win32 api 裡定義的 MoveFile( ... ) ... 這種範例有點不入流,要處理檔案總不可能只有這樣吧? 接著我們再來看看需要 Open File 加上讀寫檔案內容的應用。

    Windows 是個以 HANDLE 為主的作業系統,一般在寫 C / C++ 程式,都是以指標(POINTER)的方式在處理資料,但是在 windows 裡,作業系統提供的資料用的指標,則特別以 "HANDLE" 來稱呼,它比一般的 POINTER 來說多了一些管理的動作。因此你開啟的檔案,或是建立的視窗,通通都以 HANDLE 來稱呼,而不是單純的用 POINTER (雖然它也是個 POINTER 啦)。接下來的例子就來看看 HANDLE 的應用。

    [copy code]
       1:  public class PInvokeTest2
       2:  {
       3:      [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
       4:      public static extern IntPtr CreateFile(
       5:             string lpFileName,
       6:             uint dwDesiredAccess,
       7:             uint dwShareMode,
       8:             IntPtr SecurityAttributes,
       9:             uint dwCreationDisposition,
      10:             uint dwFlagsAndAttributes,
      11:             IntPtr hTemplateFile
      12:             );
      13:      [DllImport("kernel32.dll", SetLastError = true)]
      14:      static extern bool CloseHandle(IntPtr hObject);
      15:      public static void Main(string[] args)
      16:      {
      17:          IntPtr pFile = CreateFile(
      18:              @"c:\file1.txt",
      19:              0x80000000,
      20:              0x00000001,
      21:              IntPtr.Zero,
      22:              3,
      23:              0,
      24:              IntPtr.Zero);
      25:          Stream fs = new FileStream(pFile, FileAccess.Read);
      26:          TextReader tr = new StreamReader(fs);
      27:          Console.WriteLine(tr.ReadToEnd());
      28:          tr.Close();
      29:          fs.Close();
      30:          CloseHandle(pFile);
      31:      }
      32:  }

     

    這個例子裡,很 "神奇" 的把 unmanaged code 拿到的指標 (IntPtr), 丟給 System.IO.* 底下的 FileStream 來用,竟然還能成功的開啟檔案並且把資料讀出來... 沒錯,MS寫的東西本來就都是同一家人,System.IO 那堆東西就是這樣包出來的。 這段範例程式除了看起來複雜一點之外,跟那堆要查文件才知道是啥意思的 flags 之外,其它都很簡單,不過就 Open File, 然後讀出內容,接著 Close ...

    較特別的是,在 P/Invoke 的世界裡,都用 struct System.IntPtr 這個型別,來代表 unmanaged 世界裡常用到的 POINTER.. 藉著這個型別,我們就可以把兩個世界的橋樑給搭起來。不過當你執行這個範例時,編譯器會給你一個很礙眼的警告:

     

    'System.IO.FileStream.FileStream(System.IntPtr, System.IO.FileAccess)' is obsolete: 'This constructor has been deprecated.  Please use new FileStream(SafeFileHandle handle, FileAccess access) instead.  http://go.microsoft.com/fwlink/?linkid=14202'  

     

    沒錯,這是老用法了,一如用指標有數不盡的缺點,新的語言都想盡辦法扔掉它 (POINTER: 我是無辜的...),連包裝過的 IntPtr 也不例外。在 .NET Framework 2.0 裡就不再建議你用這個建構式了,請改用 SafeFileHandle .... 既然在 windows 的世界裡,一切系統的資源都是以 HANDLE 來處理,自然的在 managed code 裡也有對應的型別,它就是 System.Runtime.InteropServices.SafeHandle 。接著再來看看改用 SafeHandle 的版本:

    [copy code]
       1:  public class PInvokeTest3
       2:  {
       3:      [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
       4:      static extern SafeFileHandle CreateFile(
       5:          string fileName,
       6:          uint fileAccess,
       7:          uint fileShare,
       8:          IntPtr securityAttributes,
       9:          uint creationDisposition,
      10:          uint flags,
      11:          IntPtr template);
      12:      public static void Main(string[] args)
      13:      {
      14:          SafeFileHandle pFile = CreateFile(
      15:              @"c:\file1.txt",
      16:              0x80000000,
      17:              0x00000001,
      18:              IntPtr.Zero,
      19:              3,
      20:              0,
      21:              IntPtr.Zero);
      22:          Stream fs = new FileStream(pFile, FileAccess.Read);
      23:          TextReader tr = new StreamReader(fs);
      24:          Console.WriteLine(tr.ReadToEnd());
      25:          tr.Close();
      26:          fs.Close();
      27:          pFile.Close();
      28:      }
      29:  }

     

    執行的結果,當然也是順利的把文字檔內容印到 CONSOLE 裡了,我就不再多貼,直接看程式碼。

    其實這裡有點偷吃步,用的是 SafeFileHandle, 而不是 SafeHandle。雖然兩者有繼承關係啦。不過後面我就都把它當成 SafeHandle 看待。這個版本的程式,除了把 IntPtr 換成 SafeFileHandle 之外,及最後的 CloseHandle( pFile ) 改成直接呼叫 SafeHandle.Close( ) 之外,沒有太大的不同了。有啦,把 IntPtr 跟 CloseHandle 兩者包裝在同一個 SafeHandler 裡是安全的多,至少 SafeHandle 實作了 IDispose 的介面,在適當的情況下,它至少會自動的被呼叫 Dispose( ) 回收資源...

     

    看到這裡,以前老搞不清楚的 FileStream 為什麼有幾個怪怪的 constructor, 自從研究了 TxF 之後,意外的晃然大悟... 算是額外的收穫吧! 原來就是要配合 P/Invoke 使用...

    其實說穿了,呼叫 win32 api 大概就這幾招了,幾種 unmanaged code 裡用到的型別,指標都可以對應之後,程式寫起來就簡單了。這篇我特意漏掉一部份,就是各種用 uint 來當作 flags 的型別,沒有轉到對應的 enum 列舉型別,我是覺的這是 option 啦,畢竟查查文件就有,多列上來我得多打好多字... 篇幅有限 XD

    這幾個例子如果都看懂後,那麼 TxF 就沒有障礙了! 接下來就看下回分解了 :D    下回會看到第一個 "交易式" 的檔案處理範例! 沒灌 windows vista / 2008 / 7 / 2008 R2 的讀者門,快去準備吧!

     

    參考資訊:

    1. 有用的網站: PINVOKE.NET
      http://www.pinvoke.net/index.aspx

      這網站幫你整理好了各種 win32 api 該如何宣告它的 C# signature, 以供 p/Invoke 使用。如果你不熟它的語法,這網站可以幫你不少忙。另外它也提供了一些方便的工具,像是 winform 的查詢工具,或是 visual studio 用的 add-ins
    2. MSDN 的 API 說明: MoveFile
      http://msdn.microsoft.com/en-us/library/aa365239(VS.85).aspx
    3. 什麼是 P/Invoke ? Wiki 的說明:
      http://en.wikipedia.org/wiki/Pinvoke
    4. Safe Handle & Critical Finalization
      (實在是看不懂的中譯... "安全控制代碼和關鍵結束" ???)
      http://msdn.microsoft.com/zh-tw/library/fh21e17c(v=VS.90).aspx

    2010/03/23 系列文章: 交易式 (Transactional) NTFS .NET C# MSDN Transactional NTFS