C#有沒有方法可以直接都用已經存在的功能(比如Windows中的一些功能,C++中已經編寫好的一些方法),而不需要重新編寫代碼?
答案是肯定,就是通過接下來要說的 DllImport 。
DllImport的namespace: using System.Runtime.InteropServices;
MSDN中對DllImportAttribute的解釋:可將該屬性應用於方法。
DllImportAttribute 屬性提供對從非托管 DLL 導出的函數進行調用所必需的信息。必須提供包含入口點的 DLL 的名稱。
DllImport 屬性定義如下:
namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Method)] public class DllImportAttribute: System.Attribute { public DllImportAttribute(string dllName) {...} public CallingConvention CallingConvention; public CharSet CharSet; public string EntryPoint; public bool ExactSpelling; public bool PreserveSig; public bool SetLastError; public string Value { get {...} } } }
說明:
1、DllImport只能放置在方法聲明上。
2、DllImport具有單個定位參數:指定包含被導入方法的 dll 名稱的 dllName 參數。
3、DllImport具有五個命名參數:
a、CallingConvention 參數:指示入口點的調用約定。如果未指定 CallingConvention,則使用默認值 CallingConvention.Winapi。
b、CharSet 參數: 指示用在入口點中的字符集。如果未指定 CharSet,則使用默認值 CharSet.Auto。
c、EntryPoint 參數: 給出 dll 中入口點的名稱。如果未指定 EntryPoint,則使用方法本身的名稱。
d、ExactSpelling 參數: 指示 EntryPoint 是否必須與指示的入口點的拼寫完全匹配。如果未指定 ExactSpelling,則使用默認值 false。
e、PreserveSig 參數: 指示方法的簽名應當被保留還是被轉換。當簽名被轉換時,它被轉換為一個具有 HRESULT 返回值和該返回值的一個名為 retval 的附加輸出參數的簽名。如果未指定 PreserveSig,則使用默認值 true。
f、SetLastError 參數: 指示方法是否保留 Win32"上一錯誤"。如果未指定 SetLastError,則使用默認值 false。
4、它是一次性屬性類。
5、用 DllImport 屬性修飾的方法必須具有 extern 修飾符。
DllImport的用法:
DllImport("MyDllImport.dll")] private static extern int mySum(int a,int b);
一 在C#程序設計中使用Win32類庫
常用對應類型:
1、DWORD 是 4 字節的整數,因此我們可以使用 int 或 uint 作為 C# 對應類型。
2、bool 類型與 BOOL 對應。
示例一:調用 Beep() API 來發出聲音
Beep() 是在 kernel32.lib 中定義的,在MSDN 中的定義,Beep具有以下原型:
BOOL Beep(DWORD dwFreq, // 聲音頻率 DWORD dwDuration // 聲音持續時間);
用 C# 編寫以下原型:
[DllImport("kernel32.dll")] public static extern bool Beep(int frequency, int duration);
示例二:枚舉類型和常量
MessageBeep() 是在 user32.lib 中定義的,在MSDN 中的定義,MessageBeep具有以下原型:
BOOL MessageBeep(UINT uType // 聲音類型 );
用C#編寫一下原型:
public enum BeepType { SimpleBeep = -1, IconAsterisk = 0x00000040, IconExclamation = 0x00000030, IconHand = 0x00000010, IconQuestion = 0x00000020, Ok = 0x00000000, }
uType 參數實際上接受一組預先定義的常量,對於 uType 參數,使用 enum 類型是合乎情理的。
[DllImport("user32.dll")] public static extern bool MessageBeep(BeepType beepType);
示例三:處理結構
如果需要確定筆記本的電池狀況。Win32 為此提供了電源管理函數,搜索 MSDN 可以找到GetSystemPowerStatus() 函數。
BOOL GetSystemPowerStatus( LPSYSTEM_POWER_STATUS lpSystemPowerStatus );
此函數包含指向某個結構的指針,我們尚未對此進行過處理。要處理結構,我們需要用 C# 定義結構。我們從非托管的定義開始:
typedef struct _SYSTEM_POWER_STATUS { BYTE ACLineStatus; BYTE BatteryFlag; BYTE BatteryLifePercent; BYTE Reserved1; DWORD BatteryLifeTime; DWORD BatteryFullLifeTime; } SYSTEM_POWER_STATUS, *LPSYSTEM_POWER_STATUS;
然後,通過用 C# 類型代替 C 類型來得到 C# 版本。
struct SystemPowerStatus { byte ACLineStatus; byte batteryFlag; byte batteryLifePercent; byte reserved1; int batteryLifeTime; int batteryFullLifeTime; }
這樣,就可以方便地編寫出 C# 原型:
[DllImport("kernel32.dll")] public static extern bool GetSystemPowerStatus( ref SystemPowerStatus systemPowerStatus);
在此原型中,我們用 “ref” 指明將傳遞結構指針而不是結構值。這是處理通過指針傳遞的結構的一般方法。
此函數運行良好,但是最好將 ACLineStatus 和 batteryFlag 字段定義為 enum:
enum ACLineStatus: byte { Offline = 0, Online = 1, Unknown = 255, } enum BatteryFlag: byte { High = 1, Low = 2, Critical = 4, Charging = 8, NoSystemBattery = 128, Unknown = 255, }
請注意,由於結構的字段是一些字節,因此我們使用 byte 作為該 enum 的基本類型
示例四:處理字符串
二 C# 中調用C++代碼
int 類型
[DllImport(“MyDLL.dll")] //返回個int 類型 public static extern int mySum (int a1,int b1); //DLL中申明 extern “C” __declspec(dllexport) int WINAPI mySum(int a2,int b2) { //a2 b2不能改變a1 b1 //a2=.. //b2=... return a+b; } //參數傳遞int 類型 public static extern int mySum (ref int a1,ref int b1); //DLL中申明 extern “C” __declspec(dllexport) int WINAPI mySum(int *a2,int *b2) { //可以改變 a1, b1 *a2=... *b2=... return a+b; }
DLL 需傳入char *類型
[DllImport(“MyDLL.dll")] //傳入值 public static extern int mySum (string astr1,string bstr1); //DLL中申明 extern “C” __declspec(dllexport) int WINAPI mySum(char * astr2,char * bstr2) { //改變astr2 bstr 2 ,astr1 bstr1不會被改變 return a+b; }
DLL 需傳出char *類型
[DllImport(“MyDLL.dll")] // 傳出值 public static extern int mySum (StringBuilder abuf, StringBuilder bbuf ); //DLL中申明 extern “C” __declspec(dllexport) int WINAPI mySum(char * astr,char * bstr) { //傳出char * 改變astr bstr -->abuf, bbuf可以被改變 return a+b; }
DLL 回調函數
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)
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("Window handle is "); Console.WriteLine(hwnd); return true; } }
DLL 傳遞結構
BOOL PtInRect(const RECT *lprc, POINT pt);
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 XXXX { [DllImport("User32.dll")] public static extern bool PtInRect(ref Rect r, Point p); }