![]() |
![]() ![]() ![]() ![]() |
|
Два простых способа уведомленияDelphi , ОС и Железо , Сообщения Windows
Оформил: DeeCo Автор: Алексей Еремеев В своей работе мне частенько приходиться делать разного рода клиент-серверные системы.И совсем не обязательно на уровне глобальных сетей. Речь пойдет о внутренних подсистемах. Например, имеем компонент, который эмулирует секундомер. Запустили его с параметром типа "а напомни мне, что будет полночь" и забыли. Ну и конечно событие есть типа OnAlert. И обработчик его честно будет вызван по достижении нужной нам полуночи. Но обработчик один, а захотели узнать об этом событии сразу десять разных объектов. Не вешать же десять будильников? Конечно, проще в одном обработчике перебрать методы уведомления этих десяти объектов да и дело с концом. Но можно поступить хитрее - заставить объект-будильник самому напоминать всем кто попросит его об этом. Вот о способах такого уведомления и пойдет речь. Как условие - объект "сервер" ничего не знает об объекте "клиенте". После некоторого размышления и перебрав несколько вариантов я пришел к выводу, что наиболее приемлимые для практики есть два способа. Первый подсмотрен в WinAPI а второй - чисто Дельфи. Оба способа основаны на простой идее регистрации клиента на сервере и оповещении сервером клиентов по внутреннему списку зарегистрированных клиентов. Способ 1. Оповещение через механизм сообщений Windows. в модуле объекта-сервера в интерфейсной части определяется пользовательский номер события:const WM_NOTIFY_MSG = WM_USER + 123;в объекте-сервере реализуются две интерфейсные процедуры (вкупе с объявленным в приватной секции и созданным в конструкторе TList, в деструкторе не забудем его разрушить, естественно)
procedure RegisterHandle(HW: THandle);
var
i: integer;
begin
i := FWindList.IndexOf(pointer(HW));
if i < 0 then
FWinList.Add(pointer(HW));
end;
procedure UnregisterHandle(HW: THandle)
var
i: integer;
begin
i := FWindList.IndexOf(pointer(HW));
if i >= 0 then
FWinList.Delete(i);
end;
и создается функция оповещения в приватной секции:
procedure SendNotify(wParam, lParam: integer);
var
i: integer;
begin
i := 0;
while i < FWinList.Count do
begin
SendMessage(integer(FWinList.Items[i]), WM_NOTIFY_MSG, wParam, lParam);
Inc(i);
end;
end;
можно вместо SendMessage использовать PostMessage, будет асинхронное
сообщение, иногда это выгодней, например для исключения возможности бесконечной
рекурсии.
Объект-клиент должен иметь хэндл окна, который регистрируется на
объекте-сервере и обработчик событий этого окна, который будет вызыватся при
оповещении сервером списка клиентов (окон).У объекта-клиента можно поступить двояко. Если объект-клиент уже имеет хэндл окна (например, форма) то пишется обработчик фиксированного номера события: procedure ServMsg(var Msg: TMessage); message WM_NOTIFY_MSG;или если окна нет, то создается универсальный метод-обработчик и невидимое окно при помощи функции AllocateHWND() (пример смотрите в исходниках VCL - объект TTimer) Прелесть этого метода состоит в том, что объект-клиент может быть вообще в другом приложении, нежели объект-сервер, и такой трюк пройдет при использовании DLL. Кроме того передавать можно не только пару цифр, но и блоки данных (и даже строки) при помощи сообщения WM_COPYDATA. Но это уже другая история, а мы пока пойдем дальше. Способ 2. Оповещение через объект-посредник. В отдельном модуле создаем объект-посредник, который имеет один метод типа SendEvent и одну ссылку на обработчик события OnEvent. Я назвал такой объект TSynaps (да простят меня нейрохирурги)
unit Synaps;
interface
uses
Windows, Messages, SysUtils, Classes;
type
TSynaps = class(TObject)
private
FOnEvent: TNotifyEvent;
public
procedure SendEvent;
property OnEvent: TNotifyEvent read FOnEvent write FOnEvent;
end;
implementation
procedure SendEvent;
begin
if Assigned(FOnEvent) then
try
FOnEvent(Self);
except
end;
end;
end;
Причем методов и событий может быть много разных на любой вкус. С
очередями, асинхронными "прослойками", задержками и другими наворотами. Тут уж
кто на что горазд. Я лишь демонстрирую идею. Модуль с объектом-сервером и модуль
с объектом-клиентом имеют право знать о модуле Synaps. В объекте-сервере
реализуются уже знакомые нам три функции (чуть иначе): в интерфейсе объекта:
procedure RegisterSynaps(Syn: TSynaps);
var
i: integer;
begin
i := FSynapsList.IndexOf(pointer(Syn));
if i < 0 then
FSynapsList.Add(pointer(Syn));
end;
procedure UnregisterSynaps(Syn: TSynaps);
var
i: integer;
begin
i := FSynapsList.IndexOf(pointer(Syn));
if i >= 0 then
FSynapsList.Delete(i);
end;
и приватная функция:
procedure NotifySynapses;
var
i: integer;
begin
i := 0;
while i < FSynapsList.Count do
begin
TSynaps(FSynapsList.Items[i]).SendEvent;
Inc(i);
end;
end;
Объект-клиент создает в себе объект-синапс, назначает его событию OnEvent
свой внутренний обработчик и регистрирует этот синапс на объекте-сервере. Вуаля!
И получает оттуда уведомления. Кстати, в деструктор синапса можно встроить вызов
события OnDestroy, и тогда объект-сервер, при регистрации клиента, может
назначить ему обработчик и автоматически разрегистрировать его при уничтожении.
Но это уже навороты.
Такой подход позволяет строить обратные вызовы любой сложности. К тому-же
это чистый паскаль-код без привязки к операционке. (а вдруг Kylix :о)
Итог. Как вы могли заметить, оба способа базируются на двух базовых идеях. Первое - это регистрация клиента на сервере, и второе - вызов сервером некой функции внутри клиента. Разница только в механизмах. И выбирать тут можно исходя из вкусов, предпочтений и неких требований, связанных с ресурсоемкостью, переносимостью и т. п.На самом деле есть очень широко распространенный и давно известный метод под названием CallBack-функция. Мы вызываем кого-то и передаем как один из параметров адрес другой функции. И этот метод частенько используется в WinAPI (смотрите, к примеру, справку по функции EnumFonts). Но! Механизм прямого CallBack-а довольно некрасиво ложится на объектную модель Дельфи, так что я не стал описывать его здесь. Тем более, что оба способа - то-же самое, но красивше. И самое последнее - не забывайте разрегистрировать клиента в конце работы и освобождать ресурсы в деструкторе. И да известят вас ваши сервера только о хорошем! Два простых способа уведомления: оповещение через механизм сообщений Windows и оповещение через объект-посредник. Комментарии и вопросыПолучайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш :: Главная :: Сообщения Windows ::
|
||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 | ||||