📄 bcb 编写 dll 终极手册.htm
字号:
// Unit1.cpp // TForm1 <br>
HINSTANCE DLLInst = NULL; <br>
void __fastcall TForm1::Button2Click(TObject *Sender) <br>
{ <br>
</span> <span lang=EN-US>if( NULL == DLLInst ) DLLInst =
LoadLibrary("DLL.dll"); //</span>上面的<span lang=EN-US> Dll <br>
</span> <span lang=EN-US>if (DLLInst) { <br>
</span> <span lang=EN-US>CreateFromFunct = (void (__stdcall*)())
GetProcAddress(DLLInst, <br>
</span> <span lang=EN-US>"CreateFromFunct"); <br>
</span> <span lang=EN-US>if (CreateFromFunct) CreateFromFunct(); <br>
</span> <span lang=EN-US>else ShowMessage("Could not obtain function
pointer"); <br>
</span> <span lang=EN-US>} <br>
</span> <span lang=EN-US>else ShowMessage("Could not load
DLL.dll"); <br>
} <br>
<br>
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) <br>
{ <br>
</span> <span lang=EN-US>if ( DLLInst ) FreeLibrary (DLLInst); <br>
} <br>
<br>
</span>四<span lang=EN-US>. DLL </span>作为<span lang=EN-US> MDIChild (</span>子窗体<span
lang=EN-US>) </span>【只编写动态调用的例子】<span lang=EN-US> <br>
</span> 实际上,调用子窗体的<span lang=EN-US> DLL </span>时,系统只是检查应用程序的<span lang=EN-US>
MainForm </span>是否为<span lang=EN-US> fsMDIForm </span>的窗体,这样只<span
lang=EN-US> <br>
<br>
</span>要把调用程序的<span lang=EN-US> Application </span>的<span lang=EN-US> Handle </span>传递给<span
lang=EN-US> DLL </span>的<span lang=EN-US> Application </span>即可;同时退出<span
lang=EN-US> DLL </span>时也要恢复<span lang=EN-US> <br>
<br>
Application <br>
<br>
// MDIChildPro.cpp // Dll </span>实现<span lang=EN-US> CPP <br>
#include "unit1.h" // TForm1 </span>定义<span lang=EN-US> <br>
TApplication *SaveApp = NULL; <br>
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) <br>
{ <br>
</span> <span lang=EN-US>if ( (reason==DLL_PROCESS_DETACH) && SaveApp
) <br>
</span> <span lang=EN-US>Application = SaveApp ; // </span>恢复<span
lang=EN-US> Application <br>
</span> <span lang=EN-US>return 1; <br>
} <br>
<br>
extern "C" __declspec(dllexport) __stdcall void TestMDIChild(</span> <span
lang=EN-US>//1024X768 <br>
</span> <span lang=EN-US>TApplication* mainApp, <br>
</span> <span lang=EN-US>LPSTR lpCaption) <br>
{ <br>
</span> <span lang=EN-US>if ( NULL == SaveApp ) // </span>保存<span lang=EN-US>
Application</span>,传递<span lang=EN-US> Application <br>
</span> <span lang=EN-US>{ <br>
</span> <span lang=EN-US>SaveApp = Application; <br>
</span> <span lang=EN-US>Application = mainApp; <br>
</span> <span lang=EN-US>} <br>
</span> <span lang=EN-US>// lpCaption </span>为子窗体的<span lang=EN-US> Caption <br>
</span> <span lang=EN-US>TForm1 *Form1 = new TForm1 ( Application, lpCaption
); <br>
</span> <span lang=EN-US>Form1->Show(); <br>
} <br>
</span>注:上面的程序使用<span lang=EN-US> BCB 3.0 </span>编译成功<span lang=EN-US> <br>
<br>
</span>五<span lang=EN-US>. BCB </span>调用<span lang=EN-US> VC </span>编写的<span
lang=EN-US> DLL <br>
1. </span>名字分解:<span lang=EN-US> <br>
</span> 没有名字分解的函数<span lang=EN-US> <br>
</span> <span lang=EN-US>TestFunction1 // __cdecl calling convention <br>
</span> <span lang=EN-US>@TestFunction2 // __fastcall calling convention <br>
</span> <span lang=EN-US>TESTFUNCTION3 // __pascal calling convention <br>
</span> <span lang=EN-US>TestFunction4 // __stdcall calling convention <br>
</span> 有名字分解的函数<span lang=EN-US> <br>
</span> <span lang=EN-US>@TestFunction1$QV // __cdecl calling convention <br>
</span> <span lang=EN-US>@TestFunction2$qv // __fastcall calling convention <br>
</span> <span lang=EN-US>TESTFUNCTION3$qqrv // __apscal calling convention <br>
</span> <span lang=EN-US>@TestFunction4$qqrv // __stdcall calling convention
<br>
</span> 使用<span lang=EN-US> extern "C" </span>不会分解函数名<span
lang=EN-US> <br>
<br>
</span> 使用<span lang=EN-US> Impdef MyLib.def MyLib.DLL </span>生成<span
lang=EN-US> def </span>文件查看是否使用了名字分解<span lang=EN-US> <br>
<br>
2. </span>调用约定:<span lang=EN-US> <br>
</span> <span lang=EN-US>__cdecl </span>缺省<span lang=EN-US> <br>
</span> 是<span lang=EN-US> Borland C </span>的缺省的<span lang=EN-US> C </span>格式命名约定,它在标识符前加一下划线,以保留<span
lang=EN-US> <br>
</span> 它原来所有的全程标识符。参数按最右边参数优先的原则传递给栈,然后清栈。<span lang=EN-US> <br>
</span> <span lang=EN-US>extaern "C" bool __cdecl TestFunction(); <br>
</span> 在<span lang=EN-US> def </span>文件中显示为<span lang=EN-US> <br>
</span> <span lang=EN-US>TestFunction @1 <br>
</span> 注释:<span lang=EN-US> @1 </span>表示函数的顺序数,将在<span lang=EN-US>“</span>使用别名<span
lang=EN-US>”</span>时使用。<span lang=EN-US> <br>
<br>
</span> <span lang=EN-US>__pascal Pascal</span>格式<span lang=EN-US> <br>
</span> 这时函数名全部变成大写,第一个参数先压栈,然后清栈。<span lang=EN-US> <br>
</span> <span lang=EN-US>TESTFUNCTION @1 //def file <br>
<br>
</span> <span lang=EN-US>__stdcall </span>标准调用<span lang=EN-US> <br>
</span> 最后一个参数先压栈,然后清栈。<span lang=EN-US> <br>
</span> <span lang=EN-US>TestFunction @1 //def file <br>
<br>
</span> <span lang=EN-US>__fastcall </span>把参数传递给寄存器<span lang=EN-US> <br>
</span> 第一个参数先压栈,然后清栈。<span lang=EN-US> <br>
</span> <span lang=EN-US>@TestFunction @1 //def file <br>
<br>
3. </span>解决调用约定:<span lang=EN-US> <br>
</span> <span lang=EN-US> Microsoft </span>与<span lang=EN-US> Borland </span>的<span
lang=EN-US> __stdcall </span>之间的区别是命名方式。<span lang=EN-US> Borland </span>采用<span
lang=EN-US> <br>
</span> <span lang=EN-US>__stdcall </span>的方式去掉了名字起前的下划线。<span lang=EN-US>
Microsoft </span>则是在前加上下划线,在<span lang=EN-US> <br>
</span> 后加上<span lang=EN-US> @ </span>,再后跟为栈保留的字节数。字节数取决于参数在栈所占的空间。每一个<span
lang=EN-US> <br>
</span> 参数都舍入为<span lang=EN-US> 4 </span>的倍数加起来。这种<span lang=EN-US>
Miocrosoft </span>的<span lang=EN-US> DLL </span>与系统的<span lang=EN-US> DLL </span>不一样。<span
lang=EN-US> <br>
<br>
4. </span>使用别名:<span lang=EN-US> <br>
</span> 使用别名的目的是使调用文件 <span lang=EN-US>.OBJ </span>与<span lang=EN-US> DLL </span>的<span
lang=EN-US> .DEF </span>文件相匹配。如果还没有<span lang=EN-US> <br>
</span> <span lang=EN-US>.DEF </span>文件,就应该先建一个。然后把<span lang=EN-US> DEF </span>文件加入<span
lang=EN-US> Project</span>。使用别名应不断<span lang=EN-US> <br>
</span> 修改外部错误,如果没有,还需要将<span lang=EN-US> IMPORTS </span>部分加入<span
lang=EN-US> DEF </span>文件。<span lang=EN-US> <br>
</span> <span lang=EN-US>IMPORTS <br>
</span> <span lang=EN-US>TESTFUNCTIOM4 = DLLprj.TestFunction4 <br>
</span> <span lang=EN-US>TESTFUNCTIOM5 = DLLprj.WEP @500 <br>
</span> <span lang=EN-US>TESTFUNCTIOM6 = DLLprj.GETHOSTBYADDR @51 <br>
</span> 这里需要说明的是,调用应用程序的<span lang=EN-US> .OBJ </span>名与<span lang=EN-US>
DLL </span>的<span lang=EN-US> .DEF </span>文件名是等价的,<span lang=EN-US> <br>
</span> 而且总是这样。甚至不用考虑调用约定,它会自动匹配。在前面的例子中,函数被<span lang=EN-US> <br>
</span> 说明为<span lang=EN-US> __pascal</span>,因此产生了大写函数名。这样链接程序不会出错。<span
lang=EN-US> <br>
<br>
5. </span>动态调用例子<span lang=EN-US> <br>
VC DLL </span>的代码如下:<span lang=EN-US> <br>
extern "C" __declspec(dllexport) LPSTR __stdcall
BCBLoadVCWin32Stdcall() <br>
{ <br>
static char strRetStdcall[256] = "BCB Load VC_Win32 Dll by __stdcall
mode is OK!"; <br>
<br>
return strRetStdcall; <br>
} <br>
<br>
extern "C" __declspec(dllexport) LPSTR __cdecl
BCBLoadVCWin32Cdecl() <br>
{ <br>
static char strRetCdecl[256] = "BCB Load VC_Win32 Dll by __cdecl mode is
OK!"; <br>
<br>
return strRetCdecl; <br>
} <br>
<br>
extern "C" __declspec(dllexport) LPSTR __fastcall
BCBLoadVCWin32Fastcall() <br>
{ <br>
static char strRetFastcall[256] = "BCB Load VC_Win32 Dll by __fastcall
mode is OK!"; <br>
<br>
return strRetFastcall; <br>
} <br>
<br>
</span> 其实动态调用与调用<span lang=EN-US> BCB </span>编写的<span lang=EN-US> DLL </span>没有区别,关键是查看<span
lang=EN-US> DLL </span>的导出函数名字<span lang=EN-US> <br>
</span> 可以使用<span lang=EN-US> tdump.exe(BCB</span>工具<span lang=EN-US>) </span>或者<span
lang=EN-US> dumpbin.exe(VC</span>工具<span lang=EN-US>) </span>查看<span
lang=EN-US> <br>
</span> <span lang=EN-US> tdump -ee MyDll.dll >1.txt (</span>查看<span
lang=EN-US> 1.txt </span>文件即可<span lang=EN-US>) <br>
</span> 由于<span lang=EN-US> VC6 </span>不支持<span lang=EN-US> __pascall </span>方式,下面给出一个三种方式的例子<span
lang=EN-US> <br>
void __fastcall TForm1::btnBLVCWin32DynClick(TObject *Sender) <br>
{ <br>
</span> <span lang=EN-US>/*cmd: tdbump VCWin32.dll >1.txt <br>
Turbo Dump Version 5.0.16.4 Copyright (c) 1988, 1998 Borland International <br>
</span> <span lang=EN-US>Display of File VCWIN32.DLL <br>
<br>
EXPORT ord:0000='BCBLoadVCWin32Fastcall::' <br>
EXPORT ord:0001='BCBLoadVCWin32Cdecl' <br>
EXPORT ord:0002='_BCBLoadVCWin32Stdcall@0' <br>
</span> <span lang=EN-US>*/ <br>
</span> <span lang=EN-US>if ( !DllInst ) <br>
</span> <span lang=EN-US>DllInst = LoadLibrary ( "VCWin32.dll" ); <br>
</span> <span lang=EN-US>if ( DllInst ) <br>
</span> <span lang=EN-US>{ <br>
</span> <span lang=EN-US>BCBLoadVCWin32Stdcall = (LPSTR (__stdcall *) () ) <br>
</span> <span lang=EN-US>GetProcAddress ( DllInst,
"_BCBLoadVCWin32Stdcall@0" ); //VC Dll <br>
</span> <span lang=EN-US>// GetProcAddress ( DllInst,
"BCBLoadVCWin32Stdcall" ); //BCB Dll <br>
</span> <span lang=EN-US>if ( BCBLoadVCWin32Stdcall ) <br>
</span> <span lang=EN-US>{ <br>
</span> <span lang=EN-US>ShowMessage( BCBLoadVCWin32Stdcall() ); <br>
</span> <span lang=EN-US>} <br>
</span> <span lang=EN-US>else ShowMessage ( "Can't find the __stdcall
Function!" ); <br>
<br>
</span> <span lang=EN-US>BCBLoadVCWin32Cdecl = (LPSTR (__cdecl *) () ) <br>
</span> <span lang=EN-US>GetProcAddress ( DllInst,
"BCBLoadVCWin32Cdecl" ); <br>
</span> <span lang=EN-US>if ( BCBLoadVCWin32Cdecl ) <br>
</span> <span lang=EN-US>{ <br>
</span> <span lang=EN-US>ShowMessage( BCBLoadVCWin32Cdecl() ); <br>
</span> <span lang=EN-US>} <br>
</span> <span lang=EN-US>else ShowMessage ( "Can't find the __cdecl
Function!" ); <br>
<br>
</span> <span lang=EN-US>//Why?</span>不是<span lang=EN-US>
'BCBLoadVCWin32Fastcall::',</span>而是<span lang=EN-US>
'@BCBLoadVCWin32Fastcall@0'</span>?<span lang=EN-US> <br>
</span> <span lang=EN-US>BCBLoadVCWin32Fastcall = (LPSTR (__fastcall *) () )
<br>
</span> <span lang=EN-US>//GetProcAddress ( DllInst,
"BCBLoadVCWin32Fastcall::" ); <br>
</span> <span lang=EN-US>GetProcAddress ( DllInst,
"@BCBLoadVCWin32Fastcall@0" ); <br>
</span> <span lang=EN-US>if ( BCBLoadVCWin32Fastcall ) <br>
</span> <span lang=EN-US>{ <br>
</span> <span lang=EN-US>ShowMessage( BCBLoadVCWin32Fastcall() ); <br>
</span> <span lang=EN-US>} <br>
</span> <span lang=EN-US>else ShowMessage ( "Can't find the __fastcall
Function!" ); <br>
</span> <span lang=EN-US>} <br>
</span> <span lang=EN-US>else ShowMessage ( "Can't find the Dll!"
); <br>
} <br>
<br>
6. </span>静态调用例子<span lang=EN-US> <br>
</span> 静态调用有点麻烦,从动态调用中可以知道导出函数的名字,但是直接时<span lang=EN-US>(</span>加入<span
lang=EN-US> lib </span>文件到工程文件<span lang=EN-US>) <br>
<br>
Linker </span>提示不能找到函数的实现<span lang=EN-US> <br>
</span> 从<span lang=EN-US> 4 </span>看出,可以加入<span lang=EN-US> def </span>文件连接<span
lang=EN-US> <br>
</span> <span lang=EN-US> (</span>可以通过<span lang=EN-US> impdef MyDll.def
MyDll.dll </span>获得导出表<span lang=EN-US>) <br>
</span> 建立与<span lang=EN-US> DLL </span>文件名一样的<span lang=EN-US> def </span>文件与<span
lang=EN-US> lib </span>文件一起加入到工程文件<span lang=EN-US> <br>
</span> 上面的<span lang=EN-US> DLL(VCWIN32.dll) </span>的<span lang=EN-US> def </span>文件为<span
lang=EN-US>(VCWIN32.def)</span>:<span lang=EN-US> <br>
LIBRARY</span> <span lang=EN-US> VCWIN32.DLL <br>
<br>
IMPORTS <br>
</span> <span lang=EN-US>@BCBLoadVCWin32Fastcall</span> <span lang=EN-US> =
VCWIN32.@BCBLoadVCWin32Fastcall@0 <br>
</span> <span lang=EN-US>_BCBLoadVCWin32Cdecl</span> <span lang=EN-US>=
VCWIN32.BCBLoadVCWin32Cdecl <br>
</span> <span lang=EN-US>BCBLoadVCWin32Stdcall</span> <span lang=EN-US> =
VCWIN32._BCBLoadVCWin32Stdcall@0 <br>
<br>
</span>对应的函数声明和实现如下:<span lang=EN-US> <br>
extern "C" __declspec(dllimport) LPSTR __fastcall
BCBLoadVCWin32Fastcall(); <br>
extern "C" __declspec(dllimport) LPSTR __cdecl
BCBLoadVCWin32Cdecl(); <br>
extern "C" __declspec(dllimport) LPSTR __stdcall
BCBLoadVCWin32Stdcall(); <br>
<br>
void __fastcall TfrmStatic::btnLoadDllClick(TObject *Sender) <br>
{ <br>
</span> <span lang=EN-US>ShowMessage ( BCBLoadVCWin32Fastcall() ); <br>
</span> <span lang=EN-US>ShowMessage ( BCBLoadVCWin32Cdecl() ); <br>
</span> <span lang=EN-US>ShowMessage ( BCBLoadVCWin32Stdcall() ); <br>
} <br>
</span>注意:在<span lang=EN-US> BCB 5.0 </span>中,可能直接按下<span lang=EN-US> F9 </span>是不能通过<span
lang=EN-US> Linker </span>的,请先<span lang=EN-US> Build </span>一次<span
lang=EN-US> <br>
</span>注:上面的程序使用<span lang=EN-US> BCB 5.0 </span>与<span lang=EN-US> VC6.0 </span>编译成功<span
lang=EN-US><o:p></o:p></span></span></p>
</td>
<td style='padding:0cm 0cm 0cm 0cm'>
<p class=MsoNormal align=left style='text-align:left;mso-pagination:widow-orphan'><span
lang=EN-US style='font-size:10.0pt;mso-font-kerning:0pt'><o:p> </o:p></span></p>
</td>
<td style='padding:0cm 0cm 0cm 0cm'>
<p class=MsoNormal align=left style='text-align:left;mso-pagination:widow-orphan'><span
lang=EN-US style='font-size:10.0pt;mso-font-kerning:0pt'><o:p> </o:p></span></p>
</td>
</tr>
</table>
</div>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
</div>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -