在c#中使用WINDOWS API)

來源:互聯網
上載者:User
下面以C#為例簡單介紹調用API的基本過程:
動態連結程式庫函數的聲明
 動態連結程式庫函數使用前必須聲明,相對於VB,C#函式宣告顯得更加羅嗦,前者
通過 Api Viewer粘貼以後,可以直接使用,而後者則需要對參數作些額外的變化工
作。

 動態連結程式庫函式宣告部分一般由下列兩部分組成,一是函數名或索引號,二是
動態連結程式庫的檔案名稱。
  譬如,你想調用User32.DLL中的MessageBox函數,我們必須指明函數的名字
MessageBoxA或MessageBoxW,以及庫名字User32.dll,我們知道Win32 API對每一個涉及
字串和字元的函數一般都存在兩個版本,單位元組字元的ANSI版本和雙位元組字元的
UNICODE版本。

 下面是一個調用API函數的例子:
[DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
public static extern bool MoveFile(String src, String dst);

 其中進入點EntryPoint標識函數在動態連結程式庫的入口位置,在一個受管轄的工
程中,目標函數的原始名字和序號進入點不僅標識一個跨越互操作界限的函數。而且,
你還可以把這個進入點映射為一個不同的名字,也就是對函數進行重新命名。重新命名可以
給調用函數帶來種種便利,通過重新命名,一方面我們不用為函數的大小寫傷透腦筋,同
時它也可以保證與已有的命名規則保持一致,允許帶有不同參數類型的函數共存,更重
要的是它簡化了對ANSI和Unicode版本的調用。CharSet用於標識函數調用所採用的是
Unicode或是ANSI版本,ExactSpelling=false將告訴編譯器,讓編譯器決定使用
Unicode或者是Ansi版本。其它的參數請參考MSDN線上協助.

 在C#中,你可以在EntryPoint域通過名字和序號聲明一個動態連結程式庫函數,如
果在方法定義中使用的函數名與DLL進入點相同,你不需要在EntryPoint域顯示聲明函
數。否則,你必須使用下列屬性格式指示一個名字和序號。

[DllImport("dllname", EntryPoint="Functionname")]
[DllImport("dllname", EntryPoint="#123")]
值得注意的是,你必須在數字序號前加“#”
下面是一個用MsgBox替換MessageBox名字的例子:
[C#]
using System.Runtime.InteropServices;

public class Win32 {
[DllImport("user32.dll", EntryPoint="MessageBox")]
public static extern int MsgBox(int hWnd, String text, String caption,
uint type);
}
許多受管轄的動態連結程式庫函數期望你能夠傳遞一個複雜的參數類型給函數,譬如
一個使用者定義的結構類型成員或者受管轄代碼定義的一個類成員,這時你必須提供額外
的資訊格式化這個類型,以保持參數原有的布局和對齊。

C#提供了一個StructLayoutAttribute類,通過它你可以定義自己的格式化類
型,在受管轄代碼中,格式化類型是一個用StructLayoutAttribute說明的結構或類成
員,通過它能夠保證其內部成員預期的布局資訊。布局的選項共有三種:

布局選項
描述
LayoutKind.Automatic
為了提高效率允許運行態對類型成員重新排序。
注意:永遠不要使用這個選項來調用不受管轄的動態連結程式庫函數。
LayoutKind.Explicit
對每個域按照FieldOffset屬性對類型成員排序
LayoutKind.Sequential
對出現在受管轄類型定義地方的不受管轄記憶體中的類型成員進行排序。
傳遞結構成員
下面的例子說明如何在受管轄代碼中定義一個點和矩形類型,並作為一個參數傳
遞給User32.dll庫中的PtInRect函數,
函數的不受管轄原型聲明如下:
BOOL PtInRect(const RECT *lprc, POINT pt);
注意你必須通過引用傳遞Rect結構參數,因為函數需要一個Rect的結構指標。
[C#]
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct Point {
public int x;
public int y;
}

[StructLayout(LayoutKind.Explicit]
public struct Rect {
[FieldOffset(0)] public int left;
[FieldOffset(4)] public int top;
[FieldOffset(8)] public int right;
[FieldOffset(12)] public int bottom;
}

class Win32API {
[DllImport("User32.dll")]
public static extern Bool PtInRect(ref Rect r, Point p);
}
類似你可以調用GetSystemInfo函數獲得系統資訊:
? using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_INFO {
public uint dwOemId;
public uint dwPageSize;
public uint lpMinimumApplicationAddress;
public uint lpMaximumApplicationAddress;
public uint dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public uint dwProcessorLevel;
public uint dwProcessorRevision;
}

[DllImport("kernel32")]
static extern void GetSystemInfo(ref SYSTEM_INFO pSI);

SYSTEM_INFO pSI = new SYSTEM_INFO();
GetSystemInfo(ref pSI);

類成員的傳遞
同樣只要類具有一個固定的類成員布局,你也可以傳遞一個類成員給一個不受管
轄的動態連結程式庫函數,下面的例子主要說明如何傳遞一個sequential順序定義的
MySystemTime類給User32.dll的GetSystemTime函數, 函數用C/C++調用規範如下:

void GetSystemTime(SYSTEMTIME* SystemTime);
不像傳實值型別,類總是通過引用傳遞參數.
[C#]
[StructLayout(LayoutKind.Sequential)]
public class MySystemTime {
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
class Win32API {
[DllImport("User32.dll")]
public static extern void GetSystemTime(MySystemTime st);
}
回呼函數的傳遞:
從受管轄的代碼中調用大多數動態連結程式庫函數,你只需建立一個受管轄的函數定
義,然後調用它即可,這個過程非常直接。
如果一個動態連結程式庫函數需要一個函數指標作為參數,你還需要做以下幾步:
首先,你必須參考有關這個函數的文檔,確定這個函數是否需要一個回調;第
二,你必須在受管轄代碼中建立一個回呼函數;最後,你可以把指向這個函數的指標作
為一個參數創遞給DLL函數,.

回呼函數及其實現:
回呼函數經常用在任務需要重複執行的場合,譬如用於枚舉函數,譬如Win32 API
中的EnumFontFamilies(字型枚舉), EnumPrinters(印表機), EnumWindows (視窗枚舉)
函數. 下面以視窗枚舉為例,談談如何通過調用EnumWindow 函數遍曆系統中存在的所有
視窗

分下面幾個步驟:
1. 在實現調用前先參考函數的聲明
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)
顯然這個函數需要一個回呼函數地址作為參數.
2. 建立一個受管轄的回呼函數,這個例子聲明為代表類型(delegate),也就是我
們所說的回調,它帶有兩個參數hwnd和lparam,第一個參數是一個視窗控制代碼,第二個參數
由應用程式定義,兩個參數均為整形。

  當這個回呼函數返回一個非零值時,標示執行成功,零則暗示失敗,這個例
子總是返回True值,以便持續枚舉。
3. 最後建立以代表對象(delegate),並把它作為一個參數傳遞給EnumWindows
函數,平台會自動地 把代錶轉化成函數能夠識別的回調格式。

[C#]
using System;
using System.Runtime.InteropServices;

public delegate bool CallBack(int hwnd, int lParam);

public class EnumReportApp {

[DllImport("user32")]
public static extern int EnumWindows(CallBack x, int y);

public static void Main()
{
CallBack myCallBack = new CallBack(EnumReportApp.Report);
EnumWindows(myCallBack, 0);
}

public static bool Report(int hwnd, int lParam) {
Console.Write("視窗控制代碼為");
Console.WriteLine(hwnd);
return true;
}
}

指標型別參數傳遞:
 在Windows API函數調用時,大部分函數採用指標傳遞參數,對一個結構變數
指標,我們除了使用上面的類和結構方法傳遞參數之外,我們有時還可以採用數組傳遞
參數。

 下面這個函數通過調用GetUserName獲得使用者名稱
BOOL GetUserName(
LPTSTR lpBuffer, // 使用者名稱緩衝區
LPDWORD nSize // 存放緩衝區大小的地址指標
);
 
[DllImport("Advapi32.dll",
EntryPoint="GetComputerName",
ExactSpelling=false,
SetLastError=true)]
static extern bool GetComputerName (
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
  [MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );
 這個函數接受兩個參數,char * 和int *,因為你必須分配一個字串緩衝區
以接受字串指標,你可以使用String類代替這個參數類型,當然你還可以聲明一個字
節數組傳遞ANSI字串,同樣你也可以聲明一個只有一個元素的長整型數組,使用數組
名作為第二個參數。上面的函數可以調用如下:

byte[] str=new byte[20];
Int32[] len=new Int32[1];
len[0]=20;
GetComputerName (str,len);
MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));
 最後需要提醒的是,每一種方法使用前必須在檔案頭加上:
 using System.Runtime.InteropServices;

以下是我補充的一些內容:
1. LPTSTR 可以Map為StringBuilder
2. 將LPWORD,LPDWORD等Map為數組時可以不加MarshalAs Attribute.
3. 有些時候需要將指標型別宣告為IntPtr而不是ref;
4. 使用Marshal.PtrToStructure和Marshal.StructureToPtr可以協助完成IntPtr和Structure之間的轉換;
5. 可以參照MS使用的API的聲明(如System.Windows.Forms.UnsafeNativeMethods中的內容)。

希望以上內容對大家有所協助。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.