Прежде, чем можно будет инсталлировать службу на определенной машине, следует ввести дополнительный тип инсталлятора к текущему проекту CarWinService. В частности, любая служба Windows (написанная на. NET или Win32 API) требует нескольких записей в реестре для того, чтобы позволить операционной системе взаимодействовать с ней. Вместо того чтобы создавать эти записи вручную, можно просто добавить инсталлятор к проекту службы Windows, который корректно сконфигурирует сервис (службу) после установке его на целевой машине.
Чтобы добавить инсталлятор к проекту CarService, откройте в окне Solution Explorer редактор служб (двойным щелчком на файле CarService.cs), после этого щелкните правой клавишей мыши в любом месте дизайнера инсталлятора и выберите в контекстном меню (см. рис.2) пункт Add Installer (Добавить Инсталлятор).
В результате этих операций в проект сервиса будет добавлен новый элемент инсталлятора, унаследованного от базового класса System.Configuration.Install.Installer. В конструкторе инсталлятора будет находится два компонента. Один из них имеет тип serviceInstaller1 и представляет собой инсталлятор конкретной службы в проекте. Если курсором мыши выбрать эту пиктограмму и заглянуть в окно его свойств (Properties), то обнаружится, что свойство ServiceName было установлено в тип класса службы - CarService.
|
|
Второй компонент (serviceProcessInstaller1) позволяет установить идентичность (имя, идентификатор), под которой будет выполняться инсталлированная служба. По умолчанию свойство Account установлено в User. Используя окно Properties среды Visual Studio, измените это значение на LocalService (рис.4).
Рис. 4. Установка идентичности для CarService.
После компиляции проект службы будет готов к использованию.
1.6. Инсталляция службы CarService
Инсталляция CarService.exe на определенной машине (локальной или удаленной) требует выполнения двух шагов.
1. Перенос готовой сборки службы (и всех необходимых ей внешних сборок; в данном примере — CarGeneralAsm.dll) на удаленную машину.
2. Запуск инструмента командной строки installutil.exe с указанием службы в качестве аргумента. (Эту утилиту можно найти, например, в каталоге: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727)
Если первый шаг выполнен, откройте командное окно Visual Studio, перейдите к месту нахождения сборки CarWinService.exe и выполните следующую команду
installutil carwinservice.exe
Обратите внимание, что тот же инструмент может применяться и для деинсталляции службы, но уже с указанием аргумента /u, например, installutil /u carwinservice.exe). Проще всего это сделать, если разместить файлы CarGeneralAsm.dll, carwinservice.exe и installutil.exe в корне каталога, например, каталога d:\.
Как только служба Windows установлена, то можно запускать и конфигурировать ее в аплете Services (Службы) панели управления. Найдя CarService (рис.5), щелкните на ссылке Start (Запуск) для загрузки и запуска программы службы.
|
|
Рис.5. Аплет Services
То же самое можно сделать из среды Visual Studio в меню "Вид" выбрать пункт "Обозреватель серверов", в котором открыть папку "Серверы", а внутри нее – "Службы". Если установка службы прошла успешно, то в папке служб вы найдете свою службу " CarService " (рис.5). Если подвести курсор к папке "Службы" и нажать на правую клавишу мыши, то появиться то же самое окно для загрузки и запуска служб, показанное на рис.5.
Рис.5. Окно Visual Studio для просмотра служб, установленных на вашем компьютере.
В результате запуска сервиса CarService мы получаем службу, которая способна выполнять роль полноправного заменителя сервера удаленного объекта, не требующего запуска самого сервера в виде отдельного приложения (хоста).
1.5. Создание консольного приложения клиента.
Создание приложения клиента удаленного объекта-поставщика автомобилей производим на основании опыта, приобретенного при выполнении предыдущей лабораторной работе. Для этого добавляем в наше решение новый проект консольного приложения клиента, в котором объявляем класс CarClient, включающей в себя функцию Main(), в соответствии со следующим листингом:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
//using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using CarGeneralAsm;
using System.Collections.Generic;
namespace CarClient
{
class CarClient
{
static bool rad = false;//Переменная о вкл.\выкл. состоянии радиоприемника
//Статическая функцияпо выводу данных об автомобиле
private static void UseCar(JamesBondCar c)
{
Console.WriteLine("-> Имя: {0}", c.PetName);
Console.WriteLine("-> Максимальная скорость: {0}", c.MaxSpeed);
Console.WriteLine("-> Может двигаться под водой?: {0}",
c.GetSubmerge());
Console.WriteLine("-> Может летать?: {0}", c.GetFly());
Console.WriteLine();
c.TurnOnRadio(rad);
rad =!rad;
}
static void Main(string[] args)
{
Console.WriteLine("Клиент стартовал! Для продолжения нажмите на
клавишу ENTER");
// Конфигурирование клиента с помощью файла конфигурации
// RemotingConfiguration.Configure("CarProviderClient.exe.config", false);
TcpChannel c = new TcpChannel(); // Создание канала
ChannelServices.RegisterChannel(c, false);// Регистрация канала
// Получение ссылки на прокси удаленного объекта
object remoteObj = Activator.GetObject(typeof (CarGeneralAsm.CarProvider),
"tcp://localhost:32469/CarProvider.rem");
// Приведение ссылки на прокси к типу удаленного объекта.
CarProvider cp = (CarProvider)remoteObj;
// Получить автомобиль, стоящий первым в списке.
JamesBondCar qCar = cp.GetJBCByIndex(0);
// Получить весь список автомобилей.
List<JamesBondCar> allJBCs = cp.GetAllAutos();
// Вывести на экран информацию о первом автомобиле.
UseCar(qCar);
// Вывести на экран информацию обо всех автомобилях в списке
foreach (JamesBondCar j in allJBCs)
UseCar(j);
Console.ReadLine();
}
}
}
Теперь, при условии, что на вашей машине уже запущена разработанная ранее служба CarService, и наличии успешно скомпилированного проекта приложения клиента, можно запустить это приложение клиента и убедиться в том, что оно прекрасно работает с удаленным объектом. При этом следует заметить, что взаимодействие между приложениями клиентов и удаленным объектом организуется уже не приложением сервера, а созданной вами специально службой операционной системы Windows.
Внешний вид работающего приложения клиента показан на рис.6.
Рис.6. Внешний консольного приложения клиента под управлением службы CarSrvice для доступа к удаленному объекту поставщика автомобилей.
1.6. Создание Windows приложения клиента
В данном примере используется уже имеющаяся сборка библиотеки класса CarProvider и Windows-служба CarService, созданные для консольного приложения. Таким образом, все необходимые изменения касаются только модификации приложения клиента. Основной особенностью данного примера Windows приложения клиента заключается не только в изменении его внешнего вида, но и в иллюстрации возможности использования делегата при обращении к одному из методов удаленного объекта. Вот его программный код:
|
|
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
//using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using CarGeneralAsm;
using System.Collections.Generic;
namespace AsyncWKOCarProviderClient
{
public partial class Form2: Form
{
// Обявление типа делегата, задающего сигнатуру функций, с которыми он будет
// работать
internal delegate List<JamesBondCar> GetAllAutosDelegate();
GetAllAutosDelegate CarDelegate = null; // Переменная типа делегата (сам делегат)
// Ссылка на список автомобилей для их отображения на форме приложения клиента
List<JamesBondCar> allJBCs=null;
CarProvider cp = null; // Ссылка на удаленный объект (прокси)
public Form2()
{
InitializeComponent();
//Создание прокси удаленного поставщика
object remoteObj = Activator.GetObject(typeof(CarGeneralAsm.CarProvider),
"tcp://localhost:32469/CarProvider.rem");
cp = (CarProvider)remoteObj; // Инициализация ссылки на удаленный объект (прокси)
//Создание объекта делегата (иницииализация ссылки на объект делегата)
CarDelegate = new GetAllAutosDelegate(cp.GetAllAutos);
}
//Метод для вызова делегата
void CallDelegate()
{
//Асинхронный вызов делегата
IAsyncResult ar = CarDelegate.BeginInvoke(null, null);
if (ar == null)
{
MessageBox.Show("Делегат не хочет вызываться!!!");
return;
}
MessageBox.Show("Удаленный объект заполняет список.");
//Работа делегата (то есть работа метода, включенного в делегат)
while (!ar.IsCompleted) { } // Пока делегат не закончит свою работу, ждем…
allJBCs = CarDelegate.EndInvoke(ar);//Делегат возвращает список автомобилей
string str = "Удаленный объект заполнил весь список! Всего: ";
str += allJBCs.Count.ToString();
str += " Машин";
MessageBox.Show(str);// Отображаем строку str в виде окна с сообщением
}
// Обработчик события при нажатии на клавишу "Добавить автомобиль…"
private void AddCarButton_Click(object sender, EventArgs e)
{
if (cp == null) // если нет ссылки на удаленный объект, то (защита от "дурака")
{
// Получаем ссылку на удаленный объект (его прокси)
object remoteObj = Activator.GetObject(typeof(CarGeneralAsm.CarProvider),
"tcp://localhost:32469/CarProvider.rem");
cp = (CarProvider)remoteObj;
}
if (cp == null) return; //Если ссылки – нет, то выходим из функции
// Добавляем новый автомобиль в список их поставщика на удаленном объекте
cp.AddNewJBCar(NameTextBox.Text, int.Parse(SpeedTextBox.Text),
FlyCheckBox.Checked, SweemCheckBox.Checked);
|
|
}
// Обработчик события при нажатии на клавишу "Заполнить список на форме клиента"
private void FillListButton_Click(object sender, EventArgs e)
{
CallDelegate();// Вызов делегата (содержащего соотв. метод удаленного объекта)
List<string> carNames = new List<string>();// Вспомогательный список строк
string str = ""; // Служебная (локальная) переменная типа строки
foreach (JamesBondCar car in allJBCs)// Формирование вспомогательного списка строк
{
str = car.PetName;
str += " Макс.скорость = ";
str += (car.MaxSpeed.ToString()+"; ");
if (car.CanFly) str += "Может летать! ";
else str += "Не может летать. ";
if (car.CanSubmerge) str += "Может плавать!";
else str += "Не может плавать.";
carNames.Add(str);
}
//Связывание вспомогательного списка с содержимым компоненты списка
listBox1.DataSource = carNames;
}
}
}
После компиляции и запуске приложения клиента и включенной службе CarService для хостинга (владения) удаленных объектов типа CarProvider получим приложение способное взаимодействовать с зарегистрированным удаленным объектом. Его внешний вид показан на рис.7.
Рис.7. Внешний вид запущенного Windows приложения клиента.