2011年5月27日翻译,语言或许会不通顺,语文学的不好,尽请原谅。
引言<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
我已经看过了许多解释在程序中如何用从一个DLL中的导出类的代码。但都是描述隐式连接DLL导出类的用法。跟新我们的DLL文章,在一个程序中有两种方式去用一个DLL中的函数,第一种方式就是简单地将你的程序的源代码的引用标志包含在DLL中。这会使加载者在程序被引用时隐式加载连接需要的DLL。这就是大家所知道的隐式连接。
第二种方式就是显式加载所需的DLL(用一个LoadLibrary()函数调用)并且在程序运行时显式连接到所想要的导出标志。换句话说,如果程序决定想要在一个DLL中调用一个函数,它就能将DLL显式加载进进出的地址空间里,获得在DLL中函数的虚拟地址。这种方式的好处是当程序运行时候可以做所有事情并且程序也能够在DLL完成它的任务后从进程中将它卸载掉。正如你所猜想的,这种技术就是众所周知的显式连接。
背景知识
到目前为止,我所说的都是关于函数,但是如何用从DLL中导出的类呢?在隐式链接DLL中,这不是问题。但是如何显式加载DLL并用导出类呢?在正常情况下,这是无法进行的,我写这篇文章不是为了给你解释它为什么不能做,而是给你一些它如何能做的思路和想法。对了,通过用LoadLibrary()函数调用来加载一个DLL可以用导出类。
在进行后面的操作之前,我所要提醒的是下面的方法可以说是有点黑客的行为,如果你计划在你的工程里用它的话,清首先获得你老板的同意。。。(如果你可能获得他/她对于这项技术的同意的话)。这篇文章总的来说是为了你更好的理解并且在某些极端情况下如果你能够不搞非法动作。
代码的运用
如果你看了例子代码,你会看到我已经创建了一个叫做Calc.DLL的计算动态库,并且在我的UserOfCalc控制台程序里面用了这计算方法。
// Calc.DLL 包含了一个导出类
// CCalc类里面包含了三个方法Add,Sub,GetLastFunc()如下所示:
// CALC.H – 声明CCalc类
// 从DLL中导出,引进EXE里
#include <tchar.h>
#ifdef CALC_EXPORTS
#define CALC_API __declspec (dllexport)
#else
#define CALC_API __declspec (dllimport)
#endif
#define SOME_INSTN_BUF 260
class CALC_API CCalc
{
private:
TCHAR m_szLastUsedFunc[SOME_INSTN_BUF];
public:
CCalc ();
int Add (int i, int j);
int Sub (int i, int j);
TCHAR* GetLastUsedFunc ();
};
The implementation of this DLL is as shown in the file Calc.cpp:
Calc.cpp中DLL的执行体:
#include "Calc.h"
#include <windows.h>
BOOL APIENTRY DllMain (HANDLE, DWORD, LPVOID)
{
return TRUE;
}
// Ctor, 初始化m_szLastFuncCalled 数组
CCalc::CCalc ()
{
memset (m_szLastUsedFunc, 0, sizeof (m_szLastUsedFunc));
strcpy (m_szLastUsedFunc, "No function used yet");
}
int CCalc::Add (int i, int j)
{
strcpy (m_szLastUsedFunc, "Add used");
return (i + j);
}
int CCalc::Sub (int i, int j)
{
strcpy (m_szLastUsedFunc, "Sub used");
return (i - j);
}
现在,我们如何显式加载DLL来用Calc类里面的函数呢?步骤如下:
第一步就是用LoadLibrary把Calc.dll 动态库加载进程序里。
HMODULE hMod = LoadLibrary ("Calc.dll");
if (NULL == hMod)
{
printf ("LoadLibrary failed\n");
return 1;
}