![]() |
Модераторы: Snowy, bartram, MetalFan, bems, Poseidon, Riply |
![]() ![]() ![]() |
|
kami |
|
||||||||||||||||||||||||||||||||||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 15 Всего: 72 |
Попробую рассказать, как я «доработал» стандартный модуль SvcMgr.pas для того, чтобы он отвечал последним реалиям работы в Windows. Сразу оговорюсь – я не претендую на полноту изложения. Эта тема (до статьи, конечно, не дотягивает, хотя бы из-за вольности и неточности формулировок) рассчитана на тех, кто знает о том, что такое MSDN, может создавать потоки в Delphi и работать с ними, имеет общее представление о сервисах и исходниках VCL. Гуру вряд ли найдут для себя что-то полезное, но если помогут исправить ошибки изложения, то заранее большое спасибо. К сожалению, много материала останется «за кадром», но с учетом требования к «знанию, что такое MSDN» самостоятельное изучение не должно оказаться трудным. По ходу изложения опускаться до комментирования каждой строчки я считаю излишним, хотя иногда буду
![]() Краткий «экскурс в историю»: Думаю, каждый, кто недавно столкнулся с необходимостью написать сервис, обратил внимание на то, что уже установленные на компьютере службы имеют такие параметры как «Описание», «Параметры восстановления» и т.п. К сожалению, имеющимися средствами Delphi их установить невозможно. А так хотелось бы. Кроме того, службы могут воспринимать сообщения о смене пользователя, о подключении/отключении устройств и т.д. Это так же не предусмотрено в Delphi. Здесь я постараюсь рассказать, как устранить этот недостаток. Опорная версия Delphi – 2010. Посему, чтобы избежать недоразумений, расскажу как и что изменялось, чтобы владельцы других версий Delphi (в большей части это касается до-юникодных) также могли воспользоваться преимуществами «апгрейда». Добавлено через 17 секунд ПОЭТАПНАЯ МОДЕРНИЗАЦИЯ. Подготовительные мероприятия. В первую очередь нам понадобится создать новый сервис (зачем – расскажу в конце). Сохраните созданный проект куда-нибудь в отдельную папку, чтобы облегчить себе дальнейшую работу. Кроме того, понадобится исходник SvcMgr.pas. Откройте его непосредственно в Delphi (для версии 2010 этот файл лежит в папке $(BDS)\Source\Win32\vcl\) и сохраните как SvcMgrEx.pas в папку с созданным проектом сервиса. Менять будем в основном этот файл, чтобы не портить генофонд Delphi. В созданном модуле сервиса (Unit1.pas) и в .dpr файле в uses заменить SvcMgr на SvcMgrEx. В принципе, подготовительные мероприятия закончились, можно приступать. В дальнейшем, если не оговорено особо, весь код будет относиться именно к SvcMgrEx.pas. От простого – к сложному. SvcMgrEx.pas Сперва постараемся сделать так, чтобы наш сервис мог выполнять то, что чаще всего встречается в вопросах – менять свое описание и действия при сбое. За эти вещи в Windows отвечает функция ChangeServiceConfig2, находящаяся в библиотеке advapi32. Рассматривать будем ее юникодный вариант, так как Ansi-вариант обладал какими-то глюками. Сейчас уже не вспомню, что это были за глюки, но – лучше не рисковать. Во-первых, нужно объявить эту функцию. Прототип объявим в interface-секции модуля, место определите сами, я сделал это перед
Объявляем:
Но – объявить мало, нужно еще получить ее адрес. Для этого вносим изменения в initialization. После изменений она будет выглядеть как-то так:
Начало положено. Теперь разберемся с параметрами ChangeServiceConfig2: 1. scHandle: хендл сервиса, параметры которого подлежат изменению. В самом сервисе он неизвестен, придется его получить. 2. dwInfoLevel: определяет что именно нужно изменить. Возможные варианты этого параметра – чуть ниже. 3. lpInfo: указатель на новые данные изменяемого параметра. Что именно должно содержаться здесь – зависит от dwInfoLevel. Объявим константы, которые воспринимаются в dwInfoLevel. С двумя разберемся здесь, остальные оставляю на самостоятельное изучение. Место можете выбрать сами, но желательно – в секции interface модуля SvcMgrEx:
Каждому из кодов соответствует своя структура, передаваемая через параметр lpInfo. Объявим их все сразу (рядом с константами для dwInfoLevel), чтобы по нескольку раз не возвращаться к этому:
Теперь – самое интересное. Объявим новое public – свойство для TService (надеюсь, ни для кого не является секретом, что нажав Ctrl+Shift+C, когда курсор находится внутри описания класса, Delphi сгенерирует нужные методы и приватные поля):
И реализацию сеттера:
Вроде, по коду всё должно быть понятно, за исключением Get и FreeServiceHandles. Эти методы предназначены для получения и освобождения хендла сервиса, который необходим для работы ChangeServiceConfig2. Реализация этих методов такая:
С описанием - всё. Использовать – проще простого: в любом месте модуля сервиса (Unit1.pas) (думаю, лучше всего сделать это в OnAfterInstall) пишем:
Добавлено через 3 минуты и 49 секунд Теперь – о действиях сервиса при сбое. За них отвечает структура SERVICE_FAILURE_ACTIONS: dwResetPeriod: через какое количество секунд сбрасывается счетчик сбоев сервиса lpRebootMsg: сообщение, которое будет выведено перед перезагрузкой компьютера, если сервис будет аварийно завершен. Учитывается, если одним из действий при сбое сервиса будет указана перезагрузка; lpCommand: командная строка для запуска приложения, если сервис будет аварийно завершен. Учитывается, если при сбое сервиса одним из действий будет указан запуск приложения; cActions: количество действий в lpsaActions lpsaActions: указатель на массив действий, предпринимаемых при сбое сервиса. Если посмотреть в менеджере служб на закладку «Восстановление» любой службы, все вопросы должны отпасть сами собой. Если нет – отправляю вас читать MSDN. Сам метод:
Вызов, опять-таки, достаточно прост. Дополним тот же метод в модуле сервиса (Unit1.pas):
Добавлено через 5 минут и 3 секунды С простыми вещами разобрались. Перейдем к более сложному, но и более интересному – получению сервисом дополнительных сообщений (или контрольных кодов) о смене сессии, подключении/отключении устройств и так далее. SvcMgrEx.pas. Большое лирическое отступление, необходимое для понимания вносимых изменений (с некоторыми прегрешениями против истины для простоты изложения). Сервис получает указания от SCM или других приложений через специальную функцию - Handler. C небольшой натяжкой ее можно сравнить с оконной функцией десктопного приложения. Через Handler сервис получает команды на остановку, постановку на паузу и продолжение работы, завершение работы и запрос статуса. Также через Handler могут передаваться другие команды, определяемые пользователем (они посылаются через функцию ControlService). То, что сервис не смог обработать самостоятельно, то есть – не знает о таких командах, он отдает в protected-метод DoCustomControl на откуп программисту. Полный перечень команд, пересылаемых в Handler смотрите в MSDN. Сама функция – Handler находится в каждом модуле сервиса, создаваемого с помощью команды File =>New =>Service. Выглядит она так:
Windows «знает» только об этой функции – она указывается системе при запуске сервиса. Все команды сервису пересылаются именно через нее. Как видно из кода, сервис обрабатывает полученную команду в методе Controller. Если посмотреть «внутрь» этого метода, то увидим следующее:
Оказывается, сервис просто переправляет полученную команду потоку ServiceThread и при необходимости «пробуждает» этот поток. Это сделано не случайно: Microsoft очень настоятельно рекомендует не задерживать обработку полученных команд и возвратить управление системе как можно быстрее. Настоящая обработка команды будет идти позже, когда управление будет передано ServiceThread-у. Именно из ServiceThread-а будут вызываться события, относящиеся к принятой команде, к примеру OnContinue, OnStop и так далее. В том числе в дополнительном потоке будет вызвано событие OnExecute, которое, правда, не требует команды от системы. Промежуточный вывод – практически весь код любого создаваемого в Delphi сервиса работает в дополнительном потоке. Полный вывод делайте сами, если еще не прочитали статью «Многопоточность – как это делается в Delphi» - настоятельно рекомендую ознакомиться с ней. К сожалению, нам для полноценной работы сервиса Handler-функция не подойдет. Она не в состоянии воспринять большинство из того, что нас интересует. Поэтому придется воспользоваться другим обработчиком – HandlerEx. Он регистрируется функцией RegisterServiceCtrlHandlerEx, которая объявлена в той же библиотеке – advapi32.dll (в отличие от обычного Handler – обработчика, который регистрируется функцией RegisterServiceCtrlHandler). Значит, опять изменения в SvcMgrEx.pas. Новый код буду показывать рядом с уже введенным, чтобы не останавливаться на том «а куда его писать»: Объявляем и получаем функцию:
Кроме этого, нужно объявить и HandlerEx. Рекомендую найти в SvcMgr.pas объявление TServiceController и написать новое определение рядом. У меня получилось так:
TServiceControllerEx – это и есть прототип нашей будущей HandlerEx-функции, а TServiceController – объявленный изначально Handler. Ну хорошо, «регистратор» и прототип HandlerEx объявлены. Но толку от этого пока мало – нужно «сказать» Delphi, что нас больше не интересует Handler, а мы хотим использовать HandlerEx. Естественно – если функция RegisterServiceCtrlHandlerEx была найдена, в противном случае нужно продолжить использовать «стандартный» Handler. Для этого сделаем следующее: 1. Объявляем новые константы и обработчик команд сервиса в protected-секции. После изменений код будет выглядеть примерно так:
……………… Реализацию метода ControllerEx пока оставим пустой, немного не до него сейчас 2. Там же, но уже в public-секции объявим еще один метод:
Реализация GetServiceControllerEx выглядит просто:
Почему nil? А вдруг вам будет нужна обычная реализация сервиса, без всяких дополнительных возможностей? Тогда в своем модуле сервиса можно будет просто не объявлять ничего, и пользоваться стандартным кодом VCL. 3. Модернизируем модуль сервиса (Unit1.pas):
Подготовительные мероприятия завершены. 4. Теперь – модернизируем непосредственно код (в SvcMgrEx.pas), который говорит Windows использовать нужный нам обработчик. Этот код находится в методе TService.Main. Особенностей модернизации будет несколько: во-первых, может не быть функции RegisterServiceCtrlHandlerEx, во-вторых – вы в модуле сервиса можете не перекрыть функцию TService1.GetServiceControllerEx. В обоих случаях использовать «расширенную версию» контроллера нельзя.
Добавлено через 11 минут и 49 секунд Итак, что мы имеем на текущий момент? Давайте рассмотрим всё накодированное в порядке использования: Во-первых, когда сервис инсталлирован в системе, то при старте (опустим всякие «промежуточные» методы) вызывается метод TService.Main (см. пункт 4). В нем проверяется выполнение условий по наличию нужных нам функций (т.е. наличие RegisterServiceCtrlHandlerEx, в модуле сервиса (Unit1.pas) объявлена функция ServiceControllerEx и указатель на нее возвращается методом GetServiceControllerEx). Если всё удачно, то обработчиком команд сервиса назначается функция ServiceControllerEx, объявленная в модуле Unit1.pas. Если же нет, то обработчиком остается «стандартная» функция ServiceController, C запуском сервиса разобрались. Теперь – о порядке работы сервиса при получении какой-либо команды. Когда системе нужно передать что-либо сервису, она вызывает функцию ServiceControllerEx, объявленную в модуле Unit1.pas. Смотрим п.3 – единственной задачей этой функции является передача всех полученных параметров в метод ControllerEx нашего сервиса. А его мы в п.1 оставили пустым. Настало время заняться им вплотную. Причем – действовать будем по образу и подобию метода TService.Controller (я говорил раньше о рекомендациях Microsoft по поводу скорейшего возвращения из обработчика, будь то Handler или HandlerEx). Но для начала – нужно еще несколько подготовительных мероприятий. Основная проблема на текущем этапе в том, что в метод ControllerEx основные параметры передаются системой по указателю. Если пытаться передать параметры так же, как это сделано в методе TService.Controller – через PostThreadMessage, то во-первых не хватит параметров для передачи, а во-вторых эти указатели потеряют свою силу сразу после выхода из ServiceControllerEx, практически со 100% вероятностью – до того, как отправленное сообщение достигнет ServiceThread. Значит, нужны обходные маневры. Определяемся со стратегией. Во-первых, для правильной передачи нужно скопировать себе всё то, что передается системой по указателю (и не забыть потом освободить выделенную под копии память). И во-вторых, для того, чтобы была возможность передать внутрь ServiceThread-а всё, что сообщила нам Windows, нужно определить свое сообщение со своей структурой. |
||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||
kami |
|
||||||||||||||||||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 15 Всего: 72 |
Новые объявления в SvcMgrEx.pas:
Пора перейти к реализации метода ControllerEx и передаче содержимого четырех параметров, исходя из выбранной стратегии:
Вот как-то так. Всё что нужно – отправлено потоку нашего сервиса и в то же время управление быстро возвращено системе. Но, отправить – это же только пол-дела, нужно теперь это всё принять и обработать. Для этого «влезаем» в метод, который ответственный за прием сообщений от ServiceController[Ex]:
Особое внимание обратите на вложенность begin-end – в этом методе два раза встречается DispatchMessage. Добавлено @ 23:31 Осталось объявить private-метод TServiceThread.ServiceControlEx, который мы задействовали в изменившемся обработчике полученных сообщений. Как видно, часть кода благополучно перекочевала сюда из TServiceThread.ProcessRequests:
Теперь – объявить «пользовательскую» обработку тех кодов управления, которые не прошли «стандартную» обработку, и основную часть изменений можно считать выполненной:
И его реализация:
Как использовать: В модуле сервиса (Unit1.pas) перекрываем метод DoCustomControlEx и …всё. Дальнейшее зависит от того, что именно передано в параметре dwControl и что сказано в MSDN в описании HandlerEx. Рассмотрим на примере узнавания о смене сессии:
Техническую реализацию обработки расширенных кодов управления сервиса можно считать законченной – есть ServiceController (он же – HandlerEx) в модуле Unit1.pas; поступившие данные успешно копируются и передаются в ServiceThread, который в свою очередь, передает их в DoCustomControlEx, если они не относятся к запуску/остановке и т.п. Добавлено через 2 минуты и 36 секунд Но вот беда – воспринимать команды сервис может, а система их ему не пересылает. Всё дело в том, что мало объяснить Windows «я могу принимать расширенные команды» - это мы сделали, использовав RegisterServiceCtrlHandlerEx, нужно еще объявить «я хочу принимать такие и такие команды». Это разумно – представьте, какая нагрузка ляжет на систему, если все подряд сервисы будут получать абсолютно все сообщения, даже если они им не нужны. Значит – еще одна модификация SvcMgr.pas. В первую очередь, объявляем перечисляемый тип (я его объявил сразу после «некоторых видов lpEventData из HandlerEx»):
Он содержит те дополнительные команды, которые может воспринимать сервис. Здесь нет acServiceControlDeviceEvent – средствами сервиса на эти события подписаться невозможно. Для того, чтобы получать события, связанные с устройствами, нужно вызывать RegisterDeviceNotification, а работа с ним выходит за рамки рассматриваемого сейчас. В public-секции TService объявляем новое свойство:
и реализацию сеттера:
Кроме того, понадобится дополнение метода TService.GetNTControlsAccepted – именно в нем определяется, какие управляющие коды воспринимаются сервисом:
Воспринимаемые сервисом управляющие коды сообщаются Windows в методе ReportStatus, который мы вызываем в сеттере AllowAdditionalControls. Пример использования:
После этого наш сервис начнет получать события о смене сессии, ее блокировке/разблокировке и т.д. в методе DoCustomControlEx. Реализацию этого метода, применительно к получению событий о смене сессии мы написали чуть выше. Вот теперь – всё. Новый модуль готов. ЗАКЛЮЧИТЕЛЬНЫЕ ОПЕРАЦИИ Исключительно для удобства пользования предлагаю скомпилировать полученный проект, и если всё отлично, то: 1. Скопировать полученный SvcMgrEx.pas в папку vcl, «положив» его рядом со стандартным SvcMgr.pas 2. Скопировать файл SvcMgrEx.dcu в папку lib, положив его рядом с SvcMgr.dcu 3. Внести в репозиторий получившийся проект (желательно – удалив код из событий, но сойдет как угодно). Напоследок: архив с модулем SvcMrgEx.pas (еще раз хочу обратить внимание - работа шла в D2010) и проектом Service application. Это сообщение отредактировал(а) kami - 28.5.2012, 23:32 Присоединённый файл ( Кол-во скачиваний: 59 ) ![]() |
||||||||||||||||||||||||
|
|||||||||||||||||||||||||
drkot |
|
|||
![]() Ищущий ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1042 Регистрация: 5.5.2006 Репутация: нет Всего: 8 |
Во первых, хвала и вечная память, за проделанный труд. Пост полезен и заслуживает попасть в раздел статей.
Ну и немного критики... извините не могу без этого...
если придерживаться стратегии предложенной разработчиками Delphi, то логично добавлять функции в WinSvcЕх (WinSvc модуль с прототипами). + динамическая загрузка тоже усложняет код и несколько противоречит концепции Delphi. Имхо она оправдана в случае не гарантированного присутствия функции в библиотеке (например при работе с различными версиями). Дальше идет одно сплошное замечание. Поясню: 1) к моему сожалению вижу полное непонимание ОПП, в следствие все построено несколько не корректно в своей основе 2) работать в IDE с таким проектом в режиме автоматического создания и добавления невозможно 3)явный конфликт имен, что не позволит в одном проекте создать два сервиса (один простой другой расширенный) 4) ряд действий (например описание для сервиса) рациональнее добавлять как отдельный компонент Имхо, даже автоматическая (встроенная) регистрация сервиса сама по себе избыточна и может привести к конфликту если сервис был зарегистрирован внешним способом (не через встроенный регистратор). Но все что написано мной есть лишь жалкий писк в сравнении с тем титаническим трудом который проделал автор. Но ОПП надо учить и применять, чтоб не получилось "100 метров колючей проволоки" в процессе расширения функциональности объектов. ![]() -------------------- Ошибка не становится истиной по причине широкого распространения, как и Истина не становится Ошибкой из-за того, что никто её не видит. |
|||
|
||||
kami |
|
||||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 15 Всего: 72 |
ChangeServiceConfig2 и RegisterServiceCtrlHandlerEx отсутствуют в Win2000 Не спорю, но влезать еще и туда не хотелось.
Пожалуйста, поясните на чем-нибудь из выложенного кода.
Да. Если подскажете, как этого добиться - будет великолепно. Всё, что нашел в интернете - это как добавить в репозиторий форму. Кстати, будет очень даже неплохо, если подскажете, как заставить Delphi видеть новые published свойства в "обновленном" TService.
Не вижу в этом смысла, если честно.
На вкус и цвет... Вообще не понял, поясните ? |
||||||||||
|
|||||||||||
drkot |
|
||||
![]() Ищущий ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1042 Регистрация: 5.5.2006 Репутация: нет Всего: 8 |
например это ОПП демонстрирует использование принципа наследования
а то что у Вас приведено
это просто непонимание сути ОПП, Вы просто разрушаете имеющийся объект, вместо того чтобы создать наследника и расширить (и/или подменить) функционал. Надеюсь что понятно изъяснился. Обычно процедурный функционал отделяют от объектного, это повышает читаемость кода. Посмотрите модуль WinSvc. В частности на принцип описания функций, хотя бы для общего развития. смысл в концепциях ОПП, одновременное использование SvcMgr и SvcMgrEx приведет к конфликту имен, причем к множественному. об этом Application.Installing -------------------- Ошибка не становится истиной по причине широкого распространения, как и Истина не становится Ошибкой из-за того, что никто её не видит. |
||||
|
|||||
drkot |
|
||||
![]() Ищущий ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1042 Регистрация: 5.5.2006 Репутация: нет Всего: 8 |
справедливый аргумент. Например TApplicationEvents, это добавочный компонент который упрощает работу событиями TApplication которые изначально недоступны в режиме дизайна. Также и в случае описания сервиса, оно не является неотъемлемой частью объекта TService и не требуется для его работы, поэтому включать его в структуру нет необходимости, а вот использовать в качестве объекта расширения функционала при инсталляции наиболее уместно. ИМХО.
если одним словом и не конкретно, то ToolAPI. К сожалению по этой теме фактически нет материалов, а то что есть относится к версии до 5-й включительно. Можете посмотреть примеры по ToolAPI входящие в поставку с IDE. Если возникнут вопросы поднимайте тему с удовольствием приму участие.
то что содержится в репозитории подгружено из зарегистрированных bpl, очень похоже на регистрацию новых компонентов. По сути регистрация компонентов это тоже ToolAPI. -------------------- Ошибка не становится истиной по причине широкого распространения, как и Истина не становится Ошибкой из-за того, что никто её не видит. |
||||
|
|||||
kami |
|
||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 15 Всего: 72 |
Более чем понятно. На счет "разрушаю" - сильно сказано ![]()
На WinSvc смотрел не раз, честное слово ![]()
Сам не раз получал подобное, (в последний раз "Declaration of '...' differs from previous declaration")- из-за PLPSTR в WinSvc и PLPSTR в Windows.pas. Опять не понял. Ну выведется сообщение "сервис уже установлен/нет такого", и все. Или не выведется, смотря что там в командной строке прописать. По-моему, очень даже удобная вещь - запустил с нужным ключем, и не нужно знать про всякие CreateService и прочая. Добавлено через 4 минуты и 43 секунды Тут мы с Вами не сошлись, мое имхо - описание сервиса это его неотъемлимая часть.
Жаль, к сожалению - разбираться с этим пока нет большого желания (оно, думаю, появится только когда будет очень надо)... Это сообщение отредактировал(а) kami - 1.6.2012, 20:13 |
||||||
|
|||||||
drkot |
|
||||
![]() Ищущий ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1042 Регистрация: 5.5.2006 Репутация: нет Всего: 8 |
Плохо, то что он не сможет использовать одновременно два типа в одном приложении, на этом и был изначальный акцент. а это результат участия в разработке множества плохо (мало) взаимодействующих групп программистов. только вот далеко не всегда это нужно, а в ряде случаев даже вредно (например при необходимости отслеживать процесс инсталяции/деинсталяции отдельным приложением). Хотя полностью согласен, что с точки зрения "не нужно знать" вариант идеальный. Поясню, "описание сервиса" - это неотъемлемая часть процесса его регистрации и только. На работу сервиса "описание" (как и большинство propertys задаваемых в инспекторе объектов) никак не влияет. Регистрация сервиса не является частью его функционала и практически во всех сервисах написанных без помощи TServiceApplication отсутствует. Именно это и есть моя позиция. Поэтому и выведение в добавочный функционал. На самом деле оно не просто очень надо, а просто overneed. Там можно такие штуки делать ... который заметно упрощают (автоматизируют) разработку Хотя разбираться долго, и надо хорошо знать RTTI и интерфейсы. Единственный минус (и причина по которой я забросил это направление), что от версии к версии IDE сильно меняется и многие наработки требуют переделки под новые требования. В остальном как вы и говорили "на вкус и цвет..." все фломастеры разные. И все же главный мой тезис:
-------------------- Ошибка не становится истиной по причине широкого распространения, как и Истина не становится Ошибкой из-за того, что никто её не видит. |
||||
|
|||||
drkot |
|
|||
![]() Ищущий ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1042 Регистрация: 5.5.2006 Репутация: нет Всего: 8 |
Вы правы на все 100%. Это "вина" разработчиков, они просто не предполагали что Вы будете менять и переопределять эти поля. Выход из этой сложной ситуации достаточно прост... нужно в наследнике переопределить переменные и перекрыть методы. Конечно это грубо, но другого способа я не знаю. Может кто знает научит уму разуму. Наиболее правильно привязывать к полям свойства, чтобы их можно было перекрывать в наследниках. можно использовать например секцию protected вместо private. Это сообщение отредактировал(а) drkot - 1.6.2012, 21:40 -------------------- Ошибка не становится истиной по причине широкого распространения, как и Истина не становится Ошибкой из-за того, что никто её не видит. |
|||
|
||||
drkot |
|
|||
![]() Ищущий ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1042 Регистрация: 5.5.2006 Репутация: нет Всего: 8 |
вот пример наследования по данным
Присоединённый файл ( Кол-во скачиваний: 24 ) ![]() -------------------- Ошибка не становится истиной по причине широкого распространения, как и Истина не становится Ошибкой из-за того, что никто её не видит. |
|||
|
||||
kami |
|
||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 15 Всего: 72 |
Только подобрались к самому интересующему меня моменту, а именно - к обвинению меня в полном непонимании ООП. Думаю, самое время этим вопросом заняться вплотную. То, что Вы предлагаете, только усложнит (причем - значительно) реализацию, и ничего простого в этом нет. Ознакомьтесь чуть более, чем поверхностно с тем, в какие методы вносятся изменения. Переопределить переменные/поля, объявить свойства в наследнике можно, а вот с не-виртуальными методами (тот же TService.Main) как быть? Возьмем для примера только TService - чтобы сделать полноценного наследника, придется не только менять (и дублировать) многие его методы (а не некоторые, как в предложенном мной коде), но и искать нужные и менять в соответствующем наследнике методы TServiceApplication, которые тоже в большинстве своем тоже не-виртуальны. Упустили упоминание - и всё пропало. В конечном итоге это приведет к запутыванию всех, включая самого автора "наследников" сервисных классов.
Прописные истины ![]() Добавлено @ 22:52 Ваш пример как минимум не соответствует теме - если бы то, что подлежит изменению имело директивы virtual/dynamic, я бы не стал городить топик на несколько листов А4. Это сообщение отредактировал(а) kami - 1.6.2012, 23:05 |
||||
|
|||||
drkot |
|
|||
![]() Ищущий ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1042 Регистрация: 5.5.2006 Репутация: нет Всего: 8 |
работать в данном случае будет и без директив. с ними просто правильнее
не буду спорить, ОПП вещь хитрая. Просто класс TService разработан как конечный и разработчики не позаботились о возможности его функционального расширения. Раз уж мы тут устроили такую дискуссию потрачу время с пользой... переделаю TService, а Вы оцените... -------------------- Ошибка не становится истиной по причине широкого распространения, как и Истина не становится Ошибкой из-за того, что никто её не видит. |
|||
|
||||
northener |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1361 Регистрация: 2.9.2010 Репутация: нет Всего: 20 |
Почему не сможет? Разве префиксы запрещены? -------------------- Но только лошади летают вдохновенно. Иначе лошади разбились бы мгновенно! |
|||
|
||||
drkot |
|
|||
![]() Ищущий ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1042 Регистрация: 5.5.2006 Репутация: нет Всего: 8 |
ClassName это несколько больше чем простой тип или имя переменной и даже при удачной компиляции не гарантий что в runtime все пройдет гладко. Скажем мне не совсем понятно как будут созданы два объекта (окна) имеющие одинаковый ClassName и различную реализацию. Насколько я знаю в RTTI не учитывается происхождение (модуль) класса, хотя могу ошибаться, все меняется. Добавлено через 6 минут и 32 секунды kami, а как Вы представляете работу
в DesignMode? Что при каждом обновлении свойства будет проводится манипуляция по изменению описания? а если сервис не установлен в системе? -------------------- Ошибка не становится истиной по причине широкого распространения, как и Истина не становится Ошибкой из-за того, что никто её не видит. |
|||
|
||||
northener |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1361 Регистрация: 2.9.2010 Репутация: нет Всего: 20 |
В RTTI, возможно, (не знаю) и не учитывается nobless ![]() Но при чём тут RTTI, создание объекта и ClassName? Да и вообще runtime? -------------------- Но только лошади летают вдохновенно. Иначе лошади разбились бы мгновенно! |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Delphi: WinAPI и системное программирование" | |
|
Запрещено: 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, bartram, MetalFan, bems, Poseidon, Rrader, Riply. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Delphi: WinAPI и системное программирование | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |