`
isiqi
  • 浏览: 16078873 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

使用DEF文件修复函数名(转载)

阅读更多

使用DEF文件修复函数名
——对《使用LoadLibrary调用从Dll中输出的class的一点补充

作者 李成竹

在《使用……》一文中,作者在“代码”的第三点提到了“使用一个DEF文件修复了函数名”,但是并没有讲解什么是DEF文件,也没有说明应该如何修复,可能会使某些初学者(包括我自己)感到疑惑。我也上网搜索了一下,讲解DEF文件作用以及详细使用方法的文章不多且比较零散,本文在此用一个简单例子简单阐述一下DEF文件一般的使用方法,以方便需要者查阅。

DEF文件的全称是Module-Definition File,即模块定义文件,是用来定义EXEDLL文件的一种文件格式,以文本形式保存(可用记事本创建/编辑)。由于链接器为大多数模块定义声明提供了对应的命令行选项,所以一般的Win32程序并不需要.DEF文件。但是在编写DLL时,尤其是在编写C++DLL时,(由于名称修饰)DEF文件还是有它的用武之地的。

※注:关于“名称修饰”在很多地方都有介绍,文中不作讲解。

DEF文件的主要内容是由一系列的声明(statement)组成,包括NAMELIBRARYDISCRIPTIONSTACKSIZESECTIONSEXPORTSVERSION

¨ NAME:指定输出文件的文件名,设置image基址

¨ LIBRARY(DLL):指定DLL的内部名称和加载时的基址

¨ DISCRIPTION:文件描述

¨ STACKSIZE:设置栈的大小

¨ SECTIONS:设置image文件的一个或多个段属性

¨ EXPORTS:定义输出列表

¨ VERSION:指定文件版本

其中最常用的是LIBRARYEXPORTSDISCRIPTION

示例

1. 现在有一个已经编写好的类要用DLL输出,并通过函数名对DLL进行动态调用。其头文件和源文件如下:

Header File:

#ifdef LIBDLL_EXPORTS
#define LIBDLL_API __declspec(dllexport)
#else
#define LIBDLL_API __declspec(dllimport)
#endif

#include <iostream.h>

// This class is exported from the LibDll.dll
class LIBDLL_API CTest
{
int data;

public:
CTest();
void print();
};

Source File:

#include "stdafx.h"
#include "LibDll.h"

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

CTest::CTest()
{
this->data = 0;
}

void CTest::print()
{
cout<<"The member function print() is from a DLL.\n";
}


从代码中可以看到,DLL中定义了一个CTest类,它有一个构造函数和一个成员函数print()

2. 然后新建一个Win32 Console Application来调用DLL

Header File:

#define LIBDLL_API __declspec(dllimport)

#include <iostream.h>

// This class is exported from the LibDll.dll
class LIBDLL_API CTest
{
int data;

public:
CTest();
void print();
};

Source File:

#include "stdafx.h"
#include <iostream.h>
#include <malloc.h>
#include <windows.h>
#include "LibDll.h"

typedef void (WINAPI *PCTOR)();
typedef void (*PPRINT)();

inline void CTest_print(HMODULE, CTest*);
inline void CTest_CTest(HMODULE, CTest*);

int main(int argc, char* argv[])
{
//
加载DLL
HMODULE hmod = LoadLibrary("LibDll.dll");
if(hmod == NULL)
{
cout<<"Failed loading DLL.\n";

return 1;
}

//
创建类对象
CTest* pCTest = (CTest*)malloc(sizeof(CTest));

//
初始化CTest对象
CTest_CTest(hmod, pCTest);

//
调用成员函数
CTest_print(hmod, pCTest);

FreeLibrary(hmod);
free(pCTest);

cout<<"Press [Enter] to exit.";
cin.peek();

return 0;
}//end main


void CTest_print(HMODULE hMod, CTest* pObj)
{
PPRINT pprint = (PPRINT)GetProcAddress(hMod, "print");
if(pprint == NULL)
{
cout<<"Function print() not found.\n";
}
else
{
__asm{ MOV ECX, pObj}

pprint();
}
}

void CTest_CTest(HMODULE hMod, CTest* pObj)
{
PCTOR pCtor = (PCTOR)GetProcAddress(hMod, "CTest");
if(pCtor == NULL)
{
cout<<"Function CTest() not found.\n";
}
else
{
__asm{ MOV ECX, pObj}

pCtor();
}
}

本来到这里就应该可以正常运行了,但是你会发现在执行CTest_CTest函数时会提示"Function CTest() not found."。为什么会找不到函数呢?那是因为C++编译器在生成DLL时对输出函数的名称进行来“修饰”,所以DLL中的函数名称已经不再是我们在代码中所写的函数名,这个时候就需要用DEF文件来进行“函数名称修复”。

3. Dumpbin/EXPORTS参数打开LibDll.Dll,截图如下:

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 288.75pt; HEIGHT: 60.75pt" type="#_x0000_t75"><imagedata src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png" o:title=""></imagedata></shape>

其中13就是CTest类的构造函数和print()函数的实际名称(吓人吧……)。然后我们在DLL的工程目录中新建一个“LibDll.def”文件,并在“工程->设置->Link”中添加参数(/def:".\libdll.def"),并编

DEF文件内容如下:

LIBRARY LibDll
EXPORTS
CTest = ??0CTest@@QAE@XZ
print = ?print@CTest@@QAEXXZ

相信你已经看出来了,这实际上是一个函数名映射。

再用Dumpbin重新编译得到的DLL文件,截图如下:

<shape id="_x0000_i1026" style="WIDTH: 285.75pt; HEIGHT: 84.75pt" type="#_x0000_t75"><imagedata src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image003.png" o:title=""></imagedata></shape>

图中的45就是修复后的函数名。

现在再运行第二步中的程序就可以成功地调用DLL里的函数了!

尾注

文中只使用了DEF文件的一小部分功能,详细资料请参见MSDN

按照MSDN上的说法,有三种方法可以用来输出函数,按推荐顺序如下:

  1. 在源代码中使用__declspec(dllexport)关键字(调用工程需包含*.lib

  2. 使用DEF文件中的EXPORTS声明(不需要*.lib,可实现动态调用)

  3. LINK命令中使用/EXPORT参数(效果和DEF文件相同)

具体应该使用哪种方法,还应该视用途由使用者自己决定。

作者水平有限,文中难免不当之处,欢迎大家指出和批评。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics