C++ Builder 創(chuàng)建和使用動(dòng)態(tài)加載的包 (.bpl)

C++ Builder 參考手冊 ? 創(chuàng)建和使用動(dòng)態(tài)加載的包 (.bpl)


  1. 動(dòng)態(tài)加載的包 (.bpl) 簡介
  2. 創(chuàng)建一個(gè)動(dòng)態(tài)加載的包 (.bpl)
  3. 使用動(dòng)態(tài)加載的包 (.bpl)

1. 動(dòng)態(tài)加載的包 (.bpl) 簡介

組件包文件 .bpl 不安裝在控件面板上,不通過拖拽放置在 Form 上,也沒有頭文件的情況下,只有一個(gè) .bpl 文件,通過 LoadPackage 加載,然后使用里面的函數(shù)和類。C++ 沒有原生支持的反射,不像 Delphi 直接通過類名反射出對象,但是仍然有辦法創(chuàng)建和使用動(dòng)態(tài)加載的包:

  • 動(dòng)態(tài)加載的 bpl 文件里面要有創(chuàng)建類的函數(shù),并且按照 dll 函數(shù)導(dǎo)出;
    ? Delphi 只需要知道類名就可以使用了;
    ? C++ 類名沒有用,需要知道創(chuàng)建類的對象的函數(shù)名;
  • 只要?jiǎng)?chuàng)建了對象,通過對象指針可以枚舉屬性、事件和方法;
  • 通過成員函數(shù)名 (字符串) 可以調(diào)用成員函數(shù)。

2. 創(chuàng)建一個(gè)動(dòng)態(tài)加載的包 (.bpl)

2.1. 創(chuàng)建組件包項(xiàng)目

和普通的組件包一樣:
選擇菜單 File -> New -> Package - C++ Builder
或者菜單 File -> New - Other... 在打開的對話框里面,左面的樹形結(jié)構(gòu)選擇 C++ Builder,右面選擇 Package
創(chuàng)建一個(gè) .bpl 組件包項(xiàng)目

2.2. 添加組件

和普通的組件包一樣:
選擇菜單 Component -> New Component
選擇 VCL for C++ Win32 添加一個(gè) VCL 組件,然后選擇從哪個(gè)類繼承,運(yùn)行時(shí)不可見的組件,可以選擇 TComponent 作為父類,也可以根據(jù)需要選擇其他的類作為父類。

然后:
選擇安裝在組件面板上的哪個(gè)頁面,也可以自己起個(gè)名字;
生成的源程序存放位置和文件名;

類的 __published 訪問權(quán)限的屬性、事件和方法支持通過對象指針枚舉和通過名稱 (字符串) 訪問。

2.3. 添加其他類

所有從 TObject 繼承的類的 __published 訪問權(quán)限的成員都支持枚舉屬性、事件和方法,也支持通過成員函數(shù)名調(diào)用成員函數(shù),只要給類加上 PACKAGE 屬性就可以從組件包里面導(dǎo)出,在組件包外面調(diào)用,例如:class PACKAGE THsuanluTest : public TObject { ...

2.4. 添加創(chuàng)建類的函數(shù)

創(chuàng)建類的函數(shù)必須定義為 C 語言格式 (extern "C") 和 __stdcall 調(diào)用約定,并且也需要指定 PACKAGE 屬性從包里面導(dǎo)出這個(gè)函數(shù)。

例:通過菜單 Component -> New Component 創(chuàng)建的繼承 TComponent 的類 THsuanluComponent1,寫個(gè)函數(shù) CreateComponent1 創(chuàng)建 THsuanluComponent1 類:

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

3. 使用動(dòng)態(tài)加載的包 (.bpl)

  • 使用 LoadPackage 加載 .bpl
  • 使用 GetProcAddress 通過函數(shù)名字符串獲取創(chuàng)建類的函數(shù),例如前面說的 CreateComponent1 函數(shù),然后調(diào)用這個(gè)函數(shù)創(chuàng)建類,得到 TObject 對象指針;
  • 通過 TObject 指針可以枚舉所有的屬性、事件和方法
  • 通過 TObject 指針調(diào)用 MethodAddress 方法,可以通過成員函數(shù)名字符串獲取成員函數(shù)的地址,然后通過 TObject 指針和成員函數(shù)地址合成 __closure 指針,調(diào)用這個(gè) __closure 指針即調(diào)用了這個(gè)對象的成員函數(shù)。

例子:

創(chuàng)建 HsuanluTestPackage.bpl 包:

  1. 選擇菜單 File -> New -> Package - C++ Builder 創(chuàng)建一個(gè) .bpl 組件包項(xiàng)目
  2. 選擇菜單 Component -> New Component,然后選擇 VCL for C++ Win32 添加一個(gè) VCL 組件,選擇 TComponent 作為父類,類名為 THsuanluComponent1
  3. 在生成的類里面添加 __published: 成員 void TestFunc(void);

以下為類的定義和實(shí)現(xiàn)部分、以及創(chuàng)建這個(gè)類的對象的函數(shù) 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);
}

調(diào)用 HsuanluTestPackage.bpl 包:
這個(gè)例子的目標(biāo)是調(diào)用 THsuanluComponent1::TestFunc 成員函數(shù),沒有頭文件,不知道類的具體定義和實(shí)現(xiàn)。

由于動(dòng)態(tài)加載組件包不需要頭文件和鏈接庫文件,這個(gè)例子直接在按鈕點(diǎn)擊事件里面:通過 Sysutils::LoadPackage 加載 bpl 包,通過函數(shù)名創(chuàng)建對象,通過成員函數(shù)名調(diào)用成員函數(shù),然后通過 Sysutils::FreeAndNil 釋放對象占用資源,然后通過 Sysutils::UnloadPackage 釋放 bpl 包占用的資源。

TMethod 有兩個(gè)成員:Code 和 Data,是 __closure 的組成,調(diào)用這個(gè) __closure 指針相當(dāng)于調(diào)用 Data->Code(); 其中 Data 直接指向?qū)ο?,Code 指向 MethodAddress 方法獲取的成員函數(shù)的地址。

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);
    }
}

運(yùn)行結(jié)果:

運(yùn)行結(jié)果

相關(guān):


C++ Builder 參考手冊 ? 創(chuàng)建和使用動(dòng)態(tài)加載的包 (.bpl)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容