7using System.ServiceProcess;
10using System.Threading.Tasks;
20#pragma warning disable CA1416
29 private bool autoPaused =
false;
30 private bool starting =
false;
40 this.ServiceName = ServiceName;
41 if (!
string.IsNullOrEmpty(InstanceName))
42 this.ServiceName +=
" " + InstanceName;
45 this.CanHandlePowerEvent =
true;
46 this.CanHandleSessionChangeEvent =
true;
47 this.CanPauseAndContinue =
true;
48 this.CanShutdown =
true;
53 protected override void OnStart(
string[] args)
63 using PendingTimer Timer =
new(
this);
65 Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
67 Gateway.GetDatabaseProvider +=
Program.GetDatabase;
68 Gateway.RegistrationSuccessful +=
Program.RegistrationSuccessful;
69 Gateway.OnTerminate += this.TerminateService;
79 this.starting =
false;
85 Log.
Alert(
"Gateway being started in another process.");
86 ThreadPool.QueueUserWorkItem(
_ => this.Stop());
97 private Task TerminateService(
object Sender, EventArgs e)
100 ThreadPool.QueueUserWorkItem(
_ => this.Stop());
101 return Task.CompletedTask;
104 private class PendingTimer : IDisposable
108 private bool disposed =
false;
112 this.service = Service;
113 this.timer =
new Timer(this.MoreTime,
null, 0, 1000);
116 public void Dispose()
118 this.disposed =
true;
119 this.timer?.Dispose();
123 private void MoreTime(
object State)
129 this.service.RequestAdditionalTime(2000);
131 catch (InvalidOperationException)
133 this.timer?.Dispose();
161 using PendingTimer Timer =
new(
this);
163 Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
165 Gateway.GetDatabaseProvider +=
Program.GetDatabase;
166 Gateway.RegistrationSuccessful +=
Program.RegistrationSuccessful;
167 Gateway.OnTerminate += this.TerminateService;
169 this.starting =
true;
176 this.starting =
false;
182 Log.
Alert(
"Gateway being started in another process.");
183 ThreadPool.QueueUserWorkItem(
_ => this.Stop());
201 case PowerBroadcastStatus.BatteryLow:
202 case PowerBroadcastStatus.OemEvent:
203 case PowerBroadcastStatus.PowerStatusChange:
204 case PowerBroadcastStatus.QuerySuspend:
208 case PowerBroadcastStatus.ResumeAutomatic:
209 case PowerBroadcastStatus.ResumeCritical:
210 case PowerBroadcastStatus.ResumeSuspend:
213 this.autoPaused =
false;
216 Log.
Warning(
"Gateway is in the process of starting, called from another source.");
225 case PowerBroadcastStatus.Suspend:
226 this.autoPaused =
true;
231 case PowerBroadcastStatus.QuerySuspendFailed:
247 DateTime Now = DateTime.UtcNow;
248 int SessionId = ChangeDescription.SessionId;
249 List<KeyValuePair<string, object>> Tags =
251 new(
"Date", Now.ToShortDateString()),
252 new(
"Time", Now.ToLongTimeString() +
"Z"),
253 new(
"SessionId", SessionId),
257 AddWtsUserName(Tags, SessionId);
258 AddWtsName(Tags,
"Initial Program", SessionId,
WtsInfoClass.WTSInitialProgram, NullTerminatedString);
259 AddWtsName(Tags,
"Application Name", SessionId,
WtsInfoClass.WTSApplicationName, NullTerminatedString);
260 AddWtsName(Tags,
"Working Directory", SessionId,
WtsInfoClass.WTSWorkingDirectory, NullTerminatedString);
261 AddWtsName(Tags,
"Station Name", SessionId,
WtsInfoClass.WTSWinStationName, NullTerminatedString);
262 AddWtsName(Tags,
"Connect State", SessionId,
WtsInfoClass.WTSConnectState, EnumerationString<WtsConnectClass>);
263 AddWtsName(Tags,
"Client Build Number", SessionId,
WtsInfoClass.WTSClientBuildNumber, IntegerValue);
264 AddWtsName(Tags,
"Client Name", SessionId,
WtsInfoClass.WTSClientName, NullTerminatedString);
265 AddWtsName(Tags,
"Client Directory", SessionId,
WtsInfoClass.WTSClientDirectory, NullTerminatedString);
266 AddWtsName(Tags,
"Client Product ID", SessionId,
WtsInfoClass.WTSClientProductId, IntegerValue);
267 AddWtsName(Tags,
"Client Hardware ID", SessionId,
WtsInfoClass.WTSClientHardwareId, IntegerValue);
268 AddWtsName(Tags,
"Client Address", SessionId,
WtsInfoClass.WTSClientAddress, ClientAddress);
269 AddWtsName(Tags,
"Client Display", SessionId,
WtsInfoClass.WTSClientDisplay, ClientDisplay);
270 AddWtsName(Tags,
"Client Protocol Type", SessionId,
WtsInfoClass.WTSClientProtocolType, EnumerationString<WtsClientProtocolType>);
271 AddWtsName(Tags,
"Client Info", SessionId,
WtsInfoClass.WTSClientInfo, NullTerminatedString);
275 switch (ChangeDescription.Reason)
277 case SessionChangeReason.ConsoleConnect:
278 Message =
"User connected to machine via console interface.";
281 case SessionChangeReason.ConsoleDisconnect:
282 Message =
"User disconnected console interface.";
285 case SessionChangeReason.RemoteConnect:
286 Message =
"User connected remotely to machine.";
289 case SessionChangeReason.RemoteDisconnect:
290 Message =
"User disconnected remote interface.";
293 case SessionChangeReason.SessionLock:
294 Message =
"User session locked.";
297 case SessionChangeReason.SessionLogoff:
298 Message =
"User logged off.";
301 case SessionChangeReason.SessionLogon:
302 Message =
"User logged on.";
305 case SessionChangeReason.SessionRemoteControl:
306 Message =
"User remote control status of session has changed.";
309 case SessionChangeReason.SessionUnlock:
310 Message =
"User session unlocked.";
314 Tags.Add(
new KeyValuePair<string, object>(
"Reason", ChangeDescription.Reason.ToString()));
315 Message =
"Session changed.";
323 if ((Setup.NotificationConfiguration.Instance.Addresses?.Length ?? 0) == 0)
329 StringBuilder Markdown =
new();
332 Markdown.AppendLine();
333 Markdown.AppendLine(
"| Details ||");
334 Markdown.AppendLine(
"|:----|:---|");
336 foreach (KeyValuePair<string, object> Tag
in Tags)
338 Markdown.Append(
"| ");
340 Markdown.Append(
" | ");
342 Markdown.AppendLine(
" |");
350 private static void AddWtsUserName(List<KeyValuePair<string, object>> Tags,
int SessionId)
352 string Value = GetUserName(SessionId);
353 if (!
string.IsNullOrEmpty(Value))
354 Tags.Add(
new KeyValuePair<string, object>(
"User Name", Value));
357 private static void AddWtsName(List<KeyValuePair<string, object>> Tags,
string Key,
int SessionId,
360 object Value = GetWtsValue(SessionId, InfoClass, Parser);
361 if (Value is not
null)
362 Tags.Add(
new KeyValuePair<string, object>(Key, Value));
365 private static string GetUserName(
int SessionId)
369 string UserName = GetWtsValue(SessionId,
WtsInfoClass.WTSUserName, NullTerminatedString) as string;
370 if (
string.IsNullOrEmpty(UserName))
373 string Domain = GetWtsValue(SessionId,
WtsInfoClass.WTSDomainName, NullTerminatedString) as string;
374 if (!
string.IsNullOrEmpty(Domain))
375 UserName = Domain +
"\\" + UserName;
386 private delegate
object ParseWtsInfo(IntPtr Buffer,
int Len);
388 private static object NullTerminatedString(IntPtr Buffer,
int _)
390 string s = Marshal.PtrToStringAnsi(Buffer);
392 if (!
string.IsNullOrEmpty(s))
398 private static long GetInteger(IntPtr Buffer,
int Len)
402 case 1:
return Marshal.ReadByte(Buffer, 0);
403 case 2:
return Marshal.ReadInt16(Buffer, 0);
404 case 4:
return Marshal.ReadInt32(Buffer, 0);
405 case 8:
return Marshal.ReadInt64(Buffer, 0);
410 byte[] Bin =
new byte[8];
413 for (i = 0; i < Len; i++)
414 Bin[i] = Marshal.ReadByte(Buffer, i);
416 return BitConverter.ToInt64(Bin, 0);
420 private static object IntegerValue(IntPtr Buffer,
int Len)
422 return GetInteger(Buffer, Len);
425 private static object EnumerationString<T>(IntPtr Buffer,
int Len)
428 long i = GetInteger(Buffer, Len);
429 return Enum.ToObject(typeof(T), i);
432 private static object ClientAddress(IntPtr Buffer,
int Len)
436 if (EnumerationString<AddressFamily>(Buffer, Math.Min(4, Len)) is not AddressFamily AddressFamily)
439 switch (AddressFamily)
441 case AddressFamily.InterNetwork:
443 return AddressFamily;
450 Marshal.Copy(Buffer, Bin, 6, 4);
452 return new IPAddress(Bin).ToString();
456 return AddressFamily;
459 case AddressFamily.InterNetworkV6:
461 return AddressFamily;
466 Marshal.Copy(Buffer, Bin, 4, 16);
468 return new IPAddress(Bin).ToString();
472 return AddressFamily;
476 return AddressFamily;
480 private static object ClientDisplay(IntPtr Buffer,
int Len)
487 byte[] Bin =
new byte[12];
488 Marshal.Copy(Buffer, Bin, 0, 12);
490 int Width = BitConverter.ToInt32(Bin, 0);
491 int Height = BitConverter.ToInt32(Bin, 4);
492 int ColorDepth = BitConverter.ToInt32(Bin, 8);
493 string s = Width.ToString() +
"x" + Height.ToString();
498 s +=
" (4 bits per pixel.)";
502 s +=
" (8 bits per pixel.)";
506 s +=
" (16 bits per pixel.)";
510 s +=
" (3 byte RGB).";
514 s +=
" (15 bits per pixel.)";
518 s +=
" (24 bits per pixel.)";
522 s +=
" (32 bits per pixel.)";
529 private static object GetWtsValue(
int SessionId,
WtsInfoClass InfoClass, ParseWtsInfo Parser)
535 if (
Win32.WTSQuerySessionInformation(IntPtr.Zero, SessionId, InfoClass, out IntPtr Buffer, out
int Len) &&
536 Buffer != IntPtr.Zero && Len > 1)
540 Result = Parser(Buffer, Len);
541 Win32.WTSFreeMemory(Buffer);
542 Buffer = IntPtr.Zero;
546 if (Buffer != IntPtr.Zero)
547 Win32.WTSFreeMemory(Buffer);
562 private static readonly
char[] specialCharactersToEscape =
596 private static readonly
string[] specialCharacterEscapes =
645 using PendingTimer Timer =
new(
this);
647 Gateway.GetDatabaseProvider -=
Program.GetDatabase;
648 Gateway.RegistrationSuccessful -=
Program.RegistrationSuccessful;
649 Gateway.OnTerminate -= this.TerminateService;
661 private static void Flush()
685#pragma warning restore CA1416
Helps with parsing of commong data types.
static string Escape(string s, char[] CharactersToEscape, string EscapeSequence)
Escapes a set of characters in a string.
Contains a markdown document. This markdown document class supports original markdown,...
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
static void Notice(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a notice event.
static async Task TerminateAsync()
Must be called when the application is terminated. Stops all event sinks that have been registered.
static void Alert(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an alert event.
Static class managing the runtime environment of the IoT Gateway.
static CaseInsensitiveString Domain
Domain name.
static Task< bool > Start(bool ConsoleOutput)
Starts the gateway.
static async Task Stop()
Stops the gateway.
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
static bool HasDomain
If a domain name is configured.
static async Task< bool > ExecuteServiceCommand(int CommandNr)
Executes a service command.
override void OnShutdown()
override void OnStart(string[] args)
override void OnContinue()
override async void OnCustomCommand(int command)
override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
override void OnSessionChange(SessionChangeDescription ChangeDescription)
GatewayService(string ServiceName, string InstanceName)
Gateway Service
IoT Gateway Windows Service Application.
static string InstanceName
Instance name
Handles interaction with Windows Service API.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static bool HasProvider
If a database provider is registered.
static IDatabaseProvider Provider
Registered database provider.
Static interface for ledger persistence. In order to work, a ledger provider has to be assigned to it...
static bool HasProvider
If a ledger provider is registered.
static ILedgerProvider Provider
Registered ledger provider.
Static class that dynamically manages types and interfaces available in the runtime environment.
static void SetModuleParameter(string Name, object Value)
Sets a module parameter. This parameter value will be accessible to modules when they are loaded.
Task Flush()
Persists any pending changes.
Task Flush()
Persists any pending changes.
WtsInfoClass
Windows Terminal Services Infomration class.