这两天在学习C#调用c++ 编写的DLL,折腾了两天今天终于搞定了。
先来阐述一下DLL(DynamicLinkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
对动态链接库,我们还需建立如下概念:
(1)DLL
的编制与具体的编程语言及编译器无关
只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是c#、Visual
C++还是Delphi。
(2)动态链接库随处可见
windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。
(3)VC动态链接库的分类
C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC
Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。
此次主要是学习C#调用 非MFC的 DLL,总体的思路大致如此:
DLL中声明导出的函数(或变量或类),应用工程以某种特定的方式调用DLL的函数(或变量或类)。
对函数(或者变量或者类)的声明前面添加了__declspec(dllexport)语句。这个语句的含义是声明函数(或者变量或者类)为DLL的导出函数(或者变量或者类)。DLL内的函数分为两种:
(1)DLL导出函数,可供应用程序调用;
(2) DLL内部函数,只能在DLL程序使用,应用程序无法调用它们。
下面是声明一个导出类跟三个导出函数,函数调用类的变量跟函数,应用工程调用导出函数。
1:新建一个C++ dll项目,建立的时候选择Win32 Console(dll),选择Dll:
新建了一个point类:
头文件 point.h
#ifndef POINT_H #define POINT_H #ifdef DLL_FILE class __declspec(dllexport) point #else class __declspec(dllimport) point #endif { public: point(int x,int y); int x; int y; int GetX(); int GetY(); int add(int a,int b); }; #endif
cpp文件point.cpp
#ifndef DLL_FILE #define DLL_FILE #endif #include "StdAfx.h" #include "point.h" extern "C" __declspec(dllexport)point::point(int m_x,int m_y) { x = m_x; y = m_y; } extern "C" __declspec(dllexport) int point::GetX() { return x; } extern "C" __declspec(dllexport) int point::GetY() { return y; } extern "C" __declspec(dllexport) int point::add(int a,int b) { return a+b; } point GetPoint() { point p = point(2,5); return p; } extern "C" __declspec(dllexport) int Add(int a,int b) { point p = point(0,0); int sum = p.add(a,b); return sum; } extern "C" __declspec(dllexport)int GetX() { point p = GetPoint(); return p.GetX(); } extern "C" __declspec(dllexport)int GetY() { point p = GetPoint(); return p.GetY(); }
2. 编译工程。将生成的Dll拷贝留着下面工程用。
3.新建C# winform工程,在FrmMain.cs中添加引用:using System.Runtime.InteropServices;
4.做一个简单的winform界面:
5.添加相关代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Threading; namespace FrmDLLDemo { public partial class FrmMain : Form { [DllImport("MyDllDemo.dll", EntryPoint = "Add", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] public static extern int Add(int a, int b); [DllImport("MyDllDemo.dll", EntryPoint = "GetX", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] public static extern int GetX(); [DllImport("MyDllDemo.dll", EntryPoint = "GetY", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] public static extern int GetY(); //public static extern int add(int a, int b); public FrmMain() { InitializeComponent(); } private void btnAdd_Click(object sender, EventArgs e) { int a = Convert.ToInt32(txtA.Text); int b = Convert.ToInt32(txtB.Text); int sum = 0; sum = Add(a, b); txtSum.Text = sum.ToString(); } private void btnGetX_Click(object sender, EventArgs e) { txtA.Text = GetX().ToString(); } private void btnGetY_Click(object sender, EventArgs e) { txtB.Text = GetY().ToString(); } } }
生成,运行,结果:
C#里面调用DLL的函数思路大致是:
使用 [DllImport("MyDllDemo.dll", EntryPoint = "Add", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
加载引用的DLL,同时指定入口函数接着在当前页面声明DLL内同名的静态函数:
public static extern int Add(int a, int b);
最后在函数中调用DLL的函数。