dll知识

来源:互联网 发布:linux 命令返回结果 编辑:程序博客网 时间:2024/05/04 15:06

C++动态链接库(dll)(二)

C++学习2010-05-11 16:26:03 阅读103评论0   字号: 订阅

 

(一)名字改编问题:

C++编译器生成DLL时,会对导出的函数进行名字改编,由于编译器采用的改编原则是不一样的,因此改编后的名字也是不一样的。这样如果利用不同的编译器分别生成DLL和访问该DLL的客户端程序的话,后者在访问该DLL的导出函数时会出现问题。

当希望动态链接库文件在编译时,导出函数的名称不要发生改变,可在定义导出函数时,需要加上限定符:extern “C”。还是以Dll1和DllTest为例。

Dll1.h如下:

#ifdef DLL1_API

#else

#define DLL1_API extern "C" _declspec(dllimport)

#endif

 

DLL1_API int add(int a,int b);

DLL1_API int subtract(int a,int b);

Dll1.cpp如下:

#define DLL1_API extern "C" _declspec(dllexport)

#include"Dll1.h"

#include<Windows.h>

#include<stdio.h>

 

int add(int a,int b)

{

    return a+b;

}

int subtract(int a,int b)

{

    return a-b;

}

则利用dumpbin命令查看可看到导出的函数名为add和subtract,但这种方法有一个缺陷,就是不能用于导出一个类的成员函数,只能用于导出全局函数这种情况,所以Dll1里的类定义被注释掉了。另外,如果导出函数的调用约定发生了改变,这种方法也会失效的。一种万全的方法是定义一个模块定义文件(.def)文件,在项目——>添加新项中选择添加def文件即可,然后在其中输入:

LIBRARY DLL1

 

EXPORTS

add

subtract

当链接器分析def文件时发现EXPORTS语句下有add和subtract这两个符号名,并且和源文件中定义的add和subtract函数的名字是一样的时候,它就会以add和subtract这两个符号名导出相应的函数。如果要导出的符号名和源文件定义的函数名不一样,则可以按照下述语法导出:

entryname=internalname

entryname是要导出的符号名,internalname是DLL中将要导出的函数的名字。

(二)显式加载DLL:

显式加载DLL,也即动态加载DLL时,客户端程序不再需要包含导出函数声明的头文件(.h)和引入库文件(.lib),只需要.dll文件即可。显式加载最主要有两个函数,分别是LoadLibrary和GetProcAddress,声明如下:

HMODULE LoadLibrary(LPCTSTR lpFileName);

FARPROC GetProcAddress(HMODULE hModule,LPCTSTR lpProcName);

LoadLibrary可以加载DLL,也可以加载可执行模块(.exe),返回的是加载的模块的句柄,注意HMODULE和HINSTANCE是可以通用的。

GetProcAddress中,hModule是动态链接库模块的句柄,lpProcName是要导出的函数的名字,当然也可以是序号,序号则需要用到MAKEINTRESOURCE宏,该宏会把指定的函数序号转换为相应的函数名字字符串,如上例,MAKEINTRESOURCE(1)表示函数add,要查看函数的序号可以用dumpbin命令中的exports选项查看。下面给出一个调用的例子:

{

HINSTANCE hInst;

    hInst=LoadLibrary(_T("Dll3.dll"));      //动态加载DLL

    typedef int (*ADDPROC)(int a,int b);          //定义函数指针类型

    ADDPROC Add=(ADDPROC)GetProcAddress(hInst,”add”);

    //也可以用(ADDPROC Add=(ADDPROC)GetProcAddress(hInst,(LPCSTR)MAKEINTRESOURCE(1));)

    if(!Add)

    {

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

         return;

    }

    CString str;

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

    MessageBox(str);

    FreeLibrary(hInst);

}

上述代码中定义了一个函数指针类型:ADDPROC,用来接收通过GetProcAddress返回的函数地址,当然定义要根据导出的函数进行定义,由于GetProcAddress返回的是FARPROC类型,需要强制转换。另外注意的是,当不再需要访问该DLL时,必须调用FreeLibrary函数释放对该DLL的引用。一般地,使用动态加载效率更高。

以上是学习孙鑫老师的《VC++深入详解》相关内容后的笔记。接下来打算解决的问题是dll的搜索顺序和字符的区分和转换。