這是三篇網(wǎng)上收集的技術(shù)文章的合集,分別講解了如何創(chuàng)建Win32 DLL,然后如何在C#里面調(diào)用這個DLL的教程。
首先是創(chuàng)建Win32 DLL的文章。講解這個的文章到處都有,這里給出一篇我看過的:http://www.flipcode.com/articles/article_creatingdlls.shtml。Win32 DLL的創(chuàng)建其實在Visual Studio里面已經(jīng)給出了比較好的模板,只是需要注意的,有些XXX_API宏并沒有把extern "C"加進去,這樣會造成在C#里面找不到這個函數(shù)的錯誤。所以,請記住,一定要把函數(shù)定義extern "C"。
第二篇文章來之于http://edu.100down.com/it/program/Csharp/105256797.html,下面是轉(zhuǎn)貼內(nèi)容:
平臺調(diào)用服務(wù) (PInvoke) 允許托管代碼調(diào)用在 DLL 中實現(xiàn)的非托管函數(shù)。
本教程說明使用什么方法才能從 C# 調(diào)用非托管 DLL 函數(shù)。該教程所討論的屬性允許您調(diào)用這些函數(shù)并使數(shù)據(jù)類型得到正確封送。
教程
C# 代碼有以下兩種可以直接調(diào)用非托管代碼的方法:
對于這兩種技術(shù),都必須向 C# 編譯器提供非托管函數(shù)的聲明,并且還可能需要向 C# 編譯器提供如何封送與非托管代碼之間傳遞的參數(shù)和返回值的說明。
該教程由下列主題組成:
直接從 C# 調(diào)用 DLL 導(dǎo)出
默認(rèn)封送處理和為非托管方法的參數(shù)指定自定義封送處理
為用戶定義的結(jié)構(gòu)指定自定義封送處理
注冊回調(diào)方法
該教程包括下列示例:
示例 1 使用 DllImport
示例 2 重寫默認(rèn)封送處理
示例 3 指定自定義封送處理
直接從 C# 調(diào)用 DLL 導(dǎo)出
若要聲明一個方法使其具有來自 DLL 導(dǎo)出的實現(xiàn),請執(zhí)行下列操作:
使用 C# 關(guān)鍵字 static 和 extern 聲明方法。
將 DllImport 屬性附加到該方法。DllImport 屬性允許您指定包含該方法的 DLL 的名稱。通常的做法是用與導(dǎo)出的方法相同的名稱命名 C# 方法,但也可以對 C# 方法使用不同的名稱。
還可以為方法的參數(shù)和返回值指定自定義封送處理信息,這將重寫 .NET Framework 的默認(rèn)封送處理。
示例 1
本示例顯示如何使用 DllImport 屬性通過調(diào)用 msvcrt.dll
中的 puts
輸出消息。
// PInvokeTest.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(string c);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();
public static void Main()
{
puts("Test");
_flushall();
}
}
輸出
Test
代碼討論
前面的示例顯示了聲明在非托管 DLL 中實現(xiàn)的 C# 方法的最低要求。PlatformInvokeTest.puts
方法用 static 和 extern 修飾符聲明并且具有 DllImport 屬性,該屬性使用默認(rèn)名稱 puts
通知編譯器此實現(xiàn)來自msvcrt.dll
。若要對 C# 方法使用不同的名稱(如 putstring
),則必須在 DllImport 屬性中使用 EntryPoint 選項,如下所示:
[DllImport("msvcrt.dll", EntryPoint="puts")]
有關(guān) DllImport 屬性的語法的更多信息,請參見 DllImportAttribute 類。
默認(rèn)封送處理和為非托管方法的參數(shù)指定自定義封送處理
當(dāng)從 C# 代碼中調(diào)用非托管函數(shù)時,公共語言運行庫必須封送參數(shù)和返回值。
對于每個 .NET Framework 類型均有一個默認(rèn)非托管類型,公共語言運行庫將使用此非托管類型在托管到非托管的函數(shù)調(diào)用中封送數(shù)據(jù)。例如,C# 字符串值的默認(rèn)封送處理是封送為 LPTSTR(指向 TCHAR 字符緩沖區(qū)的指針)類型??梢栽诜峭泄芎瘮?shù)的 C# 聲明中使用 MarshalAs 屬性重寫默認(rèn)封送處理。
示例 2
本示例使用 DllImport 屬性輸出一個字符串。它還顯示如何通過使用 MarshalAs 屬性重寫函數(shù)參數(shù)的默認(rèn)封送處理。
// Marshal.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(
[MarshalAs(UnmanagedType.LPStr)]
string m);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();
public static void Main()
{
puts("Hello World!");
_flushall();
}
}
輸出
運行此示例時,字符串
Hello World!
將顯示在控制臺上。
代碼討論
在前面的示例中,puts
函數(shù)的參數(shù)的默認(rèn)封送處理已從默認(rèn)值 LPTSTR 重寫為 LPSTR。
MarshalAs 屬性可以放置在方法參數(shù)、方法返回值以及結(jié)構(gòu)和類的字段上。若要設(shè)置方法返回值的封送處理,請將 MarshalAs 屬性與返回屬性位置重寫一起放置在方法上的屬性塊中。例如,若要顯式設(shè)置puts
方法返回值的封送處理:
...
[DllImport("msvcrt.dll")]
[return : MarshalAs(UnmanagedType.I4)]
public static extern int puts(
...
有關(guān) MarshalAs 屬性的語法的更多信息,請參見 MarshalAsAttribute 類。
注意 In 和 Out 屬性可用于批注非托管方法的參數(shù)。它們與 MIDL 源文件中的 in 和 out 修飾符的工作方式類似。請注意,Out 屬性與 C# 參數(shù)修飾符 out 不同。有關(guān) In 和 Out 屬性的更多信息,請參見 InAttribute 類和 OutAttribute 類。
為用戶定義的結(jié)構(gòu)指定自定義封送處理
可以為傳遞到非托管函數(shù)或從非托管函數(shù)返回的結(jié)構(gòu)和類的字段指定自定義封送處理屬性。通過向結(jié)構(gòu)或類的字段中添加 MarshalAs 屬性可以做到這一點。還必須使用 StructLayout 屬性設(shè)置結(jié)構(gòu)的布局,還可以控制字符串成員的默認(rèn)封送處理,并設(shè)置默認(rèn)封裝大小。
示例 3
本示例說明如何為結(jié)構(gòu)指定自定義封送處理屬性。
請考慮下面的 C 結(jié)構(gòu):
typedef struct tagLOGFONT
{
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;
在 C# 中,可以使用 StructLayout 和 MarshalAs 屬性描述前面的結(jié)構(gòu),如下所示:
// logfont.cs
// compile with: /target:module
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public class LOGFONT
{
public const int LF_FACESIZE = 32;
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public int lfWeight;
public byte lfItalic;
public byte lfUnderline;
public byte lfStrikeOut;
public byte lfCharSet;
public byte lfOutPrecision;
public byte lfClipPrecision;
public byte lfQuality;
public byte lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)]
public string lfFaceName;
}
有關(guān) StructLayout 屬性的語法的更多信息,請參見 StructLayoutAttribute 類。
然后即可將該結(jié)構(gòu)用在 C# 代碼中,如下所示:
// pinvoke.cs
// compile with: /addmodule:logfont.netmodule
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr CreateFontIndirect(
[In, MarshalAs(UnmanagedType.LPStruct)]
LOGFONT lplf // characteristics
);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(
IntPtr handle
);
public static void Main()
{
LOGFONT lf = new LOGFONT();
lf.lfHeight = 9;
lf.lfFaceName = "Arial";
IntPtr handle = CreateFontIndirect(lf);
if (IntPtr.Zero == handle)
{
Console.WriteLine("Can't creates a logical font.");
}
else
{
if (IntPtr.Size == 4)
Console.WriteLine("{0:X}", handle.ToInt32());
else
Console.WriteLine("{0:X}", handle.ToInt64());
// Delete the logical font created.
if (!DeleteObject(handle))
Console.WriteLine("Can't delete the logical font");
}
}
}
運行示例
C30A0AE5
代碼討論
在前面的示例中,CreateFontIndirect
方法使用了一個 LOGFONT 類型的參數(shù)。MarshalAs 和 In 屬性用于限定此參數(shù)。程序?qū)⒂纱朔椒ǚ祷氐臄?shù)值顯示為十六進制大寫字符串。
注冊回調(diào)方法
若要注冊調(diào)用非托管函數(shù)的托管回調(diào),請用相同的參數(shù)列表聲明一個委托并通過 PInvoke 傳遞它的一個實例。在非托管端,它將顯示為一個函數(shù)指針。有關(guān) PInvoke 和回調(diào)的更多信息,請參見平臺調(diào)用詳解。
例如,考慮以下非托管函數(shù) MyFunction
,此函數(shù)要求 callback 作為其參數(shù)之一:
typedef void (__stdcall *PFN_MYCALLBACK)();
int __stdcall MyFunction(PFN_ MYCALLBACK callback);
若要從托管代碼調(diào)用 MyFunction
,請聲明該委托,將 DllImport 附加到函數(shù)聲明,并根據(jù)需要封送任何參數(shù)或返回值:
public delegate void MyCallback();
[DllImport("MYDLL.DLL")]
public static extern void MyFunction(MyCallback callback);
同時,請確保委托實例的生存期覆蓋非托管代碼的生存期;否則,委托在經(jīng)過垃圾回收后將不再可用。
第三篇文章來之于http://www.njpro.cn/8918/ShowPost.aspx,一篇關(guān)于C/C++和C#中的數(shù)據(jù)類型的對應(yīng)表。
Wtypes.h 中的非托管類型 | 非托管 C 語言類型 | 托管類名 | 說明 |
---|
HANDLE | void* | System.IntPtr | 32 位 |
BYTE | unsigned char | System.Byte | 8 位 |
SHORT | short | System.Int16 | 16 位 |
WORD | unsigned short | System.UInt16 | 16 位 |
INT | int | System.Int32 | 32 位 |
UINT | unsigned int | System.UInt32 | 32 位 |
LONG | long | System.Int32 | 32 位 |
BOOL | long | System.Int32 | 32 位 |
DWORD | unsigned long | System.UInt32 | 32 位 |
ULONG | unsigned long | System.UInt32 | 32 位 |
CHAR | char | System.Char | 用 ANSI 修飾。 |
LPSTR | char* | System.String 或System.StringBuilder | 用 ANSI 修飾。 |
LPCSTR | Const char* | System.String 或System.StringBuilder | 用 ANSI 修飾。 |
LPWSTR | wchar_t* | System.String 或System.StringBuilder | 用 Unicode 修飾。 |
LPCWSTR | Const wchar_t* | System.String 或System.StringBuilder | 用 Unicode 修飾。 |
FLOAT | Float | System.Single | 32 位 |
DOUBLE | Double | System.Double | 64 位 |