与很多.net attributes一样,DllAttribute 定义了一些公有的属性来允许开发人员去控制它的行为。与大部分的.net attributes一样,这些属性可以通过它的构造函数参数来进行设置。首先,我们来看一下类型的定义:
public sealed class DllImportAttribute : Attribute
{
// Fields (first two listings are not typos!)
// These fields are used to control exactly
// how the attribute should be applied to the
// unmanaged function export.
public CallingConvention CallingConvention;
public CharSet CharSet;
public string EntryPoint;
public bool ExactSpelling;
public bool PreserveSig;
public bool SetLastError;
// Constructor (string param used to set fields
// as name / value pairs).
public DllImportAttribute(string dllName);
// Properties.
public object TypeId { virtual get; }
public string Value { get; }
Understanding Platform Invocation Services
// Methods (basic .NET infrastructure stuff).
public virtual bool Equals(object obj);
public virtual int GetHashCode();
public Type GetType();
public virtual bool IsDefaultAttribute();
public virtual bool Match(object obj);
public virtual string ToString();
}
在此我们可以看到,DllImportAttribute定义了两个字段,CallingConvention and CharSet,可以使用两个Enum来进行赋值。
// 指定非托管的函数的调用方式
public enum CallingConvention
{
Cdecl,
FastCall, // 在 .NET version 1.0.*.中不被支持
StdCall,
ThisCall,
Winapi
}
// 定义传递到非托管函数所使用的字符集
public enum CharSet
{
Ansi,
Auto,
None,
Unicode
}
下面是一个简单的PInvoke的示例:
namespace SimpleAPIInvoke
{
using System;
// Must reference to gain access to the PInvoke types.
using System.Runtime.InteropServices;
public class PInvokeClient
{
// The Win32 MessageBox() function lives in user32.dll.
[DllImport("user32.dll")]
public static extern int MessageBox(int hWnd, String pText,
String pCaption, int uType);
public static int Main(string[] args)
{
// Send in some managed data.
String pText = "Hello World!";
String pCaption = "PInvoke Test";
MessageBox(0, pText, pCaption, 0);
return 0;
}
}
}
在上面的代码中,我们使用:
[DllImport("user32.dll")]
public static extern int MessageBox(…);
来调用了Win32的MessageBox函数。在DllImportAttribute中,还定义了一些公有的字段,允许我们在函数导出的过程中对它进行配置。下表给出了它的字段和对应的含义:
DllImportAttribute 字段 |
含义 |
CallingConvention |
用于建立函数的调用约定,默认为CallingConvention.WinAPI,相当于__stdcall |
CharSet |
指定字符串参数如何传递,默认值为CharSet.Ansi |
EntryPoint |
指定被调用函数的名字或是函数的序号 |
ExactSpelling |
PInvoke会尝试匹配函数的真实原型名称 |
PreserveSig |
默认值为true,设置为true后,非托管的方法签名将不会被转换为托管的方法去返回HRESULT和一个附加的[out, retval]参数用来做返回值 |
SetLastError |
如果指定为true,标识调用者使用Marshal.GetLastWin32Error()去判定在执行方法时是否有错误发生,在C#中默认为false,在VB.Net中默认为true |
下面我们来看一下其中的几个字段的用法:
[DllImport("user32.dll", ExactSpelling = true)]
public static extern int MessageBox(…); // ERROR!
将ExactSpelling = true标记后上面的代码就会出错,因为在user32.dll中并没有MessageBox这个函数,只有MessageBoxA(ANSI版本)和MessageBoxW(Unicode版本),使用了ExactSpelling = true后将会对方法的签名进行严格的匹配,不符合的话将会有EntryPointNotFoundException产生。
[DllImport("user32.dll", ExactSpelling = true, CharSet=CharSet.Unicode)]
public static extern int MessageBoxW(…);
CharSet比较容易理解,也比较简单,在此不做过多的介绍。下面讲一下Calling Conventions
CallingConvention Enumeration value |
含义 |
Cdecl |
调用者清理堆栈,它允许调用函数使用可变参数 |
FastCall |
此项在目前的.Net Framework中还没有被支持 |
StdCall |
被调用者清理堆栈,它是默认的从托管代码中调用非托管函数的转换 |
ThisCall |
第一个参数为“this”指针,并将其存储在ECX中,其它参数顺序入栈。这个调用转换被用于调用非托管的类中的方法。 |
Winapi |
平台的默认转换,在Windows中使用StdCall,在Windows CE中使用Cdecl |
如果读者对其中的说明仍有不理解,可以去读一些Windows程序设计的资料进行了解。下面介绍指定函数入口点
// 映射MessageBoxW() function 为 'DisplayMessage'.
[DllImport("user32.dll", ExactSpelling = true, CharSet=CharSet.Unicode, EntryPoint = "MessageBoxW")]
public static extern int DisplayMessage(int hWnd, String pText, String pCaption, int uType);
通过在EntryPoint中指定函数的入口点名称,然后在下面的函数声明就可以使用不同的函数名来使用对应的非托管函数了。此处使用DisplayMessage来使用MessageBoxW函数。
(部分内容参考<<Com And Dot Net Interoperability>>)
转载于:https://www.cnblogs.com/JonathanWang/archive/2008/07/15/1243682.html