BCB编写DLL终极手册

来源:互联网 发布:单片机与fpga 编辑:程序博客网 时间:2024/05/16 17:27

BCB编写DLL终极手册

一. 编写 DLL 
File/New/Dll 生成 Dll 的向导,然后可以添加导出函数和导出类 
导出函数:extern "C" __declspec(dllexport) ExportType FunctionName(Parameter) 
导出类:class __declspec(dllexport) ExportType ClassName{...} 
例子:(说明:只是生成了一个 DLL.dll )

#include "DllForm.h" // TDllFrm 定义

USERES("Dll.res"); 
USEFORM("DllForm.cpp", DllFrm);

class __declspec(dllexport) __stdcall MyDllClass { //导出类 
public: 
MyDllClass(); 
void CreateAForm(); 
TDllFrm* DllMyForm; 
};

TDllFrm* DllMyForm2; 
extern "C" __declspec(dllexport) __stdcall void CreateFromFunct();//导出函数

//--------------------------------------------------------------------------- 
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) 

return 1; 

//---------------------------------------------------------------------------

MyDllClass::MyDllClass() 

}

void MyDllClass::CreateAForm() 

DllMyForm = new TDllFrm(Application); 
DllMyForm->Show(); 

//--------------------------------------------------------------------------- 
void __stdcall CreateFromFunct() 

DllMyForm2 = new TDllFrm(Application); 
DllMyForm2->Show(); 
}


二. 静态调用 DLL 
使用 $BCB path/Bin/implib.exe 生成 Lib 文件,加入到工程文件中 
将该文件拷贝到当前目录,使用 implib MyDll.lib MyDll.dll 生成 
// Unit1.h // TForm1 定义 
#include "DllForm.h" // TDllFrm 定义 
//---------------------------------------------------------------------------

__declspec(dllimport) class __stdcall MyDllClass { 
public: 
MyDllClass(); 
void CreateAForm(); 
TDllFrm* DllMyForm; 
}; 
extern "C" __declspec(dllimport) __stdcall void CreateFromFunct();

class TForm1 : public TForm{...}


// Unit1.cpp // TForm1 实现 
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; 
}

三. 动态调用 DLL 
// Unit1.h 
class TForm1 : public TForm 

... 
private: // User declarations 
void (__stdcall *CreateFromFunct)(); 
... 
}

// Unit1.cpp // TForm1 
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); 
}

四. DLL 作为 MDIChild (子窗体) 【只编写动态调用的例子】 
实际上,调用子窗体的 DLL 时,系统只是检查应用程序的 MainForm 是否为 fsMDIForm 的窗体,这样只

要把调用程序的 Application 的 Handle 传递给 DLL 的 Application 即可;同时退出 DLL 时也要恢复

Application

// MDIChildPro.cpp // Dll 实现 CPP 
#include "unit1.h" // TForm1 定义 
TApplication *SaveApp = NULL; 
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) 

if ( (reason==DLL_PROCESS_DETACH) && SaveApp ) 
Application = SaveApp ; // 恢复 Application 
return 1; 
}

extern "C" __declspec(dllexport) __stdcall void TestMDIChild( //1024X768 
TApplication* mainApp, 
LPSTR lpCaption) 

if ( NULL == SaveApp ) // 保存 Application,传递 Application 

SaveApp = Application; 
Application = mainApp; 

// lpCaption 为子窗体的 Caption 
TForm1 *Form1 = new TForm1 ( Application, lpCaption ); 
Form1->Show(); 

注:上面的程序使用 BCB 3.0 编译成功

五. BCB 调用 VC 编写的 DLL 
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" 不会分解函数名

使用 Impdef MyLib.def MyLib.DLL 生成 def 文件查看是否使用了名字分解

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. 解决调用约定: 
Microsoft 与 Borland 的 __stdcall 之间的区别是命名方式。 Borland 采用 
__stdcall 的方式去掉了名字起前的下划线。 Microsoft 则是在前加上下划线,在 
后加上 @ ,再后跟为栈保留的字节数。字节数取决于参数在栈所占的空间。每一个 
参数都舍入为 4 的倍数加起来。这种 Miocrosoft 的 DLL 与系统的 DLL 不一样。

4. 使用别名: 
使用别名的目的是使调用文件 .OBJ 与 DLL 的 .DEF 文件相匹配。如果还没有 
.DEF 文件,就应该先建一个。然后把 DEF 文件加入 Project。使用别名应不断 
修改外部错误,如果没有,还需要将 IMPORTS 部分加入 DEF 文件。 
IMPORTS 
TESTFUNCTIOM4 = DLLprj.TestFunction4 
TESTFUNCTIOM5 = DLLprj.WEP @500 
TESTFUNCTIOM6 = DLLprj.GETHOSTBYADDR @51 
这里需要说明的是,调用应用程序的 .OBJ 名与 DLL 的 .DEF 文件名是等价的, 
而且总是这样。甚至不用考虑调用约定,它会自动匹配。在前面的例子中,函数被 
说明为 __pascal,因此产生了大写函数名。这样链接程序不会出错。

5. 动态调用例子 
VC DLL 的代码如下: 
extern "C" __declspec(dllexport) LPSTR __stdcall BCBLoadVCWin32Stdcall() 

static char strRetStdcall[256] = "BCB Load VC_Win32 Dll by __stdcall mode is OK!";

return strRetStdcall; 
}

extern "C" __declspec(dllexport) LPSTR __cdecl BCBLoadVCWin32Cdecl() 

static char strRetCdecl[256] = "BCB Load VC_Win32 Dll by __cdecl mode is OK!";

return strRetCdecl; 
}

extern "C" __declspec(dllexport) LPSTR __fastcall BCBLoadVCWin32Fastcall() 

static char strRetFastcall[256] = "BCB Load VC_Win32 Dll by __fastcall mode is OK!";

return strRetFastcall; 
}

其实动态调用与调用 BCB 编写的 DLL 没有区别,关键是查看 DLL 的导出函数名字 
可以使用 tdump.exe(BCB工具) 或者 dumpbin.exe(VC工具) 查看 
tdump -ee MyDll.dll >1.txt (查看 1.txt 文件即可) 
由于 VC6 不支持 __pascall 方式,下面给出一个三种方式的例子 
void __fastcall TForm1::btnBLVCWin32DynClick(TObject *Sender) 

/*cmd: tdbump VCWin32.dll >1.txt 
Turbo Dump Version 5.0.16.4 Copyright (c) 1988, 1998 Borland International 
Display of File VCWIN32.DLL

EXPORT ord:0000='BCBLoadVCWin32Fastcall::' 
EXPORT ord:0001='BCBLoadVCWin32Cdecl' 
EXPORT ord:0002='_BCBLoadVCWin32Stdcall@0' 
*/ 
if ( !DllInst ) 
DllInst = LoadLibrary ( "VCWin32.dll" ); 
if ( DllInst ) 

BCBLoadVCWin32Stdcall = (LPSTR (__stdcall *) () ) 
GetProcAddress ( DllInst, "_BCBLoadVCWin32Stdcall@0" ); //VC Dll 
// GetProcAddress ( DllInst, "BCBLoadVCWin32Stdcall" ); //BCB Dll 
if ( BCBLoadVCWin32Stdcall ) 

ShowMessage( BCBLoadVCWin32Stdcall() ); 

else ShowMessage ( "Can't find the __stdcall Function!" );

BCBLoadVCWin32Cdecl = (LPSTR (__cdecl *) () ) 
GetProcAddress ( DllInst, "BCBLoadVCWin32Cdecl" ); 
if ( BCBLoadVCWin32Cdecl ) 

ShowMessage( BCBLoadVCWin32Cdecl() ); 

else ShowMessage ( "Can't find the __cdecl Function!" );

//Why?不是 'BCBLoadVCWin32Fastcall::',而是 '@BCBLoadVCWin32Fastcall@0'? 
BCBLoadVCWin32Fastcall = (LPSTR (__fastcall *) () ) 
//GetProcAddress ( DllInst, "BCBLoadVCWin32Fastcall::" ); 
GetProcAddress ( DllInst, "@BCBLoadVCWin32Fastcall@0" ); 
if ( BCBLoadVCWin32Fastcall ) 

ShowMessage( BCBLoadVCWin32Fastcall() ); 

else ShowMessage ( "Can't find the __fastcall Function!" ); 

else ShowMessage ( "Can't find the Dll!" ); 
}

6. 静态调用例子 
静态调用有点麻烦,从动态调用中可以知道导出函数的名字,但是直接时(加入 lib 文件到工程文件)

Linker 提示不能找到函数的实现 
从 4 看出,可以加入 def 文件连接 
(可以通过 impdef MyDll.def MyDll.dll 获得导出表) 
建立与 DLL 文件名一样的 def 文件与 lib 文件一起加入到工程文件 
上面的 DLL(VCWIN32.dll) 的 def 文件为(VCWIN32.def): 
LIBRARY VCWIN32.DLL

IMPORTS 
@BCBLoadVCWin32Fastcall = VCWIN32.@BCBLoadVCWin32Fastcall@0 
_BCBLoadVCWin32Cdecl = VCWIN32.BCBLoadVCWin32Cdecl 
BCBLoadVCWin32Stdcall = VCWIN32._BCBLoadVCWin32Stdcall@0

对应的函数声明和实现如下: 
extern "C" __declspec(dllimport) LPSTR __fastcall BCBLoadVCWin32Fastcall(); 
extern "C" __declspec(dllimport) LPSTR __cdecl BCBLoadVCWin32Cdecl(); 
extern "C" __declspec(dllimport) LPSTR __stdcall BCBLoadVCWin32Stdcall();

void __fastcall TfrmStatic::btnLoadDllClick(TObject *Sender) 

ShowMessage ( BCBLoadVCWin32Fastcall() ); 
ShowMessage ( BCBLoadVCWin32Cdecl() ); 
ShowMessage ( BCBLoadVCWin32Stdcall() ); 

原创粉丝点击