VC++动态链接库(DLL)编程(关键知识点)

原文: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 DLLMFC规则DLL)、MFC Extension DLLMFC扩展DLL)。

MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFCMFC编写的应用程序所调用;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规则 DLLCWinApp对象的InitInstance 和 ExitInstance
MFC扩展 DLLMFC DLL向导生成DllMain 函数

  对于MFC扩展DLL,系统会自 动在工程中添加如下表所示的宏,这些宏为DLL和应用程序的编写提供了方便。像AFX_EXT_CLASS、AFX_EXT_API、 AFX_EXT_DATA这样的宏,在DLL和应用程序中将具有不同的定义,这取决于_AFXEXT宏是否被定义。这使得在DLL和应用程序中,使用统一 的一个宏就可以表示出输出和输入的不同意思。在DLL中,表示输出(因为_AFXEXT被定义,通常是在编译器的标识参数中指定/D_AFXEXT);在 应用程序中,则表示输入(_AFXEXT没有定义)。

定义

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_CLASS_EXPORT
#else
 AFX_CLASS_IMPORT

AFX_EXT_API

#ifdef _AFXEXT
 AFX_API_EXPORT
#else
 AFX_API_IMPORT

AFX_EXT_DATA

#ifdef _AFXEXT
 AFX_DATA_EXPORT
#else
 AFX_DATA_IMPORT

 

 

这个DLL中有两点需要注意:
  (1)使用extern "C"修饰函数声明,否则,生成的DLL只能供C++调用;
  (2)使用__stdcall修饰函数声明及定义,__stdcall是Windows API的函数调用方式。

纯资源DLL

  在与这个应用程序相同的工作区里利用MFC向导建立两个简单的DLL,把应用工程中的资源全选后分别拷贝到ChineseDll和EngLishDll,在EnglishDll工程的资源文件中搜索下面的语句:

/

// 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

  修改应用工程的InitInstance()函数,在

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 ); //设置当前的资源句柄

  这样的应用程序将具有自适应性质,在中文OS中显示中文资源,在英文OS中则显示英文资源。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值