« Поставить закладку » « Сделать стартовой »

« Форумы » « Блоги » « Статьи » « Новости » « Файлы » « Realcoding IRC » « Site map » « Поиск »


Главная Главная
Анонсы Анонсы
Форумы Форумы
Каталог Каталог
Поиск Поиск
Опросы Опросы
Книжный магазин Книжный магазин
Реклама на сайте
Публикации Публикации
Партнеры Партнеры
Карта Карта сайта
Рассылки Рассылки
RSS экспорт
Настройки Настройки
О нас пишут О нас пишут
Контакты Контакты
Гостевая книга Гостевая книга

Создание сайта за 3999 руб.!

ПнВтСрЧтПтСбВс
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
    Популярное
Сессии - всегда ли они нужны?

Функция GetCursorPos

Пишем программу для пересылки файлов через сокеты

Несколько слов о том, как работают роботы (spiders) поисковых машин.

Фабричный метод. Factory Method.

Адаптеры

Создаем базу данных

Поддержка языков в СУБД Microsoft SQL Server 2005

Использование коэффициентов

Использование редактора Microsoft Word




    Архив файлов



    Сообщества



    Документация

Статьи:: C# C Sharp) :: Создание C# сервисов Шаг-За-Шагом. Часть 1


отправить ссылку другу версия для печати  Обсудить на форуме

Создание C# сервисов Шаг-За-Шагом. Часть 1



Недавно я попытался создать C# сервис, используя .NET framework и столкнулся с большим количеством проблем, не описанных ни в одной из документаций, а если и описанных, то решения там были не из легких. Так что я решил написать серию статей, описывающих этот процесс пошагово. В них я осветил проблемы, с которыми я столкнулся.

Автор: Terry Denham 10 Apr 2003. Перевод: MC707 29 Jan 2005 для Realcoding.NET

Эта мульти-статья была поделена мной на следующие части:

  1. Создание проекта, инициализирующего сервиса и инсталляция.
  2. Добавление дополнительных сервисов в приложение.
  3. Добавление поддержки записи событий.

Использование кода

Начнем с создания нового C# проекта, выбрав console application. Visual Studio .NET предлагает мастер создания Windows Service используя C#, но я его не использовал, т.к. не все имеют VS.NET. Рисунок 1 показывает экран создания проекта.
 

Рисунок 1

Теперь, когда мы создали пустое приложение console application, нам нужно изменить некоторые его свойства, по умолчанию оставленные мастером. Откроем файл Class1.cs. Он содержит точку входа приложения. Переименум класс Class, чтобы лучше отразить его роль в приложении. Щелкнем правой клавишей мыши по узлу Class1.cs в Solution Explorer и выберем Rename. Переименуем файл Application.cs. Затем щелкнем правой клавишей мыши и выберем View Code. Изменим в коде класс Class на Application.

Также нам надо изменить пространство имен, чтобы показать, где существует наш класс. Я имею домен, где я использую мой пример, названный CodeBlooded и я собираюсь использовать его в качестве имени. Вы можете использовать любое имя какое пожелаете. Т.к. этот сервис – часть вымышленной игры Spades (которую я скоро закончу писать), мы будем использовать пространство имен CodeBlooded.Spades.Services. Отредактируем файл Application.cs и изменим пространство имен SpadesServer на CodeBlooded.Spades.Services.
Чтобы избежать изменений в каждом классе, добавляемом в проект, откроем диалог свойств проекта, щелкнув правой клавишей мыши по проекту SpadesServer и выберем Properties. В этом диалоге выберем Common Properties | General tab и сменим поле Application | Default Namespace на CodeBlooded.Spades.Services. Теперь любому классу будет автоматически назначено это пространство имен. Рисунок 2 показывает это.

Рисунок 2

Пока мы еще находимся в диалоге свойств проекта, перейдем на закладку Configuration Properties | Build и изменим Outputs | XML Documentation File на SpadesServer.xml. Это XML файл, который создаст компилятор для помощи в документировании проекта. Посмотрите в .NET framework SDK и посмотрите, какие тэги он поддерживает. Рисунок 3 показывает этот диалог.

Теперь позаботимся о некоторых конфигурационных деталях прежде чем заняться кодом. .NET framework имеет базовый класс ServiceBase, обеспечивающий интерфейс сервиса. Этот класс должен быть основан на коде, который мы добавим и иметь логику сервиса. Мы должны отменить стандартные методы OnStart и OnStop. Эти методы вызываются службой Service Control Manager, фактически управляющей сервисами.

Проблема в том, что методы OnStart и OnStop должны возвратить управление обратно в Service Control Manager в течение 1 минуты, чтобы Service Control Manager знал, запущен или остановлен сервис. Как же мы добьемся того, чтобы возвратить в течение 1 минуты главному управлению? Мы должны создать фоновый поток, берущий всю работу на себя.

Чтобы не переписывать заново код каждый раз при добавлении класса, я написал базовый класс, ответственный за создание потока и коммуникации с SCM. Создадим новый класс и назовем его SpadesServiceBase. Он выглядит примерно так.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;

namespace CodeBlooded.Spades.Services
{
public class SpadesServiceBase : System.ServiceProcess.ServiceBase
{
public SpadesServiceBase()
{
// TODO: Add any initialization code here
}

/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
}

/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down
// necessary to stop your service.
}
}
}

В конечном итоге получим 3 сервиса, управляющие состоянием игры со службой администрирования, которая будет управлять и/или обслуживать другие 3 сервиса. Чтобы это реализовать, служба администрирования должна происходить от класса SpadesServiceBase, но мы создадим другой класс, который происходит от класса SpadesServiceBase, от которого будут происходить 3 класса-потомка. Это поможет нам лучше изолировать общедоступный код, при наличии базового класса низшего уровня, содержащего код, применяемого ко всем службам, и классы-потомки базового класса будут иметь общий код, доступный всем порожденным службам.

Итак, создадим другой класс и назовем его SpadesChildServiceBase. Пусть он наследуется от SpadesServiceBase. Создавайте файл любым способом, но код должен выглядеть примрерно так:

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;

namespace CodeBlooded.Spades.Services
{
public class SpadesChildServiceBase : SpadesServiceBase
{
public SpadesChildServiceBase()
{
// TODO: Add any initialization code here
}

/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
base.OnStart( args );
}

/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop()
{
// TODO: Add code here to perform any
// tear-down necessary to stop your service.

base.OnStop();
}
}
}

Теперь мы собираемся написать код фонового потока и синхронизации объектов, используемых SCM, чтобы стартовать и останавливать фоновый поток. Фоновый поток будет периодически возобновляться и проделывать некоторые действия. Если мы хотим написать службу, которая зависит от сообщений, исходящих от клиента, то можно использовать любой механизм IPC для управления синхронизации. В следующих статьях я опишу другой способ, используя асинхронный вызов сокетов, но сейчас я просто создам опорный механизм, управляющий работой сервисов.

Чтобы облегчить основные классы, связанные с другими производными классами, базовый класс предоставляет виртуальный метод, называемый Execute, который должен быть реализован сервисом. Метод Execute вызывается периодически базовыми классами, поскольку это запрограммировано нашим таймером. Также, метод Execute должен содержать некоторую логику, отвечающую за периодическое действие каждого сервиса.

Мы должны добавить ссылку на System.Threading к нашему классу SpadesServiceBase и добавить переменную m_thread типа Thread. Также, мы должны использовать ManualResetEvent из пространства имен Threading, чтобы соединиться или остановить запрос от SCM. В методе OnStart класса SpadesServiceBase, мы создадим объект Thread, так же, как и ManualResetEvent. Чтобы запустить поток, нам нужно определить метод старта потока, который будет являться точкой входа потока. В нашем случае мы назовем метод ServiceMain и используем класс ThreadStart для определения нашего метода ServiceMain. Изменим код класса SpadesServiceBase на такой:

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;

namespace CodeBlooded.Spades.Services
{
public class SpadesServiceBase : System.ServiceProcess.ServiceBase
{
public SpadesServiceBase() {
// создаем новый объект timespan
// с задержкой по умолчанию 10 сек.
m_delay = new TimeSpan(0, 0, 0, 10, 0 );
}

/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args) {
// создаем объект threadstart для ServiceMain
ThreadStart ts = new ThreadStart( this.ServiceMain );

// создаем ручное событие ресета и
// инициализируем его
m_shutdownEvent = new ManualResetEvent(false);

// создаем рабочий поток
m_thread = new Thread( ts );

// запускаем поток
m_thread.Start();

// вызываем базовый класс
base.OnStart( args );
}

/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop() {
// сигнал событию - завершить
m_shutdownEvent.Set();

// ждать поток для завершения в течение 10 сек.
m_thread.Join(10000);

// вызываем базовый класс
base.OnStop();
}

/// <SUMMARY>
///
/// </SUMMARY>
protected void ServiceMain() {
bool bSignaled = false;
int nReturnCode = 0;

while( true ) {
// ждем сигнального события или
// истечения задержки
bSignaled = m_shutdownEvent.WaitOne( m_delay, true );

// если пришел сигнал о завершении, выходим из цикла
if( bSignaled == true )
break;

// выполним
nReturnCode = Execute();
}
}

/// <SUMMARY>
///
/// </SUMMARY>
/// <RETURNS></RETURNS>
protected virtual int Execute() {
return -1;
}

protected Thread m_thread;
protected ManualResetEvent m_shutdownEvent;
protected TimeSpan m_delay;
}
}

Теперь, когда мы позаботились о деталях, приступим к созданию административного сервиса. Хоть и сервис не будет ответственен за многое, в следующих нескольких уроках мы добавим в него функциональности. Сейчас создадим новый класс SpadesAdminService, производный от SpadesServiceBase. Следующий код – шаблонный для административного сервиса.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;

namespace CodeBlooded.Spades.Services
{
public class SpadesAdminService : SpadesServiceBase
{
public SpadesAdminService()
{
this.ServiceName = "SpadesAdminSvc";
}

/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args)
{
base.OnStart( args );
}

/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop()
{
base.OnStop();
}

/// <SUMMARY>
///
/// </SUMMARY>
/// <RETURNS></RETURNS>
protected override int Execute() {

// Запишем сообщение в Event Log, чтобы знать,
// что наш сервис работает
System.Diagnostics.EventLog.WriteEntry("SpadesAdminSvc",
ServiceName + "::Execute()");

return 0;
}
}
}

Теперь, когда сервис написан, нужно сделать некоторую рутинную работу, чтобы иметь именно сервис. Все что нам нужно сделать для этого – это создать совокупность объектов ServiceBase и просто вызвать метод Run базового класса ServiceBase. Добавьте следующий код к методу Main объекта Application. Следующий фрагмент кода показывает все необходимое для запуска сервиса.

static void Main(string[] args)
{
// создадим массив сервисов
ServiceBase[] servicesToRun;

// чтобы создать новый образец нового сервиса,
// просто добавьте его к списку сервисов,
// указанных в конструкторе массива ServiceBase
servicesToRun = new ServiceBase[] { new SpadesAdminService() };

// запустим все созданные сервисы.
// Вообще-то, это не запустит сервисы, но
// зарегистрирует их в Service Control Manager
ServiceBase.Run( servicesToRun );
}

Теперь мы близко подобрались к раскрытию темы статьи. Все что нам осталось – добавить наши Installers. Они могут быть вызваны программой установщиком, который я опишу в следующей статье, либо вызовом программы InstallUtil из framework SDK.

Добавим новый инсталлер к нашему проекту и назовем его SpadesInstaller. После запуска мастера Add Class, добавим код к конструктору класса. Инсталлер использует атрибуты, принадлежащие классу, в котором инсталлер создает экземпляр каждого класса, которые имеют атрибут RunInstaller.

Все, что нам нужно – экземпляр ServiceProcessInstaller, чтобы управлять запуском сервиса, и создать объект ServiceInstaller для каждого устанавливаемого сервиса. После установки нескольких свойств этих объектов, добавим их к коллекции Installers. Следующий фрагмент кода показывает все необходимое для установки сервиса.

using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;
namespace CodeBlooded.Spades.Services
{
/// <SUMMARY>
/// Summary description for SpadesInstaller.
/// </SUMMARY>
[RunInstaller(true)]
public class SpadesInstaller : System.Configuration.Install.Installer
{
public SpadesInstaller()
{
ServiceProcessInstaller process = new ServiceProcessInstaller();

process.Account = ServiceAccount.LocalSystem;

ServiceInstaller serviceAdmin = new ServiceInstaller();

serviceAdmin.StartType = ServiceStartMode.Manual;
serviceAdmin.ServiceName = "SpadesAdminSvc";
serviceAdmin.DisplayName = "Spades Administration Service";

// добавим созданные инсталлеры
// к нашему контейнеру
Installers.Add( process );
Installers.Add( serviceAdmin );
}
}
}
После полного тестирования кода, запустим командную строку и перейдем в папку, где создается проект. Используем папку binDebug и выполним следующую команду для установки сервисов:

installutil SpadesServer.exe

Если вы хотите удалить сервисы, выполните следующую команду:

installutil SpadesServer.exe -U

После этого мы можем запускать и останавливать сервисы из Service Control Manager.

В следующих статьях мы добавим большую функциональность framework, которую создали здесь. Добавим несколько дочерних сервисов, которые будут запускать незапущенный главный сервис. Главный сервис будет останавливать дочерние, если будет остановлен.




Рубрика: C# C Sharp)




ASP.NET и немного поисковой оптимизации.

Советы

С тех пор, как появилась Альтависта, большинство посетителей стали приходить на сайты из поисковых машин. И головной боли стало больше. Теперь мало написать сайт, нужно сделать его совместимым с поисковыми ботами. Одна из тех задач, которую приходится решать — быстрая переиндексация обновлений на сайте. Поисковые роботы чаще посещают те сайты, которые чаще меняются.


Подробнее... | Рубрика: Советы | Добавлено: 30.07.2008

Protocol buffers: библиотека обмена данными для C++, Java, Python от Google.

Программирование для Web на C

Наконец-то я могу поделиться тем, чему уже давно радуюсь сам: Google открывает исходники Protocol Buffers! Что это и почему надо радоваться? Это простой и удобный способ обмена данными. Можно сказать, альтернатива XML, но гораздо менее амбициозная и (поэтому) более быстрая и компактная. Далее - перевод отрывка анонса в блоге Open Source at Google и пример использования.


Подробнее... | Рубрика: Программирование для Web на C | Добавлено: 30.07.2008

Описание VivaVisualCode.

VivaCore

В данной статье рассмотрена программа VivaVisualCode, демонстрирующая использование библиотеки VivaCore. Программа VivaVisualCode графически отображает дерево разбора для вводимого исходного кода на языке Си++.


Подробнее... | Рубрика: VivaCore | Добавлено: 29.07.2008

Остальные статьи:

JQuery: Пара сотен плагинов в одной заметке
Касание сетки
Разработка элементов управления ASP.NET на примере навигационной панели
Сохранения параметров приложения в .Net
Custom cursors в .Net
Бегун убегает от хозяина
"Битрикс" выпустил седьмую версию CMS
Выбираем систему управления сайтом
Рынок CMS в Европе или впечатления с CeBIT 2008
32 подводных камня OpenMP при программировании на Си++
Проeкт - шифровка
А что если..? (операторы if...else в C#)
Измерение скорости работы скрипта
О том как разработчики пьют кофе
Работаем с LINQ to XML
XmlSerializer - Assembly Leak без спроса


Цитата дня (все,добавить):



    Рубрикатор

Программирование

C/С++
Обучение
Windows API
XAML
Моделирование
Паттерны
Visual Basic 7 .NET
WxWidgets
Функции WinApi
Функции С++
Разработка под Mac OS
Eiffel
Visual Studio 2008
UI дизайн
Алгоритмы
Конкурсные статьи
Turbo Pascal
Visual Studio
CASE-средства
Visual Studio 2005
Без VCL
Delphi
Тех. документация
Тестирование
Software Testing
ООП
TCP/IP
Google Android
Windows Installer
.NET Framework
Драйвера
C# C Sharp
Справка
Проектирование
Информ. системы
Visual Basic
Assembler
Оптимизация кода
Gtk+
Компоненты
Реинжиниринг
Управление проектами
Extreeme programming
Lotus Notes
Алгебраическое проектирование

Интернет технологии

PHP
Perl
ASP
WAP
Cookies
SSI
CGI
Web Servers
VB Script
DNS
CSS
XML
Html
Java Script
Java2ME
Firewall
Flash
.htaccess
Apache
VRML
Протоколы
Поисковые системы
Технология JAVA
Учебник по PHP
Учебник по JavaScript
Учебник по XML
Java Q&A
AJAX
DHTML
XHTML
Dreamweaver
Web 2.0
Python
Вебмастеру
Cisco
Ruby on Rails
Silverlight

Базы данных

Access
InterBase
MySQL
Oracle
ADO .NET
Основы SQL
Учебник по Access 2002
MS
Microsoft FoxPro
Доступ к данным
XML в MS SQL Server 2000
ODBC и MyODBC
Обучение
Caché
DB2
PostgresSQL
Sybase
Теория
Хранилища данных
Безопасность
Реляционные данные
MySQL и mSQL

Остальное:

Разное
Обзоры книг
Безопасность
Графика и дизайн
Юмор
Linux
Фракталы
Microsoft Axapta
Многоядерность
Сети
Microsoft Office
Работа
MS-DOS
Криптография
Графика и игроделание
Новости SDK
Системы защиты
Учебник по AutoCad
CVS
Windows XP
Windows Server 2003
Windows Vista
Windows 7
Мероприятия

    Кто на сайте
Вы не зарегистрированы.
Имя:

Пароль:

Запомнить

Регистрация позволит Вам пользоваться дополнительными сервисами.
Сейчас на сайте:
Гостей: 119
Пользователей: 0