链接库知识锦集

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

静态链接库:




以上是三种静态连接库的创建方法,其中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差不多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值