BCB 编写 DLL 终极手册

2016-02-19 15:04 12 1 收藏

下面图老师小编跟大家分享一个简单易学的BCB 编写 DLL 终极手册教程,get新技能是需要行动的,喜欢的朋友赶紧收藏起来学习下吧!

【 tulaoshi.com - 编程语言 】

  一. 编写 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 pathBinimplib.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

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  有名字分解的函数

  @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 文件连接

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  (可以通过 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() );
}

  注意:在 BCB 5.0 中,可能直接按下 F9 是不能通过 Linker 的,请先 Build 一次

  注:上面的程序使用 BCB 5.0 与 VC6.0 编译成功

来源:https://www.tulaoshi.com/n/20160219/1608743.html

延伸阅读
标签: Delphi
  根据Delphi提供的有关 DLL编写和调用的帮助信息,你可以很快完成一般的 DLL编写和调用的 应用程序。本文介绍的主题是如何编写和调用能够传递各种参数(包括对象实例)的 DLL。例如, 主叫程序传递给 DLL一个ADOConnection 对象示例作为参数, DLL中的函数和过程调用通过该对象 实例访问数据库。 需要明确一些基本概...
虽然能用DLL实现的功能都可以用COM来替代,但DLL的优点确实不少,它更容易创建。本文将讨论如何利用VC MFC来创建不同类型的DLL,以及如何使用他们。 一、DLL的不同类型 使用VC++可以生成两种类型的DLL:MFC扩展DLL和常规DLL。常规DLL有可以分为动态连接和静态连接。Visual C++还可以生成WIN32 DLL,但不是这里讨论的主要对象。 1、MFC扩展DL...
具体的DLL封装对象请看刘艺的《Delphi中的DLL封装和调用对象技术》及配书源码。 本人在使用DELPHI编制DLL过程中碰到了些奇怪的问题,现在将其列出来,仅供参考: 1、DELPHI生成的DLL工程中写到:ShareMem must be the first unit in your library's USES clause AND your project's。这里提到的是DLL工程和使用该DLL的工程都需...
一、安装步骤:(这是废话) 1、拿到源码,要全部源码,不要那种只有部分源码的包,。 2、找到BPK文件,如果只有DPK文件,那就用DPK2BPK程序(网上下载)生成一个BPK文件,如果生成失败,那就自己新建一个包(BPK或bdsproj),把DPK文件中包含的pas文件包含进去。 3、编译与安装。 二、注意事项: 1、Bpl包的重名问题,...
如何在C#中加载自己编写的动态链接库(DLL) 李伟华 msn:liweihua200204@hotmail.com 摘要 本文主要讲述如何在C#中逐步实现加载自己用C++语言编写的动态链接库,以及在导入时如何进行C#和C++语言的数据类型匹配关键词 C# C++ 动态链接库 加载 数据类型匹配 一、发生的背景在开发新项目中使用了新的语言开发C#和新的技术方案WEB Service,但是...

经验教程

434

收藏

76

精华推荐

常用的BCB & Delphi 函数

常用的BCB & Delphi 函数

notebook无悔

BCB日常使用小集锦

BCB日常使用小集锦

龙腾盛世0416

BCB利用组件传送文件

BCB利用组件传送文件

746479077

微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部