C++ Builder 参考手册 ➙ 创建和使用动态加载的包 (.bpl)
- 动态加载的包 (.bpl) 简介
- 创建一个动态加载的包 (.bpl)
- 使用动态加载的包 (.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 包:
- 选择菜单 File -> New -> Package - C++ Builder 创建一个 .bpl 组件包项目
- 选择菜单 Component -> New Component,然后选择 VCL for C++ Win32 添加一个 VCL 组件,选择 TComponent 作为父类,类名为 THsuanluComponent1
- 在生成的类里面添加 __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)