fix
This commit is contained in:
commit
d6325ee129
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/.*/
|
||||
/bin/
|
||||
/obj/
|
||||
/Properties/
|
||||
/*.Development.json
|
||||
/*.csproj.user
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "Lib/winipc-ua"]
|
||||
path = Lib/winipc-ua
|
||||
url = https://dev.lirent.ru/lirent/winipc-ua.git
|
||||
7
Config/AppConfig.cs
Normal file
7
Config/AppConfig.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace WinIPC.Config
|
||||
{
|
||||
public class AppConfig
|
||||
{
|
||||
public IPCServiceOptions IPCService { get; set; } = new IPCServiceOptions();
|
||||
}
|
||||
}
|
||||
1
Lib/winipc-ua
Submodule
1
Lib/winipc-ua
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 4a8b51715c24f2fa0eb0313c56c6a565abe89806
|
||||
327
Program.cs
Normal file
327
Program.cs
Normal file
@ -0,0 +1,327 @@
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
//using WinIPC.Utils
|
||||
|
||||
using Google.Protobuf;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using WinIPC.Models; // Предполагается, что сгенерированные Protobuf-классы находятся здесь
|
||||
|
||||
namespace IpcCliClient
|
||||
{
|
||||
class Program
|
||||
{
|
||||
private static readonly string _pipeName = "Global\\ProcessMonitoringService.UserAgent.Pipe";
|
||||
private static uint _requestId = 0;
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Клиентское приложение для тестирования IPC.");
|
||||
Console.WriteLine("Введите команду (например, get_windows) или 'exit' для выхода.");
|
||||
Console.WriteLine("---------------------------------------------");
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.Write("> ");
|
||||
string command = Console.ReadLine()?.Trim() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(command))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (command.Equals("exit", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await ExecuteCommand(command);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Ошибка: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("Приложение завершено.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет команду, введенную пользователем.
|
||||
/// </summary>
|
||||
private static async Task ExecuteCommand(string command)
|
||||
{
|
||||
Request request = new Request();
|
||||
string responseMessage = string.Empty;
|
||||
bool handled = true;
|
||||
|
||||
switch (command.ToLowerInvariant())
|
||||
{
|
||||
case "get_windows":
|
||||
request.Id = ++_requestId;
|
||||
request.Type = CommandType.GetWindowsInfo;
|
||||
Console.WriteLine("Отправка запроса на получение списка окон...");
|
||||
responseMessage = "Список окон:";
|
||||
break;
|
||||
|
||||
// TODO: Добавить другие команды (get_screenshot, get_pixel_color, input_action)
|
||||
// как только будут определены их структуры запросов/ответов.
|
||||
|
||||
default:
|
||||
Console.WriteLine($"Неизвестная команда: '{command}'");
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
await SendAndReceive(request, responseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет запрос и получает ответ от сервера.
|
||||
/// </summary>
|
||||
private static async Task SendAndReceive(Request request, string successMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var pipeClient = new NamedPipeClientStream(".", _pipeName, PipeDirection.InOut))
|
||||
{
|
||||
Console.WriteLine("Подключение к IPC серверу...");
|
||||
await pipeClient.ConnectAsync(5000); // Таймаут 5 секунд
|
||||
Console.WriteLine("Подключено.");
|
||||
|
||||
// Сериализация и отправка запроса
|
||||
byte[] requestBytes = request.ToByteArray();
|
||||
await WriteMessageAsync(pipeClient, requestBytes, CancellationToken.None);
|
||||
|
||||
// Чтение ответа
|
||||
byte[] responseBytes = await ReadMessageAsync(pipeClient, CancellationToken.None);
|
||||
var response = Response.Parser.ParseFrom(responseBytes);
|
||||
|
||||
// Обработка ответа
|
||||
if (response.Success)
|
||||
{
|
||||
Console.WriteLine($"Успешно: {successMessage}");
|
||||
if (response.Type == CommandType.GetWindowsInfo && response.Payload != null)
|
||||
{
|
||||
var windowsResponse = WindowsResponse.Parser.ParseFrom(response.Payload);
|
||||
Console.WriteLine($"Найдено {windowsResponse.Data.Count} окон:");
|
||||
foreach (var window in windowsResponse.Data)
|
||||
{
|
||||
Console.WriteLine($" - HWND: {window.Hwnd}, PID: {window.Pid}, Title: '{window.Title}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Получен успешный ответ. Сообщение: {response.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Ошибка ответа: {response.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Console.WriteLine("Ошибка: Таймаут подключения к серверу.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Ошибка при взаимодействии с сервером: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Читает сообщение из pipe.
|
||||
/// </summary>
|
||||
private static async Task<byte[]> ReadMessageAsync(PipeStream pipe, CancellationToken cTok)
|
||||
{
|
||||
// Чтение префикса длины (4 байта)
|
||||
byte[] lengthBytes = new byte[4];
|
||||
int bytesRead = await pipe.ReadAsync(lengthBytes, 0, 4, cTok);
|
||||
if (bytesRead < 4)
|
||||
{
|
||||
throw new EndOfStreamException("Поток закрыт или достигнут конец потока при чтении длины сообщения.");
|
||||
}
|
||||
|
||||
int messageLength = BitConverter.ToInt32(lengthBytes, 0);
|
||||
|
||||
if (messageLength <= 0)
|
||||
{
|
||||
throw new InvalidDataException($"Получена некорректная длина сообщения: {messageLength}.");
|
||||
}
|
||||
|
||||
// Чтение самого сообщения
|
||||
byte[] messageBytes = new byte[messageLength];
|
||||
bytesRead = 0;
|
||||
while (bytesRead < messageLength)
|
||||
{
|
||||
int read = await pipe.ReadAsync(messageBytes, bytesRead, messageLength - bytesRead, cTok);
|
||||
if (read == 0)
|
||||
{
|
||||
throw new EndOfStreamException($"Поток закрыт или достигнут конец потока при чтении полезной нагрузки. Ожидалось {messageLength} байт, прочитано {bytesRead}.");
|
||||
}
|
||||
bytesRead += read;
|
||||
}
|
||||
|
||||
return messageBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Записывает сообщение в pipe.
|
||||
/// </summary>
|
||||
private static async Task WriteMessageAsync(PipeStream pipe, byte[] messageBytes, CancellationToken cTok)
|
||||
{
|
||||
byte[] lengthPrefix = BitConverter.GetBytes(messageBytes.Length);
|
||||
|
||||
await pipe.WriteAsync(lengthPrefix, 0, lengthPrefix.Length, cTok);
|
||||
await pipe.WriteAsync(messageBytes, 0, messageBytes.Length, cTok);
|
||||
await pipe.FlushAsync(cTok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public class Datagram
|
||||
{
|
||||
public IPCWindowInfo Info;
|
||||
public string Title { get; set; } = String.Empty;
|
||||
public int Hwnd { get => Info.Hwnd; }
|
||||
public uint Pid { get => Info.Pid; }
|
||||
public uint ThreadId { get => Info.ThreadId; }
|
||||
public bool IsActive { get => Info.IsActive; }
|
||||
public int CursorX { get => Info.CursorX; }
|
||||
public int CursorY { get => Info.CursorY; }
|
||||
public int ContentWidth { get => Info.ContentWidth; }
|
||||
public int ContentHeight { get => Info.ContentHeight; }
|
||||
public int WindowX { get => Info.WindowX; }
|
||||
public int WindowY { get => Info.WindowY; }
|
||||
public int WindowWidth { get => Info.WindowWidth; }
|
||||
public int WindowHeight { get => Info.WindowHeight; }
|
||||
|
||||
}
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("IPC Client started. Press any key to send a 'GetWindows' request...");
|
||||
Console.ReadKey();
|
||||
|
||||
await SendGetWindowsRequest();
|
||||
|
||||
Console.WriteLine("\nPress any key to exit.");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
private static async Task SendGetWindowsRequest()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var pipe = new NamedPipeClientStream(
|
||||
".", // Сервер находится на локальной машине
|
||||
IPC.PipeName,
|
||||
PipeDirection.InOut,
|
||||
PipeOptions.Asynchronous,
|
||||
TokenImpersonationLevel.None,
|
||||
HandleInheritability.None))
|
||||
{
|
||||
Console.WriteLine($"Connecting to pipe: {IPC.PipeName}...");
|
||||
await pipe.ConnectAsync(5000); // Таймаут 5 секунд
|
||||
Console.WriteLine("Connected to pipe!");
|
||||
|
||||
IPCPayload payload;
|
||||
|
||||
#if WINDOWS
|
||||
if (IPC.SerializeIPC(new IPCHeader { Type = IPCType.ListWindows }, 0, out var data) > 0)
|
||||
{
|
||||
Console.WriteLine($"Writing to pipe...");
|
||||
if (await IPC.WriteMessageAsync(pipe, data, CancellationToken.None))
|
||||
{
|
||||
Console.WriteLine($"Writing successful");
|
||||
Console.WriteLine($"Reading from pipe...");
|
||||
if ((payload = await IPC.ReadMessageAsync(pipe, CancellationToken.None)).Header.Type == (IPCType.Response | IPCType.ListWindows))
|
||||
{
|
||||
Console.WriteLine($"Reading successful");
|
||||
var s = (IEnumerable<IPCWindowInfoPayload>?)payload.Data;
|
||||
var d = new List<Datagram>();
|
||||
|
||||
if (s != null) {
|
||||
foreach (var p in s)
|
||||
{
|
||||
d.Add(new Datagram { Info = p.Info, Title = p.Title });
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Received response: {JsonSerializer.Serialize(d)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Not received! ID: {payload.Header.ID}, Code: {payload.Header.Code}, Type: {payload.Header.Type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if IMAGE
|
||||
if (IPC.SerializeIPC(new IPCHeader { Type = IPCType.GetScreenshot }, 0, out var d1) > 0 && IPC.SerializeIPC(new IPCScreenshotRequest { Hwnd = 66982 }, 0, out var d2) > 0)
|
||||
{
|
||||
byte[] data = new byte[d1.Length + d2.Length];
|
||||
|
||||
Buffer.BlockCopy(d1, 0, data, 0, d1.Length);
|
||||
Buffer.BlockCopy(d2, 0, data, d1.Length, d2.Length);
|
||||
|
||||
Console.WriteLine($"Writing to pipe...");
|
||||
if (await IPC.WriteMessageAsync(pipe, data, CancellationToken.None))
|
||||
{
|
||||
Console.WriteLine($"Writing successful");
|
||||
Console.WriteLine($"Reading from pipe...");
|
||||
if ((payload = await IPC.ReadMessageAsync(pipe, CancellationToken.None)).Header.Type == (IPCType.Response | IPCType.GetScreenshot))
|
||||
{
|
||||
Console.WriteLine($"Reading successful");
|
||||
var s = (IPCScreenshotPayload?)payload.Data;
|
||||
var d = new List<Datagram>();
|
||||
|
||||
if (s != null)
|
||||
{
|
||||
using (FileStream fs = new FileStream("C:\\Users\\lirent\\test.png", FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
await fs.WriteAsync(s.Value.Image, 0, s.Value.Image.Length);
|
||||
}
|
||||
Console.WriteLine("See C:\\Users\\lirent\\test.png");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Not received! ID: {payload.Header.ID}, Code: {payload.Header.Code}, Type: {payload.Header.Type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Console.WriteLine("Connection to pipe timed out. Ensure UserSessionAgent is running.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An error occurred: {ex.Message}");
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
267
Services/IPCClientService.cs
Normal file
267
Services/IPCClientService.cs
Normal file
@ -0,0 +1,267 @@
|
||||
/* This software is licensed by the MIT License, see LICENSE file */
|
||||
/* Copyright © 2024-2025 Gregory Lirent */
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipes;
|
||||
using WinIPC.Config;
|
||||
using WinIPC.Models;
|
||||
using WinIPC.Utils;
|
||||
|
||||
namespace WinIPC.Services
|
||||
{
|
||||
public class IPCClientService : IHostedService, IDisposable
|
||||
{
|
||||
private readonly ILogger<IPCClientService> _logger;
|
||||
private readonly IPCServiceOptions _options;
|
||||
private readonly string _pipeName;
|
||||
|
||||
private NamedPipeServerStream? _pipe;
|
||||
private CancellationTokenSource? _cTok;
|
||||
private Task? _task;
|
||||
|
||||
private Func<Request, Task<Response>>? _handler;
|
||||
|
||||
public IPCClientService(ILogger<IPCClientService> logger, IOptions<AppConfig> appConfigOptions)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_options = appConfigOptions?.Value?.IPCService ?? throw new ArgumentNullException(nameof(appConfigOptions));
|
||||
|
||||
int sid = Process.GetCurrentProcess().SessionId;
|
||||
if (sid == 0)
|
||||
{
|
||||
_logger.LogWarning("Server: Failed to get current Session ID (returned 0). Using base pipe name without Session ID suffix.");
|
||||
_pipeName = _options.BasePipeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pipeName = $"{_options.BasePipeName}.{sid}";
|
||||
_logger.LogInformation("Server: Appending Session ID {SessionId} to pipe name. Actual pipe name: {ActualPipeName}", sid, _pipeName);
|
||||
}
|
||||
|
||||
_logger.LogInformation("IPCServerService initialized. Actual pipe name: {ActualPipeName}", _pipeName);
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("IPCServerService is starting...");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("IPCServerService is stopping...");
|
||||
|
||||
_cTok?.Cancel();
|
||||
if (_task != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _task;
|
||||
}
|
||||
catch (OperationCanceledException) { /* Expected */ }
|
||||
catch (Exception ex) { _logger.LogError(ex, "Error while waiting for server listening task to complete."); }
|
||||
}
|
||||
|
||||
Dispose();
|
||||
_logger.LogInformation("IPCServerService stopped.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cTok?.Dispose();
|
||||
_pipe?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public Task StartListening(Func<Request, Task<Response>> handler, CancellationToken cTok)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
_cTok = CancellationTokenSource.CreateLinkedTokenSource(cTok);
|
||||
_task = Task.Run(() => ServerListenLoop(_cTok.Token), _cTok.Token);
|
||||
return _task;
|
||||
}
|
||||
|
||||
private async Task ServerListenLoop(CancellationToken cTok)
|
||||
{
|
||||
_logger.LogInformation($"Server: Starting listen loop on pipe '{_pipeName}'...");
|
||||
while (!cTok.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pipe = new NamedPipeServerStream(
|
||||
_pipeName,
|
||||
PipeDirection.InOut,
|
||||
NamedPipeServerStream.MaxAllowedServerInstances,
|
||||
PipeTransmissionMode.Byte,
|
||||
PipeOptions.Asynchronous);
|
||||
|
||||
_logger.LogInformation("Server: Waiting for client connection...");
|
||||
await _pipe.WaitForConnectionAsync(cTok);
|
||||
_logger.LogInformation("Server: Client connected.");
|
||||
|
||||
_ = HandleClientConnectionAsync(_pipe, cTok);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("Server: Listen loop cancelled.");
|
||||
_pipe?.Dispose();
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Server: Error in main listen loop. Disposing current pipe and continuing to next iteration.");
|
||||
_pipe?.Dispose();
|
||||
await Task.Delay(1000, cTok);
|
||||
}
|
||||
}
|
||||
_logger.LogInformation("Server: Listen loop finished.");
|
||||
}
|
||||
|
||||
private async Task HandleClientConnectionAsync(NamedPipeServerStream pipe, CancellationToken cTok)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation($"Server: Handling connection from pipe '{_pipeName}'...");
|
||||
|
||||
while (pipe.IsConnected && !cTok.IsCancellationRequested)
|
||||
{
|
||||
byte[]? messageBytes = null;
|
||||
try
|
||||
{
|
||||
messageBytes = await ReadMessageAsync(pipe, cTok);
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
_logger.LogInformation($"Server: Client disconnected from pipe '{_pipeName}'.");
|
||||
break;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation($"Server: Message reading cancelled for pipe '{_pipeName}'.");
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Server: Error reading message from pipe '{_pipeName}'.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (messageBytes == null || messageBytes.Length == 0)
|
||||
{
|
||||
_logger.LogDebug($"Server: Received empty or null message from pipe '{_pipeName}'. Continuing...");
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogDebug($"Server: Received {messageBytes.Length} bytes from pipe '{_pipeName}'.");
|
||||
|
||||
Response response = await ProcessIncomingRequest(messageBytes);
|
||||
|
||||
bool writeSuccess = await WriteMessageAsync(pipe, PayloadHandler.Serialize(response), cTok);
|
||||
if (!writeSuccess)
|
||||
{
|
||||
_logger.LogError($"Server: Failed to write response to pipe '{_pipeName}'.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Server: Unhandled exception in client connection handler for pipe '{_pipeName}'.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.LogInformation($"Server: Client connection handler finished for pipe '{_pipeName}'. Disposing pipe.");
|
||||
pipe.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> ProcessIncomingRequest(byte[] messageBytes)
|
||||
{
|
||||
Request request;
|
||||
try
|
||||
{
|
||||
request = PayloadHandler.Deserialize<Request>(messageBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Server: Failed to deserialize incoming message as Request. Returning error response.");
|
||||
return PayloadHandler.CreateErrorResponse(0, $"Failed to deserialize request: {ex.Message}");
|
||||
}
|
||||
|
||||
if (request.Id > 0 && request.Type != CommandType.UnknownCommandType)
|
||||
{
|
||||
_logger.LogInformation("Server: Received request ID {RequestId}, Type: {CommandType}.", request.Id, request.Type);
|
||||
|
||||
if (_handler != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _handler(request);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Server: Error processing request ID {request.Id}, Type {request.Type} by handler.");
|
||||
return PayloadHandler.CreateErrorResponse(request.Id, $"Handler error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Server: No request handler registered for IPCServerService.");
|
||||
return PayloadHandler.CreateErrorResponse(request.Id, "No handler registered on server.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Server: Received invalid Protobuf request (ID or Type missing). Raw bytes: {Bytes}", BitConverter.ToString(messageBytes));
|
||||
return PayloadHandler.CreateErrorResponse(request.Id, "Invalid request format.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<byte[]> ReadMessageAsync(PipeStream pipe, CancellationToken cTok)
|
||||
{
|
||||
byte[] lengthBuffer = new byte[4];
|
||||
int bytesRead;
|
||||
|
||||
bytesRead = await pipe.ReadAsync(lengthBuffer, 0, 4, cTok);
|
||||
if (bytesRead == 0) throw new EndOfStreamException("Pipe closed or no data for length prefix.");
|
||||
if (bytesRead < 4) throw new IOException($"Failed to read full 4-byte length prefix. Read {bytesRead} bytes.");
|
||||
|
||||
int messageLength = BitConverter.ToInt32(lengthBuffer, 0);
|
||||
if (messageLength <= 0) throw new InvalidDataException($"Received invalid message length: {messageLength}.");
|
||||
|
||||
byte[] messageBuffer = new byte[messageLength];
|
||||
int totalBytesRead = 0;
|
||||
while (totalBytesRead < messageLength)
|
||||
{
|
||||
bytesRead = await pipe.ReadAsync(messageBuffer, totalBytesRead, messageLength - totalBytesRead, cTok);
|
||||
if (bytesRead == 0) throw new EndOfStreamException($"Pipe closed unexpectedly while reading message payload. Expected {messageLength} bytes, read {totalBytesRead}.");
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
|
||||
return messageBuffer;
|
||||
}
|
||||
|
||||
private async Task<bool> WriteMessageAsync(PipeStream pipe, byte[] buffer, CancellationToken cTok)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] lengthPrefix = BitConverter.GetBytes(buffer.Length);
|
||||
await pipe.WriteAsync(lengthPrefix, 0, lengthPrefix.Length, cTok);
|
||||
await pipe.WriteAsync(buffer, 0, buffer.Length, cTok);
|
||||
await pipe.FlushAsync(cTok);
|
||||
return true;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("WriteMessageAsync: Operation cancelled.");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"WriteMessageAsync: Error writing to pipe. Buffer size: {buffer.Length} bytes.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
winipc-ua-test.csproj
Normal file
34
winipc-ua-test.csproj
Normal file
@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<RootNamespace>WinIPC</RootNamespace>
|
||||
<AssemblyName>UserAgentTest</AssemblyName>
|
||||
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<Version>0.1.0</Version>
|
||||
<Company>OpenSource</Company>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
|
||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.27.2" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.63.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Lib\winipc-ua\Models\ipc.proto" GrpcServices="None" ProtoRoot="." />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
25
winipc-ua-test.sln
Normal file
25
winipc-ua-test.sln
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36221.1 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winipc-ua-test", "winipc-ua-test.csproj", "{25044D91-C896-2DFB-8299-FBDF8F1D02EA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{25044D91-C896-2DFB-8299-FBDF8F1D02EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{25044D91-C896-2DFB-8299-FBDF8F1D02EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{25044D91-C896-2DFB-8299-FBDF8F1D02EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{25044D91-C896-2DFB-8299-FBDF8F1D02EA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7EC6842F-AE05-44E2-82E2-B13E5A19C4B5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Loading…
Reference in New Issue
Block a user