Предыстория.
Для службы сбора данных через DDE необходимо активировать "Взаимодействие с рабочим столом" в свойствах службы. По какой-то причине встроенные функции установки службы не позволяют управлять данной возможностью. Имеется в виду классы ServiceInstaller и ServiceProcessInstaller. Из-за этого, а также в виду необходимости перезапуска службы при наступлении определенных событий, не используя при этом bat-файлы, планировщики задач и отдельные программы, появился такой класс:
internal class ServiceControl {
private const uint SRV_ERRCTRL = 1;
private const uint SRV_FULL_ACS = 0xF01FF;
private const uint SCM_ALL_ACS = 0xF003F;
private const uint SRV_STRT = 2;
private const uint SRV_TYPE = 0x110;
private const int MAX_RETRIES = 60;
private int _retries;
private readonly string _srvDispName;
private readonly string _srvName;
private readonly string _srvPath;
private IntPtr _handle = IntPtr.Zero;
private IntPtr _servHandle = IntPtr.Zero;
internal ServiceControl(string path, string dispname) {
_srvPath = Path.GetFullPath(path);
if(string.IsNullOrEmpty(Path.GetExtension(path))) {
_srvPath += ".exe";
}
_srvName = Path.GetFileNameWithoutExtension(path);
_srvDispName = dispname;
}
private void CloseHandles() {
if (_servHandle != IntPtr.Zero) {
Interop.Services.CloseServiceHandle(_servHandle);
}
if (_handle != IntPtr.Zero) {
Interop.Services.CloseServiceHandle(_handle);
}
}
private void CheckServHand() {
if (_servHandle != IntPtr.Zero) { } else {
Interop.Services.CloseServiceHandle(_handle);
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
private void DeleteService() {
if (!Interop.Services.DeleteService(_servHandle)) {
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
private void OpenSCM() {
CloseHandles();
_handle = Interop.Services.OpenSCManager(null, null, SCM_ALL_ACS);
if (_handle == IntPtr.Zero) {
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
private void OpenService() {
_servHandle = Interop.Services.OpenService(_handle, _srvName, SRV_FULL_ACS);
CheckServHand();
}
private void CreateService() {
_servHandle = Interop.Services.CreateService(_handle, _srvName, _srvDispName, SRV_FULL_ACS, SRV_TYPE, SRV_STRT,
SRV_ERRCTRL, _srvPath, null, null, null, null, null);
CheckServHand();
}
private void StartService() {
_retries = MAX_RETRIES;
if (GetStatus() == SERVICE_STATE.SERVICE_STOPPED) {
Interop.Services.StartService(_servHandle, 0, null);
}
while (GetStatus() != SERVICE_STATE.SERVICE_RUNNING && (_retries-- > 0)) {
Thread.Sleep(250);
}
}
private SERVICE_STATE GetStatus() {
var ss = new SERVICE_STATUS();
Interop.Services.QueryServiceStatus(_servHandle, ref ss);
return ss.dwCurrentState;
}
private void StopService() {
_retries = MAX_RETRIES;
if (GetStatus() != SERVICE_STATE.SERVICE_STOPPED) {
var ss=new SERVICE_STATUS();
Interop.Services.ControlService(_servHandle, SERVICE_CONTROL.STOP, ref ss);
}
while (GetStatus() != SERVICE_STATE.SERVICE_STOPPED && (_retries-- > 0)) {
Thread.Sleep(250);
}
}
public string Control(Ctrl method) {
var s = "";
OpenSCM();
if (method == Ctrl.INSTALL) {
CreateService();
method = Ctrl.START;
}
OpenService();
switch (method) {
case Ctrl.UNINSTALL:
DeleteService();
break;
case Ctrl.START:
StartService();
break;
case Ctrl.STOP:
StopService();
break;
case Ctrl.RESTART:
StopService();
StartService();
break;
case Ctrl.STATUS:
s = GetStatus().ToString();
break;
}
CloseHandles();
return s;
}
}
public enum Ctrl {
INSTALL,
UNINSTALL,
START,
STOP,
STATUS,
RESTART
}
[Flags]
public enum SERVICE_STATE {
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007,
SERVICE_RUNNING = 0x00000004,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_STOPPED = 0x00000001
}
Для службы сбора данных через DDE необходимо активировать "Взаимодействие с рабочим столом" в свойствах службы. По какой-то причине встроенные функции установки службы не позволяют управлять данной возможностью. Имеется в виду классы ServiceInstaller и ServiceProcessInstaller. Из-за этого, а также в виду необходимости перезапуска службы при наступлении определенных событий, не используя при этом bat-файлы, планировщики задач и отдельные программы, появился такой класс:
internal class ServiceControl {
private const uint SRV_ERRCTRL = 1;
private const uint SRV_FULL_ACS = 0xF01FF;
private const uint SCM_ALL_ACS = 0xF003F;
private const uint SRV_STRT = 2;
private const uint SRV_TYPE = 0x110;
private const int MAX_RETRIES = 60;
private int _retries;
private readonly string _srvDispName;
private readonly string _srvName;
private readonly string _srvPath;
private IntPtr _handle = IntPtr.Zero;
private IntPtr _servHandle = IntPtr.Zero;
internal ServiceControl(string path, string dispname) {
_srvPath = Path.GetFullPath(path);
if(string.IsNullOrEmpty(Path.GetExtension(path))) {
_srvPath += ".exe";
}
_srvName = Path.GetFileNameWithoutExtension(path);
_srvDispName = dispname;
}
private void CloseHandles() {
if (_servHandle != IntPtr.Zero) {
Interop.Services.CloseServiceHandle(_servHandle);
}
if (_handle != IntPtr.Zero) {
Interop.Services.CloseServiceHandle(_handle);
}
}
private void CheckServHand() {
if (_servHandle != IntPtr.Zero) { } else {
Interop.Services.CloseServiceHandle(_handle);
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
private void DeleteService() {
if (!Interop.Services.DeleteService(_servHandle)) {
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
private void OpenSCM() {
CloseHandles();
_handle = Interop.Services.OpenSCManager(null, null, SCM_ALL_ACS);
if (_handle == IntPtr.Zero) {
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
private void OpenService() {
_servHandle = Interop.Services.OpenService(_handle, _srvName, SRV_FULL_ACS);
CheckServHand();
}
private void CreateService() {
_servHandle = Interop.Services.CreateService(_handle, _srvName, _srvDispName, SRV_FULL_ACS, SRV_TYPE, SRV_STRT,
SRV_ERRCTRL, _srvPath, null, null, null, null, null);
CheckServHand();
}
private void StartService() {
_retries = MAX_RETRIES;
if (GetStatus() == SERVICE_STATE.SERVICE_STOPPED) {
Interop.Services.StartService(_servHandle, 0, null);
}
while (GetStatus() != SERVICE_STATE.SERVICE_RUNNING && (_retries-- > 0)) {
Thread.Sleep(250);
}
}
private SERVICE_STATE GetStatus() {
var ss = new SERVICE_STATUS();
Interop.Services.QueryServiceStatus(_servHandle, ref ss);
return ss.dwCurrentState;
}
private void StopService() {
_retries = MAX_RETRIES;
if (GetStatus() != SERVICE_STATE.SERVICE_STOPPED) {
var ss=new SERVICE_STATUS();
Interop.Services.ControlService(_servHandle, SERVICE_CONTROL.STOP, ref ss);
}
while (GetStatus() != SERVICE_STATE.SERVICE_STOPPED && (_retries-- > 0)) {
Thread.Sleep(250);
}
}
public string Control(Ctrl method) {
var s = "";
OpenSCM();
if (method == Ctrl.INSTALL) {
CreateService();
method = Ctrl.START;
}
OpenService();
switch (method) {
case Ctrl.UNINSTALL:
DeleteService();
break;
case Ctrl.START:
StartService();
break;
case Ctrl.STOP:
StopService();
break;
case Ctrl.RESTART:
StopService();
StartService();
break;
case Ctrl.STATUS:
s = GetStatus().ToString();
break;
}
CloseHandles();
return s;
}
}
public enum Ctrl {
INSTALL,
UNINSTALL,
START,
STOP,
STATUS,
RESTART
}
[Flags]
public enum SERVICE_STATE {
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007,
SERVICE_RUNNING = 0x00000004,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_STOPPED = 0x00000001
}
Все что нужно будет потом, это:
var sc = new ServiceControl(Environment.GetCommandLineArgs()[0],
"Выводимое имя в управлении службами");
и sc.Control(Ctrl.INSTALL); - для установки или использовать любую другую команду из доступных в перечислении Ctrl.
Саму реализацию внутри службы управления через передаваемые аргументы из командной строки можно сделать, например, так:
[STAThread]
private static void Main(string[] args) {
try {
if (args.Length > 0) {
try {
var sc = new ServiceControl(Environment.GetCommandLineArgs()[0],
"Выводимое имя");
Ctrl ct;
switch (args[0].ToLower().Trim()) {
case "/install":
ct = Ctrl.INSTALL;
break;
case "/restart":
ct=Ctrl.RESTART;
break;
case "/uninstall":
ct=Ctrl.UNINSTALL;
break;
case "/start":
ct=Ctrl.START;
break;
case "/stop":
ct=Ctrl.STOP;
break;
case "/state":
Console.WriteLine(sc.Control(Ctrl.STATUS));
return;
case "/qr":
sc.Control(Ctrl.RESTART);
return;
default:
return;
}
sc.Control(ct);
Console.WriteLine(Messages.Service[(int)ct]);
} catch (Exception e) {
Console.WriteLine(e.Message);
}
} else {
Run(new Service());
}
} catch (Exception e) {
Trace.WriteLog(e);
}
}
Чтобы перезапустить службу из самой себя достаточно написать:
Process.Start(new ProcessStartInfo(Path.GetFullPath(Environment.GetCommandLineArgs()[0]), "/qr")
{WindowStyle = ProcessWindowStyle.Hidden});
Комментариев нет:
Отправить комментарий