C++中的函数声明
?
1
    
extern "C" __declspec(dllexport) int __stdcall testfunc(char* astr,int* a);
  
extern ”C”
  通常来说,C++编译器可能会改变函数和变量的名字,从而导致严重的链接程序问题。例如,假设使用C++编写一个DLL,当创建DLL时,Microsoft的编译器就会改变函数的名字。函数名将被设置一个前导下划线,再加上一个@符号的前缀,后随一个数字,表示作为参数传递给函数的字节数。例如,下面的函数是作为DLL的输出节中的_MyFunc@8输出的:
?
1
    
__declspec(dllexport) LONG __stdcall MyFunc(int a, int b);
  如果用另一个供应商的工具创建了一个可执行模块,它将设法链接到一个名叫MyFunc的函数,该函数在Microsoft编译器已有的DLL中并不存在,因此链接将失败。
  使用extern “C”关键字可以使编译器按照C语言的方式编译DLL文件,即编译时不改变函数名。
 
__declspec(dllexport)
  在 32 位编译器版本中,可以使用__declspec(dllexport) 关键字从DLL导出数据、函数、类或类成员函数。__declspec(dllexport) 会将导出指令添加到对象文件中,因此不需要使用.def文件。
  若要导出函数,__declspec(dllexport) 关键字必须出现在调用约定关键字的左边(如果指定了关键字)。例如:
?
1
    
__declspec(dllexport) void __cdecl Function1(void);
 
__stdcall
  表明被调用方清理堆栈。
 
C#中的函数声明
?
1
2
3
4
5
6
7
8
    
using System.Runtime.InteropServices;
      …
 
public class Program
{
[DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
public static extern int testfunc(StringBuilder abuf,ref int a);
}
 
using System.Runtime.InteropServices;
  System.Runtime.InteropServices 命名空间提供各种各样支持 COM interop 及平台调用服务的成员,使程序可以与非托管代码进行交互操作。
 
[DllImport(“dllfile path”)]
  代码中DllImport关键字作用是告诉编译器入口点在哪里,并将打包函数捆绑在这个类中。在声明的时候还可以添加几个属性:
?
1
2
3
4
    
[DllImport("MyDLL.dll",
EntryPoint="mySum",
CharSet=CharSet.Auto,
CallingConvention=CallingConvention.StdCall)]
  EntryPoint: 指定要调用的 DLL 入口点。默认入口点名称是托管方法的名称 。
  CharSet: 控制名称重整和封送 String 参数的方式 (默认是UNICODE)
  CallingConvention指示入口点的函数调用约定(默认WINAPI)
  注意:必须在标记为”static”和”extern”的方法上指定”DllImport”属性。
 
数据传递方法
1.基本数据类型的传递
  函数参数和返回值可以是C#和C++的各种基本数据类型,如int, float, double, char(注意不是char*)等。
  示例:
  C#代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    
using System;
using System.Text;
using System.Runtime.InteropServices;
 
class Program
{
    [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    public static extern int testfunc(int a,float b,double c,char d);
 
    static void Main(string[] args)
    {
        int a = 1;
        float b = 12;
        double c = 12.34;
        char d = 'A';
        testfunc(a,b,c,d);
        Console.ReadKey();
    }
}
  C++代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
    
<pre class="brush:cpp">#include <iostream>
using namespace std;
 
extern "C"
{
 _declspec(dllexport) int __stdcall testfunc(int a,float b,double c,char d)
 {
  cout<<a<<", "<<b<<", "<<c<<", "<<d<<endl;
  return 0;
 }
}
</pre>
2.向DLL传入字符串
  C#中使用string定义字符串,将字符串对象名传给DLL。
  注意:在DLL中更改字符串的值,C#中的值也会改变。
  缺点:无法改变字符串的长度,建议使用第3种方法。
  C#代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    
using System;
using System.Text;
using System.Runtime.InteropServices;
 
class Program
{
    [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    public static extern int testfunc(string a);
 
    static void Main(string[] args)
    {
        string a="Hello World!";
        testfunc(a);
        Console.ReadKey();
    }
}
  C++代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
    
#include <iostream>
using namespace std;
 
extern "C"
{
 _declspec(dllexport) int __stdcall testfunc(char* astr)
 {
  cout<<astr<<endl;
  *astr='A';//更改字符串的数据
  cout<<astr<<endl;
  return 0;
 }
}
3.DLL传出字符串
  C#中使用StringBuilder对象创建变长数组,并设置StringBuilder的Capacity为数组最大长度。将此对象名传递给DLL,使用char*接收。
  C#代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    
using System;
using System.Text;
using System.Runtime.InteropServices;
 
class Program
{
    [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    public static extern int testfunc(StringBuilder abuf);
 
    static void Main(string[] args)
    {
        StringBuilder abuf=new StringBuilder();
        abuf.Capacity = 100;//设置字符串最大长度
        testfunc(abuf);
        Console.ReadKey();
    }
     
}
  C++代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    
#include <iostream>
using namespace std;
 
extern "C"
{
 _declspec(dllexport) int __stdcall testfunc(char* astr)
 {
  *astr++='a';
  *astr++='b';//C#中abuf随astr改变
  *astr='\0';
 
  return 0;
 }
}
4.DLL传递结构体(需要在C#中重新定义,不推荐使用)
  C#中使用StructLayout重新定义需要使用的结构体。
  注意:在DLL改变结构体成员的值,C#中随之改变。
  C#代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    
using System;
using System.Text;
using System.Runtime.InteropServices;
 
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
    public double x;
    public double y;
}
 
class Program
{
    [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    public static extern int testfunc(Point p);
 
    static void Main(string[] args)
    {
        Point p;
        p.x = 12.34;
        p.y = 43.21;
        testfunc(p);
        Console.ReadKey();
    }   
}
C++代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    
#include <iostream>
using namespace std;
 
struct Point
{
    double x;
    double y;
};
 
extern "C"
{
 _declspec(dllexport) int __stdcall testfunc(Point p)
 {
  cout<<p.x<<", "<<p.y<<endl;
  return 0;
 }
}