2012年11月27日 星期二

Head First Object-Oriented Analysis and Design(深入淺出物件導向分析與設計)

我一直都很喜歡深入淺出系列的書籍。這本深入淺出物件導向分析與設計就是一本深入淺出系列經典的例子,用實際生活上的例子穿插有趣的圖案一步一步帶領讀者進入OOAD的世界。書中介紹了一些基本的OO原則、部份Design Patten、UML以及軟體開發的流程,幾乎可以說涵蓋了大部分軟體開發的基本議題,很適合當OOAD初學者的第一本入門書。

其中我最喜歡書中一步一步帶你思考的模式,作者會先從不好的設計開始講起,一步一步帶你運用一些OO的基本原則修改程式架構。此外書中舉的例子也十分有趣,搭配作者幽默的文字會讓你有種在讀故事書的感覺,不知不覺就看完整本書。

書中列出開發偉大軟體的三步驟,我覺得分類的非常好。開發軟體不脫離這三個步驟:
  1. 確認你的軟體做客戶要它做的事。
  2. 應用基本的OO原則,增加軟體的彈性。
  3. 努力達成可維護、可重利用的設計。
不過這本書也像其他深入淺出系列一樣,是屬於入門類型的書籍,比較不適合有豐富OO經驗的軟體工程師。此外書中範例程式都是使用Java,讀者可能需要一些Java基本的OO知識才能閱讀。

總而言之,這是一本很棒的OOAD入門書。看完這本書後可以接著針對書中的各個議題去看進階的書籍。比如說Design Patten可以接著看深入淺出系列另外一本有名的書深入淺出設計模式

2012年11月1日 星期四

[C#]建立資料夾範例程式

C#可透過System.IO.Directory.CreateDirectory(dirPath)來建立資料夾
如果dirPath中有不存在的資料夾,CreateDirectory會順便建立此資料夾。
下列範例會在C:\底下建立一個TestDir
using System;
using System.IO;

public class Program
{
    public static void Main()
    {
        string dirPath = @"C:\TestDir";
        if (Directory.Exists(dirPath))
        {
            Console.WriteLine("The directory {0} already exists.", dirPath);
        }
        else
        {
            Directory.CreateDirectory(dirPath);
            Console.WriteLine("The directory {0} was created.", dirPath);
        }
    }
}

[C#]在C#呼叫Native DLLs (P/Invoke)

在C#我們可以透過PInvoke(Platform Invocation Services)來access unmanaged DLL中的function, structs甚至是callbacks function。以下是一個簡單的例子,示範如何在C#中呼叫Windows DLL user32.dll。
MessageBox在user32.dll宣告長這樣:
MessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
C#範例程式:
using System;
using System.Runtime.InteropServices;    // For StructLayout, DllImport

class MsgBoxTest
{
    [DllImport("user32.dll"]
    static extern int MessageBox(IntPtr hWnd, String text, String caption, int type);

    public static void Main()
    {
        MessageBox(IntPtr.Zero, "Text", "Caption", 0);
    }
}
有三個地方要注意。DllImport定義function位於哪個dll中,static定義這是一個global method,extern則是告訴CLR此function是實做在外部。
看到這邊好像覺得P/Invoke沒什麼,繼續看下去就知道P/Invoke為什麼這麼讓人頭痛。

如果Native function的參數是struct該怎麼處理?讓我們看一個例子:
void GetSystemTime(LPSYSTEMTIME lpSystemTime);

typedef struct _SYSTEMTIME {
  WORD wYear;
  WORD wMonth;
  WORD wDayOfWeek;
  WORD wDay;
  WORD wHour;
  WORD wMinute;
  WORD wSecond;
  WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
要呼叫這個function,我們必須定義一個C#的Class和C Struct有一樣的結構。
using System;
using System.Runtime.InteropServices;     


[ StructLayout( LayoutKind.Sequential )]
public class SystemTime 
{
   public ushort year;
   public ushort month;
   public ushort weekday;
   public ushort day;
   public ushort hour;
   public ushort minute;
   public ushort second;
   public ushort millisecond;
}
StructLayout告訴marshaler如何把每個C# class中的field對應到C Struct中,LayoutKind.Sequential表示每個field是根據pack-size aligned sequentially。預設的pack-size是8,也就是說所有的field都會aligned成8 bytes。我們可以在StructLayout中加入Pack attribute來修改pack-size。
[StructLayout(LayoutKind.Sequential, Pack = 1)]
GetSystemTime範例由於剛好是8 byte aligned所以不需更改pack size。
要呼叫GetSystemTime很簡單:
using System;
using System.Runtime.InteropServices;

class MsgBoxTest
{
    [DllImport("Kernel32.dll")]
    public static extern void GetSystemTime(SystemTime st);

    public static void Main()
    {
        SystemTime t = new SystemTime();
        GetSystemTime(t);
        Console.WriteLine(t.Year);
    }
}
如果不知道native function如何在C#中宣告,可以去PINVOKE.NET搜尋。此網站也提供一個方便的Visual Studio add-in可讓你在VS中直接查詢。