В данной заметке, хочу поделиться с вами хорошим примером написания модуля, с помощью которого можно добавить новые возможности в вашу Delphi IDE.
Пример из этой статьи довольно простой, но между тем и очень эффективный. Он позволяет очень легко добавить обработчик события (назовём его обработчиком нажатия средней кнопки мыши), в среду Delphi, путём обработки сообщений Windows.
Пусть, для примера, в обработчике будет выполняться код, отвечающий за программное нажатие клавиши F12, тем самым позволяя нам переключаться между окнами исходного кода и дизайнера форм по средней кнопке мыши. Очень удобно, правда?
unit WinSwap; interface uses Windows, Messages, SysUtils, Classes, Controls, Forms; type TWinSwap = class(TObject) private fIDE:TApplication; procedure PressKey(Key:Word); protected procedure IDEMessage(var Msg: TMsg; var Handled: Boolean); public property IDE:TApplication read fIDE write fIDE; end; var oSwaper: TWinSwap; implementation procedure SwapInit; begin { связываем поле fIDE со средой Delphi } oSwaper.IDE := Application; { устанавливаем новый обработчик OnMessage } oSwaper.IDE.OnMessage := oSwaper.IDEMessage; end; { TWinSwap } procedure TWinSwap.IDEMessage(var Msg: TMsg; var Handled: Boolean); begin if IDE.Active then begin if Msg.Message = WM_MBUTTONUP then PressKey(VK_F12); end else Exit; end; procedure TWinSwap.PressKey(Key: Word); begin keybd_event(Key,0,KEYEVENTF_EXTENDEDKEY,0); keybd_event(Key,0,KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP,0); end; initialization oSwaper := TWinSwap.Create; { инициализируем расширение } SwapInit; finalization { скидываем обработчик } oSwaper.IDE.OnMessage := nil; oSwaper.Free; end.
Как видите, код не самый сложный и разобраться в нём не составит никакого труда. Ключевые строки располагаются в процедуре SwapInit. Происходит присвоение значения объекта Application (которое рассматривается как объект самой среды) полю fIDE типа TApplication и затем устанавливаем новый обработчик OnMessage уже самой IDE.
Таким образом можно обрабатывать многие полезные сообщения, приходящие из вне. Можно работать с отдельными частями IDE как с простыми компонентами, средствами объекта Application. Вообщем можно много всего, главное приложить немного фантазии, которой у меня к сожалению не много.
Установка
Установка такого модуля сводится к обычной инсталляции нового компонента в новый, или существующий пакет. Другими словами, установка такого модуля и нового компонента ничем не отличается.
Надеюсь, этот пример был полезен для вас. С наилучшими пожеланиями. Читайте новые интересные заметки на страницах блога Delphi Day.
Правильная версия
Если вы прочитаете комментарии ниже, поймете, что данная версия кода мягко говоря не очень правильная. Как я там написал, что:
Если написать ещё один подобный (компонент?, даже не знаю как правильно назвать) модуль. То при его инициализации, обработчик OnMessage как бы перекроет обработчики всех остальных, инсталлированых до него, что собственно одно и тоже (с небольшими поправками), о чём говорил USoft.
Я немного дополнил код, следуя совету USoft. Проверял на 10ти таких установленных в систему расширений. Теперь всё работает прекрасно. Испытания проводил в двух средах: D7, D2k6.
Хотел бы отдельно поблагодарить Алексея Тимохина за расшифровку совета USoft и USoft'a, за дельный совет. Спасибо, коллеги.Итак, правильная версия скорее всего должна быть такая:
unit WinSwap; interface uses Windows, Messages, SysUtils, Classes, Controls, Forms; type TIDEHandler = procedure (var Msg:TMsg; var Handled: Boolean) of object; TWinSwap = class(TObject) private fIDE:TApplication; fIDEHandler:TIDEHandler; procedure PressKey(Key:Word); protected procedure IDEMessage(var Msg: TMsg; var Handled: Boolean); procedure BuffHandler; public property IDE:TApplication read fIDE write fIDE; property Handler:TIDEHandler read fIDEHandler write fIDEHandler; end; var oSwaper: TWinSwap; implementation procedure SwapInit; begin { связываем поле fIDE со средой Delphi } oSwaper.IDE := Application; oSwaper.BuffHandler; { устанавливаем новый обработчик OnMessage } oSwaper.IDE.OnMessage := oSwaper.IDEMessage; end; { TWinSwap } procedure TWinSwap.BuffHandler; begin Handler := oSwaper.IDE.OnMessage; end; procedure TWinSwap.IDEMessage(var Msg: TMsg; var Handled: Boolean); begin if IDE.Active then begin if Msg.Message = WM_MBUTTONUP then PressKey(VK_F12); Handler(Msg,Handled); end else begin Handler(Msg,Handled); Exit; end; end; procedure TWinSwap.PressKey(Key: Word); begin keybd_event(Key,0,KEYEVENTF_EXTENDEDKEY,0); keybd_event(Key,0,KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP,0); end; initialization oSwaper := TWinSwap.Create; { инициализируем расширение } SwapInit; finalization { скидываем обработчик } oSwaper.IDE.OnMessage := nil; oSwaper.Free; end.
Спасибо за комментарий. Я не совсем понял, что Вы имеете ввиду, нельзя ли неможко подробнее?
ОтветитьУдалитьUSoft говорит о том, что в своём обработчики нужно обязательно вызывать оригианальный обработчик. Без такого обработчика данный код представляет интерес, только как пример говнокода.
ОтветитьУдалитьНо идея интересная!
Ааа, спасибо Алексей, завтра перепишу код. Я что-то не сразу продуплил, что имееи ввиду Usoft.
ОтветитьУдалитьИтак, проведены следующие наблюдения:
ОтветитьУдалитьCnWiz. Работает совсем иначе. CnWizards - пакет мастеров, а не пакетов работающих по такому же принципу, что
соответственно абсолютно не мешает им сосуществовать по крайней мере с данным примером класса. (я проверил это
на 7х и 2k6 дельфах - CnPack чувствует себя прекрасно)
Другое дело, если написать ещё один подобный (компонент?, даже не знаю как правильно назвать) модуль. То при его инициализации, обработчик OnMessage как бы перекроет обработчики всех остальных, инсталлированых до него, что собственно одно и тоже (с небольшими поправками), о чём говорил USoft.
Я немного дополнил код, следуя совету USoft. Проверял на 10ти таких установленных в систему расширений. Теперь всё работает прекрасно. Испытания проводил в двух средах: D7, D2k6.
Хотел бы отдельно поблагодарить Алексея Тимохина за расшифровку совета USoft и USoft'a, за дельный совет. Спасибо, коллеги.
Пожалуйста.
ОтветитьУдалитьЕщё пара замечаний:
1) что будет если в Delphi будут загружены 2 таких файла (не знаю как его лучше назвать)? А что случится, если сначала будет выгружен первый файл, а потом второй? (*1 см. ответ в конце). Использовать себя этот способ для себя - может и нормально, но делать такое в коде предназначенном для публичного использования, всё-таки лучше не стоит. Имхо.
2) Нет нужды объявлять тип TIDEHandler, вместо этого лучше воспользоваться готовыми типом TMessageEvent из юнита Forms.pas.
3) Перед вызовом Handler, стоит проверить, существует ли он с помощью конструкции if assigned(Handler) then вызвать его.
4) Необходимо восстанавливать оригинальный обработчик Application.OnMessage в деструкторе TWinSwap. Иначе IDE может быть бо-бо.
5) Замечание касающееся ООП и красоты. Удобно вынести код заменяющий обработчик и восстанавливающий оригинальный в отдельные методы класса, напримр HookEvents и UnHookEvents.
*1 правильно, оригинальный обработчик будет потерян и вместо него будет подставлен мусорный обработчик. Я писал об похожем случае здесь.