Подписка

добавить на Яндекс

Наши проекты

Delphi+Google

Google API

Google API в Delphi - проект с открытым исходным кодом.

Chrono

Chrono

Хронометр - программа для ведения списка задач.

ODFProc

ODFProc

ODFProc - работа с документами OpenOffice в Lazarus и FreePascal.

Поддержка блога

А тут я коплю на лицензию Delphi XE на iPad =).
Сумма пожертвования не фиксирована.

Публикации

Год назад

Случайный пост

Последние

Сообщения форума

Комментарии

Социальные сети

Google

Facebook

Twitter

Опрос

Вы сейчас или в ближайшем обозримом будущем планируете разрабатывать кроссплатформенное приложение с использованием Firemonkey?



Loading ... Loading ...

Блоги и сообщества

Статьи по Delphi DelphiFeeds.ru - Все Delphi-блоги Рунета Сообщество умных людей VR-Online.RU Бесплатный журнал для программистов и всех, кто интересуется IT Статьи и уроки по Delphi Новостной блог о высоких технологиях
Система Orphus
Опубликовал Vlad 11 мая 2010 в 18:39.
Категории: Моя работа, Основы Delphi.


Итак, вчера мы разобрались, что представляет из себя интерфейс в Delphi, каким образом реализуются интерфейсы и рассмотрели парочку вариантов того как эти самые интерфейсы реализуются.
Сегодня перейдем к следующему моменту - реализации своего первого плагина (plug-in, подключаемый модуль - как угодно) для приложения.
Итак, представим, что у нас есть некое приложение, работающее с текстом, например, записная книжка. Сейчас для нас неважно как реализована работа с текстом внутри программы - получаем ли мы его из Блокнота Google или записываем в вручную в Memo. Наша задача - обратиться к ресурсам нашего приложения из DLL. При этом следует также учесть, что на работу нашего импровизированного плагина можно было накладывать ограничение работы, например, запретить записывать в переменную строку "Delphi Is Dead!".

Вспомним, что интерфейс - это то посредством чего клиент общается с сервером. В нашем случае сервер - приложение, клиент - плагин. Реализацией интерфейса в Delphi выступает класс.
Вначале разрабатываем модуль, который будет содержать интерфейс. Именно этот модуль мы будем раздавать всем желающим, чтобы они с помощью него писали плагины.
Я создал следующий модуль:

unit InterfaceUnit;
 
interface
 
uses
  Classes;
 
type
  IPluginInterface = interface
  ['{34381143-4B31-11D8-8903-0020ED19BE94}']
     procedure Hello();
     function GetLocalString(): widestring;
     procedure SetLocalString(aStr: widestring);
  end;
 
implementation
 
end.

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

Реализация интерфейса IPluginInterface в приложении:

uses
 ..., InterfaceUnit;
 
const
    StopString = 'Delphi Is Dead';
 
type
  TForm1 = class(TForm, IPluginInterface)
    LoadDLL: TButton;
    UnloadDLL: TButton;
    SetI: TButton;
    GetI: TButton;
    ShowDLLForm: TButton;
    Memo1: TMemo;
    Label1: TLabel;
    Label2: TLabel;
   [...]
  private
    { Private declarations }
  public
    { Public declarations }
    procedure Hello();
    function GetLocalString(): widestring;
    procedure SetLocalString(aStr: widestring);
  end;
 
var
  Form1: TForm1;
  [...]
implementation
 
{$R *.DFM}
 
var
 MyString: wideString; //строка которую будем изменять
 
procedure TForm1.Hello();
begin
 ShowMessage('Hello World!')
end;
 
function TForm1.GetLocalString(): widestring;
begin
 Result:=MyString;
end;
 
procedure TForm1.SetLocalString(aStr: widestring);
begin
 if pos(LowerCase(StopString),LowerCase(aStr))<=0 then
   MyString:=aStr
 else
   ShowMessage('Delphi живее всех живых!')
end;

Теперь немного отвлечемся от главного приложения и займемся непосредственно разработкой плагина. Плагин будет содержать форму:

С помощью которой мы будем получать и изменять значение переменной MyString:string главного приложения.
Итак, создаем новую DLL, создаем в проекте новую форму и в unit формы подключим наш модуль интерфейса:

uses ..., InterfaceUnit,

И объявим одну глобальную переменную:

var  Plg: IPluginInterface;

Сразу распишем все обработчики нажатия кнопок, чтобы больше не возвращаться к форме:

procedure TForm2.Button1Click(Sender: TObject);
begin
//изменяем переменную в главном приложении
  Plg.SetLocalString(Memo1.Text)
end;
 
procedure TForm2.Button2Click(Sender: TObject);
begin
//получаем значение MyString
  Memo1.Text:=Plg.GetLocalString
end;
 
procedure TForm2.Button3Click(Sender: TObject);
begin
//здороваемся с пользователем
  Plg.Hello;
end;

Теперь переходим к написанию основного кода DLL плагина. Нам необходимо реализовать в библиотеке как минимум три метода:
1. Инициализация плагина;
2. Показ формы плагина;
3. Завершить работу плагина.

Инициализация плагина

В этом методе нам необходимо получить ссылку на интерфейс, чтобы впоследствии работать с переменной MyString. Для этого подключаем в uses библиотеки модуль интерфеса и пишем следующую процедуру:

library Plugin;
 
uses
  ...
  InterfaceUnit in '..\InterfaceUnit.pas';
 
{$R *.RES}
 
var
  DA: TApplication;
 
procedure InitPlugin(App: Integer); StdCall;
begin
  DA:=Application;
  Application:=TApplication(App);
  if Application.MainForm.GetInterface(IPluginInterface,Plg) then
    ShowMessage('Ссылка на интерфейс получена')
  else
    ShowMessage('Не удалось получить ссылку на интерфейс!');
end;

В нашем случае указатель на интерфейс получается посредством вызова функции:

function GetInterface(const IID:   TGUID;   out Obj): Boolean;

Так как MainForm является свойством класса TApplication, то метод QueryInterface из секции protected класса TCustomForm для него не поддерживается. Однако ничто не мешает воспользоваться методом GetInterface, реализованным еще в классе TObject в секции published.

Показ формы плагина

Здесь все достаточно просто, т.к. после инициализации плагина мы можем использовать TApplication:

function DoForm(): TForm; stdcall;
begin
  if Form2=nil then
   Form2:=TForm2.Create(Application);
  Result:=Form2;
end;

И, наконец третий и последний этап реализации работы плагина - его выгрузка.

Завершение работы плагина

Здесь нам необходимо уничтожить форму, если она была создана и передать TApplication обратно:

procedure FinishPlugin(); stdcall;
begin
 if Form2<>nil then
   FreeAndNil(Form2);
 Application:=DA;
end;
 
exports
  InitPlugin,
  DoForm,
  FinishPlugin;
 
begin
end.

На этом разработку плагина можно считать законченной. Осталось немного доработать наше основное приложение, а именно - "научить" приложение загружать и выгружать плагин и использовать его (плагина) форму.

Организация работы с плагинами из основного приложения

Как вы знаете, существует два основных способа загрузки DLL - статическая загрузка и динамическая. В Delphi 2010 появился третий способ - отсроченная загрузка. Однако в работе с плагинами, как Вы понимаете, ни статическая ни отсроченная загрузка не подойдут, поэтому придётся немного поработать ручками и организовать динамическую загрузку/выгрузку DLL.
Вначале распишем процедуру загрузки DLL:

procedure TForm1.LoadDLLClick(Sender: TObject);
var
 s: string;
begin
  s:=ExtractFilePath(ParamStr(0))+'Plugin.dll';
  DllHandle:=LoadLibrary(PChar(s));
  if DllHandle<>0 then
   begin
     @InitPlugin:=GetProcAddress(DllHandle, 'InitPlugin');
     if @InitPlugin<>nil then
      begin
        InitPlugin(Integer(Application));
        @DoForm:=GetProcAddress(DllHandle, 'DoForm');
        @FinishPlugin:=GetProcAddress(DllHandle, 'FinishPlugin');
      end;
     MessageDlg('Plugin.dll загружен!', mtInformation, [mbOk], 0);
   end
  else
   begin
     MessageDlg('Библитека Plugin.dll не загружена!', mtError, [mbOk], 0);
   end;
end;

Здесь мы вначале получаем Handle библиотеки, запрашиваем адрес процедуры InitPlugin и, если такая процедура имеется в библиотеке, то считаем, что это плагин для нашей программы и инициализируем его. После чего запрашиваем адреса функции показа формы плагина и завершения его работы.
Соответственно обработчик кнопки "Показать форму" будет следующим:

procedure TForm1.ShowDLLFormClick(Sender: TObject);
var
 DLLForm: TForm;
begin
 if DllHandle<>0 then
  begin
    if @DoForm<>nil then
     begin
      DLLForm:=DoForm();
      DLLForm.Visible:=true;
     end;
  end;
end;

И, наконец, завершение работы плагина:

procedure TForm1.UnloadDLLClick(Sender: TObject);
begin
  if DllHandle<>0 then
   begin
     if @FinishPlugin<>nil then
      FinishPlugin;
      if FreeLibrary(DllHandle) then
       begin
         DllHandle:=0;
         MessageDlg('Плагин выгружен!', mtInformation, [mbOk], 0);
       end;
   end;
end;
Также не следует забывать выгружать плагины при завершении работы программы. Для этого можно создать обработчик OnClose главной формы. Т.к. код обработчика OnClose будет практически на 100% идентичен предыдущему, то в посте я его приводить не буду, а лучше дам ссылку на скачивание исходников рассмотренного в посте проекта.
И теперь самое последнее и видимо главное. В настройках компиляции DLL и основного приложения следует указать опцию "Build with Runtime Packages" следующим образом:
<a href="http://www.webdelphi.ru/wp-content/uploads/2010/05/plugin12.png"><img src="http://www.webdelphi.ru/wp-content/uploads/2010/05/plugin12-300x199.png" alt="" title="plugin delphi" width="300" height="199" class="aligncenter size-medium wp-image-3405" /></a>

И только после этого компилировать библиотеку. Не знаю с чем это связано, но абсолютно такой же пример, написанный в Delphi 7 без проблем работал без этой опции компиляции. В Delphi 2010 постоянно происходит ошибка при выгрузке плагина "AccessViolation ...".
На этом разработку нашего нехитрого приложения и плагина к нему можно считать выполненной. В этом примере мы научились не только передавать значения функций из DLL в основное приложение, но и выполнять обратное действие - из DLL получать значения переменных в основного приложения и все это оказалось возможным с использованием интерфейса.
А вот и весь исходник проекта:
Исходник примера, рассмотренного в статье

Понравилась статья? Тогда:
Делись! Загружай! Плюсуй!
   Отправить PDF на   
Читай ещё статьи на WebDelphi.ru

Комментарии (30)

WP_Cloudy
  • Алексей Тимохин пишет:

    Нельзя передавать строки из DLL-ки в основное приложение, у них же разные менеджеры памяти! Не знаю как сейчас, но по крайней в ранних версиях Delphi точно так делать было. Самое ужасное в том, что оно даже может работать, до поры.

    У GunSmoker-a же была целая серия публикаций посвящённая плагинам. Там подробно рассматривались все нюансы. Почитай на досуге что ли. Там вся матчасть есть.

  • Vlad пишет:

    Так, а ShareMem разве не используется в случае, если строки используются? Правда лишнюю DLL таскать с собой…Проще с PChar

  • Алексей Тимохин пишет:

    Проще использовать Widestring.

  • Казанцев Алексей пишет:

    WideString можно, ими управляет система. Обычные нельзя даже при использовании ShareMem, если хочешь делать по настоящему правильно, т.к. формат заголовка строк в юникод-версиях изменился. Ты же не хочешь сказать, что плагины писать можно только с использованием конкретных версий Delphi. PChar использовать тоже нельзя, нужно конкретизировать — PAnsiChar или PWideChar, иначе при компиляции в другой (юникод/анси) версии Delphi жди беды. Соглашение о вызовах, опять же, декларировать следует всегда (касательно интерфейсов отдаваемых во вне).

  • Алексей Тимохин пишет:

    Sharemem должен подключаться явно.
    Для старых версий Delphi ещё можно было использовать FastShareMem или как-то так, который не требовал DLL-ки.
    Не знаю, изменилась ли ситуация с тех пор как главным менеджером памяти стал FastMM. Тем не менее даже Delphi 2010 при создании библиотеки генерирует следующий комментарий:

    { Important note about DLL memory management: ShareMem must be the
    first unit in your library’s USES clause AND your project’s (select
    Project-View Source) USES clause if your DLL exports any procedures or
    functions that pass strings as parameters or function results. This
    applies to all strings passed to and from your DLL—even those that
    are nested in records and classes. ShareMem is the interface unit to
    the BORLNDMM.DLL shared memory manager, which must be deployed along
    with your DLL. To avoid using BORLNDMM.DLL, pass string information
    using PChar or ShortString parameters. }

    всё-таки почитай статьи в блоге Алексеева, у него подробно описана куча нюансов. В том числе и причина, почему не стоит делать DLL-ки в Delphi.

  • Vlad пишет:

    2Казанцев Алексей, Алексей Тимохин.
    Что тут сказать? Век живи- век учись :) Первый ком блином

    В том числе и причина, почему не стоит делать DLL-ки в Delphi

    Тут у меня проблемка, может и не стоит, а надо, т.к. ни а чём другом написать не смогу. У меня ж базового образования по программированию нет..самоучка блин. До сих пор писал только для себя и близлежащего окружения -тех с кем непосредственно работаю и как-то даже и не заморачивался плагинами, dll-ками и пр. А тут чё-то приспичило.
    Так что буду читать и учить матчасть и ваши ЦУ :) Кстати,спасибо, что так быстро среагировали и поправили…даже не ожидал.

  • Алексей (Тамбов) пишет:

    Я использую dll-ки подключаемые динамически к основной программе, в большом проекте.
    Так вот перед тем как все это дело затеять, много чего перечитал.
    Обязательным конечно прописывать FastShareMem в самое начало в список юнитов, после этого работа со строками проходит абсолютно нормально и в программу и обратно.
    Решил использовать именно dll. А те кто говорит, что не стоит использовать dll, просто плохо знают эту область. Не скрою, что много нюансов приходится учитывать при создании dll, но для этих целей я создал образец (каркас) dll, по которой создаются все другие. У меня сейчас в программе используется 52 dll-ки. Работают 2 человека именно на создание плагинов. Из плагинов доступны все функции основной программы программы, плагины могут использовать все формы основной программы. Ни разу не пожалел что выбрал данный путь, т.к. разработка упрастилась в разы. Конечно есть минус, в большом размере dll, но это не существенно, т.к. при решении проблемм обычно отлаживать приходится всего одну dll-ку. И не нужно Влада дезинформировать, если вы серьезно не работали с dll, то не нужно прикрываться чужими статьями.
    Влад, я тебе советую прежде чем отказываться от чего то, реши для себя чем тебя не устраивает данный способ. Я решил все проблемы с dll. Могу помочь если что. Так что решать тебе.

  • Vlad пишет:

    2Алексей (Тамбов) Так я не отказываюсь, наоборот буду дальше врубаться в работу с DLL. У меня просто очень сильно ограничен выбор в плане языка программирования, точнее сказать — вообще нет выбора. Поэтому единственный путь — это разбираться, слушать то, что говорят другие, опять учиться и т.д. Появилась такая проблема, что с одной стороны есть желающие продолжить ту работу, которую я затеял еще, когда учился в универе и зашибить на этом бабло + т.к. разработка более научная, чем просто «софт для всех» то и не исключено, что в научном направлении тоже кто-то преуспеет, а с другой стороны — отдать все исходники нахалявку над которыми работал кучу лет — тоже не резон. Поэтому и решил залезть в dll-ки, плагины, интерфейсы и т.д. Дать возможность разрабатывать людям что-то свое, но на базе того, что уже есть.

  • Казанцев Алексей пишет:

    >В том числе и причина, почему не стоит делать DLL-ки в Delphi.

    Вот это да… Пойду вены вскрою :)))

  • Vlad пишет:

    Да не стоит так принимать всё близко к сердцу :) Все в порядке…жизнь продолжаеся

  • Алексей Тимохин пишет:

    >В том числе и причина, почему не стоит делать DLL-ки в Delphi.

    Сорри, я страшно наврал. Я имел в виду проблему с DllMain. Она описана здесь а также обсуждается в комментариях к первой статье о плагинах gunsmoker-a.

  • Vlad пишет:

    Млин, я уж думал и вправду чего-то упустил у GunSmoker’а…нихрена ссе думаю, даже DLL-ку не собрать без проблем.
    Надеюсь, Казанцев Алексей ещё не покончил жизнь самоубийством…
    А прикольно получилось — три Алексея в посте комментят и 1 Vlad…Мужики, я Вас не отвлекаю? :)

  • Казанцев Алексей пишет:

    Алексей, никакой проблемы с DllMain нет. Есть определенное поведение DLL’ей обусловленое системными соглашениями. Это не проблема Delphi. Это вообще не проблема. Это нужно просто знать. Есть прекрасная литература описывающая работу системных механизмов, например «Windows для профессионалов» от Джефри Рихтера или «Системное программирование в Windows» от Ала Вильямса(уверяю, узнаете больше чем из переводов блогеров). А без подобных знаний любой собственный косяк можно считать проблемой Delphi. Без обид.

  • deksden пишет:

    советую скачать tms plugin framework trial и изучить методологию построения такого софта — все таки коммерческое решение! 75 евров за него просят, существует оно давно и работает вроде бы достаточно успешно.

    Если мне не изменет память — кое что можно посмотреть также у джедаев в вкл паке — но там с документацией немного хуже, зато все исходники открыты и доступны для изучения.

  • irwin пишет:

    а если приложение пишется, исходя из того что основным и единственным языком будет делфи, и плагины будут тоже на делфи, стоит поглядеть в сторону bpl?

  • Казанцев Алексей пишет:

    2irwin: Если еще и версия Delphi одинакова у приложения и плагинов. Однако, лично я считаю идею использования пакетов в качестве плагинов не самой лучшей.

  • Алексей Тимохин пишет:

    Алексей Казанцев, я готов согласится с тем, что при использовании только стандартных юнитов проблем нет.
    Но при использовании чужих юнитов, которые могут пихать в initializtion секцию всё что угодно, проблемы всё-таки могут появится. Причём не зная откуда растут ноги, найти источник такой проблемы будет не так просто. Посему, насколько я понимаю, вместо Dll-ок в Delphi рекомендуется создавать BPL-ки (которые не имеют такой проблемы), и уже их использовать как DLL.

    Gunsmoker здесь говорит примерно то же самое.

    Рихтера я обязательно почитаю, спасибо.

  • Казанцев Алексей пишет:

    > Но при использовании чужих юнитов, которые могут пихать в initializtion секцию всё что угодно, проблемы всё-таки могут появится.

    Так можно продолжать бесконечно. При попытке ручного управления временем жизни строк/динамических массивов/интерфейсов проблемы могут появиться. При использовании чужих унитов имеющих утечки памяти проблемы могут появится. При использовании адресной арифметики могут возникнуть проблемы.

    Можно ли называть это проблемами Delphi? Нет. Это проблема (не)грамотного использования.

    p.s. По поводу пакетов ты заблуждаешься.

  • Казанцев Алексей пишет:

    А есть еще конструкторы классов (тоже, кстати, отрабатывают неявно), а есть еще циклические ссылки… Как страшно жить ;)

  • Александр пишет:

    >>> Однако, лично я считаю идею использования пакетов в качестве плагинов не самой лучшей.

    Лично я тоже так считаю, но только по той причине, что это 1). сложно 2). отключается smart-linking (по определению пакета). Использование пакетов само по себе ещё не означает привязку к Delphi, если вопрос про это.

    По поводу же DllMain — хотя я не вижу смысла в споре чей это баг, но факт остаётся фактом. Проблема есть и про неё нужно хотя бы знать.

    Почему я считаю, что это проблема Delphi (окей, проблема Delphi, диктуемая системой)? Да по той простой причине, что initialization/finalization преподносится в Delphi как общее решение для кода инициализации/финализации. Нигде в справке Delphi не указано, что это как-то имеет привязку к DllMain, нигде в справке Delphi не указано, какой код допускается там вызывать, а какой нет. Справка Delphi говорит нам: помещайте код инициализации в initialization или begin/end, а код очистки — в finalization. Он будет вызван при запуске и выгрузке процесса. Всё. Точка. Внимание, вопрос: как я могу «просто знать», что здесь что-то не так?

    Более того, в справке Delphi напрямую рекомендуется вызывать вещи, вообще говоря, запрещенные MSDN, цитирую: «The statements in a library’s block constitute the library’s initialization code. These statements are executed once every time the library is loaded. They typically perform tasks like registering window classes and initializing variables».

    И это ещё не всё. Это не проблема документации, потому что… код самой Delphi тут тоже не безгрешен!

    Опуская наглядный пример с компонентами Interbase из стандартной поставки Delphi, если вам кажется, что проблема в сторонних модулях, то вы глубоко заблуждаетесь. Простая DLL Delphi с модулями System и SysUtils может вызывать проблемы в определённых ситуациях. Я лично такое видел. К сожалению, сейчас я уже не помню деталей, но смысл был в том, что код DllMain из DLL Delphi получал управление до инициализации User32 — там была какая-то хитрая зависимость. Что, понятно, приводило к вылету процесса.

    Вот чего в Delphi не хватает — так возможности явно задать DLLMain. Чтобы выполнялась чисто моя функция, без RTL. А RTL я бы и сам вызвал. Вот, кстати, соответствующее предложение на UserVoice.

    >>> Можно ли называть это проблемами Delphi?

    Неудачный пример. Одно дело, когда ты пишешь код и допускаешь баг. Типа утечки памяти или выход индекса за диапазон. Другое дело — когда тебе сама справка Delphi говорит писать так-то, ты пишешь — а в итоге получаешь баг. Согласитесь, это далеко не одно и то же.

  • Казанцев Алексей пишет:

    Любому адекватному разработчику ясно, что код инициализирующих секций отрабатывающий до основного блока Begin End будет одинаково отрабатывать до него, как в случае обычного приложения, так и в случае dll. В справке отдельно указаны моменты важные для написания dll. Вот о какой проблеме тут идет речь? Что разработчик был не в курсе, как происходит загрузка библиотек и как они инициализируются (без технических дебрей, достаточно ключевых фаз)? В таком случае, это проблема того разработчика.

    >Внимание, вопрос: как я могу “просто знать”, что здесь что-то не так?

    Все так. Проблем нет. Нужно просто знать среду которую используешь и под которую пишешь. Все. Других проблем нет.

    >Более того, в справке Delphi напрямую рекомендуется вызывать вещи, вообще говоря, запрещенные MSDN, цитирую: “The statements in a library’s block constitute the library’s initialization code. These statements are executed once every time the library is loaded. They typically perform tasks like registering window classes and initializing variables”

    Осталось процитировать MSDN на предмет запрета вызова RegisterClass (о ней ведь речь?) из DllMain.

    >К сожалению, сейчас я уже не помню деталей, но смысл был в том, что код DllMain из DLL Delphi получал управление до инициализации User32 – там была какая-то хитрая зависимость.

    Без деталей, конечно, не разговор, но если я правильно понял проблемный код был в DllMain, т.е. это был твой код? А если так, причем тут Delphi? В любом случае, всегда есть возможность провести раннюю инициализацию, просто понимая механизмы. Это не проблемы, это by design.

    >Согласитесь, это далеко не одно и то же.

    Не соглашусь. Справка не дает ни каких рекомендаций относительно выполняемой работы в инициализирующих секциях (They typically perform tasks like — на рекомендации не тянет, увы) т.е. это все на совести разработчика (мы все еще считаем его адекватным?). Если человек берется писать системные вещи, а dll это все же «системная вещь», то он должен отдавать себе отчет и полностью понимать, что он делает.

  • Александр пишет:

    Алексей, давайте вы просто взгляните правде в глаза: разработчики Delphi не слишком-то осведомлены о проблемах DllMain. Отсюда идёт и справка, которая молчит об этом, и кривые стандартные модуля (вы же как «адекватный разработчик» не собираетесь это отрицать? Это баг в явном виде), и даже сам дизайн дельфёвых DLL.
    > т.е. это был твой код?
    Нет, конечно. Стал бы я тогда об этом упоминать.

  • Казанцев Алексей пишет:

    >разработчики Delphi не слишком-то осведомлены о проблемах DllMain.
    Ты прав. Разработчики Delphi не осведомлены о проблемах DllMain. Потому, что проблем DllMain нет. Есть проблема разработчика не знающего/не понимающего происходящих процессов (в том числе, разработчика пишущего компоненты).
    >Отсюда идёт и справка, которая молчит об этом

    О чем еще должна говорить справка? О том, когда отрабатывают init/fin секции написано. Когда отрабатывает основной блок Begin End написано. Дальше идут системные механизмы, описывать которые нет смысла т.к. для этой информации существуют другие источники. Более того, это информация относящаяся уже к системе, а следовательно и литературу нужно читать соответствующую (ссылка на MSDN, где запрещается регистрировать классы из DllMain будет?).

    >и кривые стандартные модуля (вы же как “адекватный разработчик” не собираетесь это отрицать? Это баг в явном виде)
    Я так и не увидел деталей, лишь упоминание System и SysUtils. Что тут можно сказать?
    >> т.е. это был твой код?
    >Нет, конечно. Стал бы я тогда об этом упоминать.
    А чей? Инициализационных секций?

  • Александр пишет:

    > в том числе, разработчика пишущего компоненты
    …забыли добавить: в том числе, разработчиков Delphi.
     
    > О том, когда отрабатывают init/fin секции написано
    Ткните пальцем.
    Только не надо тыкать в «These statements are executed once every time the library is loaded». У пакетов тоже такое написано. Что не означает, что их инициализация как-то связана с DllMain.
     
    > ссылка на MSDN, где запрещается регистрировать классы из DllMain будет?
    Простой вывод из следующих кусков:
    1. RegisterClass принадлежит User32.
    2. В описании DllMain написано «Calling functions that require DLLs other than Kernel32.dll may result in problems that are difficult to diagnose». Прямым текстом сказано, что вызовы User32 — запрещены.
    (бонус) 3. Добавьте сюда позыв вызывать RegisterClass из initialization в DLL (как говорит нам справка Delphi): «The statements in a library’s block constitute the library’s initialization code. … They typically perform tasks like registering window classes and initializing variables» — мы и получаем, что разработчики Delphi не в курсе про проблемы DllMain.
     
    > Я так и не увидел деталей
    Смотрите внимательнее: http://delphikingdom.ru/asp/answer.asp?IDAnswer=68224
    Заметьте, что это — стандартные компоненты Delphi.

  • Казанцев Алексей пишет:

    >Только не надо тыкать в «These statements are executed once every time the library is loaded». У пакетов тоже такое написано. Что не означает, что их инициализация как-то связана с DllMain.

    Именно в это и надо. Ну еще: «The initialization section contains statements that are executed, in the order in which they appear, on program start-up.». Адекватному (подчеркиваю!) разработчику достаточно знания того, что инициализирующий код будет выполнен до основного блока Begin End. Дальше только поиск информации о том, как это будет выглядеть в случае с dll, и погружение в дела системные. А чего ожидалось? Инструкции для «чайников»?

    >2. В описании DllMain написано «Calling functions that require DLLs other than Kernel32.dll may result in problems that are difficult to diagnose». Прямым текстом сказано, что вызовы User32 — запрещены.

    Я не вижу тут прямого запрета. Есть предупреждение (!), но не более. Ну, что мне код dll привести, где из DllMain загружается (а ведь на этот счет нас тоже предупреждают: «It must not call the LoadLibrary or LoadLibraryEx function (or a function that calls these functions), because this may create dependency loops in the DLL load order.») user32.dll и дергается RegisterClass?

    >Смотрите внимательнее: http://delphikingdom.ru/asp/answer.asp?IDAnswer=68224

    Обязательно посмотрю, как только они решат проблему с регистрацией домена.

    >Заметьте, что это — стандартные компоненты Delphi.

    К слову сказать, это (речь об IBX, так?) приобретенные компоненты от стороннего разработчика.

  • Александр пишет:

    Показательный пост. Allen Bayer глаголет: а используйте-ка вы пакеты, ребята, и не парьтесь.

  • Казанцев Алексей пишет:

    А как писать плагины к другим, не Delphi, приложениям? А как писать расширения оболочки (Explorer)? Как писать system wide hooks без умения писать и использовать dll ? Вопросы риторические. Все нежелающие париться давно уже свалили на C#.

  • Александр пишет:

    > А как писать плагины к другим, не Delphi, приложениям?
    Нормальные не-Delphi приложения предусматриваю в своей системе плагинов функции Init и Done, а не полагаются на DllMain плагина.
    Никто не запрещает вам использовать пакеты в качестве DLL-плагинов к не-Delphi программам. То, что вы собираете пакет — ещё не означает какой-то привязки к Delphi. Вы делаете пакет, выставляете наружу его Init/Done, которые вызываются системой плагинов — и, пожалуйста, никаких проблем в принципе не может возникнуть.
    Было бы неплохо, если бы обычная DLL позволяла бы такой трюк, без необходимости использовать такие черезжопные решения.
    > А как писать расширения оболочки (Explorer)?
    А в чём проблема? Расширение оболочки — это DLL с явным аналогом Init/Done. Нет никакой необходимости использовать инициализацию в DllMain (как это делают Дельфёвые DLL).

  • Казанцев Алексей пишет:

    >Никто не запрещает вам использовать пакеты в качестве DLL-плагинов к не-Delphi программам.
    Конечно никто, кроме здавого смысла. Мне, например, не улыбается перспектива иметь маленький плагин и 4х-метровый рантайм для него. А так конечно, пакет суть обычная dll.

  • GS пишет:

    Я тоже в свое время прошел через данную проблему с плугинами, и в качестве изначального решения использовались пакеты. Все было круто, но только до того момента, пока Borland не выпустил Update Pack 3 к Delphi 4 (мы до этого разрабатывали на Delphi 4 с Update pack 2). Суть проблемы была в несовместимости пакетов между 2 и 3 апдейтами одной версии делфи! Перекомпиляция же всех плугинов и внедрение было невозможно из за большой удаленности от цивилизации рабочих мест с нашим софтом (в некоторые места можно только вертолетом добраться) и серьезного количества разных заказчиков. Поэтому на собственной шкуре понял, что приложение и плугины должны быть независимы от используемого языка, то бишь построены на dll.

    Что касается инициализации, ничто не запрещает при старте dll инициализировать nil-ом внутренние глобальные данные (хоть в DllMain, WinAPI это не разрушает, не правда ли? :) ). А инициализировать сессию в плугине уже вызовом например Init — это корректно работает от delphi 2 до delphi xe2.

    По использованию памяти — я использую функции CoTaskMem — это позволяет передавать память даже между ASP.NET и 32-битными dll. Попозжа постраюсь выложить упрощенный пример своего видения плугинов (в какои виде у нас работает уже 10 лет)

    P.S. Всех с 2012 годом, и надеюсь майя с концом света ошиблись — обидно будет перестать программировать :)

Ваш ответ

Внимание: Все комментарии модерируются, и это может вызвать задержку их публикации. Отправлять комментарий заново не требуется.

Пожалуйста, заключайте исходный код в тэги [code][/code].
Если код большой, то воспользуйтесь Вставкой кода на отдельной странице и оставьте в комментарии ссылку на исходник

   


flv кодек --|--. hp pavilion dv2899er