Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2005.10.02;
Скачать: [xml.tar.bz2];

Вниз

Создание невиз. компонента   Найти похожие ветки 

 
Dr. Genius   (2005-09-05 16:50) [0]

Заинтересовался недавно созданием собственных компонент в Delphi. Пишу сейчас компонент для перехвата системных WM-сообщений Windows – таких как WM_HOTKEY, WM_QUERYENDSESSION, WM_SYSTEMERROR и др. Но компонент не перехватывает события. Ниже выложен текст модуля компонента.
Бросаю я свой компонент на форму, обрабатываю событие OnHotKey. Запускаю свой проект. Событие перехвата нажатия «горячей» клавиши не работает. Точно так же не работает событие перехвата OnQueryEndSession. Событие OnSystemError я не тестировал, но понятно, что и оно наверняка не будет работать. В чем проблема? Почему не перехватываются WM-сообщения.
Я выложил текст модуля целиком, т. к. может быть в нем есть ещё ошибки. Заниматься созданием компонентов я начал всего пару дней назад и ещё плохо разбираюсь в этом. Поэтому не исключено, что в модуле есть и другие ошибки, или я что-то сделал не так. Сообщите, если заметите.

unit Eventio;

interface

uses
 Windows, Messages, SysUtils, Classes;

type
 THotKeyEvent = procedure (var Msg: TWMHotKey) of object;
 TQueryEndSessionEvent = procedure (var Msg: TWMQueryEndSession) of object;
 TSystemErrorEvent = procedure (var Msg: TWMSystemError) of object;
 TEventio = class (TComponent)
 private
   { Private declarations }
   FHotKey: THotKeyEvent;
   FQueryEndSession: TQueryEndSessionEvent;
   FSystemError: TSystemErrorEvent;
   function GetVersion: String;
   procedure SetVersion (const Value: String);
   procedure WMHotKey (var Msg: TWMHotKey); message WM_HOTKEY;
   procedure WMQueryEndSession (var Msg: TWMQueryEndSession); message WM_QUERYENDSESSION;
   procedure WMSystemError (var Msg: TWMSystemError); message WM_SYSTEMERROR;
 protected
   { Protected declarations }
 public
   { Public declarations }
   constructor Create (AOwner: TComponent); override;
   destructor Destroy; override;
 published
   { Published declarations }
   property Version: String read GetVersion write SetVersion stored False;
   property OnHotKey: THotKeyEvent read FHotKey write FHotKey;
   property OnQueryEndSession: TQueryEndSessionEvent read FQueryEndSession write FQueryEndSession;
   property OnSystemError: TSystemErrorEvent read FSystemError write FSystemError;
 end;

procedure Register;

implementation

constructor TEventio.Create (AOwner: TComponent);
begin
 inherited Create (AOwner);
end;
//-----------------------------------------------------------
destructor TEventio.Destroy;
begin
 inherited Destroy;
end;
//-----------------------------------------------------------
function TEventio.GetVersion: String;
begin
 Result := "1.0";
end;
//-----------------------------------------------------------
procedure TEventio.SetVersion (const Value: String);
begin

end;
//-----------------------------------------------------------
procedure TEventio.WMHotKey (var Msg: TWMHotKey);
begin
 if Assigned (FHotKey) then FHotKey (Msg);
end;
//-----------------------------------------------------------
procedure TEventio.WMQueryEndSession (var Msg: TWMQueryEndSession);
begin
 if Assigned (FQueryEndSession) then FQueryEndSession (Msg);
end;
//-----------------------------------------------------------
procedure TEventio.WMSystemError (var Msg: TWMSystemError);
begin
 if Assigned (FSystemError) then FSystemError (Msg);
end;
//-----------------------------------------------------------
procedure Register;
begin
 RegisterComponents ("Dr. Genius", [TEventio]);
end;
//-----------------------------------------------------------
end.


 
Digitman ©   (2005-09-05 16:56) [1]

а где собственно перехват-то ?


 
Джо ©   (2005-09-05 16:58) [2]

Волшебства не бывает. С какого перепугу вашему невизуальному компоненту будут приходить все эти сообщения?


 
Джо ©   (2005-09-05 16:59) [3]

Один из возможных путей - замена оконной процедуры на свою.


 
Dr. Genius   (2005-09-05 17:21) [4]


> Один из возможных путей - замена оконной процедуры на свою.


Не понял...


 
Digitman ©   (2005-09-05 17:26) [5]

может тебе книжки какие-нть умные почитать для начала ?

в 2-х словах ведь не объяснить, что такое "оконная процедура" и с чем ее едят ..


 
Джо ©   (2005-09-05 17:27) [6]


> [4] Dr. Genius   (05.09.05 17:21)
> Не понял...

Что же сделать? Предлагаю разобраться для начала в том, что есть "сообщение". Перед тем, как их "перехватывать".


 
Dr. Genius   (2005-09-05 17:35) [7]


> Что же сделать? Предлагаю разобраться для начала в том,
> что есть "сообщение". Перед тем, как их "перехватывать".



> Волшебства не бывает. С какого перепугу вашему невизуальному
> компоненту будут приходить все эти сообщения?


Так хотя бы скажите: можно ли невизуальным компонентом перехватывать сообщения windows.


 
Dr. Genius   (2005-09-05 17:41) [8]

Я в своей программе вручную перехватываю и обрабатываю такие сообщения как WM_HOTKEY, WM_QUERYENDSESSION. Но мне хочется сделать компонент для этого. Что такое сообщение Windows я знаю, иначе я бы не брался за создание компонента для их перехвата.


 
Джо ©   (2005-09-05 17:46) [9]


>  [7] Dr. Genius   (05.09.05 17:35)
> Так хотя бы скажите: можно ли невизуальным компонентом перехватывать
> сообщения windows.

Вопрос некорректно поставлен, что тут можно ответить? Сначала необходимо разобраться в том, что есть "сообщение". Если ты этого не понимаешь, то как можно его "перехватывать"?
С одной стороны - да, можно, в некотором смысле. Так, как, например некоторые сообщения "перехватывает" невизуальный компонент TApplicationEvent (смотри исходный код).
С другой стороны - нет, нельзя. Потому как для того, чтобы нечто "перехватить" необхоимо, чтобы тебе это нечто "посылалос". Опять же, ключевой вопрос - "С какого перепугу вашему невизуальному компоненту будут приходить все эти сообщения"?
Следовательно, сначала нужно добиться того, чтобы этому компоненту приходили какие-то там "сообщения". А для этого нужно понимать механизм сообщений вообще и специфику их обработки в Делфи.


 
Джо ©   (2005-09-05 17:48) [10]

[8] Dr. Genius   (05.09.05 17:41)
>  Что такое сообщение Windows я знаю

Ну, тогда должен знать что такое "оконная процедура". Иначе - ложь.


 
Юрий Зотов ©   (2005-09-05 17:52) [11]

> Dr. Genius   (05.09.05 17:41) [8]

В Вашем коде нет никакого перехвата сообщений, а есть только их обработка. Но чтобы сообщение можно было обработать, его сначала надо получить, так ведь? А Windows рассылает сообщения только окнам и ни о каких компонентах не имеет ни малейшего понятия. Ваш же компонент окна не содержит и, соответственно, никаких сообщений не получает.

> Что такое сообщение Windows я знаю

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


 
Digitman ©   (2005-09-05 17:54) [12]


> можно ли невизуальным компонентом перехватывать сообщения
> windows


можно.


> в своей программе вручную перехватываю и обрабатываю такие
> сообщения как WM_HOTKEY, WM_QUERYENDSESSION


вручную это как ?


> Что такое сообщение Windows я знаю


тогда ты должен знать и то, что сообщения эти посылаются не в пустоту , а окну-адресату (или группе окон-адресатов)

у тебя же в коде твоего компонента нет никаких окон, зато окно обязательно есть у объекта Application твоего ехе-приложения

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

перехват в дан.случае сводится к подмене адреса этой ф-ции на адрес своей ф-ции, определенной в юните твоего компонента

установка нового адреса оконной ф-ции сводится к вызову ф-ции SetWindowLong()


 
Юрий Зотов ©   (2005-09-05 18:10) [13]

>  Dr. Genius   (05.09.05 17:35) [7]

> Так хотя бы скажите: можно ли невизуальным компонентом
> перехватывать сообщения windows.

Можно, несмотря на все сказанное выше. Только не самим компонентом, а опосредованно. Если нужно перехватывать сообщения только одного конкретного окна, то можно подменить его оконную функцию своей; если же интересует перехват сообщений всех окон, то можно установить хук, а в нем вызывать метод Dispatch компонента.


 
Dr. Genius   (2005-09-06 09:15) [14]


> если же интересует перехват сообщений всех окон, то можно
> установить хук, а в нем вызывать метод Dispatch компонента.


А можно ли небольшой пример перехвата одного из каких-нибудь сообщений. Мне бы разобраться на примере перехвата одного сообщения (ну, например, WM_QUERYENDSESSION – его Windows рассылает всем запущенным приложениям, когда пользователь завершает сессию Windows). А дальше я сам все сделаю с остальными.


 
Юрий Зотов ©   (2005-09-06 11:06) [15]

> Dr. Genius   (06.09.05 09:15) [14]

Что-то типа этого (в работе не проверял).

unit YzHook;

interface

uses
 Windows, Messages, SysUtils, Classes;

type
 TYzHook = class;
 TYzMessageEvent = procedure(Sender: TYzHook; Wnd: HWND; const Msg: TMessage) of object;

 TYzHook = class(TComponent)
 private
   FOnMessage: TYzMessageEvent;
 protected
   procedure DoMessage(Wnd: HWND; const Msg: TMessage); dynamic;
 public
   class function NewInstance: TObject; override;
   procedure FreeInstance; override;
   constructor Create(AOwner: TComponent); override;
   destructor Destroy; override;
 published
   property OnMessage: TYzMessageEvent read FOnMessage write FOnMessage;
 end;

implementation

var
 HookInstance: TObject = nil;
 HookHandle: HHOOK = 0;

function HookProc(Code: integer; WParam, LParam: LongInt): LongInt; stdcall;
var
 Msg: TMessage;
begin
 with PCWPStruct(LParam)^ do
 try
   Msg.Msg := message;
   Msg.WParam := wParam;
   Msg.LParam := lParam;
   TYzHook(HookInstance).DoMessage(hwnd, Msg)
 finally
   Result := CallNextHookEx(HookHandle, Code, WParam, LParam)
 end
end;

{ TYzHook }

constructor TYzHook.Create(AOwner: TComponent);
begin
 inherited;
 if not (csDesigning in ComponentState) then
 begin
   HookHandle := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, 0, MainThreadID);
   Win32Check(HookHandle <> 0)
 end
end;

destructor TYzHook.Destroy;
begin
 inherited;
 if HookHandle <> 0 then
   Win32Check(UnhookWindowsHookEx(HookHandle))
end;

procedure TYzHook.DoMessage(Wnd: HWND; const Msg: TMessage);
begin
 if Assigned(FOnMessage) then
   FOnMessage(Self, Wnd, Msg)
end;

procedure TYzHook.FreeInstance;
begin
 HookInstance := nil;
 inherited
end;

class function TYzHook.NewInstance: TObject;
begin
 if HookInstance = nil then
   HookInstance := inherited NewInstance;
 Result := HookInstance
end;

end.


 
Юрий Зотов ©   (2005-09-06 19:55) [16]

Подправил плюшки. Вот работающий компонент. Очень простой, как видите.

unit YzHook;

interface

uses
 Windows, Messages, SysUtils, Classes;

type
 TYzHook = class;
 TYzMessageEvent = procedure(Sender: TYzHook; Wnd: HWND; const Msg: TMessage) of object;

 TYzHook = class(TComponent)
 private
   FOnMessage: TYzMessageEvent;
 protected
   procedure DoMessage(Wnd: HWND; const Msg: TMessage); dynamic;
 public
   constructor Create(AOwner: TComponent); override;
   destructor Destroy; override;
 published
   property OnMessage: TYzMessageEvent read FOnMessage write FOnMessage;
 end;

implementation

var
 HookInstance: TYzHook;
 HookHandle: HHOOK = 0;

function HookProc(Code: integer; WParam, LParam: LongInt): LongInt; stdcall;
var
 Msg: TMessage;
begin
 with PCWPStruct(LParam)^ do
 try
   Msg.Msg := message;
   Msg.WParam := wParam;
   Msg.LParam := lParam;
   HookInstance.DoMessage(hwnd, Msg)
 finally
   Result := CallNextHookEx(HookHandle, Code, WParam, LParam)
 end
end;

{ TYzHook }

constructor TYzHook.Create(AOwner: TComponent);
begin
 if HookInstance <> nil then
   raise Exception.CreateFmt("Component %s already exists", [ClassName]);
 inherited;
 if not (csDesigning in ComponentState) then
 begin
   HookHandle := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, 0, MainThreadID);
   Win32Check(HookHandle <> 0)
 end;
 HookInstance := Self
end;

destructor TYzHook.Destroy;
begin
 inherited;
 if HookInstance = Self then
 begin
   HookInstance := nil;
   if HookHandle <> 0 then
   begin
     Win32Check(UnhookWindowsHookEx(HookHandle));
     HookHandle := 0
   end
 end
end;

procedure TYzHook.DoMessage(Wnd: HWND; const Msg: TMessage);
begin
 if Assigned(FOnMessage) and not (csLoading in ComponentState) then
   FOnMessage(Self, Wnd, Msg)
end;

end.


 
Dr. Genius   (2005-09-08 11:05) [17]

Огромное спасибо Вам, Юрий Зотов!



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2005.10.02;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.51 MB
Время: 0.004 c
1-1126092225
oSa
2005-09-07 15:23
2005.10.02
Обработка математических выражений


1-1126690385
integery
2005-09-14 13:33
2005.10.02
как запонить отмечиние ячейки в TStringGrid


1-1126000466
melena
2005-09-06 13:54
2005.10.02
jpg в exe


1-1126526856
infom
2005-09-12 16:07
2005.10.02
Выполнение команды после билда.


1-1126561026
lauren
2005-09-13 01:37
2005.10.02
bold button





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский