若要声明一个方法使其具有来自 DLL 导出的实现,请执行下列操作: 本示例显示如何使用 DllImport 属性通过调用 前面的示例显示了声明在非托管 DLL 中实现的 C# 方法的最低要求。 有关 DllImport 属性的语法的更多信息,请参见 DllImportAttribute 类。 当从 C# 代码中调用非托管函数时,公共语言运行库必须封送参数和返回值。 对于每个 .NET Framework 类型均有一个默认非托管类型,公共语言运行库将使用此非托管类型在托管到非托管的函数调用中封送数据。例如,C# 字符串值的默认封送处理是封送为 LPTSTR(指向 TCHAR 字符缓冲区的指针)类型。可以在非托管函数的 C# 声明中使用 MarshalAs 属性重写默认封送处理。 本示例使用 DllImport 属性输出一个字符串。它还显示如何通过使用 MarshalAs 属性重写函数参数的默认封送处理。 运行此示例时,字符串 将显示在控制台上。 在前面的示例中, MarshalAs 属性可以放置在方法参数、方法返回值以及结构和类的字段上。若要设置方法返回值的封送处理,请将 MarshalAs 属性与返回属性位置重写一起放置在方法上的属性块中。例如,若要显式设置 有关 MarshalAs 属性的语法的更多信息,请参见 MarshalAsAttribute 类。 可以为传递到非托管函数或从非托管函数返回的结构和类的字段指定自定义封送处理属性。通过向结构或类的字段中添加 MarshalAs 属性可以做到这一点。还必须使用 StructLayout 属性设置结构的布局,还可以控制字符串成员的默认封送处理,并设置默认封装大小。 本示例说明如何为结构指定自定义封送处理属性。 请考虑下面的 C 结构: 在 C# 中,可以使用 StructLayout 和 MarshalAs 属性描述前面的结构,如下所示: 有关 StructLayout 属性的语法的更多信息,请参见 StructLayoutAttribute 类。 然后即可将该结构用在 C# 代码中,如下所示: 在前面的示例中, 若要注册调用非托管函数的托管回调,请用相同的参数列表声明一个委托并通过 PInvoke 传递它的一个实例。在非托管端,它将显示为一个函数指针。有关 PInvoke 和回调的更多信息,请参见平台调用详解。 例如,考虑以下非托管函数 若要从托管代码调用 同时,请确保委托实例的生存期覆盖非托管代码的生存期;否则,委托在经过垃圾回收后将不再可用。 一篇关于C/C++和C#中的数据类型的对应表。直接从 C# 调用 DLL 导出
示例 1
msvcrt.dll
中的 puts
输出消息。// PInvokeTest.csusing 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
代码讨论
PlatformInvokeTest.puts
方法用static 和 extern 修饰符声明并且具有 DllImport 属性,该属性使用默认名称 puts
通知编译器此实现来自msvcrt.dll
。若要对 C# 方法使用不同的名称(如 putstring
),则必须在 DllImport 属性中使用EntryPoint 选项,如下所示:[DllImport("msvcrt.dll", EntryPoint="puts")]
默认封送处理和为非托管方法的参数指定自定义封送处理
示例 2
// Marshal.csusing 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
函数的参数的默认封送处理已从默认值 LPTSTR 重写为 LPSTR。puts
方法返回值的封送处理:...[DllImport("msvcrt.dll")] [return : MarshalAs(UnmanagedType.I4)]public static extern int puts( ...
注意 In 和 Out 属性可用于批注非托管方法的参数。它们与 MIDL 源文件中的 in 和 out 修饰符的工作方式类似。请注意,Out 属性与 C# 参数修饰符 out 不同。有关 In 和 Out 属性的更多信息,请参见 InAttribute 类和 OutAttribute 类。
为用户定义的结构指定自定义封送处理
示例 3
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;
// logfont.cs// compile with: /target:moduleusing 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; }
// pinvoke.cs// compile with: /addmodule:logfont.netmoduleusing 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 类型的参数。MarshalAs 和 In 属性用于限定此参数。程序将由此方法返回的数值显示为十六进制大写字符串。注册回调方法
MyFunction
,此函数要求 callback 作为其参数之一:typedef void (__stdcall *PFN_MYCALLBACK)();int __stdcall MyFunction(PFN_ MYCALLBACK callback);
MyFunction
,请声明该委托,将 DllImport 附加到函数声明,并根据需要封送任何参数或返回值:public delegate void MyCallback();[DllImport("MYDLL.DLL")]public static extern void MyFunction(MyCallback callback);
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