From 93717f04046494569cc48d4fcbfbfe3e76e19e9f Mon Sep 17 00:00:00 2001 From: RobertK66 Date: Sun, 28 Nov 2021 13:16:50 +0100 Subject: [PATCH] Cleanup version with remote connection --- EmbeddedTools.sln | 31 ++- ScreenLib/MainScreen.cs | 2 +- ScreenLib/Screen.cs | 8 +- StatusConsole/HostedCmdlineApp.cs | 13 +- StatusConsole/Program.cs | 2 +- StatusConsole/RemoteCli.cs | 182 ++++++++---------- .../{ObcEm2Uarts.cs => ServiceCollection.cs} | 6 +- StatusConsole/StatusConsole.csproj | 6 + StatusConsole/StatusConsoleMainView.cs | 26 +-- StatusConsole/UartCli.cs | 3 - StatusConsole/appsettings.json | 13 +- StatusConsole/scripts/ComToPort.sh | 30 +++ testtemp/HostedService.cs | 33 ++++ testtemp/Program.cs | 24 +++ testtemp/testtemp.csproj | 14 ++ 15 files changed, 234 insertions(+), 159 deletions(-) rename StatusConsole/{ObcEm2Uarts.cs => ServiceCollection.cs} (85%) create mode 100644 StatusConsole/scripts/ComToPort.sh create mode 100644 testtemp/HostedService.cs create mode 100644 testtemp/Program.cs create mode 100644 testtemp/testtemp.csproj diff --git a/EmbeddedTools.sln b/EmbeddedTools.sln index a11f1e4..1adced9 100644 --- a/EmbeddedTools.sln +++ b/EmbeddedTools.sln @@ -1,11 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenLib", "ScreenLib\ScreenLib.csproj", "{A807DB7A-A5FD-4DCC-8BDE-3DF94DC18653}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenLib", "ScreenLib\ScreenLib.csproj", "{A807DB7A-A5FD-4DCC-8BDE-3DF94DC18653}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatusConsole", "StatusConsole\StatusConsole.csproj", "{D99E0D06-B412-49BF-98E2-6DE72C2366EB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StatusConsole", "StatusConsole\StatusConsole.csproj", "{D99E0D06-B412-49BF-98E2-6DE72C2366EB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testtemp", "testtemp\testtemp.csproj", "{5EDCAF27-CA84-4BD6-B192-569CEE955363}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -16,9 +18,6 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A807DB7A-A5FD-4DCC-8BDE-3DF94DC18653}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A807DB7A-A5FD-4DCC-8BDE-3DF94DC18653}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -44,5 +43,23 @@ Global {D99E0D06-B412-49BF-98E2-6DE72C2366EB}.Release|x64.Build.0 = Release|Any CPU {D99E0D06-B412-49BF-98E2-6DE72C2366EB}.Release|x86.ActiveCfg = Release|Any CPU {D99E0D06-B412-49BF-98E2-6DE72C2366EB}.Release|x86.Build.0 = Release|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Debug|x64.ActiveCfg = Debug|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Debug|x64.Build.0 = Debug|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Debug|x86.ActiveCfg = Debug|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Debug|x86.Build.0 = Debug|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Release|Any CPU.Build.0 = Release|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Release|x64.ActiveCfg = Release|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Release|x64.Build.0 = Release|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Release|x86.ActiveCfg = Release|Any CPU + {5EDCAF27-CA84-4BD6-B192-569CEE955363}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {762B20EF-471D-4A98-B8BF-DC8C24ACDDFF} EndGlobalSection EndGlobal diff --git a/ScreenLib/MainScreen.cs b/ScreenLib/MainScreen.cs index ffb2d35..9199cb8 100644 --- a/ScreenLib/MainScreen.cs +++ b/ScreenLib/MainScreen.cs @@ -51,7 +51,7 @@ public void RunLine(String exitCmd) { } - abstract public void HandleConsoleInput(Screen inputScreen, string debugOption, int sleep); + abstract public void HandleConsoleInput(Screen inputScreen); } } diff --git a/ScreenLib/Screen.cs b/ScreenLib/Screen.cs index 8452642..b8468cc 100644 --- a/ScreenLib/Screen.cs +++ b/ScreenLib/Screen.cs @@ -76,9 +76,9 @@ public void Write(String message, ConsoleColor? col = null) { var cs = GetConsoleState(); Console.BackgroundColor = this.BackgroundColor; Console.ForegroundColor = col??this.TextColor; - //if (((Offset.Left + CursorPos.Left) >= Console.BufferWidth-1) || ((Offset.Left + CursorPos.Left) < 0)) { - // int i = 55; - //} + if (((Offset.Left + CursorPos.Left) >= Console.BufferWidth-1) || ((Offset.Left + CursorPos.Left) < 0)) { + Console.BackgroundColor = ConsoleColor.Yellow; + } Console.SetCursorPosition(Offset.Left + CursorPos.Left, Offset.Top + CursorPos.Top); Console.Write(message); CursorPos.Left += message.Length; @@ -213,7 +213,7 @@ public void WriteData(byte[] inBuffer, int bytesRead) { String received = String.Empty; int bytesProcessed = bytesRead; - while(String.IsNullOrEmpty(received)) { + while(String.IsNullOrEmpty(received) && (bytesProcessed > 0)) { try { received = encoding.GetString(buffer, 0, bytesProcessed); } catch (Exception ) { diff --git a/StatusConsole/HostedCmdlineApp.cs b/StatusConsole/HostedCmdlineApp.cs index 3095c97..b98cef5 100644 --- a/StatusConsole/HostedCmdlineApp.cs +++ b/StatusConsole/HostedCmdlineApp.cs @@ -14,18 +14,13 @@ public class HostedCmdlineApp : IHostedService { private readonly Dictionary uarts; private Task guiInputHandler; public ITtyService uartInFocus { get; set; } - private Screen appLogScreen; - private String debugOption; - private int sleepTime; - + - // Constructor with IOC injection + // Constructor called by IOC with injected ServiceCollection and Config public HostedCmdlineApp(IConfigurableServices service, IConfiguration conf) { _myServices = service ?? throw new ArgumentNullException(nameof(service)); - uarts = service.GetTtyServices(); - debugOption = conf.GetValue("Option") ?? "A"; - sleepTime = conf.GetValue("Sleep") ?? 100; + uarts = _myServices.GetTtyServices(); } public async Task StartAsync(CancellationToken cancellationToken) { @@ -75,7 +70,7 @@ public async Task StartAsync(CancellationToken cancellationToken) { appLogScreen.WriteLine("Use TAB to switch input control."); // prepare Input cursor - guiInputHandler = Task.Run(() => main.HandleConsoleInput(appLogScreen, debugOption, sleepTime)); + guiInputHandler = Task.Run(() => main.HandleConsoleInput(appLogScreen)); uartInFocus = _myServices.GetCurrentService(); } diff --git a/StatusConsole/Program.cs b/StatusConsole/Program.cs index 117c327..9f20569 100644 --- a/StatusConsole/Program.cs +++ b/StatusConsole/Program.cs @@ -38,7 +38,7 @@ private static IHostBuilder CreateHostBuilder(string[] args) { }) .ConfigureServices(services => { services.AddHostedService(); - services.AddTransient(); + services.AddTransient(); }); } diff --git a/StatusConsole/RemoteCli.cs b/StatusConsole/RemoteCli.cs index 347abc6..1d1fb83 100644 --- a/StatusConsole/RemoteCli.cs +++ b/StatusConsole/RemoteCli.cs @@ -12,66 +12,69 @@ using System.Threading.Tasks; namespace StatusConsole { - + // // This allows to access a remote socket on hostname:PORTNR // If the remote machine is running linux there we could connect up any available tty device by running // nc -l 9000 > /dev/ttyUSB0 < /dev/ttyUSB0 // where 9000 is the port number and ttyUSB0 an example device. - + // The version + // while :; do nc -l 9801 > /dev/ttyUSB1 < /dev/ttyUSB1 ; done + // gives repeated call to the nc commans when connection was closed somehow. + // public class RemoteCli : ITtyService { - private IConfigurationSection Config; - Socket sock = null; + protected String IfName; + + private String HostName; + private int Port; + + private const int BufferSize = 255; + private byte[] Buffer = new byte[BufferSize+5]; + private Socket socket = null; private bool Continue; + private String NewLine = String.Empty; + + // TODO: refactor to seperate UI Config ...???... IConOutput Screen; - Task Receiver; - char NewLine = '\n'; + private IConfigurationSection screenConfig; public void Initialize(IConfigurationSection cs) { - Config = cs; + IfName = cs.Key; + HostName = cs?.GetValue("RemoteHost") ?? "localhost"; + Port = cs?.GetValue("RemotePort") ?? 9000; + NewLine = cs?.GetValue("NewLine"); + + screenConfig = cs.GetSection("Screen"); } string ITtyService.GetInterfaceName() { - return Config.Key; + return IfName; } IConfigurationSection ITtyService.GetScreenConfig() { - return Config.GetSection("Screen"); + return screenConfig; } - - Task IHostedService.StartAsync(CancellationToken cancellationToken) { - try { - String server = Config?.GetValue("RemoteHost") ?? "localhost"; - int port = Config?.GetValue("RemotePort") ?? 9000; - - // Instanziere ein gültiges Socket Objekt mit den übergebenen Argumenten - sock = ConnectSocket(server, port); - - //_serialPort.ReadTimeout = 10; - Screen.WriteLine("Remote port " + server + ":" + port + " connected"); - Continue = true; + // TODO: refactor .... + public void SetScreen(IConOutput scr) { + Screen = scr; + } - // Receiver = Task.Run(() => Read()); - sock.BeginReceive(buffer, 0, 5, 0, new AsyncCallback(ReceiveCallback), sock); - + Task IHostedService.StartAsync(CancellationToken cancellationToken) { + try { + BeginnConnect(HostName, Port); } catch (Exception ex) { Continue = false; - //ConsoleColor csave = Screen.TextColor; - //Screen.TextColor = ConsoleColor.Red; - Screen.WriteLine("Error starting '" + Config?.GetValue("ComName")??"->COM1" + "' !", ConsoleColor.Red); + Screen.WriteLine("Error starting '" + IfName + "' !", ConsoleColor.Red); Screen.WriteLine(ex.Message, ConsoleColor.Red); - //Screen.TextColor = csave; } return Task.CompletedTask; } - byte[] buffer = new byte[256]; + // Searches for all Host Endpoints and initates Connect to (all of) them. + private List BeginnConnect(string server, int port) { + List retVal = new List (); - // Initialisiert die Socketverbindung und gibt diese zurück - private static Socket ConnectSocket(string server, int port) { - Socket sock = null; IPHostEntry hostEntry = null; - hostEntry = Dns.GetHostEntry(server); // Nutze die Eigenschaft AddressFamily von IPEndPoint um Konflikte zwischen @@ -81,93 +84,68 @@ private static Socket ConnectSocket(string server, int port) { Socket tempSocket = new Socket(ipo.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + retVal.Add(tempSocket.BeginConnect(ipo, ConnectCallback, tempSocket)); + } + return retVal; + } - tempSocket.Connect(ipo); - - if (tempSocket.Connected) { - sock = tempSocket; - break; + private void ConnectCallback(IAsyncResult ar) { + try { + // Retrieve the socket from the state object. + if (socket == null) { // First one wins, all other are ignored..... + socket = (Socket)ar.AsyncState; + // Complete the connection. + socket.EndConnect(ar); + Screen.WriteLine("Remote port " + HostName + ":" + Port + " connected"); + + Continue = true; + // Start Receiving data by entering a data Buffer and a callback. + socket.BeginReceive(Buffer, 0, BufferSize, 0, new AsyncCallback(ReceiveCallback), null); } else { - continue; + Screen.WriteLine("Duplicate connection to host !!!"); + // If more than one addresss was tried. gracefully close others. (to be tested somehow ;-) .....) + ((Socket)ar.AsyncState)?.EndConnect(ar); + ((Socket)ar.AsyncState)?.Disconnect(false); } + } catch (Exception ex) { + socket = null; + Continue = false; + Screen.WriteLine("Error connecting " + IfName + " to " + HostName + ":" + Port + " !", ConsoleColor.Red); + Screen.WriteLine(ex.Message, ConsoleColor.Red); } - return sock; } - private void ReceiveCallback(IAsyncResult ar) { try { - // Retrieve the state object and the client socket - // from the asynchronous state object. - Socket client = (Socket)(ar.AsyncState); - - // Read data from the remote device. - int bytesRead = client.EndReceive(ar); - + int bytesRead = socket.EndReceive(ar); if (bytesRead > 0) { - // Encoding is ISO Layer 6 !!!??? - // How to solve this here for chunked Buffer reads !? - //String received = Encoding.UTF8.GetString(buffer, 0, bytesRead); - - //for (int i=0; i rrestart receive again.... - // - Screen.Write("."); - client.BeginReceive(buffer, 0, 5, 0, - new AsyncCallback(ReceiveCallback), client); + // Nothing left -> This gets called only if socket was closed. Either when Disconnect() was called locally or remote connection closed. + Screen.WriteLine("Socket to remote port " + HostName + ":" + Port + " closed."); + Continue = false; + socket?.Disconnect(false); + socket = null; } } catch (Exception e) { - Console.WriteLine(e.ToString()); + Screen.WriteLine("Exception in ReceiverCallback: " + e.ToString(), ConsoleColor.Red); } } - //public void Read() { - // int idx = 0; - // byte[] buffer = new byte[256]; - - // while (Continue) { - // int rec = sock.Receive(buffer, 1, SocketFlags.None); - // if (rec == 1) { - // char ch = (char)buffer[0]; - // if (ch.ToString().Equals(NewLine)) { - // Screen.WriteLine(""); - // } else { - // Screen.Write(ch.ToString()); - // } - // } - // } - - //} - - - - async Task IHostedService.StopAsync(CancellationToken cancellationToken) { - // terminate the reader Task. - Continue = false; - if(Receiver != null) { - //await Receiver; // reader Task will be finished and execution "awaits it" and continues afterwards. (Without blocking any thread here) - sock.Close(); - Screen.WriteLine("Socket closed."); + if(socket != null) { + if (socket.Connected) { + await socket.DisconnectAsync(false, cancellationToken); + } } } void ITtyService.SendUart(string line) { - if(Continue) { - line += '\n'; - sock.Send(Encoding.UTF8.GetBytes(line)); + if(IsConnected()) { + line += NewLine; + socket.Send(Encoding.UTF8.GetBytes(line)); } } @@ -175,9 +153,5 @@ public bool IsConnected() { return Continue; } - public void SetScreen(IConOutput scr) { - Screen = scr; - } - } } diff --git a/StatusConsole/ObcEm2Uarts.cs b/StatusConsole/ServiceCollection.cs similarity index 85% rename from StatusConsole/ObcEm2Uarts.cs rename to StatusConsole/ServiceCollection.cs index 4df2223..50ecd34 100644 --- a/StatusConsole/ObcEm2Uarts.cs +++ b/StatusConsole/ServiceCollection.cs @@ -6,13 +6,15 @@ using System.Threading.Tasks; namespace StatusConsole { - public class ObcEm2Uarts : IConfigurableServices { + public class ServiceCollection : IConfigurableServices { private Dictionary services = new Dictionary(); private List keys = new List(); private ITtyService currentService = null; - public ObcEm2Uarts(IConfiguration conf) { + // This gets called by IOC Container and allows to read the Configuration (from appsettings.json) + // Here we Instanciate all tty Services ("UARTS") configured + public ServiceCollection(IConfiguration conf) { var uartConfigs = conf?.GetSection("UARTS").GetChildren(); foreach(var uc in uartConfigs) { var type = Type.GetType(uc.GetValue("Impl")??"dummy"); diff --git a/StatusConsole/StatusConsole.csproj b/StatusConsole/StatusConsole.csproj index 4c11ec1..ceb67bb 100644 --- a/StatusConsole/StatusConsole.csproj +++ b/StatusConsole/StatusConsole.csproj @@ -19,6 +19,12 @@ + + + PreserveNewest + + + Exe net6.0 diff --git a/StatusConsole/StatusConsoleMainView.cs b/StatusConsole/StatusConsoleMainView.cs index 2637524..0bc2298 100644 --- a/StatusConsole/StatusConsoleMainView.cs +++ b/StatusConsole/StatusConsoleMainView.cs @@ -15,27 +15,13 @@ public StatusConsoleMainView(int x, int y, HostedCmdlineApp model) : base(x, y, } private int selIdx = -1; - public override void HandleConsoleInput(Screen logScreen, String debugOption, int sleep) { + public override void HandleConsoleInput(Screen logScreen) { String line = ""; - while(line != "quit") { - ConsoleKeyInfo? k = null; - if (debugOption.Equals("A")) { - k = Console.ReadKey(true); - } else if (debugOption.Equals("B")) { - k = Console.ReadKey(false); - } else if (debugOption.Equals("C")) { - if (Console.KeyAvailable) { - k = Console.ReadKey(false); - } else { - Thread.Sleep(sleep); - } - } else { - while (!Console.KeyAvailable) { - Thread.Sleep(sleep); - } - k = Console.ReadKey(false); + while (line != "quit") { + while (!Console.KeyAvailable) { + Thread.Sleep(100); } - + ConsoleKeyInfo? k = Console.ReadKey(false); if (k != null) { if (k?.Key == ConsoleKey.Tab) { @@ -84,7 +70,7 @@ public override void HandleConsoleInput(Screen logScreen, String debugOption, in } logScreen.WriteLine("Input Handler closed!"); Model.StopAsync(new System.Threading.CancellationToken()).Wait(); - Environment.Exit(-22); + Environment.Exit(-22); } private void ClearInputLine(string line) { diff --git a/StatusConsole/UartCli.cs b/StatusConsole/UartCli.cs index 8f65bc6..631f4f3 100644 --- a/StatusConsole/UartCli.cs +++ b/StatusConsole/UartCli.cs @@ -46,11 +46,8 @@ Task IHostedService.StartAsync(CancellationToken cancellationToken) { Receiver = Task.Run(() => Read()); } catch (Exception ex) { Continue = false; - //ConsoleColor csave = Screen.TextColor; - //Screen.TextColor = ConsoleColor.Red; Screen.WriteLine("Error starting '" + Config?.GetValue("ComName")??"->COM1" + "' !", ConsoleColor.Red); Screen.WriteLine(ex.Message, ConsoleColor.Red); - //Screen.TextColor = csave; } return Task.CompletedTask; } diff --git a/StatusConsole/appsettings.json b/StatusConsole/appsettings.json index a1d73c8..76af8f9 100644 --- a/StatusConsole/appsettings.json +++ b/StatusConsole/appsettings.json @@ -1,6 +1,6 @@ { "UARTS": { - "DEBUG": { + "DEBUG-31": { "Impl": "StatusConsole.UartCli", "ComName": "COM31", "Baud": "19200", @@ -14,12 +14,11 @@ "Background": "Cyan" } }, - "COM_A": { + "COM_Remote_9801": { + "Impl": "StatusConsole.RemoteCli", "RemoteHost": "mymint.rkos", "RemotePort": 9801, - "ComName": "COM31", - "Baud": "19200", - "Impl": "StatusConsole.RemoteCli", + //"NewLine": "\n", "Screen": { "Width": 100, "Height": 20, @@ -40,7 +39,5 @@ // "Pos_Y": 0 // } //} - }, - "Option": "D", - "Sleep": 100 + } } \ No newline at end of file diff --git a/StatusConsole/scripts/ComToPort.sh b/StatusConsole/scripts/ComToPort.sh new file mode 100644 index 0000000..b80caf8 --- /dev/null +++ b/StatusConsole/scripts/ComToPort.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# With this script you can redirect a local USB Com device (e.g. FTDI USB_RS232 Converter) +# to be conectable via a remote host:port socket connection. + +if [[ -z $1 ]]; then + CTP_DEVICE='/dev/ttyUSB0' +else + CTP_DEVICE=$1 +fi + +if [[ -z $2 ]]; then + CTP_PORT='9801' +else + CTP_PORT=$2 +fi + +# +# Lets make this standard tty (as it is initialized after USB plugin) +# usable as simple COM port - writing and reading from UART rx/tx without any 'linediscipline' +# applied by the tty device (e.g. local echo on RX gives endless loop if RX/TX is hardware loopbacked.....) +# +stty -F $CTP_DEVICE -isig -icanon -echo -echoe -echok +stty -F $CTP_DEVICE + +while :; do +echo redirecting $CTP_DEVICE to port: $CTP_PORT +nc -l $CTP_PORT > $CTP_DEVICE < $CTP_DEVICE ; +echo port closed ... reopening ... +done diff --git a/testtemp/HostedService.cs b/testtemp/HostedService.cs new file mode 100644 index 0000000..95c5430 --- /dev/null +++ b/testtemp/HostedService.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Hosting; + + +namespace testtemp { + internal class HostedService : IHostedService { + List lines = new List(); + + + public Task StartAsync(CancellationToken cancellationToken) { + String txt = "StartAsyncAdded in " + GetContext(); + lines.Add(txt); + Console.WriteLine(txt); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) { + String txt = "StopAsyncAdded in " + GetContext(); + lines.Add(txt); + Console.WriteLine(txt); + return Task.CompletedTask; + } + + + private String GetContext() { + String retVal = "TaskId:'" + (Task.CurrentId == null ? "" : Task.CurrentId) + "'" ; + retVal += " Thread[" + Thread.CurrentThread.ManagedThreadId + "]: '" + Thread.CurrentThread.Name??"" + "'"; + retVal += " Proz: " + Thread.GetCurrentProcessorId(); + retVal += " SyncContext: " + (SynchronizationContext.Current?.ToString() ?? ""); + retVal += " TaskScheduler: " + (TaskScheduler.Default?.Id ?? -42); + return retVal; + } + } +} \ No newline at end of file diff --git a/testtemp/Program.cs b/testtemp/Program.cs new file mode 100644 index 0000000..68d8329 --- /dev/null +++ b/testtemp/Program.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; + + +namespace testtemp { + + class Program { + public async static Task Main(string[] args) { + var host = CreateHostBuilder(args); + await host.RunConsoleAsync(); + // This lines are not reached if Environment.Exit() is used somewhere in Services..... + Console.WriteLine("Exit Code in Main[]:" + Environment.ExitCode); + return Environment.ExitCode; + } + + private static IHostBuilder CreateHostBuilder(string[] args) { + return Host.CreateDefaultBuilder() + .ConfigureServices(services => { + services.AddHostedService(); + }); + } + + } +} \ No newline at end of file diff --git a/testtemp/testtemp.csproj b/testtemp/testtemp.csproj new file mode 100644 index 0000000..a31c16c --- /dev/null +++ b/testtemp/testtemp.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + +