原文:http://blog.csdn.net/todototry/article/details/1640150
静态链接库与动态链接库都是共享代码的方式
静态链接库lib中的指令都被直接包含在最终生成的EXE文件中了
使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
VC动态链接库的分类
Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。
非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。
头文件
extern "C" int add(int x,int y);
调用方法
#include "../lib.h"
#pragma comment( lib, "..//debug//libTest.lib" ) //指定与静态库一起连接
VC++动态链接库编程
LoadLibrary-GetProcAddress-FreeLibrary(这种调用方式称为DLL的动态调用)
extern "C" int __declspec(dllexport) CjwFun(int a,int b ,int c);
typedef int(*pFun)(int ,int ,int);
HINSTANCE hDll; //DLL句柄
pFun pfun; //函数指针
hDll = LoadLibrary(L"../Debug/DllTest.dll");
if (hDll != NULL)
{
pfun = (pFun)GetProcAddress(hDll, "CjwFun");
if(pfun)
{
CString temp;
temp.Format(L"%d",pfun(10,5,3));
AfxMessageBox(temp);
}
}
FreeLibrary(hDll);
静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载
#pragma comment(lib,"../Debug/dLLTest.lib")
extern "C" __declspec(dllimport) int CjwFun(int ,int ,int);
CString temp;
temp.Format(L"%d",CjwFun(10,5,3));
AfxMessageBox(temp);
按照MSDN说明,当链接dll的导出函数时,只需要包含头文件和lib,__declspec(dllimport)修饰符不是必须的,但加上该修饰能使导出函数的调用效率更高。
DLL定义和调用的一般概念
(1)DLL中需以某种特定的方式声明导出函数(或变量、类);
__declspec(dllexport),模块定义(.def) 文件声明
APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;
进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了DLL模块在进程虚拟空间中的起始地 址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数hModule的来历。
代码中的GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意,它直接通过.def文件中为add函数指定的顺序号访问add函数,具体体现在MAKEINTRESOURCE ( 1 ),MAKEINTRESOURCE是一个通过序号获取函数名的宏,定义为(节选自winuser.h):
#define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))
#define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i)))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#else
#define MAKEINTRESOURCE MAKEINTRESOURCEA
__stdcall约定
如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C+ +缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声 明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而 __cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
Windows编程中常见的几种函数类型声明宏都是与__stdcall和__cdecl有关的(节选自windef.h):
#define CALLBACK __stdcall //这就是传说中的回调函数 #define WINAPI __stdcall //这就是传说中的WINAPI #define WINAPIV __cdecl #define APIENTRY WINAPI //DllMain的入口就在这里 #define APIPRIVATE __stdcall #define PASCAL __stdcall |
在lib.h中,应这样声明add函数:
int __stdcall add(int x, int y); |
在应用工程中函数指针类型应定义为:
typedef int(__stdcall *lpAddFun)(int, int); |
导出变量:
.h文件 extern "C" __declspec(dllexport) int dllGlobalVar ;声明
.cpp文件 __declspec(dllexport) int dllGlobalVar ;定义
dll中导出命名空间中的变量
MFC规则DLL
“是规则的”意味着它不同于MFC扩展DLL,在MFC规则DLL的内部虽然可以使用MFC,但是其与应用程序的接口不能是MFC。而MFC扩展DLL与应用程序的接口可以是MFC,可以从MFC扩展DLL中导出一个MFC类的派生类。
Regular DLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。在这种动态连接库中,包含一个从CWinApp继承下来的类,DllMain函数则由MFC自动提供。
Regular DLL分为两类:(1)静态链接到MFC 的规则DLL(2)动态链接到MFC 的规则DLL(注意模块切换)
资源模块句柄的切换,其实现方法有三
方法一 在DLL接口函数中使用:
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
方法二 在DLL接口函数中使用:
HINSTANCE save_hInstance = AfxGetResourceHandle();
AfxSetResourceHandle(theApp.m_hInstance);
状态还原
AfxSetResourceHandle(save_hInstance);
方法三 由应用程序自身切换
HINSTANCE exe_hInstance = GetModuleHandle(NULL);
//或者HINSTANCE exe_hInstance = AfxGetResourceHandle();
//获取DLL模块句柄
HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll");
AfxSetResourceHandle(dll_hInstance); //切换状态
ShowDlg(); //此时显示的是DLL的对话框
AfxSetResourceHandle(exe_hInstance); //恢复状态
MFC扩展DLL
MFC扩展DLL与MFC规则DLL的相同点在于在两种DLL的内部都可以使用MFC类库,其不同点在于MFC扩展DLL与应用程序的接口可以是MFC 的。MFC扩展DLL的含义在于它是MFC的扩展,其主要功能是实现从现有MFC库类中派生出可重用的类。MFC扩展DLL使用MFC 动态链接库版本,因此只有用共享MFC 版本生成的MFC 可执行文件(应用程序或规则DLL)才能使用MFC扩展DLL。
MFC规则DLL被MFC向导自动添加了一个CWinApp的对象,而MFC扩展DLL则不包含该对象,它只是被自动添加了DllMain 函数。对于MFC扩展DLL,开发人员必须在DLL的DllMain函数中添加初始化和结束代码。
从下表我们可以看出三种DLL对DllMain入口函数的不同处理方式:
DLL类型 | 入口函数 |
非 MFC DLL | 编程者提供DllMain函数 |
MFC规则 DLL | CWinApp对象的InitInstance 和 ExitInstance |
MFC扩展 DLL | MFC DLL向导生成DllMain 函数 |
宏 |
定义 |
AFX_CLASS_IMPORT |
__declspec(dllexport) |
AFX_API_IMPORT |
__declspec(dllexport) |
AFX_DATA_IMPORT |
__declspec(dllexport) |
AFX_CLASS_EXPORT |
__declspec(dllexport) |
AFX_API_EXPORT |
__declspec(dllexport) |
AFX_DATA_EXPORT |
__declspec(dllexport) |
AFX_EXT_CLASS |
#ifdef _AFXEXT |
AFX_EXT_API |
#ifdef _AFXEXT |
AFX_EXT_DATA |
#ifdef _AFXEXT |
这个DLL中有两点需要注意:
(1)使用extern "C"修饰函数声明,否则,生成的DLL只能供C++调用;
(2)使用__stdcall修饰函数声明及定义,__stdcall是Windows API的函数调用方式。
/ // Chinese (P.R.C.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) #ifdef _WIN32 LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #pragma code_page(936) #endif //_WIN32 |
/ // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 |
CResourceDllCallDlg dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); |
//获取操作系统的语言 WORD wLangPID = PRIMARYLANGID( GetSystemDefaultLangID() ); if( LANG_CHINESE == wLangPID ) { hLanguageDll = LoadLibrary( "ChineseDll.dll" ); //加载中文资源 } else { hLanguageDll = LoadLibrary( "EnglishDll.dll" ); //加载英文资源 } if( NULL == hLanguageDll ) { AfxMessageBox( "Load DLL failure" ); return FALSE; } AfxSetResourceHandle( hLanguageDll ); //设置当前的资源句柄 |