Связь по именованному каналу Windows между службой Windows C# .NET и клиентом C++ зависаетC++

Программы на C++. Форум разработчиков
Ответить Пред. темаСлед. тема
Anonymous
 Связь по именованному каналу Windows между службой Windows C# .NET и клиентом C++ зависает

Сообщение Anonymous »

Мне нужна помощь с именованными каналами Windows.
  • Цель: запустить автоматические установщики .msi из приложения C++, работающего БЕЗ прав администратора p>
  • Идея: объединить .msi с другим продуктом в качестве демона установщика, работающего в фоновом режиме с необходимыми разрешениями

    Написал службу Windows C# .NET, которая открывает NamedPipeServerStream для ожидания запроса

    < li>Служба вернет клиенту ответное сообщение в виде строки
  • Клиент C++ использует CreateFileA() для привязывается к каналу и отправляет строковый запрос
  • Как убедиться, что канал не приостанавливает выполнение?
  • Мой подход в целом приемлем?
  • Есть какие-нибудь советы?
Сервис C#:

Код: Выделить всё

using System;
using System.IO.Pipes;
using System.IO;
using System.Threading.Tasks;

public interface ICommandService
{
string ExecuteCommand(string json);

string CommandAddress { get; }
}

public class ServiceCommunicationPipe
{
private static ICommandService _service;
private static readonly Logger _log = Logger.Instance;
private NamedPipeServerStream _pipe;

public ServiceCommunicationPipe(ICommandService service)
{
_service = service;
}

public void Start()
{
string pipeAddress = $"InstallerService\\{_service.CommandAddress}";
_pipe = new NamedPipeServerStream(pipeAddress, PipeDirection.InOut, 1, PipeTransmissionMode.Message);

Task.Run(() =>
{
while (true)
{
_log.Log($"Waiting for client connection on {pipeAddress}...");
_pipe.WaitForConnection();
_log.Log("Client connected.");

try
{
StreamReader reader = new StreamReader(_pipe);
string request = reader.ReadToEnd();

string response = _service.ExecuteCommand(request);

StreamWriter writer = new StreamWriter(_pipe);
writer.Write(response);
writer.Flush();
_pipe.WaitForPipeDrain();
}
catch (Exception e)
{
_log.Log($"Error during pipe stream: {e.Message}");
}
finally
{
_pipe.Disconnect();
}
}
});
}

public void Stop()
{
_pipe?.Close();
_log.Log("Pipe server stopped.");
}
}
Клиент C++:

Код: Выделить всё

#include "windows_service_calling.h"
#include 

#define BUFSIZE 512

std::string GetLastErrorAsString()
{
DWORD errorMessageID = ::GetLastError();
return std::to_string(errorMessageID);
}

std::string AsJsonString(const std::string& action, const std::string& path) {
std::string jsonString = "{ \"action\": \"" + action + "\", \"path\": \"" + path + "\" }";
return jsonString;
}

// The C++ application opens the named pipe using CreateFileA().
// The C++ application writes the request to the named pipe using WriteFile().  The request includes the method name (ExecuteCommand) and the method parameters (action and path).
// The C# service reads the request from the named pipe, executes the ExecuteCommand method with the provided parameters, and writes the response to the named pipe.
// The C++ application reads the response from the named pipe using ReadFile().
std::string ExecuteActionAtWindowsService(std::string action, std::string path)
{
HANDLE hPipe;
char  chBuf[BUFSIZE];
BOOL   fSuccess = FALSE;
DWORD  cbRead, cbToWrite, cbWritten, dwMode;
std::string pipeName = "\\\\.\\pipe\\InstallerService\\executeInstaller";
std::string response = "";

hPipe = CreateFileA(
pipeName.c_str(),   // pipe name
GENERIC_READ |  // read and write access
GENERIC_WRITE,
0,              // no sharing
NULL,           // default security attributes
OPEN_EXISTING,  // opens existing pipe
0,              // default attributes
NULL);          // no template file

if (hPipe == INVALID_HANDLE_VALUE)
{
return "Failure: Error occurred while connecting to the service via CreateFileA: " + GetLastErrorAsString();
}

dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe,    // pipe handle
&dwMode,  // new pipe mode
NULL,     // don't set maximum bytes
NULL);    // don't set maximum time

if (!fSuccess)
{
return "Failure: Error occurred while SetNamedPipeHandleState: " + GetLastErrorAsString();
}

std::string json = AsJsonString(action, path);

size_t length = (json.length() + 1) * sizeof(char);
if (length > MAXDWORD) {
return "Failure: json too long for WriteFile function";
}
cbToWrite = static_cast(length);

fSuccess = WriteFile(
hPipe,                  // pipe handle
json.c_str(),        // message
cbToWrite,              // message length
&cbWritten,             // bytes written
NULL);

if (!fSuccess)
{
return "Error occurred while writing to the server: " + GetLastErrorAsString();
}

FlushFileBuffers(hPipe); // Ensure all data is written to the pipe

do
{
fSuccess = ReadFile(
hPipe,    // pipe handle
chBuf,    // buffer to receive reply
BUFSIZE*sizeof(char),  // size of buffer
&cbRead,  // number of bytes read
NULL);    // not overlapped

if (!fSuccess && GetLastError() != ERROR_MORE_DATA)
break;

response = std::string(chBuf, cbRead);  // Convert char array to string

} while (!fSuccess);

if (!fSuccess)
{
return "Error occurred while reading from the server: " + GetLastErrorAsString();
}

CloseHandle(hPipe);

return response;
}
Ожидается:
  • Служба выполняет команду и отвечает клиенту через именованный канал.
  • Клиент можно использовать во время выполнения
Ошибка:
  • < li>Клиент зависает
  • Служба регистрирует «Клиент подключен».
  • При уничтожении клиента служба регистрирует правильный json< /strong>
  • При завершении службы (в отдельном тесте) клиент регистрирует «Произошла ошибка при чтении с сервера: 109» (разрыв канала)
Анализ:
  • Вероятно, сервис зависает в строке request = reader.ReadToEnd();
  • Возможно, клиент зависает в ReadFile()
  • Я новичок в обоих языках и не уверен, что все методы , например _pipe.WaitForPipeDrain(); или FlushFileBuffers(hPipe); необходимы
Другие тесты:
Я пробовал вместо этого используйте WCF на стороне C#:

Код: Выделить всё

    [ServiceContract]
public interface ICommandService
{
[OperationContract]
string ExecuteCommand(string json);

string CommandAddress { get;  }
}

public class ServiceCommunicationPipe
{
private static readonly Uri ServiceUri = new Uri("net.pipe://localhost/InstallerService");
private static ICommandService _service;
private static ServiceHost _host = null;
private static ServiceCommunicationPipe _instance;
private static Timer _timer;
private static readonly Logger _log = Logger.Instance;

public static ServiceCommunicationPipe Instance
{
get
{
if (_instance == null)
{
throw new Exception("Instance not initialized. Call Initialize() first.");
}
return _instance;
}
}

private ServiceCommunicationPipe(ICommandService service)
{
_service = service;
}

public static void Initialize(ICommandService service)
{
if (_instance != null)
{
throw new Exception("Instance already initialized.");
}
_instance = new ServiceCommunicationPipe(service);
}

public void Start()
{
_host = new ServiceHost(_service, ServiceUri);
_host.AddServiceEndpoint(typeof(ICommandService), new NetNamedPipeBinding(), _service.CommandAddress);
_host.Open();
}

public void Stop()
{
if ((_host != null) && (_host.State != CommunicationState.Closed))
{
_host.Close();
_host = null;
}
}
}

  • С помощью WCF мне не удалось подключить каналы от C++ к C#.
    Пробовал разные адреса с обеих сторон< /li>


Подробнее здесь: https://stackoverflow.com/questions/785 ... e-and-c-cl
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C++»