MFC- 动态链接库编程(孙鑫-第十九讲笔记整理)

1.DLL简介,动态库,静态库。动态库节约磁盘空间,静态库体积大。可以用多种语言编写DLL文件。动态库有两种加载方式:隐式调用和动态加裁!

 

2.新建一个DLL1dll工程,加入一源文件名为dll1.cpp,加入addsubtract两个函数,注意此时须在函数名前加_declspec(dllexport),并且编译。用dumpbi -exports dll1.dll查看其导出的函数,发现函数名字已经被改成了?add@@YAHHH@Z,这种现象叫做名字粉碎,是为了支持函数重载而做的。

 

3.编写一个程序测试DLL,工程名为DllTest,基于对话框的,放置两个按纽addsubtract,响应按纽消息,调用这个Dlladdsubtract函数。使用这两个函数前要先声明函数,//extern int add(int a,int b);

//extern int subtract(int a,int b);

还需要将Dll1.libDll1.dll拷贝到当前目录下!另外还需要在Project->Setting->Link->Object/Library中加入Dll1.lib,此种方式为隐式调用!OK!用Dumpbin -imports DllTest.exe查看它的输入信息,可以看到它加载了dll1.dll。同时也可以用depends程序查看程序需要哪些dll文件!除了用extern外,还可以用//_declspec(dllimport) int add(int a,int b);

//_declspec(dllimport) int subtract(int a,int b);

告诉编译器,此函数是动态链接库中的函数,这样可以提高效率。

 

4.通常写Dll时在dll1.h中声明函数,然后在DllTest.h中包含这个头文件,另外会用一组宏来取代_declspec(dllimport)

Dll1.h

#ifdef DLL1_API

#else

#define DLL1_API extern "C" _declspec(dllimport)

#endifDLL1_API int _stdcall add(int a,int b);

DLL1_API int _stdcall subtract(int a,int b);

Dll1.cpp的代码:

#define DLL1_API extern "C" _declspec(dllexport)

#include "Dll1.h"

#include <Windows.h>

#include <stdio.h>int _stdcall add(int a,int b)

{

 return a+b;

}int _stdcall subtract(int a,int b)

{

 return a-b;

}

 

5.Dll1中加入类Point它有一个函数output(int a,intb),它的功能是在屏幕上输出x,y值。须包含头文件windows.hstdio.h.然后在DllTest中加入一个按纽来测试这个函数!此时我们可以dumpbin来查看dll1.dlldllTest.exe的导出导入情况。注意,也可以只导出类的某个函数。

 

6.我们希望导出的函数名不被改变,加extern "C"大写的C!即可,#define DLL1_API extern "C" _declspec(dllexport),但它只能导出全局函数,不能导出类的成员函数,并且如果调用约定被改成了别的方式,此时函数名也被改变。所以这种方式不太好。

 

7.解决之道是用模块定义文件。

  a.新建dll2.dll工程;

  b.dll2.cpp中写两个函数addsubtract

  c.在目录中新建dll2.def文件,增加到工程。

  d.dll2.def中加入如下代码:

LIBRARY Dll2EXPORTS

add

subtract

   e.编译后用dumpbin查看函数名是否被改变?

   f.测试,我们这次用动态加载的方法来调用dll文件。以前是用隐式链接的方法,嘿嘿。动态加载的好处是需要时再加载,可以提高执行的效率。代码如下:

 HINSTANCE hInst;

 hInst=LoadLibrary("Dll3.dll");

 typedef int (/*_stdcall*/ *ADDPROC)(int a,int b);

 //ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"?add@@YAHHH@Z");

 ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));

 if(!Add)

 {

  MessageBox("获取函数地址失败!");

  return;

 }

 CString str;

 str.Format("5+3=%d",Add(5,3));

 MessageBox(str);

 FreeLibrary(hInst);

 

7.此时你改变调用约定,函数名不会被改变,但如果你加上_stdcall定义函数,调用时也需要加入_stdcall,否则会出错!

 

8.DllMain()Dll的入口点,不过不是必须的。但在DllMain中不要做复杂的调用。为什么?因为DllMain加载时,某些核心Dll文件不一定已经被加载。

 

9.创建一个基于MFCDLL工程,简介。

 

10.当不使用DLL时,调用FreeLibrary减少DLL的使用计数,释放DLL资源,减少系统负担。明白?

 

11.上面总结:

a.*.def使函数名不改变;

b.定义时为_stdcall,调用时也必须用_stdcall.

 

下面是具体代码和截图:

1新建win32动态链接库

2新建CPP源文件:

[cpp]  view plain copy
  1. int add(int a,int b)  
  2. {  
  3.     return a+b;  
  4. }  
  5.   
  6. int sub(int a,int b)  
  7. {  
  8.     return a-b;  
  9. }  

3vc安装目录---->vc98---->bin---->
把这个拖进 运行--->cmd

4

 

5

这个时候 并没有导出相关的函数,要在CPP文件中加入关键字

这个时候就可以导出函数了:

并不是按照函数原来的名字导出的,这可能是VC的某种规范吧

 

下面是几种方法对dll文件的调用:

1新建一个基于对话框的MFC程序

[cpp]  view plain copy
  1. extern int add(int a,int b);  
  2. extern int sub(int a,int b);  
  3. void CDllTestDlg::OnAdd()   
  4. {  
  5.     // TODO: Add your control notification handler code here  
  6.     CString cstring;  
  7.     cstring.Format("10086+86=%d",add(10086,86));  
  8.     MessageBox(cstring);  
  9.       
  10. }  
  11.   
  12. void CDllTestDlg::OnSub()   
  13. {  
  14.     // TODO: Add your control notification handler code here  
  15.     CString cstring;  
  16.     cstring.Format("10086-86=%d",sub(10086,86));  
  17.     MessageBox(cstring);  
  18.       
  19. }  


2工程目录添加XX.lib,XX.dll文件

3#pragma comment(lib,"LianJieKu1.lib")

就能运行了:

 

 

VC的一个工具,查看谁依赖谁:

 

[cpp]  view plain copy
  1. //extern int add(int a,int b);  
  2. //extern int sub(int a,int b);  
  3.   
  4. _declspec(dllimportint add(int a,int b);  
  5. _declspec(dllimportint sub(int a,int b);  


把上面这种声明换成下面这种声明,效率更高,其他步骤跟上面的一样。

 

 

把_declspec(dllimport) int XXXX;注册掉,

然后在LianJieKu1中添加头文件:

[cpp]  view plain copy
  1. _declspec(dllimportint add(int a,int b);  
  2. _declspec(dllimportint sub(int a,int b);  


#include"../LianJieKu1/LianJieKu1.h" 导入文件即可。

 

也可以修改DLL的头文件和源文件为

[cpp]  view plain copy
  1. #ifndef DLL_API _declspec(dllimport)  
  2. #define DLL_API _declspec(dllimport)  
  3. #endif  
  4. DLL_API int add(int a,int b);  
  5. DLL_API int sub(int a,int b);  


 

[cpp]  view plain copy
  1. #ifndef DLL_APII _declspec(dllexport)  
  2. #define DLL_APII _declspec(dllexport)  
  3. #endif  
  4. #include"LianJieKu1.h"  
  5. DLL_APII int add(int a,int b)  
  6. {  
  7.     return a+b;  
  8. }  
  9.   
  10. DLL_APII int sub(int a,int b)  
  11. {  
  12.     return a-b;  
  13. }  

 

同样也可以在DLL中引入“类”

[cpp]  view plain copy
  1. #ifndef DLL_API _declspec(dllimport)  
  2. #define DLL_API _declspec(dllimport)  
  3. #endif  
  4. DLL_API int add(int a,int b);  
  5. DLL_API int sub(int a,int b);  
  6.   
  7. class DLL_API Point  
  8. {  
  9. public:  
  10.     void say(int x,int y);  
  11. };  

也可以只导出其中的成员函数,

class Point

{

 public:

            void DLL_API say(int x,int y);

}

[cpp]  view plain copy
  1. #ifndef DLL_APII _declspec(dllexport)  
  2. #define DLL_APII _declspec(dllexport)  
  3. #endif  
  4. #include"LianJieKu1.h"  
  5. #include<windows.h>  
  6. #include<stdio.h>  
  7. DLL_APII int add(int a,int b)  
  8. {  
  9.     return a+b;  
  10. }  
  11.   
  12. DLL_APII int sub(int a,int b)  
  13. {  
  14.     return a-b;  
  15. }  
  16.   
  17. void DLL_APII Point::say(int x,int y)  
  18. {  
  19.     HWND hwnd;  
  20.     hwnd=GetForegroundWindow();  
  21.     HDC hdc=GetDC(hwnd);  
  22.     char ch[20];  
  23.     sprintf(ch,"x=%d,y=%d",x,y);  
  24.     TextOut(hdc,0,0,ch,strlen(ch));  
  25.     ReleaseDC(hwnd,hdc);  
  26. }  


 

[cpp]  view plain copy
  1. void CDllTestDlg::OnPoint()   
  2. {  
  3.     // TODO: Add your control notification handler code here  
  4.       
  5.     Point p;  
  6.     p.say(100,200);  
  7. }  


 

如果要使得Dll中函数,可以在C语言或其他集成环境中使用,则要加上一个extern "C",这样使得函数编译时候,函数名字不变

头文件和源文件都改:如

[cpp]  view plain copy
  1. #define DLL1_API extern "C" _declspec(dllimport)  


 

[cpp]  view plain copy
  1. #define DLL1_API extern "C" _declspec(dllexport)  



XX.def文件可以很好的解决 编译时,函数名字变化的问题

动态连接,如果要调用很多的dll文件时,这样可以节省内存

不需要连接lib对象,也不需要头文件,只需要一个dll文件即可。

[cpp]  view plain copy
  1. int add(int a,int b)  
  2. {  
  3.     return a+b;  
  4. }  
  5.   
  6. int sub(int a,int b)  
  7. {  
  8.     return a-b;  
  9. }  


 

[cpp]  view plain copy
  1. LIBRARY DllTest2  
  2. EXPORTS  
  3. add  
  4. sub  


 

[cpp]  view plain copy
  1. void CTestDll2Dlg::OnButton1()   
  2. {  
  3.     // TODO: Add your control notification handler code here  
  4.     HINSTANCE hInst;  
  5.     hInst=LoadLibrary("DllTest2.dll");  
  6.     typedef int (*ADDPROC)(int a,int b);  
  7.     ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"add");  
  8.       
  9.     if(!Add)  
  10.     {  
  11.         MessageBox("获取函数地址失败");  
  12.         return ;  
  13.     }  
  14.     CString cstring;  
  15.     cstring.Format("5+8=%d",Add(5,8));  
  16.     MessageBox(cstring);  
  17.       
  18. }  

 

如果是标准输出,则typedef int (_stdcall *ADDPROC)(int a,int b);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值