DllImport用法


要使用DllImport需要引用命名空間: System.Runtime.InteropServices;
DllImport 屬性定義
如下:
namespace System.Runtime.InteropServices
{
  [AttributeUsage(AttributeTargets.Method)]
  public class DllImportAttribute: System.Attribute
  {
public DllImportAttribute(string dllName){...} //定位參數為dllName
public CallingConvention CallingConvention; //入口點調用約定
public CharSet CharSet; //入口點采用的字符接
public string EntryPoint; //入口點名稱
public bool ExactSpelling; //是否必須與指示的入口點拼寫完全一致,默認false
public bool PreserveSig; //方法的簽名是被保留還是被轉換
public bool SetLastError; //FindLastError方法的返回值保存在這里
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的用法示例(是用來寫入ini文件的一個win32api):
DllImport("kernel32")
private static extern long WritePrivateProfileString(string section,string key,string val,string filePath);
用此方法調用WinAPI的數據類型對應:DWORD=int或uint,BOOL=bool,預定義常量=enum,結構=struct。
DllImport路徑問題:
DllImport會按照順序自動去尋找的地方:
1、exe所在目錄
2、System32目錄
3、環境變量目錄
所以只需要你把引用的DLL 拷貝到這三個目錄下 就可以不用寫路徑了。
web中的,同時也是應用程序中的
后來發現用[DllImport(@"C:\OJ\Bin\Judge.dll")]這樣指定DLL的絕對路徑就可以正常裝載。
這個問題最常出現在使用第三方非托管DLL組件的時候,我的也同樣是這時出的問題,Asp.Net Team的官方解決方案如下:
首先需要確認你引用了哪些組件,那些是托管的,哪些是非托管的.托管的很好辦,直接被使用的需要引用,間接使用的需要拷貝到bin目錄下.非托管的處理會比較麻煩。實際上,你拷貝到bin沒有任何幫助,因為CLR會把文件拷貝到一個臨時目錄下,然后在那運行web,而CLR只會拷貝托管文件,這就是為什么我們明明把非托管的dll放在了bin下卻依然提示不能加載模塊了。
具體做法如下:
首先我們在服務器上隨便找個地方新建一個目錄,假如為C:\DLL;
然后,在環境變量中,給Path變量添加這個目錄;
最后,把所有的非托管文件都拷貝到C:\DLL中,或者更干脆的把DLL放到system32目錄。
對於可以自己部署的應用程序,這樣未償不是一個解決辦法,然而,如果我們用的是虛擬空間,我們是沒辦法把注冊PATH變量或者把我們自己的DLL拷到system32目錄的。同時我們也不一定知道我們的Dll的物理路徑。
DllImport里面只能用字符串常量,而不能夠用Server.MapPath(@"~/Bin/Judge.dll")來確定物理路徑。
DllImport加載速度慢的問題:
不過,我發現,調用這種"非托管Dll”相當的慢,可能是因為我的方法需要遠程驗證吧,但是實在是太慢了。經過一翻研究,終於想到了一個完美的解決辦法。
首先我們用
[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(String path);
[DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
[DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib);
分別取得了LoadLibrary和GetProcAddress函數的地址,再通過這兩個函數來取得我們的DLL里面的函數。
我們可以先用Server.MapPath(@"~/Bin/Judge.dll")來取得我們的DLL的物理路徑,然后再用LoadLibrary進行載入,最后用GetProcAddress取得要用的函數地址。
以下自定義類的代碼完成LoadLibrary的裝載和函數調用:
public class DllInvoke
{

[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(String path);
[DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
[DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib);
private IntPtr hLib;
public DllInvoke(String DLLPath)
{
hLib = LoadLibrary(DLLPath);
}
~DllInvoke()
{
FreeLibrary(hLib);
}
//將要執行的函數轉換為委托
public Delegate Invoke(String APIName,Type t)
{
IntPtr api = GetProcAddress(hLib, APIName);
return (Delegate)Marshal.GetDelegateForFunctionPointer(api,t);
}
}
下面代碼進行調用
public delegate int Compile(String command, StringBuilder inf);//編譯
DllInvoke dll = new DllInvoke(Server.MapPath(@"~/Bin/Judge.dll"));
Compile compile = (Compile)dll.Invoke("Compile", typeof(Compile));
StringBuilder inf;
compile(@“gcc a.c -o a.exe“,inf); //這里就是調用我的DLL里定義的Compile函數

DllImport用法示例:
一 在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 boolMessageBeep(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")]
public static extern int mySum (int a1,int b1); //返回個int 類型
extern “C” __declspec(dllexport) int WINAPI mySum(int a2,int b2) //DLL中申明
{
//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)
{
//改變astr2bstr 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)

usingSystem;
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);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM