mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
238 lines
6.8 KiB
C++
238 lines
6.8 KiB
C++
/*
|
|
* PROJECT: ReactOS Automatic Testing Utility
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Class that manages an unidirectional anonymous byte stream pipe
|
|
* COPYRIGHT: Copyright 2015 Thomas Faber (thomas.faber@reactos.org)
|
|
* Copyright 2019 Colin Finck (colin@reactos.org)
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
LONG CPipe::m_lPipeCount = 0;
|
|
|
|
|
|
/**
|
|
* Constructs a CPipe object and initializes read and write handles.
|
|
*/
|
|
CPipe::CPipe()
|
|
{
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
|
|
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
|
SecurityAttributes.bInheritHandle = TRUE;
|
|
SecurityAttributes.lpSecurityDescriptor = NULL;
|
|
|
|
// Construct a unique pipe name.
|
|
WCHAR wszPipeName[MAX_PATH];
|
|
InterlockedIncrement(&m_lPipeCount);
|
|
swprintf(wszPipeName, L"\\\\.\\pipe\\TestmanPipe%ld", m_lPipeCount);
|
|
|
|
// Create a named pipe with the default settings, but overlapped (asynchronous) operations.
|
|
// Latter feature is why we can't simply use CreatePipe.
|
|
const DWORD dwDefaultBufferSize = 4096;
|
|
const DWORD dwDefaultTimeoutMilliseconds = 120000;
|
|
|
|
m_hReadPipe = CreateNamedPipeW(wszPipeName,
|
|
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
|
1,
|
|
dwDefaultBufferSize,
|
|
dwDefaultBufferSize,
|
|
dwDefaultTimeoutMilliseconds,
|
|
&SecurityAttributes);
|
|
if (m_hReadPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
FATAL("CreateNamedPipe failed\n");
|
|
}
|
|
|
|
// Use CreateFileW to get the write handle to the pipe.
|
|
// Writing is done synchronously, so no FILE_FLAG_OVERLAPPED here!
|
|
m_hWritePipe = CreateFileW(wszPipeName,
|
|
GENERIC_WRITE,
|
|
0,
|
|
&SecurityAttributes,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (m_hWritePipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
FATAL("CreateFileW failed\n");
|
|
}
|
|
|
|
// Prepare the OVERLAPPED structure for reading.
|
|
ZeroMemory(&m_ReadOverlapped, sizeof(m_ReadOverlapped));
|
|
m_ReadOverlapped.hEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
|
|
if (!m_ReadOverlapped.hEvent)
|
|
{
|
|
FATAL("CreateEvent failed\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destructs a CPipe object and closes all open handles.
|
|
*/
|
|
CPipe::~CPipe()
|
|
{
|
|
if (m_hReadPipe)
|
|
CloseHandle(m_hReadPipe);
|
|
if (m_hWritePipe)
|
|
CloseHandle(m_hWritePipe);
|
|
}
|
|
|
|
/**
|
|
* Closes a CPipe's read pipe, leaving the write pipe unchanged.
|
|
*/
|
|
void
|
|
CPipe::CloseReadPipe()
|
|
{
|
|
if (!m_hReadPipe)
|
|
FATAL("Trying to close already closed read pipe\n");
|
|
CloseHandle(m_hReadPipe);
|
|
m_hReadPipe = NULL;
|
|
}
|
|
|
|
/**
|
|
* Closes a CPipe's write pipe, leaving the read pipe unchanged.
|
|
*/
|
|
void
|
|
CPipe::CloseWritePipe()
|
|
{
|
|
if (!m_hWritePipe)
|
|
FATAL("Trying to close already closed write pipe\n");
|
|
CloseHandle(m_hWritePipe);
|
|
m_hWritePipe = NULL;
|
|
}
|
|
|
|
/**
|
|
* Reads data from a pipe without advancing the read offset and/or retrieves information about available data.
|
|
*
|
|
* This function must not be called after CloseReadPipe.
|
|
*
|
|
* @param Buffer
|
|
* An optional buffer to read pipe data into.
|
|
*
|
|
* @param BufferSize
|
|
* The size of the buffer specified in Buffer, or 0 if no read should be performed.
|
|
*
|
|
* @param BytesRead
|
|
* On return, the number of bytes actually read from the pipe into Buffer.
|
|
*
|
|
* @param TotalBytesAvailable
|
|
* On return, the total number of bytes available to read from the pipe.
|
|
*
|
|
* @return
|
|
* True on success, false on failure; call GetLastError for error information.
|
|
*
|
|
* @see PeekNamedPipe
|
|
*/
|
|
bool
|
|
CPipe::Peek(PVOID Buffer, DWORD BufferSize, PDWORD BytesRead, PDWORD TotalBytesAvailable)
|
|
{
|
|
if (!m_hReadPipe)
|
|
FATAL("Trying to peek from a closed read pipe\n");
|
|
|
|
return PeekNamedPipe(m_hReadPipe, Buffer, BufferSize, BytesRead, TotalBytesAvailable, NULL);
|
|
}
|
|
|
|
/**
|
|
* Reads data from the read pipe, advancing the read offset accordingly.
|
|
*
|
|
* This function must not be called after CloseReadPipe.
|
|
*
|
|
* @param Buffer
|
|
* Buffer to read pipe data into.
|
|
*
|
|
* @param NumberOfBytesToRead
|
|
* The number of bytes to read into Buffer.
|
|
*
|
|
* @param NumberOfBytesRead
|
|
* On return, the number of bytes actually read from the pipe into Buffer.
|
|
*
|
|
* @return
|
|
* Returns a Win32 error code. Expected error codes include:
|
|
* - ERROR_SUCCESS: The read has completed successfully.
|
|
* - WAIT_TIMEOUT: The given timeout has elapsed before any data was read.
|
|
* - ERROR_BROKEN_PIPE: The other end of the pipe has been closed.
|
|
*
|
|
* @see ReadFile
|
|
*/
|
|
DWORD
|
|
CPipe::Read(PVOID Buffer, DWORD NumberOfBytesToRead, PDWORD NumberOfBytesRead, DWORD TimeoutMilliseconds)
|
|
{
|
|
if (!m_hReadPipe)
|
|
{
|
|
FATAL("Trying to read from a closed read pipe\n");
|
|
}
|
|
|
|
if (ReadFile(m_hReadPipe, Buffer, NumberOfBytesToRead, NumberOfBytesRead, &m_ReadOverlapped))
|
|
{
|
|
// The asynchronous read request could be satisfied immediately.
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD dwLastError = GetLastError();
|
|
if (dwLastError == ERROR_IO_PENDING)
|
|
{
|
|
// The asynchronous read request could not be satisfied immediately, so wait for it with the given timeout.
|
|
DWORD dwWaitResult = WaitForSingleObject(m_ReadOverlapped.hEvent, TimeoutMilliseconds);
|
|
if (dwWaitResult == WAIT_OBJECT_0)
|
|
{
|
|
// Fill NumberOfBytesRead.
|
|
if (GetOverlappedResult(m_hReadPipe, &m_ReadOverlapped, NumberOfBytesRead, FALSE))
|
|
{
|
|
// We successfully read NumberOfBytesRead bytes.
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
dwLastError = GetLastError();
|
|
if (dwLastError == ERROR_BROKEN_PIPE)
|
|
{
|
|
// The other end of the pipe has been closed.
|
|
return ERROR_BROKEN_PIPE;
|
|
}
|
|
else
|
|
{
|
|
// An unexpected error.
|
|
FATAL("GetOverlappedResult failed\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This may be WAIT_TIMEOUT or an unexpected error.
|
|
return dwWaitResult;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This may be ERROR_BROKEN_PIPE or an unexpected error.
|
|
return dwLastError;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes data to the write pipe.
|
|
*
|
|
* This function must not be called after CloseWritePipe.
|
|
*
|
|
* @param Buffer
|
|
* Buffer containing the data to write.
|
|
*
|
|
* @param NumberOfBytesToWrite
|
|
* The number of bytes to write to the pipe from Buffer.
|
|
*
|
|
* @param NumberOfBytesWritten
|
|
* On return, the number of bytes actually written to the pipe.
|
|
*
|
|
* @return
|
|
* True on success, false on failure; call GetLastError for error information.
|
|
*
|
|
* @see WriteFile
|
|
*/
|
|
bool
|
|
CPipe::Write(LPCVOID Buffer, DWORD NumberOfBytesToWrite, PDWORD NumberOfBytesWritten)
|
|
{
|
|
if (!m_hWritePipe)
|
|
FATAL("Trying to write to a closed write pipe\n");
|
|
|
|
return WriteFile(m_hWritePipe, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten, NULL);
|
|
}
|