转自:http://anony3721.blog.163.com/blog/static/5119742010102692514827/
当我们使用VS.Net 2008开发自己的Win32动态链接库时,我们会发现,当我们通过向导生成Win32项目时
,我们不选用空项目,而勾选导出一些符号时,我们会发现我们的项目生成的文件和过去版本的向导生成
的文件内容发生了变化。
例如我们输入TestingStructClassInDll项目名称,就会生成:
头文件:stdafx.h、targetver.h和TestingStructClassInDll.h三个头文件;
实现文件:dllmain.cpp、stdafx.cpp和TestingStructClassInDll.cpp三个实现文件。
我们可以看到:在dllmain.cpp文件中是如下内容
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE 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;
}
相信,IT业界人士对这个函数都很熟悉,它是dll开发的入口点函数,每个dll文件都必须有它(除了纯资
源dll)。
我们继续看一下所包含的头文件:stdafx.h
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头中排除极少使用的资料
// Windows 头文件:
#include <windows.h>
// TODO: 在此处引用程序需要的其他头文件
可以看出,这两个文件和我们过去的dll文件内容差不多一致。
而stdafx.cpp文件如下:
// stdafx.cpp : 只包括标准包含文件的源文件
// TestingStructClassInDll.pch 将作为预编译头
// stdafx.obj 将包含预编译类型信息
#include "stdafx.h"
// TODO: 在 STDAFX.H 中
// 引用任何所需的附加头文件,而不是在此文件中引用
另外,这里有个文件:targetver.h
#pragma once
// 以下宏定义要求的最低平台。要求的最低平台
// 是具有运行应用程序所需功能的 Windows、Internet Explorer 等产品的
// 最早版本。通过在指定版本及更低版本的平台上启用所有可用的功能,宏可以
// 正常工作。
// 如果必须要针对低于以下指定版本的平台,请修改下列定义。
// 有关不同平台对应值的最新信息,请参考 MSDN。
#ifndef WINVER // 指定要求的最低平台是 Windows Vista。
#define WINVER 0x0600 // 将此值更改为相应的值,以适用于 Windows 的其他版本。
#endif
#ifndef _WIN32_WINNT // 指定要求的最低平台是 Windows Vista。
#define _WIN32_WINNT 0x0600 // 将此值更改为相应的值,以适用于 Windows 的其他版本。
#endif
#ifndef _WIN32_WINDOWS // 指定要求的最低平台是 Windows 98。
#define _WIN32_WINDOWS 0x0410 // 将此值更改为适当的值,以适用于 Windows Me 或更高版本。
#endif
#ifndef _WIN32_IE // 指定要求的最低平台是 Internet Explorer 7.0。
#define _WIN32_IE 0x0700 // 将此值更改为相应的值,以适用于 IE 的其他版本。
#endif
我们可以看到这是一个有关于平台的文件,如果要让我们的dll适应各种平台,只需要修改对应的值即可
。
★其实我们最关心的问题是现在文件的变化。因此我们要重点研究的文件,是根据我们的项目名称成生的
一组头文件和实现文件:TestingStructClassInDll.h和TestingStructClassInDll.cpp。
首先我们观察一下.h文件:
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 TESTINGSTRUCTCLASSINDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// TESTINGSTRUCTCLASSINDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef TESTINGSTRUCTCLASSINDLL_EXPORTS
#define TESTINGSTRUCTCLASSINDLL_API __declspec(dllexport)
#else
#define TESTINGSTRUCTCLASSINDLL_API __declspec(dllimport)
#endif
// 此类是从 TestingStructClassInDll.dll 导出的
class TESTINGSTRUCTCLASSINDLL_API CTestingStructClassInDll {
public:
CTestingStructClassInDll(void);
// TODO: 在此添加您的方法。
};
extern TESTINGSTRUCTCLASSINDLL_API int nTestingStructClassInDll;
TESTINGSTRUCTCLASSINDLL_API int fnTestingStructClassInDll(void);
我们很容易发现:#ifdef...#define...#else...#define...#endif语句,它给我们的动态链接库定义了
一个输入输出符号,并且在我们的项目名称后面加上了_API后缀,Visual C++ 用 __declspec
(dllimport) 和 __declspec(dllexport) 取代以前在 16 位版的 Visual C++ 中使用的 __export 关键
字。
我们可以看到注释语句:我们从我们在.dll文件中导出了一个类,而在声明这个类的时候,我们必须将我
们定义的输放输出符号放在我们的类前面。而为了和C版本的兼容,我们引用了外部关键字extern,同时
,将它作为一个整型值导出该类。另外导出了一个整型函数fnTestingStructClassInDll。
接下来让我们来看一下实现文件部分:.cpp文件
// TestingStructClassInDll.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "TestingStructClassInDll.h"
// 这是导出变量的一个示例
TESTINGSTRUCTCLASSINDLL_API int nTestingStructClassInDll=0;
// 这是导出函数的一个示例。
TESTINGSTRUCTCLASSINDLL_API int fnTestingStructClassInDll(void)
{
return 42;
}
// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 TestingStructClassInDll.h
CTestingStructClassInDll::CTestingStructClassInDll()
{
return;
}
通过观察我们可以发现,这里主要是包含"stdafx.h"和自身头文件,当导出变量时,通过我们的自定义输
入输出符号加上我们的变量即可;当要导出函数时,我们也只需要先引用我们的输入输出符号,然后再书
写相关的导出函数;而类自身的函数,我们依然采取以前的方式进行即可。
理解了以上的变化,有利于我们更完善的写我们自己的动态链接库。
通过Dependency Walker我们可以观察到我们生成的动态链接库导出了以下函数:
Ordinal Hint Function
1 (0x0001) 0 (0x0000) CTestingStructClassInDll::CTestingStructClassInDll(void)
2 (0x0002) 1 (0x0001) class CTestingStructClassInDll & CTestingStructClassInDll::
operator= (class CTestingStructClassInDll const &)
3 (0x0003) 2 (0x0002) struct _Meimei & _Meimei::operator=(struct _Meimei const &)
4 (0x0004) 3 (0x0003) int fnTestingStructClassInDll(void)
5 (0x0005) 4 (0x0004) int nTestingStructClassInDll
当然,depends还可以得到dll的Entry Point,以及模块的文件时间戳、链接时间戳、文件大小、文件的
访问属性、链接校验地址、真实校验地址、CPU类型、子系统类型、符号、参考基地址等等信息。
这里我们看到了在TestingStructClassInDll.h头文件中加入的一个自定义结构,用于导出:
//此结构是在项目中导出的
typedef struct TESTINGSTRUCTCLASSINDLL_API _Meimei
{
int Num;
char Name;
}Meimei;
有了以上完整的分析,相信我们对VC2008动态链接库项目有了更深一步的理解了!对比可以令一切更加一目了然!