#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#:
Мне нужна помощь с именованными каналами Windows. [list] [*]Цель: запустить автоматические установщики .msi из приложения C++, работающего БЕЗ прав администратора p>
[*]Идея: объединить .msi с другим продуктом в качестве демона установщика, работающего в фоновом режиме с необходимыми разрешениями
Написал службу Windows C# .NET, которая открывает NamedPipeServerStream для ожидания запроса
< li>Служба вернет клиенту ответное сообщение в виде строки
[*]Клиент C++ использует CreateFileA() для привязывается к каналу и отправляет строковый запрос
[/list] [list] [*]Как убедиться, что канал не приостанавливает выполнение? [*]Мой подход в целом приемлем? [*]Есть какие-нибудь советы? [/list]Сервис C#: [code]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.");
// 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);
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; } [/code] Ожидается: [list] [*]Служба выполняет команду и отвечает клиенту через именованный канал.[*]Клиент можно использовать во время выполнения [/list] Ошибка: [list] < li>Клиент зависает [*]Служба регистрирует «Клиент подключен». [*]При уничтожении клиента служба регистрирует правильный json< /strong> [*]При завершении службы (в отдельном тесте) клиент регистрирует «Произошла ошибка при чтении с сервера: 109» (разрыв канала) [/list] Анализ: [list] [*]Вероятно, сервис зависает в строке request = reader.ReadToEnd(); [*]Возможно, клиент зависает в ReadFile() [*]Я новичок в обоих языках и не уверен, что все методы , например _pipe.WaitForPipeDrain(); или FlushFileBuffers(hPipe); необходимы [/list] Другие тесты: Я пробовал вместо этого используйте WCF на стороне C#: [code] [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; } }
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; } } }
[/code] [list] [*]С помощью WCF мне не удалось подключить каналы от C++ к C#. Пробовал разные адреса с обеих сторон< /li> [/list]
Я создал новый проект форм Windows в .NET 5.0. Дизайнер работал, но как только я добавляю ссылку на свой проект веб-API ASP.NET Core и пытаюсь загрузить дизайнер форм, я получаю сообщение «Время ожидания истекло при подключении к именованному...
Ошибка при запуске через имя входа Tomcat — Local Service
Не удалось запустить новый сеанс. Код ответа 500. Сообщение: сеанс не создан: Microsoft Edge не удалось запустить: произошел сбой. (сеанс не создан: файл DevToolsActivePort не существует)...
Это моя первая встреча с размерами вкуса. Я пытаюсь собрать готовую код, но каким-то образом я закончил с этой проблемой. >
Все ароматы теперь должны принадлежать к именованному ароматическому измерению. Узнайте...