C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Пакеты RunTime (bpl) или узкое место в Builder
Faster
  Отправлено: 17.10.2006, 05:16


Ученик-кочегар

Группа: Участник
Сообщений: 4



Доброе время суток, всезнающий All

Суть проблеы такая:
Есть приложение, фактически состоящее из Одного главного Exe и кучи библиотек, каждая собственно отдельный модуль , от транспортной мелочи до крупных программ (например база данныз со всей требухой) , так же они между собой должны вузаимодействовать вызывать процедуры друк друга и так далее ... оговорюсь колво этих модулей будет колебаться от 78 до 110 на конкретной машине ...
Много знаю ....

Аспект, реализация этой проги есть но без пакетов и без Dll и BPL в одном Exe...
Спросите в чём прикол ? да у меня не нужные куски #If ами в компеляторе отключаются и простоне компилятся и так далее, но представьте у меня уже 147 метров исходника только , можно заблудиться же , что и происходит даже супер комментарии не дают уже возможности рабоатть ... а в программу нужно ещё добавиить три модуля ...
Да ипредставьте под конкретный случай делается конкретный компил ... БРЕД, но нет другого варианта ...

Что делалось ....
Естественно когда проект достиг 20 модулей я задумался что оченьдаже было бы хорошо просто сделать единый EXEи кучу DLL можно даже былобы поручить и другим программерам писать DLL супер ...
Но вот проблема, как известно в билдере проблема с глобальными объектами и например MDICheld форму засунть в DLL правтически не возможно , а если на неё поместить какой либо компонент типа TListView то вообще проимсходят неповторимые галы ...
Все попытки найти решение не увенчались успехом , вылетали необъяснимые приколы, переполнялся CALL stack и прочее хотя на месте кода где не должнобыло такогоприсходить ...

Что хочется сделать ...
нужно весь проект перевести в BPL , так как сам билдер заботится о этих злополучных глобальных объетах, и как бы всё должно решиться .. но увы я облазил весь нет и не кто не может дать конкретные примеры ... чтобы это понять , а ресурс моего времени не позволяет тратить время на чтение литературы ....

И наконец сам вопрос :
Нужны следующие примеры кода,
1.Загрузка BPL и выгрузка
2.Вызов функции расположенной в BPL и обратно чтобы BPL могла вызвать функцию которая в вызывающем EXE
3.MDI форма главная часть в Exe а вот Детё в BPL
Ну экспорт Структур, из BPL... и классов ...

Народ я уже этак с 2 месяца пытаюсь получить примеры, многие пишут что это детский вопрос, ну коль это детский так дайте ответы ... не надо тыкать в архангельского, так как такоготам нет !!! :wall:

Примеры бы ещё и скоментариями но это уже .... :
olegenty
Отправлено: 17.10.2006, 06:38


Ветеран

Группа: Модератор
Сообщений: 2412



1. То, что ты спрашиваешь, объяснял AVC
2. Переход на ИЗД не обязателен, достаточно DLL билдить с опцией Build with runtime packages и Use dynamic RTL
3. Ну ты даёшь, у меня было значительно меньшее число модулей, когда я проект разнёс по запускающей EXE и DLL-плагинам.
Faster
Отправлено: 17.10.2006, 07:03


Ученик-кочегар

Группа: Участник
Сообщений: 4



Да вот это я называю один из вариантов чего я просил не делать ...
В ответе кроме AVC ссылки небылол ... а если ссылку цитату или пример ????
olegenty
Отправлено: 17.10.2006, 07:29


Ветеран

Группа: Модератор
Сообщений: 2412



да, и ещё, перейдя на BPL, ты всё равно сделаешь то, чего не хочешь: BPL собираются ТОЛЬКО Build with runtime packages... (вроде и только Dynamic RTL). Кроме того, при необходимости распространения BPL в рамках групповой разработки с BPL ОБЯЗАТЕЛЬНО поставлять DFM входящих форм, иначе хрена с два скомпилируешь.
AVC
Отправлено: 17.10.2006, 14:58


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE (olegenty @ 17.10.2006, 07:29)
при необходимости распространения BPL в рамках групповой разработки с BPL ОБЯЗАТЕЛЬНО поставлять DFM входящих форм, иначе хрена с два скомпилируешь.

Отнюдь. При использовании формы как черного ящика или самонастраивающихся форм достаточно просто bpi

По существу вопроса — дублирую свой пост с XPortala.
(надейсь темы с вопрсами о bpl хоть на время прекратятся)

Итак еще раз и без спешки.
Пример использования run time bpl (это лишь один из возможных путей)
Создаем каталог где будет размещаться группа проектов. Пусть это будет Faster.
(Управление удобно вести через окно группы проектов.)
В Builder’е (у меня 5) создаем новый пакет и сохраняем его как Fasbpl1.
Удаляем из списка необходимых bpl’ей все ненужные и оставляем только vcl и vclx.
В редакторе свойств проекта проверяем списки путей к include, lib и, при необходимости, настраиваем пути Final и Bpl/Lib.
В пакет, просто для примера, помещаем форму Form1 и глобальную переменную PublicString_Fast
Сохраняем в файле FBpl1.
На форме лежит Label — Label1
Код
CODE

// FBpl1.h
//---------------------------------------------------------------------------
#ifndef FBpl1H
#define FBpl1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TLabel *Label1;
private: // User declarations
public:  // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
//extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

// FBpl1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "FBpl1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

//TForm1 *Form1;

extern PACKAGE AnsiString PublicString_Fast;
// либо #include "EtrFBpl1.h"
AnsiString PublicString_Fast =  = "aaa";

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
}

//---------------------------------------------------------------------------


Задумываем и разрабатываем «внешнюю» функцию для управления формой в пакете и помещаем её в FBpl1.cpp
CODE

PACKAGE void __fastcall FBpl1Show (void)
{
TForm1 *frm = new TForm1(Application);
try {
frm->Label1->Caption = PublicString_Fast;
frm->ShowModal();
}
__finally {delete frm;}
}

Что она делает, надеюсь, комментировать не нужно.

Для собственного удобства создаем h файл с описанием возможностей пакета — EtrFBpl1.h
Содержание
CODE

#ifndef EtrFBpl1H
#define EtrFBpl1H
extern PACKAGE AnsiString PublicString_Fast;
extern PACKAGE void __fastcall FBpl1Show (void);

(Обратите особое внимание на использование PACKAGE с переменной)

Строим пакет.
Обычно, при первых построениях, я еще закрываю Builder и поправляю файл bpk. Засоряется зараза J . Тэги LIBRARIES, SPARELIBS
После построения в каталоге где сказано, обычно я указываю каталог проекта, должны появиться файлы Fastbpl1.lib, Fastbpl1.bpl и Fastbpl1.bpi.



Переходим к написанию ведущего приложения
Добавляем в группу проектов нового члена — Application
Сохраняем новую форму под именем FMain, а новый проект Fastexe
В редакторе свойств проекта опять проверяем пути и на странице «Пакеты» ставим галочку Build with runtime packages. В списке runtime packages должны быть теже, что и у пакета плюс добавляем сюда наш пакет Fastbpl1. Строка выглядит так VCL50;VCLX50;Fastbpl1.
Переходим к работе с пакетом.
Для этого в файл главной формы приложения добавляем строку
#include "EtrFBpl1.h"
И на самой форме делаем кнопку для тестирования — Bt_RtBpl
CODE

//---------------------------------------------------------------------------
void __fastcall TF_Main::Bt_RtBplClick(TObject *Sender)
{
static int i = 0;
if (i == 0) FBpl1Show();

i++;
PublicString_Fast = "i = " + AnsiString(i);
FBpl1Show();
}
//---------------------------------------------------------------------------

Строим приложение и наслаждаемся результатом. Если нет ошибок в настройке путей (счастливчик если так), то все получается сразу.

Теперь часть два — как работать с загружаемыми bpl’ями.
Так же как и с dll’ями, но немного другими функциями загрузки / выгрузки.
Для примера создадим другую bpl — Fastbpl2.
[/code]
//---------------------------------------------------------------------------
#include
#pragma hdrstop

#include "FBpl2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner) : TForm(Owner)
{
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

PACKAGE void __fastcall FBpl2Show (void)
{
TForm2 *frm = new TForm2(Application);
try {
frm->ShowModal();
}
__finally {delete frm;}
}

//---------------------------------------------------------------------------[/code]

Теперь слегка модифицируем приложение
Добавим в него unit который инкапсулирует функции вызова функций из загружаемого пакета — LEUPFun.cpp
(LEUP — это Load Execute Unload Package)
Там размещаем функции
CODE

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "LEUPFun.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//      Динамические BPL's
//              Загрузить bpl   (если нужно)
//              Выполнить ???
//              Выгрузить bpl   (если был загружен)
//
//      Магические символы, добавляемые к названию функции при записи в библиотеку
//      Для:
//              extern "C" __declspec(dllexport) void Name (void);              "_Name"
//
//              PACKAGE void __fastcall Name (void);                            "@Name$qqrv"
//              PACKAGE void __fastcall Name (int);                             "@Name$qqri"
//              PACKAGE void __fastcall Name (const int);                       "@Name$qqrxi"
//              PACKAGE void __fastcall Name (const long);                      "@Name$qqrxl"
//- — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - -

int __fastcall  loc_LEUP_GetFunAdr      (const AnsiString& funname,
                                        AnsiString&       bplname,
                                        int*              hlib
                                       )
{
int  funadr = 0;
   *hlib   = 0;

if (funname.IsEmpty())          return funadr;

int             i;
AnsiString      str;

bplname = bplname.Trim().UpperCase();
if (!bplname.IsEmpty())
       if (bplname.Pos(".") <= 0)      bplname += ".BPL";

AnsiString      nativefunname = funname.SubString(2,10000);
i = nativefunname.Pos("$");
if (i > 1)      nativefunname = nativefunname.SubString(1,i-1);

//                                                          Поиск в загруженных
TLibModule      *lbm;
char             buf [255];
bool             bplinmemory = false;

funadr = 0;
for (lbm = LibModuleList; lbm; lbm = lbm->next)
{      funadr = (int)GetProcAddress((HMODULE)lbm->instance, funname.c_str());
       if (funadr)             break;
       if (!bplname.IsEmpty())
        {      GetModuleFileName((HMODULE)lbm->instance, buf, 254);
               str = AnsiString(buf).Trim().UpperCase();
               if (str.IsEmpty())              continue;
               str = ExtractFileName(str);
               if (str == bplname)             bplinmemory = true;
        }
}

if (funadr)             return funadr;

if (bplname.IsEmpty() || bplinmemory)
{      str = AnsiString("Function ") + nativefunname + " not found in active modules list";
       throw Exception(str);
}

//                                                                 Подгрузить библиотеку

try                     {       *hlib = (int)LoadPackage(bplname);      }
catch (Exception &xcp)
{      *hlib = 0;
       throw Exception(AnsiString("LoadPackage (") + bplname + ")\r\n" + xcp.Message);
}

if (*hlib <= 32)
{      str = AnsiString("Can't load package ") + bplname;
       *hlib = 0;
       throw Exception(str);
}

funadr = (int)GetProcAddress((HMODULE)(*hlib), funname.c_str());

if (!funadr)
{      try                     {       UnloadPackage((HINST)(*hlib));  }
       catch (...)     {}
       *hlib = 0;
       str = AnsiString("Function ") + nativefunname + " not found in module " + bplname;
       throw Exception(str);
}

return funadr;
}

//- — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - -

PACKAGE int __fastcall  LEUP_GetFunAdr  (const AnsiString&      pfunname,
                                        const AnsiString&      pbplname,
                                        int*                   phlib
                                       )
{
AnsiString      funname = pfunname.Trim();
AnsiString      bplname = pbplname.Trim().UpperCase();

return loc_LEUP_GetFunAdr(funname, bplname, phlib);
}

//- — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - — - -
//                                                  void _fastcall ??? (void)

PACKAGE void __fastcall LEUP_void (const AnsiString& pfunname, const AnsiString& pbplname)
{
if (pfunname.IsEmpty())         return;

AnsiString      funname = pfunname.Trim();
AnsiString      bplname = pbplname.Trim().UpperCase();
int                     funadr;
int                     hlib;
bool            aspkg;
AnsiString      errmsg = "";

if (funname.SubString(1,1) == "_")              aspkg = false;          // C   style name
else                                                                    // bpl style name
{      funname = AnsiString("@") + funname + "$qqrv";
       aspkg = true;
}

funadr = loc_LEUP_GetFunAdr(funname, bplname, &hlib);

try
{      if (funadr)
        {      if (aspkg)      ((void __fastcall (*)(void))funadr)();
               else            ((void __stdcall  (*)(void))funadr)();
        }
}
catch (Exception &xcp)  {       errmsg = xcp.Message;   }

if (hlib)
{      try                     {       UnloadPackage((HINST)hlib);     }
       catch (...)     {}
}

if (!errmsg.IsEmpty())          throw Exception(errmsg);
return;
}
//---------------------------------------------------------------------------


А на главное окно приложения добавим кнопку Bt_DynBpl
CODE

void __fastcall TF_Main::Bt_DynBplClick(TObject *Sender)
{
LEUP_void("FBpl2Show", "Fastbpl2");
}

И все.


Для использования в пакете 2 переменных из runtime пакета 1 надо к 2 подключить 1 bpi (Опция управления пакетом Requires Add).
Пример (проект Fastbpl2 файл FBpl2)
CODE

#include "EtrFBpl1.h"
//---------------------------------------------------------------------------

PACKAGE void __fastcall FBpl2Show2 (void)
{
TForm2 *frm = new TForm2(Application);
try {
frm->Label1->Caption = PublicString_Fast;
frm->ShowModal();
}
__finally {delete frm;}
}




Ну и наконец по поводу вызова из bpl функций — размещенных в exe. Есть два пути — вынести все что можно из exe в bpl ну а дальше понятно или отыскивать функции по их именам (GetProcAddress).
Пример второго варианта.
Пусть в приложении есть следующий код
CODE

static int Test_Val = 0;

PACKAGE int  __fastcall Test_Get (void)     { return Test_Val; }
PACKAGE void __fastcall Test_Set (int pint) { Test_Val = pint; }


Вызывать функции Test_Get и Test_Set из bpl2 можно так
CODE

typedef int  _fastcall(*Test_Get)(void);
typedef void _fastcall(*Test_Set)(int );

void __fastcall TForm2::Bt_GetClick(TObject *Sender)
{
int hlib;
int fadr = 0;
for (TLibModule *lbm = LibModuleList; lbm; lbm = lbm->next)
{ fadr = (int)GetProcAddress((HMODULE)lbm->instance, "@Test_Get$qqrv");
if (fadr)  break;
}

Label1->Caption = (fadr)? AnsiString((Test_Get(fadr))()) : AnsiString("Get функция не найдена");
}

//---------------------------------------------------------------------------

void __fastcall TForm2::Bt_SetClick(TObject *Sender)
{
static int NN = 0;
NN += 7;
int hlib;
for (TLibModule *lbm = LibModuleList; lbm; lbm = lbm->next)
{ int fadr = (int)GetProcAddress((HMODULE)lbm->instance, "@Test_Set$qqri");
if (fadr)
    { ((void __fastcall (*)(int))fadr)(NN);
       break;
    }
}
}
//---------------------------------------------------------------------------


Для того, что бы посмотреть реальные имена глобальных символов в exe / dll / bpl можно воспользоваться утилитом Impdef командная сторка имеет вид Impdef -h Fastexe.def Fastexe.exe> nul

Уф ... Кажется все.
Текст проекта прикладываю.

User Attached Image Скачать файл
exe_bpl.rar


Faster
Отправлено: 19.10.2006, 02:32


Ученик-кочегар

Группа: Участник
Сообщений: 4



Спасибо AVC ноя просто создавал разом кучу постов в разных форумах .. так чтоне обессуть за дубли я думаю не надо отвечать копиями на все smile.gif

Спасибо ещё раз ...
AVC
Отправлено: 19.10.2006, 09:49


Ветеран

Группа: Модератор
Сообщений: 1583



На здоровье, лишь бы помогло.

На разных форумах — разные посетители, а вопросы по этой теме возникают неоднократно.
+ здесь к ответу можно прикрепить файл.
vvv40
Отправлено: 15.11.2006, 15:06


Ученик-кочегар

Группа: Участник
Сообщений: 8



а как сделать MDI форму главную часть в Exe а Child в BPL ?
( т.е. в данном примере MIDIForm в Fastexe.exe,
MIDIChild в Fastbpl1.bpl или Fastbpl2.bpl )
olegenty
Отправлено: 15.11.2006, 15:09


Ветеран

Группа: Модератор
Сообщений: 2412



лучше всего никак и MDI больше вообще не поддерживать: от них даже Microsoft открещивается, как от тупиковой ветви эволюции

Вернуться в Вопросы программирования в C++Builder