[ZZ]BCB编写DLL终极手册

2023-10-31 15:38
文章标签 dll 编写 手册 终极 zz bcb

本文主要是介绍[ZZ]BCB编写DLL终极手册,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 BCB编写DLL终极手册
由于现在比较多的网友老是在 CSDN 上询问关于 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() );
}
注意:在 BCB 5.0 中,可能直接按下 F9 是不能通过 Linker 的,请先 Build 一次
注:上面的程序使用 BCB 5.0 与 VC6.0 编译成功 
 

这篇关于[ZZ]BCB编写DLL终极手册的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/316366

相关文章

电脑找不到mfc90u.dll文件怎么办? 系统报错mfc90u.dll丢失修复的5种方案

《电脑找不到mfc90u.dll文件怎么办?系统报错mfc90u.dll丢失修复的5种方案》在我们日常使用电脑的过程中,可能会遇到一些软件或系统错误,其中之一就是mfc90u.dll丢失,那么,mf... 在大部分情况下出现我们运行或安装软件,游戏出现提示丢失某些DLL文件或OCX文件的原因可能是原始安装包

电脑显示mfc100u.dll丢失怎么办?系统报错mfc90u.dll丢失5种修复方案

《电脑显示mfc100u.dll丢失怎么办?系统报错mfc90u.dll丢失5种修复方案》最近有不少兄弟反映,电脑突然弹出“mfc100u.dll已加载,但找不到入口点”的错误提示,导致一些程序无法正... 在计算机使用过程中,我们经常会遇到一些错误提示,其中最常见的就是“找不到指定的模块”或“缺少某个DL

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Python虚拟环境终极(含PyCharm的使用教程)

《Python虚拟环境终极(含PyCharm的使用教程)》:本文主要介绍Python虚拟环境终极(含PyCharm的使用教程),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录一、为什么需要虚拟环境?二、虚拟环境创建方式对比三、命令行创建虚拟环境(venv)3.1 基础命令3

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

Python使用date模块进行日期处理的终极指南

《Python使用date模块进行日期处理的终极指南》在处理与时间相关的数据时,Python的date模块是开发者最趁手的工具之一,本文将用通俗的语言,结合真实案例,带您掌握date模块的六大核心功能... 目录引言一、date模块的核心功能1.1 日期表示1.2 日期计算1.3 日期比较二、六大常用方法详

mss32.dll文件丢失怎么办? 电脑提示mss32.dll丢失的多种修复方法

《mss32.dll文件丢失怎么办?电脑提示mss32.dll丢失的多种修复方法》最近,很多电脑用户可能遇到了mss32.dll文件丢失的问题,导致一些应用程序无法正常启动,那么,如何修复这个问题呢... 在电脑常年累月的使用过程中,偶尔会遇到一些问题令人头疼。像是某个程序尝试运行时,系统突然弹出一个错误提

电脑提示找不到openal32.dll文件怎么办? openal32.dll丢失完美修复方法

《电脑提示找不到openal32.dll文件怎么办?openal32.dll丢失完美修复方法》openal32.dll是一种重要的系统文件,当它丢失时,会给我们的电脑带来很大的困扰,很多人都曾经遇到... 在使用电脑过程中,我们常常会遇到一些.dll文件丢失的问题,而openal32.dll的丢失是其中比较

电脑win32spl.dll文件丢失咋办? win32spl.dll丢失无法连接打印机修复技巧

《电脑win32spl.dll文件丢失咋办?win32spl.dll丢失无法连接打印机修复技巧》电脑突然提示win32spl.dll文件丢失,打印机死活连不上,今天就来给大家详细讲解一下这个问题的解... 不知道大家在使用电脑的时候是否遇到过关于win32spl.dll文件丢失的问题,win32spl.dl

Spring Boot中定时任务Cron表达式的终极指南最佳实践记录

《SpringBoot中定时任务Cron表达式的终极指南最佳实践记录》本文详细介绍了SpringBoot中定时任务的实现方法,特别是Cron表达式的使用技巧和高级用法,从基础语法到复杂场景,从快速启... 目录一、Cron表达式基础1.1 Cron表达式结构1.2 核心语法规则二、Spring Boot中定