скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Создание компонентов для KOL и MCK - Часть 1 - Создание невизуального KOL компонента



Ведущий раздела KOL и MCK: Анатолий aka XVeL
Автор: Жаров Дмитрий aka Gandalf
WEB-сайт: http://kolmck.net/

Введение: Дорога из желтого кирпича

С появлением KOL, а после и MCK в программировании под Delphi началась новая эпоха - стало возможно создавать быстро, быстрый и легкий код, не отказываясь от любимого языка. Но среди достоинств этого, на данной стадии развития - есть и недостатки, нехватка компонентов, сравнимая почти с нехваткой воздуха, и что самое главное, нехватка людей, которые могут их написать. Данная статья ставит себе целью изменить ситуацию и перевести написание компонентов для KOL и MCK из разряда "для избранных" в разряд "для всех". При этом она не в коей мере не ставит себе целью обучением вас Delphi, KOL, MCK считается, что эти знания у вас уже есть. Материал преподносится в форме "от простого к сложному", и так начнем.

Создание не визуального KOL компонента: Шапка невидимка

Кладов при создании KOL оставил возможность создавать невизуальные компоненты. Для этого нам необходимо объявить новый объект, наследуясь от TObj (из модуля KOL):

TNewKOLUnVisual = object(TObj)
         private
         public
         end;

Заметьте, что когда я сказал объект, я имел в виду именно объект, а не класс! Такова особенность модели наследования KOL, к ней придется привыкнуть. Данный переход на "устаревшую" модель обусловлен ее эффективностью и спецификой внутренней реализации, что дало возможность построить на ее основе KOL. Далее, чтобы наше повествование не было некой абстракцией, давайте перейдем на построение компонента, я взял за пример постройку AboutDialog.
Хочу сделать небольшое отступление по поводу названия файла, ныне принят негласный стандарт kolComponentName.pas либо KOLComponentName.pas, я придерживаюсь второго, поскольку KOL - это аббревиатура и по правилам ее надо писать в верхнем регистре (тем более так она логически выделяется в имени). Я и далее буду вставлять рекомендации, они сделаны на фоне моего опыта и наблюдений, и исходят из неких соображений, я ни в коем случаи их вам не навязываю, причем, если вы не согласны с моими посылами - дискуссия открыта, адрес вы знаете.

unit KOLMHAboutDialog;
interface
uses
  KOL;
type
  PMHAboutDialog =^TMHAboutDialog;
  TKOLMHAboutDialog = PMHAboutDialog;
  TMHAboutDialog = object(TObj)
  private
  public
       
end;

implementation

end.

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

PMHAboutDialog =^TMHAboutDialog;
TKOLMHAboutDialog = PMHAboutDialog;

Эти строки также результат специфики KOL, поскольку мы в KOL работаем не с самими объектами, а с указателями на них. Потому мы и написали первую строку. Вторая нужна для психологического успокоения нас и компилятора, поскольку механизм MCK, как мы увидим позже, использует именно этот тип.
Хочу обратить ваше внимание на череду имен (PMHAboutDialog, TMHAboutDialog, TKOLMHAboutDialog) и правила их образования (думаю их комментировать не стоит) - это тоже устоявшееся положение.
Далее давайте добавим немного свойств, для нашего нового компонента, получим нечто такое.
При создании компонентов для KOL и MCK (будь то визуальные компоненты или нет) я руководствуюсь следующими правилами:
1. Раньше мы работали на VCL, потому переход на KOL и MCK будет безболезненным, если KOL и MCK компонент будет повторять VCL (его свойства, методы и прочее).
2. Мы делаем KOL и MCK фактически как инструмент для упрощения работы с API, поэтому логично, что мы должны перенести все возможности API.
3. Код который будет в KOL части большей частью перейдет в наш проект и от качества кода (и его размера) будет зависеть качество полученного exe-файла (или модуля или dll-библиотеки), по этому:
" Не бойтесь использовать ассемблер
" Не делайте лишнего или громоздкого кода
" Но оптимизация не должна идти за счет удобства

unit KOLMHAboutDialog;
interface
uses
  KOL, Windows, ShellAPI;
type
  TIconType = (itShell, itApplication, itCustom);
  PMHAboutDialog =^TMHAboutDialog;
  TKOLMHAboutDialog = PMHAboutDialog;
  TMHAboutDialog = object(TObj)
    private
    FTitle: String;
    FCopyRight: String;
    FText: String;
    FIcon: HIcon;
    FIconType: TIconType;
    public
    property Title: String read FTitle write FTitle;
    property CopyRight: String read FCopyRight write FCopyRight;
    property Text: String read FText write FText;
    property Icon: HIcon read FIcon write FIcon;
    property IconType: TIconType read FIconType write FIconType;
end;

implementation

end.

Думаю, что никто для себя ничего нового не нашел. Но мог возникнуть вопрос, зачем все вынесено в свойства, когда в этом нет необходимости (чтение и запись идут напрямую), отвечу - как пример, и поскольку необходимости в этом действительно нет, мы переделаем код:

unit KOLMHAboutDialog;
interface
uses
  KOL, Windows, ShellAPI;
type
  TIconType = (itShell, itApplication, itCustom);
  PMHAboutDialog = ^TMHAboutDialog;
  TKOLMHAboutDialog = PMHAboutDialog;
  TMHAboutDialog = object(TObj)
  private

  public
  Title: String;
  CopyRight: String;
  Text: String;
  Icon: String;
  IconType: TIconType;
end;

implementation

end.

Если посмотреть размер, после этих несложных манипуляций уменьшился почти в 2 раза. Да, если кого удивило появление модулей Windows и ShellAPI, не пугайтесь, они понадобиться чуть позже. Ну что же пора вносить работоспособность в компонент, сделаем это добавив два метода Destroy и Execute:

unit KOLMHAboutDialog;
interface
uses
  KOL, Windows, ShellAPI;
type
  TIconType = (itShell,itApplication,itCustom);
  PMHAboutDialog = ^TMHAboutDialog;
  TKOLMHAboutDialog = PMHAboutDialog;
  TMHAboutDialog = object(TObj)
    private
    public
      Title: String;
      CopyRight: String;
      Text: String;
      Icon: HIcon;
      IconType: TIconType;
      destructor Destroy; virtual;
      procedure Execute;
end;

implementation

destructor TMHAboutDialog.Destroy;
begin
  DestroyIcon(Icon);
  inherited;
end;

procedure TMHAboutDialog.Execute;
var
  HWndOwner: THandle;
  TMPIcon: HIcon;
begin
  if Assigned(Applet) then
    HWndOwner := Applet.Handle
  else
    HWndOwner := 0;
  case IconType of
    itShell: TMPIcon := 0;
    itApplication: TMPIcon := Applet.Icon;
    itCustom: TMPIcon := Icon;
  end;//case
  ShellAbout(HWndOwner, PChar(Title + '#' + Text), PChar(CopyRight), TMPIcon);
end;

end.

Начнем разбираться, Destroy (не забывайте virtual, а то компилятор не пустит). Фактически в нем идет освобождение ресурса иконки (Проверку на наличие иконки делать не надо, поскольку мы используем функцию DestroyIcon - если иконку уничтожить нельзя она вернет не нуль, но программа будет работать корректно), а далее выполнения Destroy предка. Метод Execute не должен вызывать испуга у людей знакомых с API. Вопрос может возникнуть только по поводу переменной Applet - это глобальная переменная из KOL типа PControl, аналог Application из Delphi. У наблюдательных читателей, возможно, возник вопрос: Destroy есть, а где Create? Будет, вот он:

unit KOLMHAboutDialog;
interface
uses
  KOL, Windows, ShellAPI;
type
  TIconType = (itShell, itApplication, itCustom);
  PMHAboutDialog = ^TMHAboutDialog;
  TKOLMHAboutDialog = PMHAboutDialog;
  TMHAboutDialog = object(TObj)
    private
    public
      Title: String;
      CopyRight: String;
      Text: String;
      Icon: HIcon;
      IconType: TIconType;
      destructor Destroy; virtual;
      procedure Execute;
end;

function NewMHAboutDialog: PMHAboutDialog;

implementation

function NewMHAboutDialog:PMHAboutDialog;
begin
  New(Result, Create);
end;

destructor TMHAboutDialog.Destroy;
begin
  DestroyIcon(Icon);
  inherited;
end;


procedure TMHAboutDialog.Execute;
var
  HWndOwner: THandle;
  TMPIcon: HIcon;
begin
  if Assigned(Applet) then
    HWndOwner := Applet.Handle
  else
    HWndOwner := 0;
  case IconType of
    itShell: TMPIcon := 0;
    itApplication: TMPIcon := Applet.Icon;
    itCustom: TMPIcon := Icon;
  end;//case
  ShellAbout(HWndOwner, PChar(Title + '#' + Text), PChar(CopyRight), TMPIcon);
end;

end.

Те кто задавали этот вопрос, наверное, ждали нечто иное чем внешнюю функцию NewMHAboutDialog, но ничего не поделаешь KOL. Помните, я говорил, что нам придется работать с указателями, да и ООП модель у нас на базе объектов, а не классов. Сама функция как мы видим, выделяет память для объекта.
Теперь немного о самой форме New функции, опять же обратите внимание на название - без комментариев. Могут возникнуть вопросы: что делать внутри функции, и какие передавать ей параметры? Ответ, внутри функция должна выполнять минимум операций, т.е. тот минимум, который необходим для начала работы с компонентом. С параметром так же, передаются только необходимые непосредственно функции New и обязательно используемые с нею (обычно эти группы неразделимы). Вы можете сказать, что тогда надо добавить в New загрузку иконки, ведь мы ее всегда уничтожаем - но это неправильно, мы ее всегда уничтожаем, Но не всегда создаем! И как можно заметить не одно из свойств не является обязательным, т.е. метод Execute можно вызвать сразу после New. Возможно, это не самый удачный пример, будут и лучше. Таким образом, мы экономим немного кода, а в итоге выигрываем в скорости и размере. Надеюсь, я вас убедил.
Хммм… неслышу возгласа "ура". Почему? Так ведь мы KOL компонент сделали! Теперь давайте проверим его в деле. Возьмем, какой-нибудь проект (KOL конечно) и добавим строки:

uses{$ENDIF}, KOLMHAboutDialog; …
var
 TMP: TKOLMHAboutDialog;
begin
  TMP := NewMHAboutDialog;
  TMP.Execute;
  TMP.Free;
end;

Запустили, посмотрели - работает.
Вернусь к нашим баранам, если бы мы сделали функцию New с параметром-иконкой, код, был бы таким:

var
  TMP: TKOLMHAboutDialog;
begin
  TMP := NewMHAboutDialog(0);
  TMP.Execute;
  TMP.Free;
end;

Замечаете, мы фактически задаем ненужный параметр, да к тому же сама функция New потолстела (правда несильно). Тут мы выигрываем не много (байты), но это принципиальный момент. Много помалу - много!






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook