Спочатку я написав програмку, яка виконувала резервне копіювання файлів конфігурації і помістив її в автозавантаження. Та ця програма виконувала свої функції лише тоді, коли користувач заходив в систему. Це мене не влаштовувало, адже не кожен день я заходив не цей сервер.
В результаті я вирішив написати сервіс для вирішення цієї проблеми.
Опис ділиться на декілька кроків
Крок 1. Створення сервісу
Спочатку я створював Service Application і з меню File-New-Other.
Далі присвоїв сервісу ім’я, як видно на скріні нижче copyconfig. Оскільки мій сервіс примітивний, який виконує всього лиш декілька дій – пошук в директорії файлів з маскою *.cfg і наступне їх копіювання в потрібну мені директорію, то я описував лише подію OnExecute.
OnExecute – служба стартує, виконує певні дії і завершує свою роботу.
Нижче приведений код всієї служби з коментарями.
unit Serv;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,Inifiles;
type
Tcopyconfig = class(TService)
procedure ServiceExecute(Sender: TService);
private
{ Private declarations }
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
copyconfig: Tcopyconfig;
listfile: array[0..20] of string; // масив для збереження імен файлів з потрібним нам розширенням
i:integer;
implementation
{$R *.DFM}
{Процедура пошуку по директорії потрібних нам файлів
В процедуру передається 2 параметри:
mask – маска фалів, які ми шукатимемо
path – щлях до директорії в якій буде проводитися пошук файлів заданої маски }
procedure FileFind(mask:string;path:string);
var sr:Tsearchrec;// Опис структури, яку використовує для пошуку система
found:integer; // знайдено чи ні
attr:integer;
begin
i:=0;
attr:=0;
attr:=Faanyfile;
found:=FindFirst(path + mask,attr, sr);
{по команді FindFirst програма створює структуру наступного типу
TsearchRec = record
Time: Integer; // час створення
Size: Integer; // розмір файлу
Attr: Integer;// атрибутиы
Name:TFileName // = TString; ім’я файлу
ExcludeAttr: Integer; знайдені атрибути
FindHandle: THandle; // !!! вказівник на структуру пошуку, яку створює ситсема, а не сервіс. В зв’язку з цим в кінці пошуку необхідно обов’язково писати FindClose – це звільняє паам’ять
FindData: TWin32FindData; // власне ця структура
end;}
while (found = 0) do // якщо хоч щось знайдено
begin
if (sr.name <> '.') and (sr.name <> '..') then
begin // якщо не вказували на корневі каталоги, то щось знайшли
listfile[i]:=sr.Name;
inc(i);
end;
found:=findnext(sr); // шукаємо далі, якщо ще є файли і каталоги
end;
FindClose(sr); // пошук завершений – вивільняємо пам’ять
end;
{Наступні два методи згенеровані автоматично}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
copyconfig.Controller(CtrlCode);
end;
function Tcopyconfig.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
{В цьому методі описується тіло служби, ті дії які буде вона виконувати після запуску}
procedure Tcopyconfig.ServiceExecute(Sender: TService);
var
mask,srcF,srcM,dstF,dstM:string //змінні для запису параметрів з ini файлу
dat:string; // в цю змінну ми внесемо дату потрібного нам формату
j:integer // змінна, яка потрібна при копіюванні файлів
inst:integer; // для визначення, файли конфігурації якого з продуктів Kerio копіювати
Ini:tinifile; // визначаємо змінну для файлу конфігурації сервісу
Begin
// Визначення ini файлу з якого будуть братися параметри
Ini:=Tinifile.Create('C:\BackupConfig.ini');
// зчитування параметрів з ini файлу
mask:=ini.ReadString('main','mask','*.cfg');
srcF:=ini.ReadString('source','firewall','C:\Program Files\Kerio\WinRoute FireWall');
srcM:=ini.ReadString('source','MailServer','C:\Program Files\Kerio\MailServer');
dstF:=ini.ReadString('dest','firewall','C:\Program Files\Kerio\WinRoute FireWall');
DstM:=ini.ReadString('dest','MailServer','C:\Program Files\Kerio\MailServer');
inst:=strtoint(ini.ReadString('main','inst','2'));
// після зчитування параметрів звільняємо ini файл
ini.Free;
// перетворюємо дату в потрібний формат
dateTimetostring(dat,'yyyy.mm.dd', Date);
// умовна конструкція, в якій визначається що і звідки копіювати
case inst of
// якщо 1, то відбувається копіювання файлів конфігурації тільки для MailServer
1:
Begin
// перевірка існування директорії в яку будуть копіюватися файли
if directoryexists(dstM+'\\Backup config MailServer') then
begin
//якщо не має в директорії каталогу з датою то створюємо такий
if not directoryexists (dstM+'\\Backup config MailServer\'+dat) then
mkdir(dstM+'\\Backup config MailServer\'+dat);
end
// якщо директорії немає, то створюємо таку
else
begin
mkdir(dstM+'\\Backup config MailServer');
mkdir(dstM+'\\Backup config MailServer\'+dat);
end;
// пошук файлу
Filefind(mask,srcM);
//копіювання файлу/файлів
for j:=0 to i do
begin
Copyfile(pchar(srcM+'\'+listfile[j]),pchar(dstM+'\Backup config MailServer\'+dat+'\'+listfile[j]),true);
end;
end;
// якщо 0, то відбувається копіювання файлів конфігурації тільки для FireWall
0:
begin
if directoryexists(dstF+'\'+'\Backup config Firewall') then
begin
if not directoryexists (dstF+'\'+'\Backup config Firewall\'+dat) then
mkdir(dstf+'\'+'\Backup config Firewall\'+dat);
end
else
begin
mkdir(dstF+'\'+'\Backup config Firewall');
mkdir(dstf+'\'+'\Backup config Firewall\'+dat);
end;
Filefind(mask,srcF);
for j:=0 to i do
begin
Copyfile(pchar(srcF+'\'+listfile[j]),pchar(dstF+'\Backup config FireWall\'+dat+'\'+listfile[j]),true);
end;
end;
//якщо 2, то відбувається копіювання файлів конфігурації для MailServer і FireWall
2:
begin
if directoryexists(dstF+'\'+'\Backup config Firewall') then
begin
if not directoryexists (dstF+'\'+'\Backup config Firewall\'+dat) then
mkdir(dstf+'\'+'\Backup config Firewall\'+dat);
end
else
begin
//edit1.Text:='File not exists';
mkdir(dstF+'\'+'\Backup config Firewall');
mkdir(dstf+'\'+'\Backup config Firewall\'+dat);
end;
if directoryexists(dstM+'\'+'\Backup config MailServer') then
begin
if not directoryexists (dstM+'\'+'\Backup config MailServer\'+dat) then
mkdir(dstM+'\'+'\Backup config MailServer\'+dat);
end
else
begin
mkdir(dstM+'\'+'\'+'Backup config MailServer');
mkdir(dstM+'\'+'\Backup config MailServer\'+dat);
end;
Filefind(mask,srcF);
for j:=0 to i do
begin
Copyfile(pchar(srcF+'\'+listfile[j]),pchar(dstF+'\Backup config FireWall\'+dat+'\'+listfile[j]),true);
end;
Filefind(mask,srcM);
for j:=0 to i do
begin
Copyfile(pchar(srcM+'\'+listfile[j]),pchar(dstM+'\Backup config MailServer\'+dat+'\'+listfile[j]),true);
end;
end;
end;
end;
end.
Для створення сервісу потрібно проект скомпілювати (Project-Compile або Ctrl+F9)Файл конфігурації BackupConfig.ini такого змісту:
[Main] ' 0 - Firewall ' 1 - MailServer ' 2 - Firewall and Mailserver inst=2 mask=*.cfg [Source] Firewall=C:\Program Files\Kerio\WinRoute FireWall\ Mailserver=C:\Program Files\Kerio\MailServer\ [Dest] Firewall=g:\Kerio backup\ Mailserver=g:\Kerio backup\
Крок 2. Встановлення сервісу
Є 2 способи як встановити, так і деінсталювати сервіс:
А) Ручками
Перед інсталяцією таким способом потрібно обов’язково помістити файл конфігурації в корінь диску С.
Далі Пуск-Виконати або Win+R вписати шлях до exe файлу сервісу і задати параметр install.
Натиснути ОК і в результаті ви отримаєте повідомлення про те, що сервіс встановлений. Щоб повідомлення не з’являлося потрібно дописати ще параметр /silent.
Для деінсталяції потрібно виконати ті ж самі дії тільки з параметром /uninstall.Б) Програмно
Потрібно створити новий проект, кинути на форму дві кнопки, підписати їх і заповнити обробники подій цих кнопок, плюс дописати функції інсталяції і деінсталяції сервісу.
Нижче лістинг програми з коментарями.
unit ServInst;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,shellapi,Winsvc;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button3Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{Функція інсталяції сервісу
Передаються два параметри:
ExecutablePath - шлях до exe файлу сервісу
ServiceName - ім’я сервісу, яке буде відображатись}
function CreateNTService(ExecutablePath,ServiceName: String):boolean;
var
hNewService,hSCMgr: SC_HANDLE;
// Rights: DWORD;
FuncRetVal: Boolean;
begin
FuncRetVal := False;
hSCMgr := OpenSCManager(nil,nil,SC_MANAGER_CREATE_SERVICE);
if (hSCMgr <> 0 ) then
begin
//Власні права доступу можуть описуватись тут
//використовується GENERIC_EXECUTE, що є комбінацією
//STANDARD_RIGHTS_EXECUTE, SERVICE_START, SERVICE_STOP,
//SERVICE_PAUSE_CONTINUE, and SERVICE_USER_DEFINED_CONTROL
//Можна створювати власні права і використовувати їх як показано в закоментованих рядках нижче
//Rights := STANDARD_RIGHTS_REQUIRED or SERVICE_START or SERVICE_STOP
// or SERVICE_QUERY_STATUS or SERVICE_PAUSE_CONTINUE or
// SERVICE_INTERROGATE;
// SERVICE_AUTO_START – відповідає за тип запуску сервісу Авто
hNewService := CreateService(hSCMgr,PChar(ServiceName),PChar(ServiceName),
STANDARD_RIGHTS_REQUIRED,SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,
PChar(ExecutablePath),nil,nil,nil,nil,nil);
CloseServiceHandle(hSCMgr);
if (hNewService <> 0) then
FuncRetVal := true
else
FuncRetVal := false;
end;
CreateNTService := FuncRetVal;
end;
// ***
{Функцыя для видалення сервісу
// ServiceName – ім’я сервісу для видалення
//Результат:
// true – якщо операція завершена вдало
// false - якщо видалення не було виконано}
function DeleteNTService(ServiceName: String):boolean;
var
hServiceToDelete,hSCMgr: SC_HANDLE;
RetVal: LongBool;
FunctRetVal: Boolean;
begin
FunctRetVal := false;
hSCMgr := OpenSCManager(nil,nil,SC_MANAGER_CREATE_SERVICE);
if (hSCMgr <> 0 ) then
begin
hServiceToDelete := OpenService(hSCMgr, PChar(ServiceName), SERVICE_ALL_ACCESS);
RetVal := DeleteService(hServiceToDelete);
CloseServiceHandle(hSCMgr);
FunctRetVal := RetVal;
end;
DeleteNTService := FunctRetVal;
end;
{Інсталяція сервісу}
procedure TForm1.Button1Click(Sender: TObject);
var
tmps:string;
begin
// присвоєння імені сервісу
tmpS := 'CopyConfig';
// власне інсталяція з виводом повідомлення про успішність чи ні.
if (CreateNTService(GetCurrentdir+'\Service\Copycon.exe',tmpS)) then
MessageDlg('Service '+tmpS+' has been successfully created!',mtInformation,[mbOK],0)
else
MessageDlg('Unable to create service '+tmpS+' Win32 Error code: '+IntToStr(GetLastError),mtWarning,[mbOK],0);
// копіювання фалу конфігурації сервісу в корінь диску С
CopyFile(pchar(GetCurrentdir+'\Service\BackupConfig.ini'),'C:\BackupConfig.ini',true);
end;
{Деінсталяція сервісу}
procedure TForm1.Button2Click(Sender: TObject);
var
tmpS: String;
begin
tmpS := 'CopyConfig';
// деінсталяція сервісу з виводом повідомлення про результат
if (DeleteNTService(tmpS)) then
MessageDlg('Service '+tmpS+' has been successfully deleted!',mtInformation,[mbOK],0)
else
MessageDlg('Unable to delete service '+tmpS+' Win32 Error code: '+IntToStr(GetLastError),mtWarning,[mbOK],0);
//видалення файлу конфігурації сервісу з кореню диску С
if fileexists('C:\BackupConfig.ini') then
DeleteFile('C:\BackupConfig.ini');
end;
end.
Якщо використовувати мій код в тому вигляді в якому він є, то розміщення файлів має мати такий вигляд:При обох варіантах інсталяції, після завершення сервіс буде не запущений. Для запуску потрібно або перезавантажитись, або через оснастку Service його запустити.
В принципі цей сервіс можна використовувати не тільки для копіювання файлів конфігурації Kerio, а й інших, потрібно просто задати відповідні настройки в BackupConfig.ini і при необхідності змінити розмірність масиву listfile, якщо файлів більше ніж 20.
При написанні сервісу я використовував матеріали розміщені на DelphiKingdom, HelloWorld.ru і в DRKB Explorer – домашня сторінка.
Початковий код, сервіс і його інсталятор можна взяти тут.




Немає коментарів:
Дописати коментар