文档章节

C++下的DLL编程入门

雷霄骅
 雷霄骅
发布于 2014/08/16 13:49
字数 1429
阅读 130
收藏 0

 

 

一、   编写第一个入门级dll文件

 

1. 新建一个dlltest的dll工程,加入一源文件dlltest.cpp,包含add和subtract两个函数如下:

 

_declspec(dllexport) int add(int a,int b)
{
    return a+b;
}

_declspec(dllexport) int subtract(int a,int b)
{
    return a-b;
}


注意:

在函数名前加上_declspec(dllexport)这样编译后在连接的时候才会生成dlltest.lib(引入库文件)和dlltest.exp(输出库文件),并且在命令提示符下用dumpbin命令可以看到导出了哪些函数。

方法:在命令提示符下,转到dll文件所在目录下,输入dumpbin –exports dlltest.dll,列出导出函数如下:

这里注意到函数名字已经被改成了?add@@YAHHH@Z ,这种现象叫做名字粉碎,是为了支持函数重载而做的。

 

2.编写一个基于对话框的MFC程序测试DLL,工程名为calldll,放置两个按纽add和subtract,响应按纽消息,调用这个DLL的add和subtract函数。

先添加响应按钮消息的函数OnAdd和OnSubtract,然后在函数所在源文件中写完整函数体如下:

//extern int add(int,int);
//extern int subtract(int,int);
_declspec(dllimport) int add(int,int);
_declspec(dllimport) int subtract(int,int);

void CCalldllDlg::OnAdd() 
{
    // TODO: Add your control notification handler code here
    CString str;
    str.Format("2+3=%d",add(2,3));
    MessageBox(str);
}

void CCalldllDlg::OnSubtract() 
{
    // TODO: Add your control notification handler code here
    CString str;
    str.Format("3-2=%d",subtract(3,2));
    MessageBox(str);
}




 

这里采用隐式链接的方式加载动态链接库:

注意:要用关键字extern先声明这两个函数,表明这两个函数是在外部定义的(不过程序中将它注释掉了)。最好是用_declspec(dllimport)表明函数是从动态链接库的lib文件中引入的,这样效率更高。

 

将文件dlltest.lib拷贝到此工程目录下,并在Project Settings的Link标签下添加此文件:(否则编译会成功,但连接时会出错提示找不到函数的定义)

 

编译连接生成calldll.exe,这时用Dumpbin -importscalldll.exe查看它的输入信息,可以看到它加载了dlltest.dll

 

运行calldll.exe(要成功运行还需将dlltest.dll拷贝到工程目录或此exe所在目录下)

 

这样,最简单的第一个dll就完成了。

 

 

二、   编写涉及到类和头文件的dll文件

 

1.  新建一个dlltest的dll工程,加入一头文件dlltest.h和一源文件dlltest.cpp。

dlltest.h:

#ifdef DLL_API
#else
#define DLL_API _declspec(dllimport)
#endif

DLL_API int add(int,int);
DLL_API int subtract(int,int);

class DLL_API Point1               //将整个类导出
{
public:
    void output(int x,int y);
    void output1(int x,int y);
};

class Point2
{
public:
    DLL_API void output(int x,int y);   //仅导出类中的一个函数
    void output1(int x,int y);
};



dlltest.cpp

#define DLL_API _declspec(dllexport)
#include "dlltest.h"
#include <windows.h>
#include <stdio.h>

int add(int a,int b)
{
    return a+b;
}

int subtract(int a,int b)
{
    return a-b;
}

void Point1::output(int x,int y)
{
    HWND hwnd=GetForegroundWindow();
    HDC hdc=GetDC(hwnd);
    char buf[50];
    memset(buf,0,50);
    sprintf(buf,"x=%d,y=%d",x,y);
    TextOut(hdc,0,0,buf,strlen(buf));
}

void Point1::output1(int x,int y)
{
    HWND hwnd=GetForegroundWindow();
    HDC hdc=GetDC(hwnd);
    char buf[50];
    memset(buf,0,50);
    sprintf(buf,"x=%d,y=%d",x,y);
    TextOut(hdc,0,0,buf,strlen(buf));
}

void Point2::output(int x,int y)
{
    HWND hwnd=GetForegroundWindow();
    HDC hdc=GetDC(hwnd);
    char buf[50];
    memset(buf,0,50);
    sprintf(buf,"x=%d,y=%d",x,y);
    TextOut(hdc,0,20,buf,strlen(buf));
}

void Point2::output1(int x,int y)
{
    HWND hwnd=GetForegroundWindow();
    HDC hdc=GetDC(hwnd);
    char buf[50];
    memset(buf,0,50);
    sprintf(buf,"x=%d,y=%d",x,y);
    TextOut(hdc,0,20,buf,strlen(buf));
}



2.  编写一个基于对话框的MFC程序测试DLL,此时在工程中包含上面这个头文件,不用在声明导入的类和函数了。

三、   使用模块定义文件和动态加载动态链接库

1.  新建dlltest.dll工程,加入一源文件dlltest.cpp,包含add和subtract两个函数如下:

int add(int a,int b)
{
    return a+b;
}

int subtract(int a,int b)
{
    return a-b;
}


2.       使用模块定义文件防止文件名改变,在目录中新建dlltest.def文件,增加到工程。

LIBRARY dlltest
EXPORTS
add
subtract


 

注:LIBRARYEXPORTS的用法参照MSDN.

3.       编译后用dumpbin查看到函数名没有改变。

4.       用动态加载的方法来调用dll文件。

void CCalldllDlg::OnAdd() 
{
       // TODO: Add your control notification handler code here
       HINSTANCE hInst;
       hInst=LoadLibrary("dlltest.dll");
       typedef int (/*_stdcall*/ *ADDPROC)(int a,int b);
       //ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"?add@@YAHHH@Z");
       //ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));
       ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"add");
       if(!Add)
       {
              MessageBox("获取函数地址失败!");
              return;
       }
       CString str;
       str.Format("2+3=%d",Add(2,3));
       MessageBox(str);
       FreeLibrary(hInst);
}


注意:

1.       这里调用的GetProcAddress函数中的第二个参数是动态链接库中导出的函数名,如果动态链接库中没有用模块定义文件防止函数名粉碎,则要用注释掉的GetProcAddress(hInst,"?add@@YAHHH@Z "),另外也可按序号访问GetProcAddress(hInst,MAKEINTRESOURCE(1))

2.       使用模块定义文件后,如果改变调用约定为_stdcall,函数名也不会被改变,但如果加上_stdcall定义函数,调用时也需要加上_stdcall,否则会出错。

3.       动态加载不需要将文件dlltest.lib拷贝到此工程目录下,并在Project Settings的Link标签下添加此文件,只需将dll文件拷贝到工程目录下即可,并且通过dumpbin -imports calldll.exe查看它的输入信息时,可以看到它并没有加载dlltest.dll。

4.       动态加载的好处是需要时再加载,可以提高执行的效率。当不使用DLL时,调用FreeLibrary减少DLL的使用计数,释放DLL资源,减少系统负担。

5.       不使用模块定义文件,也可用extern "C"使函数名保持不变,如#define DLL1_API extern "C" _declspec(dllexport),但它只能导出全局函数,不能导出类的成员函数,并且如果调用约定被改成了别的方式,此时函数名也被改变,所以一般不用这种方式。

 

本文转载自:http://blog.csdn.net/leixiaohua1020/article/details/12656331

雷霄骅
粉丝 211
博文 419
码字总数 2129
作品 4
朝阳
程序员
私信 提问
VS2010/MFC编程入门教程之目录和总结(鸡啄米)

鸡啄米的这套VS2010/MFC编程入门教程到此就全部完成了,虽然有些内容还未涉及到,但帮助大家进行VS2010/MFC的入门学习业已足够。以此教程的知识为基础,学习VS2010/MFC较为深入的内容已非难事...

weixin_40647819
2018/05/23
0
0
《鸡啄米C++编程入门系列》系列技术文章整理收藏

《鸡啄米C++编程入门系列》已整理成PDF文档,点击可直接下载至本地查阅 https://www.webfalse.com/read/201812.html 文章 鸡啄米:C++编程入门系列之前言 鸡啄米:C++编程入门系列之一(进制...

开元中国2015
2015/06/27
89
0
Lua入门

Lua是一个被设计成用于扩展C/C++编程的轻量级脚本语言。不幸的 是,网上很少有关于这个语言的在线教程。我主要通过阅读其源代码来了解它是怎样工作并且通过一些发布的程序学习它。希望,通过...

红薯
2009/02/06
5.2K
3
VC++动态链接库(DLL)编程[精]

1.概论   先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库...

barsoom
2012/12/07
713
1
MFC、SDK和API有什么区别

MFC、SDK和API有什么区别 MFC Microsoft Foundation Class 微软函数类库 SDK (Software Development Kit,软件开发包) 软件开发包通常是一系列API函数,而MFC 是由微软公司提供的对API封装...

huanongkou
2013/03/20
2.3K
0

没有更多内容

加载失败,请刷新页面

加载更多

PostgreSQL 11.3 locking

rudi
今天
5
0
Mybatis Plus sql注入器

一、继承AbstractMethod /** * @author beth * @data 2019-10-23 20:39 */public class DeleteAllMethod extends AbstractMethod { @Override public MappedStatement injectMap......

一个yuanbeth
今天
10
1
一次写shell脚本的经历记录——特殊字符惹的祸

本文首发于微信公众号“我的小碗汤”,扫码文末二维码即可关注,欢迎一起交流! redis在容器化的过程中,涉及到纵向扩pod实例cpu、内存以及redis实例的maxmemory值,statefulset管理的pod需要...

码农实战
今天
4
0
为什么阿里巴巴Java开发手册中不建议在循环体中使用+进行字符串拼接?

之前在阅读《阿里巴巴Java开发手册》时,发现有一条是关于循环体中字符串拼接的建议,具体内容如下: 那么我们首先来用例子来看看在循环体中用 + 或者用 StringBuilder 进行字符串拼接的效率...

武培轩
今天
8
0
队列-链式(c/c++实现)

队列是在线性表功能稍作修改形成的,在生活中排队是不能插队的吧,先排队先得到对待,慢来得排在最后面,这样来就形成了”先进先出“的队列。作用就是通过伟大的程序员来实现算法解决现实生活...

白客C
今天
81
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部