动态连接库(Dynamic link library),是一些编译过的可以执行的代码模块,后缀为.dll
1 DLL的基本理论
在使用普通函数库时,可以在程序连接时将库中的代码拷贝到执行文件中,这时静态链接,在多个同样程序执行时,体统保留了许多
代码副本,造成了内存资源的浪费。在使用dll时,不必将dll链接到程序中,而是在应用程序运行时动态的装载dll,装载dll被映射
到进程的地址空间中。同时,使用dll 并不时将库代码拷贝,只是在程序中记录了函数的入口点和接口。
2 DLL 的优点
1) 节省系统资源
2) 不仅可以包括可执行代码,还可以包括数据和各种资源
3)支持多语言
4)升级可以仅仅覆盖dll 文件
5)dll 独立编程语言,c++builder 中的dll vc 也可以使用
3导入导出匹配
DLL函数一般有两种函数,内部函数(internal)和导出函数(export).在实际情况下,许多DLL 调用了其他DLL里面的函数,因此
DLL可以同时有导入和导出函数。
DLL 包含有一个导出函数表,可以通过函数的符号化的名字和称为序号的正书来识别这些函数,函数表中包含了函数在dll内部的
地址。在动态链接进程好建立一张表,把客户的调用与dll里的函数的地址连接起来。
double dbValue(value);//内部函数
double dbValue(value);//内部函数
extern"c" _declspec(dllexpoert) double changeValue(double,bool);//外部函数
double dblValue(double value)
{
return value*vlaue;
}
double changeValue(double value,bool whichOp)
{
return whichOp?doublValue(value):halfValue(value);
}
如果我们希望dll可以用于其他语言或不同版本的c++ 需要在声明导出函数的时候加上extern "C."
4 隐式的链接和显示的链接
隐式链接(前面所讲)在创建dll 的时候,链接器产生一个lib 文件把拿获了dll中的导出符号和序号
显示链接调用Win32 的LoadLibrary 函数,使用完后,调用 FreeLibrary.
5 查找dll
依次顺序为包含exe 的目录,进程的当前目录,Windows 目录,path 环境变量里列出的目录。
6 创建动态链接库
(如何查看dll 文件的定义)
7 导出函数:extern "C" __declspec(dllexport) ExportType FunctionName(Parameter)
extern "C" __declspec(dllimport) __stdcall void CreateFromFunct();
extern "C" __declspec(dllexport) __stdcall void CreateFromFunct();//导出函数
导出类:class __declspec(dllexport) ExportType ClassName{...}
class __declspec(dllexport) __stdcall MyDllClass { //导出类
public:
MyDllClass();
void CreateAForm();
TDllFrm* DllMyForm;
};
__declspec(dllimport) class __stdcall MyDllClass {
public:
MyDllClass();
void CreateAForm();
TDllFrm* DllMyForm;
};
静态调用,build 生成dll 文件和lib 文件,并把lib 文件导入到工程中
void __fastcall TForm1::Button1Click(TObject *Sender)
{ // 导出类实现,导出类只能使用静态方式调用
DllClass = new MyDllClass();
DllClass->CreateAForm();
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{ // 导出函数实现
CreateFromFunct();
}
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
delete DllClass;
}
动态调用
class TForm1 : public TForm
{
...
private: // User declarations
void (__stdcall *CreateFromFunct)();
...
}
HINSTANCE DLLInst = NULL;
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if( NULL == DLLInst ) DLLInst = LoadLibrary("DLL.dll"); //上面的 Dll
if (DLLInst) {
CreateFromFunct = (void (__stdcall*)()) GetProcAddress(DLLInst,
"CreateFromFunct");
if (CreateFromFunct) CreateFromFunct();
else ShowMessage("Could not obtain function pointer");
}
else ShowMessage("Could not load DLL.dll");
}
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if ( DLLInst ) FreeLibrary (DLLInst);
}
8 bcb 调用vc 编写的DLL
1) 名字分解
1. 名字分解:
没有名字分解的函数
TestFunction1 // __cdecl calling convention
@TestFunction2 // __fastcall calling convention
TESTFUNCTION3 // __pascal calling convention
TestFunction4 // __stdcall calling convention
有名字分解的函数
@TestFunction1$QV // __cdecl calling convention
@TestFunction2$qv // __fastcall calling convention
TESTFUNCTION3$qqrv // __apscal calling convention
@TestFunction4$qqrv // __stdcall calling convention
使用 extern "C" 不会分解函数名
2)
__cdecl 缺省
是 Borland C++ 的缺省的 C 格式命名约定,它在标识符前加一下划线,以保留
它原来所有的全程标识符。参数按最右边参数优先的原则传递给栈,然后清栈。
extaern "C" bool __cdecl TestFunction();
在 def 文件中显示为
TestFunction @1
注释: @1 表示函数的顺序数,将在“使用别名”时使用。
__pascal Pascal格式
这时函数名全部变成大写,第一个参数先压栈,然后清栈。
TESTFUNCTION @1 //def file
__stdcall 标准调用
最后一个参数先压栈,然后清栈。
TestFunction @1 //def file
__fastcall 把参数传递给寄存器
第一个参数先压栈,然后清栈。
@TestFunction @1 //def file
3)
3. 解决调用约定:
Microsoft 与 Borland 的 __stdcall 之间的区别是命名方式。 Borland 采用
__stdcall 的方式去掉了名字起前的下划线。 Microsoft 则是在前加上下划线,在
后加上 @ ,再后跟为栈保留的字节数。字节数取决于参数在栈所占的空间。每一个
参数都舍入为 4 的倍数加起来。这种 Miocrosoft 的 DLL 与系统的 DLL 不一样。
4 查看dll 的调用接口tdump -ee MyDll.dll >1.txt (查看 1.txt 文件即可)
5 编辑c++ builder build 为一个可以直接调用的 .exe 。 点击project option 中linker 标签 去掉user dynamic RTL 选项 和package 中 去掉builder with runtime package 选项.
6 调用代参数的vl 编写的dll 调用的实例。
int (__cdecl *fun)(char*,char*,char*);
HINSTANCE DLLInst = NULL;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if( NULL == DLLInst )
{
DLLInst = LoadLibrary("HollyIVRCard.dll");
}
if(DLLInst)
{
fun=(int (__cdecl*)(char*,char*,char*))GetProcAddress(DLLInst,"hTrade");
if(fun)
{
cardid=Edit1->Text.c_str();
num=Edit2->Text.c_str();
ShowMessage(fun(cardid,num,res));
//cout<<cardid<<endl;
//cout<<num<<endl;
}
} else{
ShowMessage("load dll fail");
}
ShowMessage(AnsiString(res));
}