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中直接查詢。
沒有留言:
張貼留言