С# получить последнюю строчку консоли
Есть допустим вот такой вывод консоли. Это делается при помощи функции, и как мне получить то, что консоль мне возвратило последней строчкой? Не через какую либо функцию для получения данного вывода, а вот мы получили вывод, потом мы взяли и запихнули в переменную последнюю выведенную строку. Так можно вообще?
Вообще задание такое:
в первом процессе мы выводим строчку в консоль, запускаем второй процесс, и во втором процессе мы считываем строку, которая была записана первым процессом в консоль. Может я не правильно понял задание или просто мудрю с реализацией?
Ответы (2 шт):
Решение
Я покопался и придумал решение.
Абстракция
Мы будем брать и создавать процесс сервера с 1-м listener'ом. Listener запускается параллельно отн. потока сервера (чтоб можно было прервать работу listener'а). Затем мы запускаем клиент (если в обратном порядке, то клиент выдаст ошибку TimeoutException) и появляется соединение, затем сервер берёт, и передаёт сообщение.
Реализация
Будет 2 проекта: MessagePublisher и MessageReceiver (или сервер и клиент). Обмен будем производить с помощью библиотеки System.IO.Pipes.dll (ссылка #1,2) (включена в платформу .NET Core App), а именно именованные каналы (ссылка #3), т. к. процессы создаются независимо друг от друга и не получится передать дескриптор анонимного канала.
Примечание: пропущены полноценные обработки ошибок (причина: упрощение кода).
MessagePublisher
MessagePublisher.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>msgpub</AssemblyName>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
App.cs
using System;
using System.Text;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
using System.Buffers;
using System.Buffers.Binary;
namespace MessagePublisher
{
sealed class App
{
string? msg;
byte[]? serMsg;
public string Message
{
get
{
if (msg == null) throw new InvalidOperationException("Message is not readed");
return msg;
}
}
public byte[] SerializedMessage
{
get
{
if (msg == null) throw new InvalidOperationException("Message is not readed");
return serMsg;
}
}
App(ReadOnlySpan<string> args) { }
static void Main(string[] args) => new App(args.AsSpan()).Run();
void Run()
{
// Пропущена проверка на наличие такого же процесса
Console.WriteLine("Server is working...");
Console.WriteLine("Write message");
Console.Write("> ");
msg = Console.ReadLine();
serMsg = Encoding.Unicode.GetBytes(msg);
using var cts = new CancellationTokenSource();
var task = Listen(cts.Token);
Pause("Press any button to end server work...");
cts.Cancel();
task.Wait();
Console.WriteLine("Server resources is disposing...");
cts.Dispose();
Console.WriteLine("Server resources disposed");
Console.WriteLine("Server worked");
Pause();
}
async Task Listen(CancellationToken cancellationToken = default)
{
using var stream = new NamedPipeServerStream("msgpub/def", PipeDirection.Out);
using var bufferOwner = MemoryPool<byte>.Shared.Rent(4);
var buffer = bufferOwner.Memory.Slice(0, 4);
Console.WriteLine("Listener is working...");
while (!cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Listener is waiting for connection...");
await Task.Run((Action)(() => stream.WaitForConnectionAsync(cancellationToken)));
while (!cancellationToken.IsCancellationRequested && !stream.IsConnected) { }
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Cancellation...");
break;
}
Console.WriteLine("Connection getted");
Console.WriteLine("Message is sending...");
BinaryPrimitives.WriteInt32BigEndian(buffer.Span, serMsg!.Length);
stream.Write(buffer.Span);
stream.Write(serMsg);
Console.WriteLine("Message sended");
Console.WriteLine("Listener is disconnecting...");
if (stream.IsConnected) stream.Disconnect();
Console.WriteLine("Listener disconnected");
}
Console.WriteLine("Listener resources is disposing...");
stream.Close();
Console.WriteLine("Listener resources disposed");
Console.WriteLine("Listener worked");
}
void Pause(string? message = null)
{
const string defMsg = "Press any button...";
Console.WriteLine(message ?? defMsg);
Console.ReadKey(true);
}
void PrintQuitMessageAndExit(bool isFailed = true)
{
Pause();
Environment.Exit(isFailed ? -1 : 0);
}
}
}
MessageReceiver
MessageReceiver.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>msgrcv</AssemblyName>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
App.cs
using System;
using System.Text;
using System.IO;
using System.IO.Pipes;
using System.Diagnostics;
using System.Threading;
using System.Buffers;
using System.Buffers.Binary;
namespace MessageReceiver
{
class App
{
App(ReadOnlySpan<string> args) { }
static void Main(string[] args) => new App(args.AsSpan()).Run();
void Run()
{
Console.WriteLine("Client is working...");
var stream = new NamedPipeClientStream(".", "msgpub/def", PipeDirection.In);
Console.WriteLine("Client is connecting to server...");
try { stream.Connect(1000); }
catch (TimeoutException) { PrintErrorMessageAndExit("Connection timed out"); }
Console.WriteLine("Connection getted");
Console.WriteLine("Transmission started...");
Span<byte> encodedBufferLengthBuffer = stackalloc byte[4];
stream.Read(encodedBufferLengthBuffer);
var bufferLength = BinaryPrimitives.ReadInt32BigEndian(encodedBufferLengthBuffer);
using var bufferOwner = MemoryPool<byte>.Shared.Rent(bufferLength);
var buffer = bufferOwner.Memory.Span.Slice(0, bufferLength);
if (stream.Read(buffer)!= bufferLength)
PrintErrorMessageAndExit("Transmition failed");
Console.WriteLine("Transmission completed");
var msg = Encoding.Unicode.GetString(buffer);
Console.WriteLine($"Received message: {msg}");
Console.WriteLine("Client resources is disposing...");
stream.Close();
Console.WriteLine("Client resources disposed");
Console.WriteLine("Client worked");
Pause();
}
void Pause(string? message = null)
{
const string defMsg = "Press any button...";
Console.WriteLine(message ?? defMsg);
Console.ReadKey(true);
}
void PrintErrorMessageAndExit(string? message = null)
{
if (message != null)
{
var tempFc = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("[ERROR]: ");
Console.WriteLine(message);
Console.ForegroundColor = tempFc;
}
Pause();
Environment.Exit(-1);
}
}
}
Ссылки
App.cs (MessagePublisher.csproj)
using env = Environment;
sealed class App
{
string msg;
App(string[] args)
{
// mute compiler
msg = null!;
}
static void Main(string[] args) => new App(args).Run();
public void Run()
{
const string rcvExePath = "msgrcv.exe";
try
{
Console.WriteLine("Message publisher is working...");
Console.WriteLine("Write message");
Console.Write("> ");
msg = Console.ReadLine();
var encoding = Encoding.Unicode;
var serMsgLen = encoding.GetByteCount(msg);
var serMsgOwner = MemoryPool<byte>.Shared.Rent(serMsgLen);
var serMsg = serMsgOwner.Memory.Slice(0, serMsgLen).Span;
encoding.GetBytes(msg, serMsg);
Console.WriteLine("Message publisher resources is initializing...");
using var stream = new AnonymousPipeServerStream(PipeDirection.Out,
HandleInheritability.Inheritable); // чтобы можно было передать в дочерний процесс
Console.WriteLine("Message publisher resources is initialized");
Console.WriteLine("Message receiver process is launching...");
using var rcvProcess = new Process
{
StartInfo = new ProcessStartInfo(rcvExePath, stream.GetClientHandleAsString())
{
UseShellExecute = false,
}
};
rcvProcess.Start();
stream.DisposeLocalCopyOfClientHandle();
Console.WriteLine("Message receiver process is working...");
try
{
Console.WriteLine("Transmission started...");
Span<byte> encodedBufferLengthBuffer = stackalloc byte[4];
BinaryPrimitives.WriteInt32BigEndian(encodedBufferLengthBuffer, serMsgLen);
stream.Write(encodedBufferLengthBuffer);
stream.Write(serMsg);
stream.WaitForPipeDrain();
Console.WriteLine("Transmission completed");
}
catch (InvalidOperationException ex) when (ex.Message.Contains("connect", StringComparison.OrdinalIgnoreCase))
{ PrintQuitMessageAndExit("Transmission aborted"); return; }
Console.WriteLine("Message publisher is waiting for message receiver process exit");
rcvProcess.WaitForExit();
Console.WriteLine("Message receiver process worked");
Console.WriteLine("Message publisher resources is disposing...");
stream.Close();
serMsgOwner.Dispose();
rcvProcess.Close();
Console.WriteLine("Message publisher resources disposed");
Console.WriteLine("Message publisher worked");
}
catch (Exception ex) { PrintQuitMessageAndExit(ex.Message); }
Pause();
}
void Pause(string? message = null)
{
const string defMsg = "Press any button...";
Console.WriteLine(message ?? defMsg);
Console.ReadKey(true);
}
void PrintQuitMessageAndExit(string? message = null, bool isError = true)
{
const string unknowErrMsg = "<unknow>";
if (isError)
{
var tempFc = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("[ERROR]: ");
Console.WriteLine(message ?? unknowErrMsg);
Console.ForegroundColor = tempFc;
}
else if (message != null)
Console.WriteLine(message);
Pause();
env.Exit(isError ? -1 : 0);
}
}
App.cs (MessageReceiver.csproj)
using env = Environment;
sealed class App
{
public string StreamHandle { get; }
App(string[] args)
{
if (args.Length != 1) throw new ArgumentException("Invalid arg count", "args");
StreamHandle = args[0]; // получаем дескриптор нашего потока
}
static void Main(string[] args) => new App(args).Run();
public void Run()
{
try
{
Console.WriteLine("Message receiver is working...");
Console.WriteLine("Message receiver resources is initializing...");
using var stream = new AnonymousPipeClientStream(PipeDirection.In, StreamHandle);
Console.WriteLine("Message receiver resources initialized");
Console.WriteLine("Transmission started...");
string msg;
try
{
Span<byte> encodedBufferLengthBuffer = stackalloc byte[4];
if (stream.Read(encodedBufferLengthBuffer) != 4) PrintQuitMessageAndExit("Transmission failed");
var bufferLength = BinaryPrimitives.ReadInt32BigEndian(encodedBufferLengthBuffer);
using var bufferOwner = MemoryPool<byte>.Shared.Rent(bufferLength);
var buffer = bufferOwner.Memory.Slice(0, bufferLength).Span; // Memory.Slice эффективнее, чем Span.Slice (ссылка #1,2)
if (stream.Read(buffer) != bufferLength) PrintQuitMessageAndExit("Transmission failed");
Console.WriteLine("Transmission completed");
msg = Encoding.Unicode.GetString(buffer);
Console.Write("Message: ");
Console.WriteLine(msg);
Console.WriteLine("Message receiver resources is disposing...");
stream.Close();
bufferOwner.Dispose();
Console.WriteLine("Message receiver resources disposed");
Console.WriteLine("Message receiver worked");
}
catch (InvalidOperationException ex) when (ex.Message.Contains("connect", StringComparison.OrdinalIgnoreCase))
{ PrintQuitMessageAndExit("Transmission aborted"); }
}
catch (Exception ex) { PrintQuitMessageAndExit(ex.Message); };
Pause();
}
void Pause(string? message = null)
{
const string defMsg = "Press any button...";
Console.WriteLine(message ?? defMsg);
Console.ReadKey(true);
}
void PrintQuitMessageAndExit(string? message = null, bool isError = true)
{
const string unknowErrMsg = "<unknow>";
if (isError)
{
var tempFc = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("[ERROR]: ");
Console.WriteLine(message ?? unknowErrMsg);
Console.ForegroundColor = tempFc;
}
else if (message != null)
Console.WriteLine(message);
Pause();
env.Exit(isError ? -1 : 0);
}
}
