链接库知识锦集

静态链接库-----动态链接库

静态链接库:




以上是三种静态连接库的创建方法,其中Win32下的“Win32控制台应用程序”和“Win32项目”创建的连接库的其实内容是一样的,也就是一样的连接库

 

MFC静态规则DLL:如果静态链接到 MFC,则由于 DLL 会加载自己的私有 MFC 库代码副本,DLL 的文件大小会较大,且有可能占用额外的内存。

动态链接到 MFC 的一个缺点是必须用 DLL 发布共享 DLL:Mfcx0.dll 和 Msvcrt.dll(或类似的文件)。当不想用这些dll时使用此方法。

 

建立win32类型静态库的方法:

vc++如何创建并引入静态库.lib 

创建篇: 

用工程向导,选择Win32 Static Library 下一步把两个选项都选上,点击完成。 

新建文件,进行类/模块编写。。 

引入篇: 在工程的STDAFX.cpp 

加入这一句: 

#pragma comment(lib, “Calendar.lib”) //生成的lib文件 

编译,完成! 

  例子: 

//对应工程代码文件:static_lib.rar 

  建立静态库 

1。建立工程:win32 static library static_lib  

2。源代码: //文件:StdAfx.h 

#ifndef LIB_H 

#define LIB_H 

extern "C" int add2(int x,int y); 

#endif 

//文件:stdafx.cpp 

#include "stdafx.h" 

int add2(int x,int y) 

return x + y; 

3。编译后在debug下生成.lib文件 

注:静态链接库中可以再stdafx.h和stdafx.cpp中直接定义函数用标准C接口声明,即extern “C”(同一个编译器下,不用也可,它的作用只是保证导出的函数名和参数顺序的定义统一,使这个lib文件在另外一个编译器下可以使用),也可以在静态库中定义类,在用到它的应用程序中引入头文件即可使用。

在应用程序中:

首先引入该lib文件:

#pragma comment(lib,”XX.lib”)

引入相应的头文件后即可使用

动态链接库:

非MFC动态连接库、动态MFC规则DLL,MFC扩展DLL

MFC动态连接库:

不采用MFC类库类型,其导出函数为标准的C接口,能MFC和非MFC编写的程序调用。

导出函数、导出变量、导出类,应用程序使用方法

导入导出声明:

-------------------------------------------------------------------------------------------------------------------------------

_declspec(dllexport)_declspec(dllimport)

_declspec(dllexport)是用来导出dll中的函数、变两或者类的

_declspec(dllimport)是用来在应用程序的导入使用的(实际上用的并不多,好多情况下不用他也没关系,网上查资料说它主要是为了体现对称美,有导入就有导出,还有就是在导出类中有被static修饰的函数时,在外部调用会失败)

导入全局、静态或者类成员变量需要__declspec(dllimport)。

#define DllImport   __declspec(dllimport) 

DllImport int  j;

__declspec(dllexport)是用于避免需要自己写DEF文件的。编译器会为被__declspec(dllexport)修饰的函数自动添加一个导出函数入口。如果你在其他模块中包含__declspec(dllexport)的头文件,这些项目的导出表中也会生成一个同名导出函数。

 

用_declspec(dllimport)只是为了良好的程序设计

注:

__declspec用于指定所给定类型的实例的与Microsoft相关的存储方式。其它的有关存储方式的修饰符如static与extern等是C和C++语言的ANSI规范,而__declspec是一种扩展属性的定义。扩展属性语法简化并标准化了C和C++语言关于Microsoft的扩展。
用法:__declspec ( extended-decl-modifier )
extended-decl-modifier参数如下,可同时出现,中间有空格隔开:
align (C++)
dllimport
dllexport


__declspec关键字应该出现在简单声明的前面。对于出现在*或&后面或者变量声明中标识符的前面的__declspec,编译器将忽略并且不给出警告。

例子:align
格式:__declspec(align(n)) declarator
其中,n是对齐参数,其有效值是2的整数次幂(从1到8192字节),如2,4,8,16,32或64。参数declarator是要设置对齐方式的数据。
使用__declspec(align(n))来精确控制用户自定义数据的对齐方式。你可以在定义struct,union,class或声明变量时使用__declspec(align(n))。

-------------------------------------------------------------------------------------------------------------------------------

extern “C”的作用:

<一>:

C中函数编译后命名会在函数名前加以"_",比如add函数编译成obj文件时的实际命名为_add,而c++命名则不同,为了实现函数重载同样的函数名add因参数的不同会被编译成不同的名字 。这就是为什么当用C写的一个.h文件和.c文件要放到C++写的程序中使用时会报编译错误的原因。解决办法就是在C++调用C中的文件时调用extern “C”修饰重新声明函数,

<二>替代连接说明: 
如果在c++中编写一个程序需要用到c的库,那该如何?如果这样声明一个c函数: 
void f(int a,char b); 
c++编译器就会将这个名字变成相应的修饰名,比如:?f@@YAXHD@Z. 
然而,c编译器编译的库的内部函数名(连接器使用)是完全不同的.这样,当c++连接器连接c的函数库时,将会产生内部使用函数不匹配. 
故,c++中提供了一个替代连接说明(alternate linkage specification),它是通过重载extern关键字来实现的. 
extern后跟一个字符串来指定想声明的函数的连接类型,后面是函数声明,比如: 
extern "C" void f(int a,char b); 
这样,就是告诉编译器是c连接,这样就不会转换函数名了.此例中,编译后的内部函数名是_f.

-------------------------------------------------------------------------------------------------------------------------------

_stdcall的作用:

这是一种函数调用约定,什么是函数调用约定?

函数调用约定是指当调用一个函数时,参数会被传递给被调用函数和返回值会被传递给调用参数,函数调用约定就是描述参数是怎么被传递的和有谁平衡堆栈的,当然还有返回值。

函数调用约定有:__stdcall,__cdecl,__fastcall,__thiscall,__nakedcall,__pascal

按照参数传递顺序分类:

1.    从右到左入栈:__stdcall、__cdecl、__thiscall(都是两个下划线)

2.      从左到右入栈:__pascal、__fastcall(都是两个下划线)

堆栈清理:

1.      调用者清除栈

2.      被调用函数返回后清楚栈

_stdcall是Wind32 API函数绝大多数采用的调用约定方式,WINAPI也只是_stdcall的一个别名而已。

__stdcall调用约定的主要特征是:

1、参数是从右往左传递的,也是放在堆栈中。

2、函数的堆栈平衡操作是由被调用函数执行的。

3、在函数名的前面用下划线修饰,在函数名的后面由@来修饰并加上栈需要的字节数的空间

 

为什么要有函数的调用约定?

一般是在当跨语言编程时使用,不同语言使用的函数调用约定方式不同

 

C/C++的缺省调用方式是__cdecl,VC使用的默认调用约定方式也是__cdecl,windows API用的是__stdcall调用方式,dll中导出函数时为了跟windows API保持一直,建议使用__stdcall调用方式。

__stdcall和__cdecl说明:

1、__cdecl

__cdecl调用约定又称为 C 调用约定,是 C/C++ 语言缺省的调用约定。参数按照从右至左的方式入栈,函数本身不清理栈,此工作由调用者负责,返回值在EAX中。由于由调用者清理栈,所以允许可变参数函数存在,如int sprintf(char* buffer,const char* format,...);。

 

2、__stdcall

__stdcall 很多时候被称为 pascal 调用约定。pascal 语言是早期很常见的一种教学用计算机程序设计语言,其语法严谨。参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在EAX中。

注:

C 语言有 __cdecl、__stdcall、__fastcall、naked、__pascal。

C++ 语言有 __cdecl、__stdcall、__fastcall、naked、__pascal、__thiscall,比 C 语言多出一种__thiscall 调用方式。

参考:http://blog.csdn.net/xt_xiaotian/article/details/5363633

-------------------------------------------------------------------------------------------------------------------------------

导出函数:

建立空白项目(或者使用Wizard自动生成),使用默认的DllMain,或者手写DllMain,建立一个.h的头文件在这个头文件中声明导出函数(用__declspec(dllexport)修饰),然后再.cpp中定义。或者直接在.cpp文件中定义加导出。还有一种办法是新建一个定义文档.def文件,def文件的格式如下:

LIBRARY MyDll

EXPORTS

MyFunc @1

要使用注释时,使用“;”分号,并且换行,不能与代码行处于同一行,使用此方法导出函数时,在使用此dll的应用程序中需要使用__declspec(dllimport)。建议使用__declspec(dllexport)和__declspec(dllimport)来导出函数。函数的掉用分为隐式调用和显式调用

隐式连接:

隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中

#include”MyDll.h”

#pragam comment(lib,”MyDll.lib”)

Void main()

{

         //调用dll中导出的函数

         MyFunc(iTest);

         Return;

}

显式连接:

显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适.

Void main()

{

         Typedefint (*pfnMyFunc)(int iTest);

         HISTANCEHInst = LoadLibrary(“MyDll.dll”);

If(HInst ==NULL)

{       

         AfxMessageBox(“加载dll失败“);

         Return;

}

pfnMyFunc MyFunc= (pFnMyFunc)GetProcAddress(HInst,”MyFunc”);

MyFunc(1);

freeLibrary(HIst);

Return;

}

使用GetProcAddress()函数时,可以利用MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号

导出变量:

Dll中的全局变量可以被调用进程访问,Dll也可以访问调用进程中的全局变量。

1.用def文件导出变量的方法:

LIBRARY MyDll

EXPORTS

;MyParam CONSTANT

MyParam DATA

现在大多使用MyParam DATA导出

用__declspec进行导出

在全局变量所在的头文件中:__declspec(dllexport) extern int iTest;来导出变量。

变量的调用:

隐式调用:

用extern导入变量

#pragam comment(lib,”MyDll.lib”)

Extern int iTest;

Void main(void)

{

                   Printf(“%s”,*(int*)iTest);

Return;

}

用此种方法导出的并不是变量本身,而是变量的地址,所以必须使用强制类型转换。在给变量赋值时注意不能直接赋值。

使用__declspec(dllimport)导入

这是导入变量的更好的办法。

#pragam comment(lib,”MyDll.lib”)

Extern int __declspec(dllimport) iTest;

Void main(void)

{

                   Printf(“%s”,iTest);

Return;

}

这种情况下,导出的是变量本身,而不在是其地址,可以直接对其进行赋值等操作,看以看作是调用进程内的全局变量一样。

显示调用:

Void main()

{

         IntiTest;

         HINSTANCEHInst = LoadLibrary(“MyDll.dll”);

If(HInst == NULL)

{

         AfxMessageBox(“加载dll失败“);

         Return;

 

}

iTest = *(int *)GetProcAdress(HInst,”iTest”);

printf(“%d”,iTest);

freeLibrary(HIst);

return;

}

由此获得全局变量的地址可以对其进行操作

导出类:

与导出函数差别不大

在dll中只需要在要导出的类前加上(其实是宏定义,这里为了方便都是只写了一个)__declspec(dllexport)即可。在调用进程中使用时只能使用隐式连接,使用方法与函数的隐式连接一样。

为什么不能使用.def文件导出类或使用显式连接方式使用dll哪?

因为C++的链接器对symbol进行了修改,也就是连接时对函数名进行了修改,因为C++有重载等机制。一般从dll中导出的类中的成员都不是是我们在类中定义的函数名,因为我们没有为类中的成员指定命名方式,导出的类中的成员函数,类似于乱码方式。比如:

  int Test1(char *var1,unsigned long)    -----“?Test1@@YGHPADK@Z”
void Test2()                             -----“?Test2@@YGXXZ”
1. "?"标识函数名的开始,后跟函数名

2. 函数名后面以"@@YG"标识参数表的开始,后跟参数表

3.参数表以代号表示:

X——void,
D——char,
E——unsigned char,
F——short,
H——int,
I——unsigned int,
J——long,
K——unsigned long,
M——float,
N——double,
_N——bool,
....

  PA——表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
4.参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前; 
5.参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束

 

-------------------------------------------------------------------------------------------------------------------------------

 

MFC的规则DLL

继承自CWinApp类,但其无消息循环,能被MFC和非MFC编写的程序调用(任何语言编写的程序),这类DLL动态链接到 MFC DLL(也称作共享 MFC DLL),它的运行依赖MFCx0.dll 和 Msvcrt.dll(或类似的文件)。

 

在使用MFC共享库的时候,默认情况下,MFC使用主应用程序的资源句柄来加载资源模块,这样当DLL中的资源ID与应用程序中的ID相同的时候(即资源重复的问题),系统可能不能正确的获取资源。

 

如果所调用的dll中使用了资源,则可以使用静态连接的方式(连接Lib文件),这样便可以避免资源重复的问题。当然也可以使用动态连接,但是需要进行模块切换。

当所调用的dll中没有使用资源时,他的动态连接便于win32dll的动态连接无异。

模块切换的三种方法:

1.在dll的接口函数中使用

AFX_MANAGE_STAGE(AfxGetStaticModuleStatic());

即在函数入口处加上该函数进行模块的切换

2.在DLL接口函数中使用:
        AfxGetResourceHandle();
         AfxSetResourceHandle(HINSTANCE xxx);
         如下列代码:
void ShowDlg(void)
{
    HINSTANCE save_hInstance=AfxGetResourceHandle();
    AfxSetResourceHandle(theApp.m_hInstance);

          CDialog dlg(IDD_DLL_DIALOG);
          dlg.DoModal;

AfxSetResourceHandle(save_hInstance);
}

3. 由调用DLL的应用程序自身切换
        资源模块的切换除了可以由DLL接口函数完成以外,由应用程序自身也能完成。

        //获取exe模块句柄
        HINSTANCE exe_hInstance= GetModuleHandle(NULL);
        //或者HINSTANCEexe_hInstance = AfxGetResourceHandle();

   //获取DLL模块句柄
   HINSTANCE dll_hInstance =GetModuleHandle("SharedDll.dll");

   //切换状态
   AfxSetResourceHandle(dll_hInstance);

   //调用DLL函数
   ShowDlg();

   //资源模块恢复
   AfxSetResourceHandle(exe_hInstance);

导出类:AFX_EXT_CLASS来修饰类的声明,AFX_EXT_CLASS实际就是对__declspec的宏定义,其他的使用与非MFC dll一样。

MFC扩展DLL

         MFC扩展 DLL 是通常实现从现有 Microsoft 基础类库类派生的可重用类的 DLL。扩展 DLL 是使用 MFC 动态链接库版本(也称作共享 MFC 版本)生成的。只有用共享 MFC 版本生成的 MFC 可执行文件(应用程序或规则 DLL)才能使用扩展 DLL。使用扩展 DLL,可以从 MFC 派生新的自定义类,然后将此“扩展”版本的 MFC 提供给调用 DLL 的应用程序。

使用时和MFC规则dll差不多

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值