顯示具有 library 標籤的文章。 顯示所有文章
顯示具有 library 標籤的文章。 顯示所有文章

2007-12-18

列舉子資料夾的方法(.NET)

簡單, 就 Directory.GetDirectories() 就好啦, 但是如果要列舉的對象有著為數眾多(比如說有好幾萬)的子資料夾或是檔案的話, 那可有得等了, 而且還要有充足的記憶體供它使用, 因為它的回傳值是 string[], 所以如果有類似的需求的話, 只能另想它法囉!

當下想到的就是 WIN32 API 的 FindFirstFile, FindNextFile 函式, 可是要在 .NET 環境中使用的話, 還是稍稍封裝一下比較好, 所以第一步當然是要準備那些 DllImport 的宣告, 和最煩人的 WIN32_FIND_DATA 資料結構囉, 自己參考文件照樣重新製作一個當然也是可以, 可是如果有現成的話該多好!

先用 Reflector 參考一下 Directory.GetDirectories 是怎麼寫的, 它既然要列舉資料夾, 應該也一定會用到那些 API 才對, 果然! 相關的宣告如下:

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr FindFirstFile(string fileName, [In, Out] WIN32_FIND_DATA data);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool FindNextFile(IntPtr hndFindFile, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_DATA lpFindFileData);

[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
private static extern bool FindClose(IntPtr handle);
[Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto), BestFitMapping(false)]
private class WIN32_FIND_DATA
{
    internal int dwFileAttributes;
    internal int ftCreationTime_dwLowDateTime;
    internal int ftCreationTime_dwHighDateTime;
    internal int ftLastAccessTime_dwLowDateTime;
    internal int ftLastAccessTime_dwHighDateTime;
    internal int ftLastWriteTime_dwLowDateTime;
    internal int ftLastWriteTime_dwHighDateTime;
    internal int nFileSizeHigh;
    internal int nFileSizeLow;
    internal int dwReserved0;
    internal int dwReserved1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    internal string cFileName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
    internal string cAlternateFileName;
}

接下來要就要自己實作一個回傳 IEnumerable 的方法, 每列舉一次只回傳一筆結果, 既可避免等待的問題, 又可以隨時停止, 如果在 .NET 1.x 的時代, 要實作一個 IEnumerable 比較麻煩, 但是 .NET 2.0 因為有 yield 這個新元素可以使用, 所以問題就簡化到一個不可思議的地步, 所以先實作一個內部使用的 internalGetFileDirectoryNames 的方法, 如下:

private const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private static IEnumerable internalGetFileDirectoryNames(string path, string searchPattern, bool includeFiles, bool includeDirs, SearchOption searchOption)
{
    WIN32_FIND_DATA data = new WIN32_FIND_DATA();
    IntPtr findHandle = FindFirstFile(Path.Combine(path, searchPattern), data);

    if (findHandle == INVALID_HANDLE_VALUE) yield break;

    try
    {
        List<string> dirs = new List<string>();
        do
        {
            bool isDirectory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
            if (data.cFileName != "." && data.cFileName != "..")
            {
                if (isDirectory && searchOption == SearchOption.AllDirectories) dirs.Add(data.cFileName);

                if ((isDirectory && includeDirs) || (!isDirectory && includeFiles))
                {
                    yield return Path.Combine(path, data.cFileName);
                }
            }
        }
        while (FindNextFile(findHandle, data));

        foreach (string dir in dirs)
        {
            foreach (string f in internalGetFileDirectoryNames(Path.Combine(path, dir), searchPattern, includeFiles, includeDirs, searchOption))
                yield return Path.Combine(path, f);
        }
    }
    finally
    {
        bool r = FindClose(findHandle);
        System.Diagnostics.Debug.Assert(r);
    }
}

最後, 模擬原生的 Directory.GetFiles, Directory.GetDirectories, Directory.GetFileSystemEntries 函式簽名, 自己 overload 一下就搞定啦, 以下列出最多屬性的函式宣告:

public static IEnumerable GetFiles(string path, string searchPattern, SearchOption searchOption)
{
    return internalGetFileDirectoryNames(path, searchPattern, true, false, searchOption);
}
public static IEnumerable GetDirectories(string path, string searchPattern, SearchOption searchOption)
{
    return internalGetFileDirectoryNames(path, searchPattern, false, true, searchOption);
}
public static IEnumerable GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
    return internalGetFileDirectoryNames(path, searchPattern, true, true, searchOption);
}

完成啦! 完整的程式碼在這裡!

2007-10-07

.NET 類別庫的原始碼釋出

ScottGu 在他的 Blog 上說 .NET 的原始碼包含 BCL, ASP.NET, Windows Forms, ADO.NET, XML 和 WPF (接下來還有 WCF, Wordflow, LINQ) 將會和 .NET 3.5 以及 VS2008 一起釋出。

剛看到這個標題的時候本來沒什麼感覺, 因為 Reflector 就可以做到這件事了, 雖然有些程式碼編成 IL 之後會和原始程式有些出入, 但是已經可以看出 8, 9 成的邏輯了, 可是再往下繼續閱讀之後, 才知道原來不只是這樣!!

以前在使用 Delphi 開發程式的時候, 最令我愛不釋手的特色之一就是不管在設計時期或是執行時期都可以直接進入原始程式碼觀看使用到的類別庫的內容和註解 (雖然不是全部的類別, 但也足夠了)! 後來在使用 Eclipse 撰寫 Java 程式的時候, 只要把用到的 source code 下載回來, 也可以在 IDE 環境中做好相關的設定之後達到和 Delphi 一樣的效果! 有了這項功能, 除了可以了解那些類別的設計哲學之外, 更有助於釐清一些從外表看不出來 (像是 bug 之類) 的秘密!

本來想說這項功能應該是不會在 Microsoft 的產品中出現的, 沒想到 ScottGu 竟然宣布將會在 VS2008 加入這項功能, 並且會在需要的時候下載正確版本的原始程式碼, 不勞 Develper 動手, 的確符合 Microsoft 貼心的一貫作法, 真是令人期待!! 真相圖如下: (2 張就足以說明一切了!)

設定畫面

參考: Releasing the Source Code for the .NET Framework Libraries

2007-04-24

用 JavaScript 在網頁上畫向量圖

今天在 Google 上搜尋可以在網頁上繪製向量圖形的 Solution 時, 本來焦點是放在 VML 和 SVG 上的, 後來不小心發現一個提供以 JavaScript 繪製簡單向量圖形 Library 的網站, 除了該 Library 之外, 它還提供了 Drag'nDrop, Tooltips, RotateImage 的 Library.

Vectorgraphics 速度雖然被嫌說很慢, 但應用在簡單的 Application 還滿不錯的!
Drag'nDrop 除了字面上的功能外, 還提供按住 Shift 放大縮小的功能, 很有趣!
Tooltips 的功能就比較普通了, 和一般其他的 Library 差不多!
RotateImage 的實作方式很神奇, 沒有用到任何的 filter, 是將原圖重製成多份後, 只切出所需的點, 重新組合而成的, 所以在效能及資源上可能會有點耗!!