Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Своя программа и плагин к ней


Автор: mr.Anderson 13.6.2007, 20:22
Порыл Google, но не смог найти толковой информации по этому поводу. Как мне к собственной программе создать плагин? Каков вообще принцип построения плагинов? Что для чтения плагина должно быть в моей расширяемой программе? Прошу помочь либо подробными разъяснениями, либо статейками (крайне желательно на русском языке).

Автор: Klin 13.6.2007, 23:46
Попробую на русском smile

mr.Anderson, есть разные подходы,  в зависимости от того, какую цель вы преследуете. Плагины - всегда вещь полезная (хотя не рекомендую при создании калькуляторов). Плюсы их использования на лицо. Каждый из них отвечает за определенную часть программы, соответственно при нахождении недочетов, усовершенствовании и т.п. заменяется не сама программа, а только чать ее. Также с оперативка вам только спасибо скажет если, вы будите грузить не 100 метров, а только нужные в данный момент 5.  
Обычно это DLL файл, в нем  может быть че угодно. Будь то коллекция изображений для дизайна, тексты для перевода программы или формы, диалоги, меню и прочее. Способы создания тоже не мало. К примеру можно создать DLL (Файл->Новый->Мастер DLL). Затем создать в этом проекте новую форму (или фрейм). Будет необходимо прописать что-то типа: 

Код

extern "C" void  __declspec(dllexport) OpenModule(TForm* Owner)
{
      static TForm2* form = new TForm2(Owner);

}


Ну то, что форму нужно прописать не напоминаю smile Это будет простейший плагин. На форме может быть что угодно. Теперь необходимо написать, так называемое хост приложение. Не важно, что будет в нем, но главное чтобы был вызов нашего модуля (или плагина как кому нравиться smile ) Расскажу-ка я про динамическую загрузку smile Должны произойти следующие действия:
  •  Загрузка модуля в память
  •  Использование возможностей модуля
  •  Его закрытие
Вот это и есть залог успеха. В моем примере - выведем нашу форму на экран:

Код

Dll_name = "C:\\Project1.dll";
if (HINSTANCE Dll_adress = LoadLibrary(Dll_name))
  {
     typedef TForm* (__cdecl* TCreateMyForm)(TForm*);
     if( TCreateMyForm CreateMyForm = (TCreateMyForm)GetProcAddress(Dll_adress,"_OpenModule")) {
      form = CreateMyForm(this); 
      /* дальнейшие действия */
    }
  }  


Все, дальше можем изменять под себя. Как я и сказал, способов создания и использования достаточно много. Недавно обсуждалась тема, в которой автор хотел запихнуть в ресурсы диалоговую форму - тоже неплохой вариант, поищи на форуме.

Что еще можно сказать, я думаю, ты понимаешь, что плагины могут иметь различные расширения. Можно создать и статистический модуль, в этом случае необходимо будет прописать в программе:

Код

 #pragma resource "Project1.dll"


Но здесь имхо больше минусов:
  •  Захотите обновить ваш плагин - придется работать с ресурсами программы, а не просто заменить нужный файл
  •  Сама программа прибавляется в весе, т.е изменяется программа и тащит все старые модули на себе.
  •  Почти отсутствует возможность догрузки плагина
Хотя есть один плюс - простота в использовании. Вообще писать могу много, но ночи слишком короткие smile А преподы утром злые  smile . Возникнут вопросы - пиши.

Добавлено через 8 минут и 46 секунд
так зарисовочка плагинов smile

user posted image
Динамика

user posted image
Статика

Автор: jonie 14.6.2007, 00:00
пишешь длл-ку спец вида (спец вид определяешь ТЫ : например условие - наличие функции Init())
а далее грузишь ее динамически через LoadLibrary  (поищи по форуму, тут не раз эта тема появлялась)....
т.о. имеем:
директорию с плагинами, в ней перебираем все длл-ки, и смотрим наличие у них экспортной функции (ну или еще чего что сам опреялешь), а далее остается только передавать данные в\из плагина... Как это реализовать тож было... например передавать указатели, или функторы....
---------
реальный пример (урощенно):
есть прога проверяющая балланс счета. Суть плагинов - вызывается спец функция MainPluginFunction() в них (при каждой проверке), которой передаются данные о баллансе (через указатель на структуру).

Программа грузит динамически плагины  при старте:
Код

#define PLUGIN_EXT "*.dll"
......
CPluginList::CPluginList(string strPluginDir)
{
    bool ErrorDetected=false;
    char buf[MAX_PATH+256];    //под всякие пути
    PLUGIN plugstruct;  //струкрута содержит HMODULE - hInstanse длл-ки загруженной, и данные о функторах (функций из плагина типа GetPlugName(), MainPluginFunction()..... 
    string ErrorMessage("Следующие плагины не могут быть загружены:\n");
    if(strPluginDir[strPluginDir.length()-1]!='\\')    strPluginDir.append("\\");
    //strPluginDir.append(PLUGIN_EXT);
    StringCchPrintf(buf,sizeof(buf),"%s%s",strPluginDir.c_str(),PLUGIN_EXT);
    WIN32_FIND_DATA finddata;
    HANDLE hFind = FindFirstFile(buf,&finddata);
    if(hFind!=INVALID_HANDLE_VALUE){
        do{
            //This File (if Find) cant be == "." OR ".." !
            //if( strcmp(finddata.cFileName,".") && strcmp(finddata.cFileName,"..") ){
                ZeroMemory(&plugstruct,sizeof(PLUGIN));
                StringCchPrintf(buf,sizeof(buf),"%s%s",strPluginDir.c_str(),finddata.cFileName);
                PLUGIN_RET_CODES retVal = LoadPlugin(buf,&plugstruct);   //в LoadPlugin() грузим DLL, заносим ее HMODULE  в структуру и заполняем поля структуры функуторами по GetProcAddress()
                if(retVal!=ALL_OK){
                    StringCchPrintf(buf,sizeof(buf),"DLL : %s (CODE=%d)\n",finddata.cFileName,retVal);
                    ErrorMessage.append(buf);
                    ErrorDetected = true;
                }
                else{
                    this->pluglist.push_back(plugstruct);  //сохраняем данные о плагине в векторе
                }
            //}
        }while(FindNextFile(hFind,&finddata));
    }
    FindClose(hFind);
    if(ErrorDetected) throw ErrorMessage;  //вызываем Exception если были найдены ошибки (но я не прерываю программу при первой ошибке, может загрузить хоть часть плагинов)
}


а далее в осной программе вызываем MainPluginFunction()  для каждого плагина, перебирая наш вектор....
Код

CPluginList* lpPlugList;
......
//тут инициализация (код выше)
....
//а тут наступает момент вызова плугов....
            for(std::vector<PLUGIN>::const_iterator i=lpPlugList->pluglist.begin();i!=lpPlugList->pluglist.end();i++){
                i->MainPluginFunction()
            }//end run plugins
....
//и продолжаем прогу


ессно не забываем про ошибки, threads и прочее...
но суть везде одна...

это простенький и сильно облегченный пример.. но думаю понять хватит....
---
как вариант можно юзать COM.......... но это более сложная песня имхо....

Автор: mr.Anderson 14.6.2007, 12:29
Объясните, пожалуйста, сверхподробно вот эти строчки:
Код

 typedef TForm* (__cdecl* TCreateMyForm)(TForm*);
     if( TCreateMyForm CreateMyForm = (TCreateMyForm)GetProcAddress(Dll_adress,"_OpenModule")) {

Автор: jonie 14.6.2007, 17:18
Код

 typedef TForm* (__cdecl* TCreateMyForm)(TForm*);
определение функтора (указателя на функцию) , которая принимает указатель на экземпляр формы TForm  и возращает указатель на экземпляр TForm.
Код

  if( TCreateMyForm CreateMyForm = (TCreateMyForm)GetProcAddress(Dll_adress,"_OpenModule")) {
а это получение адреса такой функции из DLL.
Как она может ее реализовать (функцию такую в ДЛЛ)?
ну например 
Код

class TMyForm : public TForm{
 TForm* parentForm;  //указатель на форму родителя, например
....
}
....
 TForm* __cdecl _OpenModule(TForm* parentForm){
  return new TMyForm(parentForm);
.....
}

ну и экспорт не забыть.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)