轻松的学习,简单的记录

导航

关于DllImportAttribute 类型【转】

很多初学.net的程序员,也包括一些有相当经验的.net开发人员,对于在.net 中去使用Win32 API,仍不是很了解。在此分享一下我对DllImportAttribute 的认识。
  与很多.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(…);

来调用了Win32MessageBox函数。在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函数。
摘自msdn:

DllImportAttribute 属性提供对从非托管 DLL 导出的函数进行调用所必需的信息。作为最低要求,必须提供包含入口点的 DLL 的名称。

可直接将此属性应用于 C# 或 C++ 方法定义;但在使用 Declare 语句时,Visual Basic 编译器会忽略此属性。对于包含 BestFitMappingCallingConventionExactSpellingPreserveSigSetLastErrorThrowOnUnmappableChar 字段的复杂方法定义,可直接将此属性应用于 Visual Basic 方法定义。

注意    JScript .NET 不支持此属性。可以使用 C# 或 Visual Basic 包装类从 JScript .NET 程序访问非托管 API 方法。

有关使用平台调用服务访问非托管 DLL 中的函数的其他信息,请参见使用非托管 DLL 函数

示例

[Visual Basic, C#, C++] 下面的示例说明如何将 DllImportAttribute 应用于方法。

[Visual Basic] 
<DllImport("KERNEL32.DLL", EntryPoint := "MoveFileW", _
   SetLastError := True, CharSet := CharSet.Unicode, _
   ExactSpelling := True, _
   CallingConvention := CallingConvention.StdCall)> _
Public Shared Function MoveFile(src As String, dst As String) As Boolean
    ' Leave function empty - DLLImport attribute forwards calls to MoveFile to
    ' MoveFileW in KERNEL32.DLL.
End Function

[C#] 
[DllImport("KERNEL32.DLL", EntryPoint="MoveFileW",  SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
public static extern bool MoveFile(String src, String dst);

[C++] 
public:
 [DllImport(S"KERNEL32.DLL", EntryPoint=S"MoveFileW",  SetLastError=true,
 CharSet=CharSet::Unicode, ExactSpelling=true,
 CallingConvention=CallingConvention::StdCall)]
 static bool MoveFile(String* src, String* dst);

[JScript] 没有可用于 JScript 的示例。若要查看 Visual Basic、C# 或 C++ 示例,请单击页左上角的“语言筛选器”按钮 语言筛选器

要求

命名空间: System.Runtime.InteropServices

平台: Windows 98, Windows NT 4.0, Windows ME, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003 系列, .NET Framework 精简版

程序集: Mscorlib (在 Mscorlib.dll 中)


补充:DllImport是DllImportAttribute的缩写。
转自:http://www.cnblogs.com/JonathanWang/archive/2008/07/15/1243682.html

posted on 2009-10-09 15:58  菜鸟的宣言  阅读(1017)  评论(1)    收藏  举报