C++ Builder 创建和使用动态加载的包 (.bpl)

C++ Builder 参考手册 ➙ 创建和使用动态加载的包 (.bpl)


  1. 动态加载的包 (.bpl) 简介
  2. 创建一个动态加载的包 (.bpl)
  3. 使用动态加载的包 (.bpl)

1. 动态加载的包 (.bpl) 简介

组件包文件 .bpl 不安装在控件面板上,不通过拖拽放置在 Form 上,也没有头文件的情况下,只有一个 .bpl 文件,通过 LoadPackage 加载,然后使用里面的函数和类。C++ 没有原生支持的反射,不像 Delphi 直接通过类名反射出对象,但是仍然有办法创建和使用动态加载的包:

  • 动态加载的 bpl 文件里面要有创建类的函数,并且按照 dll 函数导出;
    • Delphi 只需要知道类名就可以使用了;
    • C++ 类名没有用,需要知道创建类的对象的函数名;
  • 只要创建了对象,通过对象指针可以枚举属性、事件和方法;
  • 通过成员函数名 (字符串) 可以调用成员函数。

2. 创建一个动态加载的包 (.bpl)

2.1. 创建组件包项目

和普通的组件包一样:
选择菜单 File -> New -> Package - C++ Builder
或者菜单 File -> New - Other... 在打开的对话框里面,左面的树形结构选择 C++ Builder,右面选择 Package
创建一个 .bpl 组件包项目

2.2. 添加组件

和普通的组件包一样:
选择菜单 Component -> New Component
选择 VCL for C++ Win32 添加一个 VCL 组件,然后选择从哪个类继承,运行时不可见的组件,可以选择 TComponent 作为父类,也可以根据需要选择其他的类作为父类。

然后:
选择安装在组件面板上的哪个页面,也可以自己起个名字;
生成的源程序存放位置和文件名;

类的 __published 访问权限的属性、事件和方法支持通过对象指针枚举和通过名称 (字符串) 访问。

2.3. 添加其他类

所有从 TObject 继承的类的 __published 访问权限的成员都支持枚举属性、事件和方法,也支持通过成员函数名调用成员函数,只要给类加上 PACKAGE 属性就可以从组件包里面导出,在组件包外面调用,例如:class PACKAGE THsuanluTest : public TObject { ...

2.4. 添加创建类的函数

创建类的函数必须定义为 C 语言格式 (extern "C") 和 __stdcall 调用约定,并且也需要指定 PACKAGE 属性从包里面导出这个函数。

例:通过菜单 Component -> New Component 创建的继承 TComponent 的类 THsuanluComponent1,写个函数 CreateComponent1 创建 THsuanluComponent1 类:

extern "C" TObject *PACKAGE __stdcall CreateComponent1(void)
{
    return new THsuanluComponent1(NULL);
}

3. 使用动态加载的包 (.bpl)

  • 使用 LoadPackage 加载 .bpl
  • 使用 GetProcAddress 通过函数名字符串获取创建类的函数,例如前面说的 CreateComponent1 函数,然后调用这个函数创建类,得到 TObject 对象指针;
  • 通过 TObject 指针可以枚举所有的属性、事件和方法;
  • 通过 TObject 指针调用 MethodAddress 方法,可以通过成员函数名字符串获取成员函数的地址,然后通过 TObject 指针和成员函数地址合成 __closure 指针,调用这个 __closure 指针即调用了这个对象的成员函数。

例子:

创建 HsuanluTestPackage.bpl 包:

  1. 选择菜单 File -> New -> Package - C++ Builder 创建一个 .bpl 组件包项目
  2. 选择菜单 Component -> New Component,然后选择 VCL for C++ Win32 添加一个 VCL 组件,选择 TComponent 作为父类,类名为 THsuanluComponent1
  3. 在生成的类里面添加 __published: 成员 void TestFunc(void);

以下为类的定义和实现部分、以及创建这个类的对象的函数 CreateComponent1,其他部分省略

class PACKAGE THsuanluComponent1 : public TComponent
{
private:
protected:
public:
    __fastcall THsuanluComponent1(TComponent* Owner);
__published:
    void TestFunc(void);
};
__fastcall THsuanluComponent1::THsuanluComponent1(TComponent* Owner)
    : TComponent(Owner)
{
}
//---------------------------------------------------------------------------
void THsuanluComponent1::TestFunc(void)
{
    ::MessageBox(NULL, L"测试组件包:THsuanluComponent1::TestFunc", L"TestFunc - 玄坴", MB_OK|MB_ICONINFORMATION);
}
//---------------------------------------------------------------------------
extern "C" TObject *PACKAGE __stdcall CreateComponent1(void)
{
    return new THsuanluComponent1(NULL);
}

调用 HsuanluTestPackage.bpl 包:
这个例子的目标是调用 THsuanluComponent1::TestFunc 成员函数,没有头文件,不知道类的具体定义和实现。

由于动态加载组件包不需要头文件和链接库文件,这个例子直接在按钮点击事件里面:通过 Sysutils::LoadPackage 加载 bpl 包,通过函数名创建对象,通过成员函数名调用成员函数,然后通过 Sysutils::FreeAndNil 释放对象占用资源,然后通过 Sysutils::UnloadPackage 释放 bpl 包占用的资源。

TMethod 有两个成员:Code 和 Data,是 __closure 的组成,调用这个 __closure 指针相当于调用 Data->Code(); 其中 Data 直接指向对象,Code 指向 MethodAddress 方法获取的成员函数的地址。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    try
    {
        NativeUInt hPackage = Sysutils::LoadPackage(L"HsuanluTestPackage.bpl");
        TObject *__stdcall (*pfCreate)(void) = (TObject *__stdcall(*)(void))::GetProcAddress((HINSTANCE)hPackage, "CreateComponent1");
        if(pfCreate)
        {
            TObject *pComponent = pfCreate();

            TMethod Method;
            Method.Code = pComponent->MethodAddress(L"TestFunc");
            Method.Data = pComponent;

            typedef void (__closure *pFunc)(void);
            (*(pFunc*)&Method)();

            Sysutils::FreeAndNil(&pComponent);
        }
        Sysutils::UnloadPackage(hPackage);
    }
    catch(Exception &e)
    {
        ShowMessage(e.Message);
    }
}

运行结果:

运行结果

相关:

  • System::Sysutils::LoadPackage
  • System::Sysutils::UnloadPackage
  • System::Sysutils::InitializePackage
  • System::Sysutils::FinalizePackage
  • System::Sysutils::GetModuleName
  • System::Sysutils::GetPackageInfo
  • System::Sysutils::GetPackageDescription
  • System::Sysutils::GetPackageTargets
  • System::Sysutils
  • C++ Builder 本地化 (多语言) 功能
  • C++ Builder 的字符串类型、字符类型、字符编码
  • C++ Builder 的反射 (三) - 通用 Reflection Factory
  • C++ Builder 的反射 (二) - Reflection Factory
  • C++ Builder 的反射 (一) - Reflection 简单实现
  • 枚举控件所有的属性、事件和方法
  • 枚举窗口内所有的控件
  • C++ Builder 的枚举类型
  • C / C++ 可变参数的函数
  • C / C++ 可变参数的宏,__VA_ARGS__,...
  • C++ 可变参数的模板
  • C++ Builder 的 PME 架构

C++ Builder 参考手册 ➙ 创建和使用动态加载的包 (.bpl)

你可能感兴趣的:(C++ Builder 创建和使用动态加载的包 (.bpl))