Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Отцы и дети


Автор: rOOxus 11.4.2009, 11:21
Имеется MDI приложение состоящее только из одной родительской формы. Дочерние формы поголовно создаються из DLL.
Как узнать в MDI родителе о создании или уничножении MDI child? Естесно в MDIchild-ах которые по DLL живут никакого кода по этому поводу писать нельзя.

Автор: aliks 13.4.2009, 09:33
О создании MDI child твой родитель узнает, когда ты будешь из главной формы будешь вызывать создание ребенка, при этом надо будет запомнить хендл. А потом отслеживать сообщения и ловить когда будет закрывать форма с данным хенделом, так ты поймаешь уничтожение ребенка.

Приведи код вызова/создания ребенка

Автор: rOOxus 13.4.2009, 11:46
хм... все не так просто!
я использую компонент из JVCL jvPluginManager, ну и соответственно плагины под него с помощью мастера JVCL, который встраиваеться в Delphi/ Плагины подгружються в событии OnShow родительской формы одной строкой кода:
Код

procedure TMainForm.FormShow(Sender: TObject);
begin
   PluginManager.LoadPlugins;
end;

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

procedure TModules.JvPlugInPluginMessage(Sender: TObject;
  APluginMessage: Integer; AMessageText: String);
begin
   if AMessageText = 'create_form' then
      ModulesForm := TModulesForm.Create( nil );
end;

то есть если главная программа сделает вот так
Код

PluginManager.Plugins[0].SendPluginMessage( 0, 'create_form' );

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

Автор: rOOxus 16.4.2009, 10:38
я что так всех сильно напугал?

Автор: Bose 16.4.2009, 13:09
Цитата(rOOxus @  16.4.2009,  09:38 Найти цитируемый пост)
я что так всех сильно напугал? 

Не думаю, что тут много людей которые использовали JvPlugins. 

Я когда-то смотрел, но на практике не применял. Удивительно, что это вообще работает.  smile

Могу только посоветовать попробовать копнуть в сторону:
1) Screen.FormCount и Screen.Forms[i]
2) почитать об http://msdn.microsoft.com/en-us/library/dd458665(VS.85).aspx
Может что-нибудь полезное и найдётся.

Автор: rOOxus 16.4.2009, 14:25
в том то и дело, что MDI сообщения передаються WM_MDIACTIVATE, WM_MDICREATE, WM_MDIDESTROY и т.д. передаються дочернему окну, а не главному. поэтому они бесполезны. а надо чтобы главное реагировало на создание дочернего.

Автор: Bose 17.4.2009, 01:26
У меня вообще не получилось создать MdiChild в плагине.
---------------------------
Debugger Exception Notification
---------------------------
Project MDIAPP.exe raised exception class EInvalidOperation with message 'Cannot create form. No MDI forms are currently active'.
---------------------------
Break   Continue   Help   
---------------------------

rOOxus, покажи как ты MdiChild создаёшь?

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

Автор: rOOxus 17.4.2009, 19:12
без проблем.
вот тестовый проектик, который содержит главное приложение и dll-плагин, который в свою очередь содержит дочернюю форму и которую главное приложение может вежливо попросить плагин создать.
только для компиляци есть тонкость - я немного изменил код JVCL, а точнее в JvPluginManager.pas и JvPlugin.pas согласно теме, которая обсуждалась http://forum.vingrad.ru/forum/topic-82313/hl/jvpluginmanager/index.html это необходимо для нормального оображения формы в окне родителя.

Автор: Лапоть 17.4.2009, 19:22
Цитата(rOOxus @  11.4.2009,  11:21 Найти цитируемый пост)
Имеется MDI приложение состоящее только из одной родительской формы. Дочерние формы поголовно создаються из DLL

Позвольте спросить - а зачем такой изврат?

Автор: rOOxus 17.4.2009, 21:42
Цитата(Лапоть @ 17.4.2009,  19:22)
Позвольте спросить - а зачем такой изврат?

конечно. с удовольствием объясню.
дело в том, что разработка коллективная т.е. приложение разбито на несколько модулей, первый разарабатывает один программист, второй - другой а третий - совершенно пятнадцатый и т.д. и т.п. ядро т.е. приложение с главной формой, которая являеться MDI-родителем разрабатываю я.
модули реализованы в виде dll, которые создаються с помощью инструментария JVCL для плагинов, т.е. в dll сосредоточено ядро плагина и несколько форм для реализации интерфейса пользователя. центральная форма плагина - MDI-ребенок, ну остальные в основном будут вызываться модально для разных целей.
так вот охота мне чтобы MDI интерфейс напоминал окно Opera или Internet Explorer последних версий, а именно: в главном окне есть ToolBar, на котором отображаються кнопки всех MDI-окон-детей, которые создаються при создании ребенка, утпливаються при активации ребенка и удалються при закрытии ребенка. конечно проблема решаеться предельно просто если в события OnCreate, OnActivate и OnClose форм-детей всех плагинов внедрить код на создание, изменение статуса и удаления той самой кнопеи на ToolBar, но каждому разработчику не объяснишь накой ему грузить свой плаагин этим кодом, да и совершенно неоправдано это.
вот я и ищу способ как заставить главную форму регировать на создание, активцию и удаление в ней дочереней дабы сей функционал был релизован единожды в еёйном коде

Автор: Bose 18.4.2009, 02:39
Цитата(rOOxus @  17.4.2009,  18:12 Найти цитируемый пост)
только для компиляци есть тонкость - я немного изменил код JVCL, а точнее в JvPluginManager.pas и JvPlugin.pas согласно теме, которая обсуждалась тут это необходимо для нормального оображения формы в окне родителя. 

Ой-ой-ой. Я не могу внятно сформулировать причину, но почему-то мне кажется, что так делать ни в коем случае не стоит. 

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

Прежде чем двигаться дальше, настоятельно советую прочитать http://gunsmoker.blogspot.com/2008/12/1.html.

Автор: rOOxus 18.4.2009, 13:34
2 Bose
вы же попоросили выложить пример но даже не скачали его а слеовательно не посмотрели, а сразу испугались того как там что релизовно... странно
да и потом давайте немного претормозим и вернемся к вопросу самой темы. а он заключаеться в следующем: как зставить MDI-родителя узнать о том, что создан/активирован/удален MDI-ребенок без создания какого либо код в ребенке. ну неужто нет никаких способов. мне тут кто-то на днях посоветовал hook-ами воспользоваться, но я к сожалению с ними никогда не имел дела. может кто выскажет мнение имеет ли перспективу такой подход?

Автор: Bose 18.4.2009, 20:15
rOOxus, я не скачивал его, потому что узнав о необходимость править исходники JvPluginManager я потерял интерес к исследованию вашей проблемы. 
п.с. На мой взгляд, делать так - это то же самое, что закладывать фундамент на месте захоронения часовых бомб, в надежде пострить здание и продать до того, как хотя бы одна из них рванёт. 

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

А общее моё мнение такого, что лучше сразу изменить архитектуру плагинов, придумав свой API, чем постоянно сталкиваться с разными странными глюками. А они, поверьте мне, будут.  smile 

Автор: rOOxus 18.4.2009, 22:19
за недостающий юнит уж простите не углядел. на всякий случай выкладываю полный архив.
а насчет арихитектуры плагинов - да забудте вы о ней этот вопрос пока открыт. тема же другая совсем и намного шире. к тому же приложение предназначено для внутрикорпоративного использования так что если я его кому-то и продам, то только себе же самому.
хотелось бы услышать мнение насчет hook-ов. можно ли с их помощью перехватить сообщение внутри приложения о предаче тех самых WM_MDICREATE, WM_MDIACTIVATE, WM_MDIDESTROY от приложения к MDI окну? или я опять в направлении грабель иду?

Автор: Bose 18.4.2009, 23:37
Насчёт hook-ов, сорри, не знаю - никогда с ними не работал. Но я видел что-то подобное реализованным TBX-Toolbar-ах Алекса Денисова.

http://www.mswil.ch/websvn/filedetails.php?repname=devphp&path=%2Fcomponent%2Ftbx2_1_beta%2FTBXMDI.pas&rev=6&sc=1
Смотреть все упоминания фразы CBTHook.
Не знаю, сгодится ли.

Автор: rOOxus 19.4.2009, 21:52
вы знаете - сгодилось! сверстал на основе исходников того модуля, ссылку на который вы дали. так что если кто еще интересуеться, то вот:
  •  Создаем функцию, котораая определит являеться ли окно окном MDIChild
  •  Пишем функцию hook-а, которая отловит все действия с окнами в данном приложении (т.е. имеем локальную ловушку)
  •  В событии OnCreate главной формы устанавливаем hook
  •  В событии OnDestroy главной формы снимаем hook
  •  Ну и при создании кнопки вешаем ей на OnClick событие, которое по ее Tag отышет MDI е таким же Handle и покажет его
небольшая тонкость - hook отлавливает события до их наступления, поэтому невозможно определить станет ли создаваемое окно MDIChild до его создания. поэтому создание кнопки окна на ToolBar делаеться при наступлении события установки форкуса на окно т.е. вместо HCBT_CREATEWND ловушка реагирует на HCBT_SETFOCUS и если кнопка окна уже есть, то топит ее, а если нет, то создает и топит.
Код

...

var
  CBTHookHandle: HHOOK;

// функци определения являеться ли окно MDIChild и определения его заголовка
function WindowIsMDIChild(W: HWND; var WinCaption : String ): Boolean;
var
  i: Integer;
  MainForm, ChildForm: TForm;
begin
  MainForm := Application.MainForm;
  if Assigned(MainForm) then
    for i := 0 to MainForm.MDIChildCount-1 do begin
      ChildForm := MainForm.MDIChildren[i];
      if ChildForm.HandleAllocated and (ChildForm.Handle = W) then
      begin
        WinCaption := ChildForm.Caption;
        Result := true;
        Exit;
      end;
    end;
  Result := False;
end;

// собственно Hook
function CBTHook(Code: Integer; WParam: WPARAM; LParam: LPARAM): LRESULT;
stdcall;
var
   i, Index : Integer;
   WndExists : Boolean;
   Item : TTBItem;
   WinCaption : string;
begin
   case Code of
      // активация окна
      HCBT_SETFOCUS :
      begin
         // если окно MDICild
         if WindowIsMDIChild ( HWND(WParam), WinCaption ) then
         begin
            // определяем есть ли на ToolBar кнопка с Tag равным
            // Handle окна
            WndExists := False;
            for i := 0 to MainForm.tb_WindowTB.Items.Count - 1 do
            begin
               if MainForm.tb_WindowTB.Items.Items[i].Tag = HWND(WParam) then
               begin
                  WndExists := True;
                  Index := i;
               end;
            end;

            // если нет, то создаем ее
            if not WndExists then
            begin
               Item := TTBItem.Create( nil );
               Item.Caption := WinCaption;
               Item.GroupIndex := 1;
               Item.AutoCheck := true;
               Item.Tag := HWND(WParam);
               Item.OnClick := MainForm.OnWinBtnClick;
               MainForm.tb_WindowTB.Items.Add( Item );
               Item.Checked := True;
            end
            // если есть, то топим
            else
            begin
               MainForm.tb_WindowTB.Items.Items[Index].Checked := True;
            end;
         end;
      end;
      // окно уничтожено
      HCBT_DESTROYWND :
      begin
         // если окно MDICild
         if WindowIsMDIChild ( HWND(WParam), WinCaption ) then
         begin
            // ищем на ToolBar кнопку с Tag равным Handle окна...
            for i := 0 to MainForm.tb_WindowTB.Items.Count - 1 do
            begin
               if MainForm.tb_WindowTB.Items.Items[i].Tag = HWND(WParam) then
               begin
                  Index := i;
               end;
            end;
            // ... и уничтожаем ее
            MainForm.tb_WindowTB.Items.Delete( Index );
         end;
      end;
   end;
   Result := CallNextHookEx(CBTHookHandle, Code, WParam, LParam);
end;

...

procedure TMainForm.FormCreate(Sender: TObject);
begin
   // Устанавливаем Hook
   if CBTHookHandle = 0 then
      CBTHookHandle := SetWindowsHookEx( WH_CBT, CBTHook, 0, GetCurrentThreadId );
end;

...

// Событие нажатия кнопки
procedure TMainForm.OnWinBtnClick(Sender: TObject);
var
   i : Integer;
begin
   TTBItem (Sender).Checked := true;
   for i := 0 to MainForm.MDIChildCount - 1 do
   begin
      if TTBItem (Sender).Tag = MainForm.MDIChildren[i].Handle then
         MainForm.MDIChildren[i].Show;
   end;
end;

...

procedure TMainForm.FormDestroy(Sender: TObject);
begin
   // Снимаем Hook
   UnhookWindowsHookEx(CBTHookHandle);
end;

Автор: Bose 20.4.2009, 03:03
Цитата(rOOxus @  19.4.2009,  20:52 Найти цитируемый пост)
вы знаете - сгодилось! сверстал на основе исходников того модуля, ссылку на который вы дали.

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

Автор: rOOxus 20.4.2009, 13:49
спасибо. учту smile 

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