如何生成,调用和调试DLL工程
在使用其他语言调用C/C++程序,通常会使用DLL工程文件。本篇文章介绍的内容包括以下几点:
- 如何生成C/C++的DLL文件
- 如何在C#中调用DLL文件
- 如何调试DLL文件
如何生成C/C++的DLL文件
- 新建工程,选择“Visual C++”工程中的“Win32控制台程序”类型。工程设置中,将“应用程序类型”设置为“DLL”,“附加选项”设置为“空项目”。点击“完成”,则生产成了DLL工程文件。
- 这里以将C++类导出为DLL为例。类的头文件和源文件分别如下:
/**********************************************/
/*FileName:DllDemo.h */
/**********************************************/
#ifdef DllDemoAPI
#else
#define DllDemoAPI _declspec(dllimport)
#endif
class Point
{
public:
void Print(int *x, int *y, int xSize, int ySize);
};
extern "C"
{
DllDemoAPI Point *Point_Create();
DllDemoAPI void Point_Print(Point *pt, int *x, int *y, int xSize, int ySize);
DllDemoAPI void Point_Delete(Point *pt);
}
/**********************************************/
/*FileName:DllDemo.cpp */
/**********************************************/
#define DllDemoAPI _declspec(dllexport)
#include "DllTest.h"
#include <stdio.h>
extern "C"
{
DllDemoAPI Point* Point_Create()
{
return new Point();
}
DllDemoAPI void Point_Print(Point* pPoint, int *x, int *y, int xSize, int ySize)
{
return pPoint->Print(x,y,xSize,ySize);
}
DllDemoAPI void Point_Delete(Point* pPoint)
{
delete pPoint;
}
}
void Point::Print(int *x,int *y, int xSize, int ySize)
{
for (int i = 0; i < xSize; i++)
{
printf("x[%d]=%d\n",i,x[i]);
}
for (int i = 0; i < ySize; i++)
{
printf("y[%d]=%d\n",i,y[i]);
}
}
代码分析
- 代码中使用_declspec(dllimport)和_declspec(dllexport)进行DLL函数的导入和导出。
- extern “C”的作用是使用C语言的编译逻辑导出函数,即函数名称则为定义的名称。而不会向C++一样给函数名加上前缀和后缀。
- 正是因为使用C语言的编译逻辑导出函数,所以无法对类直接进行导出,因为C语言不支持类。当然如果生成的DLL是给C++使用的,则不需要extern “C”关键字,也可以对类等C++支持的结构进行导出。
- 这里默认的调用是cdecl,如果需要使用stdcall或者fastcall进行调用,则需要另外进行声明。
如何在C#中调用DLL文件
在C#中调用C++的DLL有多种方法,例如使用Marshal模块, 托管DLL, COM组件等。这里介绍的是使用最常见的P/Invoke方法。
在C#中调用上述工程的代码如下:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
class Program
{
[DllImport("DLLTest.dll", EntryPoint = "Point_Create", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Point_Create();
[DllImport("DLLTest.dll", EntryPoint = "Point_Print", CallingConvention = CallingConvention.Cdecl)]
public static extern void Point_Print(IntPtr value, int[] x, int[] y, int xSize, int ySize);
[DllImport("DLLTest.dll", EntryPoint = "Point_Delete", CallingConvention = CallingConvention.Cdecl)]
public static extern void Point_Delete(IntPtr value);
static void Main(string[] args)
{
IntPtr temp = Point_Create();
int[] x = new int[10];
int[] y = new int[10];
for (int i = 0; i < 10; i++)
{
x[i] = i;
y[i] = i;
}
int xSize = 10;
int ySize = 10;
Point_Print(temp, x, y, xSize, ySize);
Point_Delete(temp);
}
}
}
代码分析
- 这里使用DllImport函数对DLL进行导入。
- 注意要将之前生成的DLL拷贝到exe文件所在的文件夹下。
如何调试DLL文件
在生成DLL的过程中,调试是必不可少的一步。调试的方式是写一个调用DLL中函数的工程,然后将该工程的exe文件设置为DLL工程的可执行文件即可。具体方法为在工程的属性页中,找到调试选项卡下的命令一行,然后将exe文件的路径添加进去,即可进行调试。
常见问题
如何将多个类导出到一个DLL文件中
可以尝试使用托管DLL或COM组件的方法。
当类使用其他类中的功能时,如何生成DLL文件
在这种情况下,将调用到的其他类的文件添加到DLL生成的工程中,则生成的DLL就包含了调用类中的功能。无需进行其他特殊设置。
参考文献
1.http://www.cnblogs.com/bigwangdi/archive/2012/11/23/2784535.html
2. http://www.cppblog.com/suiaiguo/
3. http://blog.csdn.net/jiangxinyu/article/details/7848015
4. http://blog.csdn.net/biyusr/article/details/7301288
5. http://blog.csdn.net/kiki113/article/details/4971886
6. http://blog.csdn.net/hbqhdlc/article/details/6843650
7. http://www.swig.org/tutorial.html
8. http://stackoverflow.com/questions/315051/using-a-class-defined-in-a-c-dll-in-c-sharp-code
9. http://www.xinterop.com/index.php/category/net/
10. http://www.codeproject.com/Articles/6244/Step-by-Step-Calling-C-DLLs-from-VC-and-VB-Part
11. http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
12. http://www.codeproject.com/Questions/530172/Howplustoplusimportplusc-b-bplusclassplustoplusc
13. http://stackoverflow.com/questions/1170892/net-call-c-functions-from-c-sharp
14. https://msdn.microsoft.com/en-us/library/ms235281.aspx
15. http://blog.csdn.net/zhoubl668/article/details/3732238