2012年11月1日 星期四

[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中直接查詢。

沒有留言:

張貼留言