Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Статья из №11 журнала Алгоритм о "Автоматически перезапускаем сервис Windows при возникновении ошибки".


.Net Собеседник #62

Содержание
  1. От автора

От автора

Здравствуйте, коллеги!

Готовится 11-й номер журнала "АЛГОРИТМ".  Здесь вы сможете прочесть предварительную версию одной из статей, которые будут размещены в этом номере. Кстати, новости регулярно выкладывются в моём ЖЖ по адресу http://hdrummer.livejournal.com/

Напоминаю адрес сайта журнала - http://dotnetgrains.sql.ru/alg/alg.htm  

Не забывайте о подписке - подписной индекс для Украины 91132 в Укрпочте, для РФ - подписка через WebMoney . Подробнее на сайте журнала.

Автоматически перезапускаем сервис Windows при возникновении ошибки

Автор: А.Лайнс
Перевод с дополнениями: Чужа В.Ф.

Эта тема стала для меня актуальной после того, как я исследовал причину внезапной остановки сервиса Windows, написанного на .NET 1.1. После небольшого блуждания по MSN я нашёл лишь несколько фрагментов кода на C#, целостного решения не было. Тогда я решил создать собственную реализацию, в процессе создания которой всплыло несколько интересных вещей.

Во-первых, подходящая функция Win32 API называется “ChangeServiceConfig2”. Эта функция – «мастер на все руки»; её гибкость, однако, доставила мне немало неприятных минут при работе с .NET. Трудность заключалась в том, что мы должны передать ей структуру, которая сама содержит массив структур.

Перед тем, как начать работать с “ChangeServiceConfig2”, необходимо получить хэндл сервиса с помощью вызова функции Win32 API “OpenService”. Ни в одной документации, которой я пользовался, не указано, что для её вызова нужны права SERVICE_CHANGE_CONFIG и SERVICE_START, если их нет, то вы получите ошибку “отказано в доступе” при вызове функции “ChangeServiceConfig2”.

Функции “OpenService” и “OpenSCManager” (функция, также необходимая для нашего проекта) возвращают хэндлы Windows. Общая ошибка программистов, работающих с .NET заключается в том, что хэндлы Windows интерпретируются как 32-битные целые (int, Int32 и т.п.). В «неуправляемом» мире тип HANDLE является указателем на void (void*). Отсюда следует, что нужно использовать IntPtr.

Ключевыми структурами для наших задач являются SC_ACTION и SERVICE_FAILURE_ACTIONS. Структура SC_ACTION описывает, что должна делать Windows в случае непредвиденной остановки сервиса. Таких структур может быть несколько, поэтому можно предусмотреть несколько разных действий при нескольких последовательных сбоях сервиса. Все они должны быть размещены в архиве, который нужно поместить в структуру SERVICE_FAILURE_ACTIONS (та проблема, о которой я уже упоминал).

struct SC_ACTION
{
   [MarshalAs(UnmanagedType.U4)]
   public SC_ACTION_TYPE Type;
   [MarshalAs(UnmanagedType.U4)]
   public UInt32 Delay;
}
 
struct SERVICE_FAILURE_ACTIONS 
{
   [MarshalAs(UnmanagedType.U4)]
   public UInt32 dwResetPeriod;
   [MarshalAs(UnmanagedType.LPStr)]
   public String lpRebootMsg;
   [MarshalAs(UnmanagedType.LPStr)]
   public String lpCommand;
   [MarshalAs(UnmanagedType.U4)]
   public UInt32 cActions;  
   public IntPtr lpsaActions;
}

При подготовке структур к передаче в функцию “ChangeServiceConfig2” необходимо соблюдать требования к арифметическим операциям с указателями при создании массива действий на случай непредусмотренной остановки сервиса. Приведение к 32-битному целому сделает наше решение несовместимым с 64-битным Windows. Лучше всего использовать тип Int64 для арифметических операций с указателями.

Marshal.StructureToPtr(action2, (IntPtr)((Int64)actionPtr + Marshal.SizeOf(typeof(SC_ACTION))), false);

После небольшого маршаллирования мы наконец-то готовы к вызову “ChangeServiceConfig2”. Хорошей новостью является то, что это достаточно просто – осталось лишь обработать ошибки так, как это делается в .NET.

if (changeResult == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Не могу изменить конфигурацию сервиса - Unable to change the Service configuration.");
}

И, наконец, поскольку мы работаем с неуправляемыми ресурсами, необходимо убедиться в том, что ресурсы корректно освобождаются. Лучшим подходом в этом случае будет использование блока try-finally.

Заметьте также, что под .NET 2.0 кое-что должно быть сделано по-другому. Например, можно использовать класс SafeHandle. Но в целом могу сказать, что решение работает и под .NET 2.0.

И напоследок замечу, что по соображениям безопасности вам нужно добавить атрибут LinkDemand к методам, использующим класс Marshal. Подробнее это будет описано в другой статье, но если вы этого не сделаете, то при вызове ваших методов могут быть проблемы с точки зрения безопасности.

[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]

Удачного кодирования!

Полную версию статьи смотрите в №11 журнала АЛГОРИТМ в конце октября 2006 года.

На этом шестьдесят второй выпуск .Net Собеседника закончен.
До следующего номера.



Чужа Виталий Ф. aka hDrummer, MCAD.Net, MCDBA, MCP
hdrummer ухо gmail точка ru - жду ваши предложения и замечания.



В избранное