winfsp/src/dotnet/Service.cs
2022-01-07 17:30:49 +00:00

192 lines
6.0 KiB
C#

/*
* dotnet/Service.cs
*
* Copyright 2015-2022 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
using System;
using Fsp.Interop;
namespace Fsp
{
/// <summary>
/// Provides the base class for a process that can be run as a service,
/// command line application or under the control of the WinFsp launcher.
/// </summary>
public class Service
{
/* const */
public const UInt32 EVENTLOG_ERROR_TYPE = 0x0001;
public const UInt32 EVENTLOG_WARNING_TYPE = 0x0002;
public const UInt32 EVENTLOG_INFORMATION_TYPE = 0x0004;
/* ctor/dtor */
/// <summary>
/// Creates an instance of the Service class.
/// </summary>
/// <param name="ServiceName">The name of the service.</param>
public Service(String ServiceName)
{
Api.FspServiceCreate(ServiceName, _OnStart, _OnStop, null, out _ServicePtr);
if (IntPtr.Zero != _ServicePtr)
Api.SetUserContext(_ServicePtr, this);
}
~Service()
{
if (IntPtr.Zero != _ServicePtr)
{
Api.DisposeUserContext(_ServicePtr);
Api.FspServiceDelete(_ServicePtr);
}
}
/* control */
/// <summary>
/// Runs a service.
/// </summary>
/// <returns>Service process exit code.</returns>
public int Run()
{
if (IntPtr.Zero == _ServicePtr)
{
const Int32 STATUS_INSUFFICIENT_RESOURCES = unchecked((Int32)0xc000009a);
Log(EVENTLOG_ERROR_TYPE,
String.Format("The service {0} cannot be created (Status={1:X}).",
GetType().FullName, STATUS_INSUFFICIENT_RESOURCES));
return (int)Api.FspWin32FromNtStatus(STATUS_INSUFFICIENT_RESOURCES);
}
Api.FspServiceAllowConsoleMode(_ServicePtr);
Int32 Result = Api.FspServiceLoop(_ServicePtr);
int ExitCode = (int)Api.FspServiceGetExitCode(_ServicePtr);
if (0 > Result)
{
Log(EVENTLOG_ERROR_TYPE,
String.Format("The service {0} has failed to run (Status={1:X}).",
GetType().FullName, Result));
return (int)Api.FspWin32FromNtStatus(Result);
}
return ExitCode;
}
/// <summary>
/// Stops a running service.
/// </summary>
public void Stop()
{
if (IntPtr.Zero == _ServicePtr)
throw new InvalidOperationException();
Api.FspServiceStop(_ServicePtr);
}
public void RequestTime(UInt32 Time)
{
if (IntPtr.Zero == _ServicePtr)
throw new InvalidOperationException();
Api.FspServiceRequestTime(_ServicePtr, Time);
}
/// <summary>
/// Gets or sets the service process exit code.
/// </summary>
public int ExitCode
{
get
{
if (IntPtr.Zero == _ServicePtr)
throw new InvalidOperationException();
return (int)Api.FspServiceGetExitCode(_ServicePtr);
}
set
{
if (IntPtr.Zero == _ServicePtr)
throw new InvalidOperationException();
Api.FspServiceSetExitCode(_ServicePtr, (UInt32)value);
}
}
public IntPtr ServiceHandle
{
get { return _ServicePtr; }
}
public static void Log(UInt32 Type, String Message)
{
Api.FspServiceLog(Type, "%s", Message);
}
/* start/stop */
/// <summary>
/// Provides a means to customize the returned status code when an exception happens.
/// </summary>
/// <param name="ex"></param>
/// <returns>STATUS_SUCCESS or error code.</returns>
protected virtual Int32 ExceptionHandler(Exception ex)
{
return unchecked((Int32)0xE0434f4D)/*STATUS_CLR_EXCEPTION*/;
}
/// <summary>
/// Occurs when the service starts.
/// </summary>
/// <param name="Args">Command line arguments passed to the service.</param>
protected virtual void OnStart(String[] Args)
{
}
/// <summary>
/// Occurs when the service stops.
/// </summary>
protected virtual void OnStop()
{
}
/* callbacks */
private static Int32 OnStart(
IntPtr Service,
UInt32 Argc,
String[] Argv)
{
Service self = (Service)Api.GetUserContext(Service);
try
{
self.OnStart(
Argv);
return 0/*STATUS_SUCCESS*/;
}
catch (Exception ex)
{
return self.ExceptionHandler(ex);
}
}
private static Int32 OnStop(
IntPtr Service)
{
Service self = (Service)Api.GetUserContext(Service);
try
{
self.OnStop();
return 0/*STATUS_SUCCESS*/;
}
catch (Exception ex)
{
return self.ExceptionHandler(ex);
}
}
private static Api.Proto.ServiceStart _OnStart = OnStart;
private static Api.Proto.ServiceStop _OnStop = OnStop;
private IntPtr _ServicePtr;
}
}