C# dllimport

一、DllImport是System.Runtime.InteropServices命名空间下的一个属性类,其功能是提供从非托管DLL导出的函数的必要调用信息

DllImport的定义如下:

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 {…} }

}

用法:

[DLLImport(“DLL文件名称”)]

修饰符   extern    返回值类型   方法名称(参数列表) ;

用法示例:

[DllImport("kernel32.dll")]

private static extern longWritePrivateProfileString(string section,string key,string val,stringfilePath);

DllImport会按照以下顺序自动去寻找:

1、exe所在目录

2、System32目录

3、环境变量目录

所以只需要你把引用的DLL 拷贝到这三个目录下,就可以不用写路径了。

 

C#在调用非托管的dll时,作为最低要求,必须提供包含入口点的 DLL 的名称,如下。

[DllImport("halcon_c32.dll")]

参数详解:       

1、DllImport只能放置在方法声明上。

2、DllImport具有单个定位参数:指定包含被导入方法的 dll 名称的 dllName 参数。

3、DllImport具有五个命名参数: 

(a)CallingConvention 参数指示入口点的调用约定。这是指定调用在非托管代码中实现的方法所需的调用约定。

如果未指定 CallingConvention,则使用默认值 CallingConvention.Winapi。即CallingConvention.StdCall。

调用约定需要设置为与dll函数所在文件的调用约定一致。如dll中函数所在的c++文件的调用约定为cdecl,如下图所示:则在C#中需设置CallingConvention =CallingConvention.Cdecl.

C# dllimport_第1张图片

否则会报如下错误:

C# dllimport_第2张图片

CallingConvention有五种方式:

CallingConvention =CallingConvention.StdCall

CallingConvention = CallingConvention.Cdecl

CallingConvention =CallingConvention.FastCall

CallingConvention =CallingConvention.ThisCall

CallingConvention =CallingConvention.Winapi

默认情况下,C和C++使用的Cdecl调用,但编组使用StdCall调用匹配的WindowsAPI。对于FastCall、ThisCall、Winapi这三种调用方式尚不清楚。

调用约定(Calling   convention):决定函数参数传送时入栈和出栈的顺序,由调用者还是被调用者把参数弹出栈,以及编译器用来识别函数名字的修饰约定 .

函数调用约定有多种,这里简单说一下:   

  1)__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++5.0中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal外,__fortran和__syscall也不被支持),取而代之的是__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。    _stdcall是Pascal程序的缺省调用方式,通常用于Win32   Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。

  2)C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。  _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。

 3)__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。_fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。

 4)thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

 5)naked   call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked   call不产生这样的代码。naked   call不是类型修饰符,故必须和_declspec共同使用。

  关键字   __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting.../C/C++   /Code   Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、 /Gd和/Gr。缺省状态为/Gd,即__cdecl。

      要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI 宏,

Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。

总结一点常用的:     

关于PASCAL这种调用约定的函数都是由它本身来清栈,而__cdecl的函数都是由调用者来清栈. 实际用的时候,个人觉得两者最大的差别在于:__cdecl的函数参数个数可以声明为不确定,比如printf,scanf之类,而PASCAL的函数是不可以这样做的,如果这样的话它不知道实参有多少个。     

VC里面:PASCAL==CALLBACK==WINAPI==__stdcall

默认情况下,C和C++使用的Cdecl调用,但编组使用StdCall调用匹配的WindowsAPI

调用WinAPI的使用stdcall,调用C++/C的使用cdelcall

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 修饰符。


你可能感兴趣的:(C#)