C++Builder
  Начало   Форум   Помощь Войти Регистрация  
Страниц: 1 [2]   Вниз
  Печать  
Автор Тема: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений  (Прочитано 33929 раз)
AVC
Администратор


Сказали спасибо: +28/-6
Offline

Сообщений: 1189


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #30 : 09 Ноябрь 2007, 12:33:37 »

Т.е. вы согласны таскать свои а Борланд пусть "обломится"? 

Я думаю, что можно сделать вариант, когда все необходимое включено в один из своих модулей, а все остальные надстройки, в том числе и сам exe'шник ссылается на эту кучу. Но эта ручная гемморная работа, которую, с моей точки зрения, можно легко заменить просто тасканием борландовских bpl'й. Особенно в условиях, когда критится более одного приложения с этими пакетами.
Записан
oxotnik
Глобальный модератор


Сказали спасибо: +100/-15
Offline

Сообщений: 2425


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #31 : 09 Ноябрь 2007, 14:07:25 »

я же не таскаю за собой *.срр и *.hpp... и прочие *.obj (а они для проекта ой как нужны)
так почему нельзя сделать что бы и bpl не приклеивались (это вопрос скорей к борланду)
Записан
oxotnik
Глобальный модератор


Сказали спасибо: +100/-15
Offline

Сообщений: 2425


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #32 : 17 Декабрь 2007, 10:00:18 »

поясните плз:
почему в длл
Код:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
        return 1;
}
выполняется не при FreeLibrary(...) а при закрытии основного приложения ?!
Записан
Termi_uc
Давний друг


Сказали спасибо: +1/-0
Offline

Сообщений: 86


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #33 : 18 Декабрь 2007, 11:24:03 »

Цитата: oxotnik от 09 Ноябрь 2007, 14:07:25
я же не таскаю за собой *.срр и *.hpp... и прочие *.obj (а они для проекта ой как нужны)
...
Для проекта конечно нужны, а вот для работы приложения - нет.

Цитата: oxotnik от 09 Ноябрь 2007, 14:07:25
...
так почему нельзя сделать что бы и bpl не приклеивались (это вопрос скорей к борланду)
Это ты сам решаешь - приклеивать их или нет. Просто иногда лучше разделять приложение на исполняемый модуль и набор библиотек (BPL - это та же библиотека DLL, только с некоторыми функциями "По-умолчанию").

Цитата: oxotnik от 17 Декабрь 2007, 10:00:18
поясните плз:
почему в длл
Код:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
        return 1;
}
выполняется не при FreeLibrary(...) а при закрытии основного приложения ?!
Ошибка в коде?
Воспользуйся этом простым кодом для того, чтобы посмотреть работает твой вызов FreeLibrary или нет.
Код:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
switch(reason)
    {
    case DLL_PROCESS_ATTACH:
        ShowMessage("DLL_PROCESS_ATTACH");
        break;
    case DLL_THREAD_ATTACH:
        ShowMessage("DLL_THREAD_ATTACH");
        break;
    case DLL_THREAD_DETACH:
        ShowMessage("DLL_THREAD_DETACH");
        break;
    case DLL_PROCESS_DETACH:
        ShowMessage("DLL_PROCESS_DETACH");
        break;
    }
        return 1;
}
(или просто Break Point в отладчике)
Записан
oxotnik
Глобальный модератор


Сказали спасибо: +100/-15
Offline

Сообщений: 2425


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #34 : 18 Декабрь 2007, 11:38:24 »

Цитировать
(или просто Break Point в отладчике)

как раз бейкпоитном и узнал, что после FreeLibrary на момент закрытия приложения (библиотека давно и успешно была выгружена) вызвается ф-я инициализации длл... с какго фига то???
Записан
Termi_uc
Давний друг


Сказали спасибо: +1/-0
Offline

Сообщений: 86


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #35 : 18 Декабрь 2007, 12:33:01 »

Я не понимаю, ты же говоришь, что библиотека бавно выгружена - тогда как  может вызыватся ЕЁ функция?
Повторюсь: Может ошибка в коде?

Проверь можешь ты удалить/переместить твою библиотеку, когда ты думаешь, что она выгружена.

Вообще DllEntryPoint не вызывается автоматически, если ты загрузил DLL динамически и забыл выгрузить и закрываешь свою прогу. Проверено опытным путем  .
Записан
oxotnik
Глобальный модератор


Сказали спасибо: +100/-15
Offline

Сообщений: 2425


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #36 : 18 Декабрь 2007, 13:48:13 »

Цитата: Termi_uc от 18 Декабрь 2007, 12:33:01
Я не понимаю, ты же говоришь, что библиотека бавно выгружена - тогда как  может вызыватся ЕЁ функция?
Повторюсь: Может ошибка в коде?

Проверь можешь ты удалить/переместить твою библиотеку, когда ты думаешь, что она выгружена.

Вообще DllEntryPoint не вызывается автоматически, если ты загрузил DLL динамически и забыл выгрузить и закрываешь свою прогу. Проверено опытным путем  .

в том то и весь вопрос: какого ... вызвывется DllEntryPoint, если библиотека выгружена ?

ЗЫ: Проверял в пошаговом режиме.
ЗЫЗЫ: Загружаю и выгружаю один раз
ЗЫЗЫЗЫ: Проверкой на удаление ничего не добъешься, т.к. система не сразу "отпускает" файл dll
« Последнее редактирование: 18 Декабрь 2007, 14:07:04 от oxotnik » Записан
AVC
Администратор


Сказали спасибо: +28/-6
Offline

Сообщений: 1189


« Ответ #37 : 18 Декабрь 2007, 17:18:57 »

Давно использую, ни разу на такие грабли не наступал при выполнении условия - что сам грузил, сам и выгружай, что грузил борланд (упоминалось в build with runtime.. или еще где-то) не трогать - сам выгрузит.

PS. к сведению, вдруг да поможет
Еще у 5-ки есть такая особенность: если в рамках основного приложения вручную создан модуль данных, то разрушать его не надо - приложение это сделает само или будет "стучать ногами"

PPS. Вот код (работающий кусок), который показывает загруженные в настоящий момент библиотеки
Код:
//---------------------------------------------------------------------------
// Получить список загруженных модулей (exe, bpl, dll)
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

TStringList *lst = (TStringList*)Memo1->Lines;
lst->Clear();

char buf [255];
TLibModule *lbm;
AnsiString fname, sname, path, drv, dir, name, ext;

for (lbm = LibModuleList; lbm; lbm = lbm->next)
 {  GetModuleFileName((HMODULE)lbm->instance, buf, 254);
    fname = AnsiString(buf).Trim().UpperCase();
    if (!fname.IsEmpty())  lst->Add(AnsiString(int(lbm->instance)) + ": " + fname);
 }
Записан
oxotnik
Глобальный модератор


Сказали спасибо: +100/-15
Offline

Сообщений: 2425


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #38 : 25 Декабрь 2007, 17:39:26 »

Еще одна проблема назрела:

Передаю в a-ю длл TObject *Sender
в самой длл:
TComboBox *cmb = dynamic_cast (Sender);
никак не хочет работать (cmb = NULL)
а вот так:
TComboBox *cmb = (TComboBox *) Sender;
работает
Что за грабли?
Записан
AVC
Администратор


Сказали спасибо: +28/-6
Offline

Сообщений: 1189


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #39 : 25 Декабрь 2007, 17:55:42 »

Возможно где-то указатель был приведен просто к void* или int или чему еще достаточно простому.
Записан
oxotnik
Глобальный модератор


Сказали спасибо: +100/-15
Offline

Сообщений: 2425


Re: Фундаментальные основы DLL/VCL плагинов. Несколько вопросов и соображений
« Ответ #40 : 25 Декабрь 2007, 20:00:58 »

Цитата: AVC от 25 Декабрь 2007, 17:55:42
Возможно где-то указатель был приведен просто к void* или int или чему еще достаточно простому.
нет, как получаю в функции-событии так же и передаю:
Код:
void __fastcall TForm1::BtnOnClick (TObject *Sender)
{
...
FuncFromDLL(Sender);
....
}
Записан
olegenty
Администратор


Сказали спасибо: +16/-7
Offline

Сообщений: 520


Re: Фундаментальные основы DLL/VCL плагинов. Неско&
« Ответ #41 : 01 Октябрь 2008, 14:50:27 »

Мой вариант - отдельная библиотека.

1. Есть некий класс-адаптер, с определенным интерфейсом регистрируемых в нем функций.
Код:
////////////////////////////////////////////////////////////////////////////
//
// Тип-указатель на некую адаптерную функцию со вполне конкретным типом
// аргумента и результатом
//
typedef boost::function1 TFuncType;

////////////////////////////////////////////////////////////////////////////
//
// Тип функции
//
typedef enum TFuncKind {
                         fkNull       = 0 // Нифига нет, указатель тупо указывает в небо
                       , fkExecutable = 1 // Функция, реализованная в EXE
                       , fkExport     = 2 // Функция, реализованная в DLL и экспортируемая оттуда
                       , fkService    = 3 // Сервисная функция (живет в течение жизни модуля, где зарегистрирована)
                       } TFuncKind;


////////////////////////////////////////////////////////////////////////////
//
// Класс, инкапсулирующий функцию.
//
class PACKAGE CFunc
{
public:
    CFunc();
    CFunc(const CFunc&);
    CFunc(TFuncKind Kind, TFuncType Func);
    TFuncKind const Kind();
    TFuncType const Func();
private:
    TFuncKind Kind_;
    TFuncType Func_;
};

////////////////////////////////////////////////////////////////////////////
//
// Шаблон будущего адаптера
//
class PACKAGE CAdapter: public OInstanceLocker
{
public:
    typedef CFunc                     FuncType;
    typedef map FuncsType;
    typedef FuncsType::size_type      SizeType;

    bool Exists(const AnsiString Name);
    bool Register(const AnsiString Name, const TFuncKind Kind, const TFuncType Func);
    bool Unregister(const AnsiString Name);
    virtual TFuncType::result_type operator()(const AnsiString Name, TFuncType::argument_type);
    SizeType Count();
protected:
    FuncType& operator[](AnsiString Index);
private:
    FuncsType Funcs_;
    FuncType  NullFunc_;
};

Функции регистрируются там так:
Код:
extern "C" __declspec (dllexport) bool __stdcall Register(OTCAdapter::CAdapter* const Adapter)
{
    try
    {
        Adapter->Register("Task.Set"                    , OAdapter::fkExport, boost::bind<:prestable>(TaskSet                    , Adapter, _1));
        Adapter->Register("Task.Default"                , OAdapter::fkExport, boost::bind<:prestable>(TaskDefault                , Adapter, _1));
        Adapter->Register("Task.Examine"                , OAdapter::fkExport, boost::bind<:prestable>(TaskExamine                , Adapter, _1));
        Adapter->Register("Task.MessageExamine"         , OAdapter::fkExport, boost::bind<:prestable>(TaskMessageExamine         , Adapter, _1));
        Adapter->Register("Task.Cancel"                 , OAdapter::fkExport, boost::bind<:prestable>(TaskCancel                 , Adapter, _1));
        Adapter->Register("Task.Pass"                   , OAdapter::fkExport, boost::bind<:prestable>(TaskPass                   , Adapter, _1));
        Adapter->Register("Task.Drop"                   , OAdapter::fkExport, boost::bind<:prestable>(TaskDrop                   , Adapter, _1));
        Adapter->Register("Task.Start"                  , OAdapter::fkExport, boost::bind<:prestable>(TaskStart                  , Adapter, _1));
        Adapter->Register("Task.Finish"                 , OAdapter::fkExport, boost::bind<:prestable>(TaskFinish                 , Adapter, _1));
        Adapter->Register("Task.ChangePlanFinish"       , OAdapter::fkExport, boost::bind<:prestable>(TaskChangePlanFinish       , Adapter, _1));
        Adapter->Register("Task.View"                   , OAdapter::fkExport, boost::bind<:prestable>(TaskView                   , Adapter, _1));
        Adapter->Register("Task.Message"                , OAdapter::fkExport, boost::bind<:prestable>(TaskMessage                , Adapter, _1));
        Adapter->Register("Task.PlanFinishChangeRequest", OAdapter::fkExport, boost::bind<:prestable>(TaskPlanFinishChangeRequest, Adapter, _1));
        Adapter->Register("Task.PlanFinishChangeAccept" , OAdapter::fkExport, boost::bind<:prestable>(TaskPlanFinishChangeAccept , Adapter, _1));
        return true;
    } catch (const Exception &E)
    {
        OUtils::CatchExceptionMessage("Ошибка регистрации функций модуля Task.dll", E);
    }
    return false;
}
или так:
Код:
    Adapter_->Register("Main.Connection"    , OAdapter::fkExecutable, boost::bind(&Tfm::Conn            , this, _1));
    Adapter_->Register("Main.MainForm"      , OAdapter::fkExecutable, boost::bind(&Tfm::MainForm        , this, _1));
    Adapter_->Register("Main.FunctionModule", OAdapter::fkExecutable, boost::bind(&Tfm::DLLByFuncName   , this, _1));
    Adapter_->Register("Main.LoadModule"    , OAdapter::fkExecutable, boost::bind(&Tfm::LoadFunctionUnit, this, _1));
    Adapter_->Register("Main.ActionCID"     , OAdapter::fkExecutable, boost::bind(&Tfm::ActionCID       , this, _1));

Интерфейс функции известен. Где бы она ни была реализована, после регистрации она доступна из любого места.

На этом механизме разработан набор классов: CPlugins, CModules, CPlugin, CModule...
CModule - загружаемый модуль.
CPlugin - плагин, наследник формы (пока):
Код:
class PACKAGE CPlugin: public TForm
{
    friend class CPlugins;
public:
    typedef CAdapter                      AdapterClass;
   
         __fastcall CPlugin(AdapterClass* const Adapter, PTable Params);
    void __fastcall Close();

    __property AdapterClass* const Adapter = {read = Adapter_};
    __property AnsiString          Title   = {read = Title_};
protected:
    AdapterClass* const Adapter_  ;
    bool                Initiated_;
    AnsiString          Title_    ;
    CFileVersion        Version_  ;
    bool                Register_ ;
    bool                SetSize_  ;
    PTable              Params_   ;
private:
    virtual void __fastcall InitComplete() = 0;
    virtual void __fastcall WndProc(Messages::TMessage &Message);
    virtual void __fastcall OnInitComplete(Messages::TMessage &Message);
};
для Plugin пишется DLL, в ней:
Код:
//---------------------------------------------------------------------------
extern "C" __declspec (dllexport) OTCPlugin::CPlugin* __stdcall Execute(OTCAdapter::CAdapter* const Adapter, const AnsiString PluginName, PTable Params)
{
    if (PluginName == "MyTasks")
    {
        return new TfmMyTasks(Adapter, Params);
    } else
    {
        OUtils::Exclamation("MyTasks.dll: не найден плагин \"MyTasks\"");
    }
    return NULL;
}

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

extern "C" __declspec (dllexport)  void __stdcall Free(CPlugin* Form)
{
    delete Form;
}

в целом - всё работает. Плагинность + адаптируемость всех атомарных функций даёт возможность легко сопровождаемой разработки.

Всё это Build with runtime packages + Use dynamic RTL
« Последнее редактирование: 08 Октябрь 2008, 09:18:04 от olegenty » Записан
Страниц: 1 [2]   Вверх
  Печать  
 
Перейти в:  




....

Powered by SMF 1.1.21 | SMF © 2025, Simple Machines
Страница сгенерирована за 0.078 секунд. Запросов: 23.