reactos/modules/rostests/rosautotest/CPipe.cpp

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);
}