Формы и модули. 
Часть 2.Создание эксперта для формы или модуля
Вячеслав Ермолаев. http://bcdev.narod.ru
e-mail:yerm@mail.ru

В первой части этой серии "Собственные свойства и события" был рассмотрен вопрос создания формы TStorageForm с пользовательскими свойствами и событиями. Отметим, что по той же методике можно создавать и модули с одним лишь ограничением. К сожалению в вашем собственном модуле вкладка "Data Diagram" и дерево объектов не появится. В первой части так же был описан способ использования вновь созданной формы посредством репозитария (Object Repository). Этот способ является достаточно простым и в большинстве случаев может вполне устроить. Но что делать, если форма предназначена не только для внутреннего использования, а поставляется, например в составе набора компонентов, на сторону. Можно конечно, наряду с прочими обычно поставляемыми в этом случае  файлами (*.h, *.bpl,*.lip), поставлять и файлы BaseStorageFrm.h, BaseStorageFrm.cpp и BaseStorageFrm.dfm,  и предоставлять к ним инструкцию по вставке формы в репозитарий. Ничего страшного, конечно, в этом нет. Все равно вся реализация TStorageForm инкапсулирована в *.bpl и будет недоступна сторонним разработчикам. Но согласитесь, что смотрится  это как-то непрофессионально. К тому же процесс написания эксперта, который может заменить необходимость использования TBaseStorageForm, представляется не на многим сложнее. Сформулируем еще раз, что в конечном счете должен делать эксперт. Применительно к нашему случаю эксперт должен сгенерить три файла *.h, *.cpp,*.dfm, вставить их в открытый проект, не вызвав конфликта имен с уже имеющимися в проекте файлами и открыть всем нам знакомую форму, которая в отличие от стандартной, будет порождена не от TForm, а от TStorageForm. Код создаваемого эксперта поместим проект design-package dclStorage, тем более, что сейчас в нем практически ничего нет помимо вызова функции регистрации модуля. Добавим в проект unit ( File->New->New Page ->Unit) и переименуем его в FormExpert. Создание эксперта заключается в создании класса, являющегося производным от  Exptintf::TIExpert, поэтому, первым делом мы его создадим и определим интерфейс:

#ifndef FormExpertH
#define FormExpertH
#include <vcl.h>
#include <ExptIntf.hpp>
#include <EditIntf.hpp>

//---------------------------------------------------------------------------
class PACKAGE TStorageFormExpert: public Exptintf::TIExpert
{
    typedef Exptintf::TIExpert inherited;
public:
    AnsiString __stdcall GetName(void);
    AnsiString __stdcall GetAuthor(void);
    AnsiString __stdcall GetComment(void);
    AnsiString __stdcall GetPage(void);
    HICON __stdcall GetGlyph(void);
    TExpertStyle __stdcall GetStyle(void);
    TExpertState __stdcall GetState(void);
    AnsiString __stdcall GetIDString(void);
    AnsiString __stdcall GetMenuText(void);
    void __stdcall Execute(void);
    __fastcall TStorageFormExpert();
protected:
    HICON hIcon;
};
//---------------------------------------------------------------------------
#endif

Все перечисленные методы являются в базовом классе  Exptintf::TIExpert "чистыми" виртуальными функциями и соответственно требуют переопределения в нашем классе, чем мы и займемся.
Прежде всего конструктор. В теле его выполняем лишь одну операцию: загрузку иконки из ресурсов 
__fastcall TStorageFormExpert::TStorageFormExpert()
: TIExpert()
{
    hIcon = LoadIcon(HInstance,
"STORAGEFORMEXPERT");
}

Теперь определим имя нашего эксперта, под которым он будет фигурировать у нас в одной из страниц диалогового окна New Items в IDE.
AnsiString __stdcall TStorageFormExpert::GetName(void)
{
    return
"StorageForm Expert";
}

Удовлетворим наше авторское тщеславие
AnsiString __stdcall TStorageFormExpert::GetAuthor(void)
{
    return
"Vyacheslav Yermolayev";
}

Определим строку для комментария
AnsiString __stdcall TStorageFormExpert::GetComment(void)
{
    return
"Creating new StorageForm in my projects";
}

Задаем страничку, на которой будет расположен наш эксперт в диалоговом окне New Items. Пуская это будет наша собственная страничка StorageForm
AnsiString __stdcall TStorageFormExpert::GetPage(void)
{
  return
"StorageForm";
}

Укажем, откуда надо взять иконку
HICON __stdcall TStorageFormExpert::GetGlyph(void)
{
  return hIcon;
}

Установим стиль эксперта, который может принимать значения esStandard, esForm, esProject и esAddIn. В нашем случае это будет esForm
TExpertStyle __stdcall TStorageFormExpert::GetStyle(void)
{
  return esForm;
}

Сделаем доступным наш эксперт
TExpertState __stdcall TStorageFormExpert::GetState(void)
{
  return TExpertState() << esEnabled;
};

Осталось из формальной части переопределить методы GetMenuText и GetIDString. Первый должен  возвратить название пункта меню и нам не потребуется, поскольку эксперт не будет вызываться из меню. Вторая функция будет возвращать уникальный идентификатор-строку эксперта, состоящую обычно из названия фирмы и эксперта 
AnsiString __stdcall TStorageFormExpert::GetMenuText(void)
{
  return "";
}
//---------------------------------------------------------------------------
AnsiString __stdcall TStorageFormExpert::GetIDString(void)
{
  return "MyFirm.StorageFormExpert";
};

На этом описание формальной части эксперта закончилось. Теперь приступим непосредственно к исполняемому коду эксперта. Содержимое необходимых трех файлов будем формировать на основе шаблонов, которые будем хранить в ресурсе. Для этого создадим файл SOURCE.RC со следующим содержимым, представляющим практически три строки с идентификаторами 100, 200 и 300 

STRINGTABLE DISCARDABLE
{
// Unit.h UNIT_H
100
"//---------------------------------------------------------------------------\
#ifndef %0:sH\
#define %0:sH\
//---------------------------------------------------------------------------\
#include <Classes.hpp>\
#include <Controls.hpp>\
#include <StdCtrls.hpp>\
#include <Forms.hpp>\
#include ""%2:s.h""\
//---------------------------------------------------------------------------\
class T%1:s : public T%2:s\
{\
__published: // IDE-managed Components\
private: // User declarations\
public: // User declarations\
__fastcall T%1:s(TComponent* Owner);\
};\
//---------------------------------------------------------------------------\
extern PACKAGE T%1:s *%1:s;\
#endif"
// Unit.cpp UNIT_CPP
200
"//---------------------------------------------------------------------------\
#include <vcl.h>\
#pragma hdrstop\
#include ""%0:s.h""\
//---------------------------------------------------------------------------\
#pragma package(smart_init)\
#pragma resource ""*.dfm""\
T%1:s *%1:s;\
//--------------------------------------------------------------------------- \
__fastcall T%1:s::T%1:s(TComponent* Owner)\
:T%2:s(Owner)\
{\
}\
//---------------------------------------------------------------------------"
//Unit.dfm UNIT_DFM
300
"object %0:s: T%0:s\
Left = 361\
Top = 189\
Width = 446\
Height = 372\
Caption = '%0:s'\
Color = clBtnFace\
Font.Charset = DEFAULT_CHARSET\
Font.Color = clWindowText\
Font.Height = -11\
Font.Name = 'MS Sans Serif'\
Font.Style = []\
OldCreateOrder = False\
PixelsPerInch = 96\
TextHeight = 13\
end"

Добавим созданный файл в проект,  а файле FormExpert добавим объявление четырех констант
const int nUnit_h = 100;  //идентификатор шаблона для UnitX.h
const int nUnit_cpp = 200;
//идентификатор шаблона для UnitX.cpp
const int nUnit_dfm = 300;
//идентификатор шаблона для UnitX.dfm
//строковый идентификатор базового класса

const AnsiString AncestorIdent =
"StorageForm";

Весь исполнительный код эксперта реализован в теле метода Execute. Далее все пояснения к тексту процедуры будут оформлены в виде комментариев, что бы не нарушать целостность восприятия

void __stdcall TStorageFormExpert::Execute(void)
{
//ToolServices - указатель на объект TIToolServices,
//объект, обеспечивающий интерфейс к IDE
//если объекта не существует, продолжать не имеет смысла 

if
(!ToolServices) exit(1);

//полное имя файла *.cpp
std::auto_ptr<TFileName> FileName(new TFileName);
//идентификатор модуля   
AnsiString UnitIdent;
//идентификатор формы
AnsiString FormIdent;

TIMemoryStream* imsHdr;
TIMemoryStream* imsCpp;
TIMemoryStream* imsDfm;
try
{
 //получаем идентификатор и полное имя модуля
 if
(ToolServices->GetNewModuleName(UnitIdent, *FileName))
 {
 //формируем идентификатор формы
 FormIdent = AncestorIdent + UnitIdent.SubString(5, 255);

 //Загружаем шаблон h-файла из ресурса и основе шаблона 
 //формируем его содержимое в объекте TStringStream

 std::auto_ptr<TStringStream> ssHdr( new TStringStream(
 FmtLoadStr(nUnit_h,ARRAYOFCONST((UnitIdent,FormIdent,AncestorIdent)))));
 std::auto_ptr<TMemoryStream> msHdr(new TMemoryStream());
 //Перегоняем содержимое из TStringStream в TMemoryStream
 msHdr->LoadFromStream(ssHdr.get());
  //На основе TMemoryStream создаем объект TIMemoryStream
 //который будет использоваться в качестве параметра
 //в вызове метода CreateCppModule

 imsHdr = new TIMemoryStream(msHdr.get(),soReference);
 try
 {
   //тоже самое проделаем для cpp
   std::auto_ptr<TStringStream> ssCpp( new TStringStream(
   FmtLoadStr(nUnit_cpp,ARRAYOFCONST((UnitIdent,FormIdent,AncestorIdent)))));
   std::auto_ptr<TMemoryStream> msCpp(new TMemoryStream());
   msCpp->LoadFromStream(ssCpp.get());
   imsCpp = new TIMemoryStream(msCpp.get(),soReference);
   try
   {
     // и для dfm
   std::auto_ptr<TStringStream> ssDfm( new TStringStream(
     FmtLoadStr(nUnit_dfm,ARRAYOFCONST((FormIdent)))));
     std::auto_ptr<TMemoryStream> msDfm(new TMemoryStream());
     ObjectTextToResource(ssDfm.get(), msDfm.get());
     imsDfm = new TIMemoryStream(msDfm.get(),soReference);
     try
     {
       TCreateModuleFlags CreateFlags;
     // устанавливаем флаги для вновь создаваемого модуля 
       CreateFlags << cmAddToProject << cmShowSource
       << cmShowForm << cmUnNamed
       << cmMarkModified;
     // создаем модуль  
     ToolServices->CreateCppModule(*FileName, "", "", "",
                                   (IStream*)(*imsHdr),
                                   (IStream*)(*imsCpp),
                                   (IStream*)(*imsDfm),
                                   CreateFlags);
    }
    catch(...)
    {
      delete imsDfm;
      throw;
    }
  }
  catch(...)
  {
    delete imsCpp;
    throw;
  }
 }
 catch(...)
 {
   delete imsHdr;
   throw;
 }
}
}
catch(...){ ToolServices->RaiseException(ReleaseException());}
}

Ну вот в общем-то и все. Отметим одну особенность. При создании эксперта ни коим образом не была задействована не только реализация TStorageForm, но и даже не было сделано  объявление интерфейса. То есть код эксперта практически не зависит от формы, для которой создавался эксперт. Единственное, что как то определяет то, что требуется создать,  это строковая константа
 const AnsiString AncestorIdent = "StorageForm"; 
и шаблоны для генерации файлов. Следовательно, в принципе можно создать универсальный эксперт форм, если хранить всю изменяемую информацию(шаблоны и имя формы) во внешних файлах, а пути к ним указывать в конфигурационном ini-файле. 

Код к этой статье можно взять здесь.

 






 
     

 



  


  

 

 

          

 



Сайт управляется системой uCoz