1 /* 2 * PROJECT: ReactOS Automatic Testing Utility 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Class that manages an unidirectional anonymous byte stream pipe 5 * COPYRIGHT: Copyright 2015 Thomas Faber (thomas.faber@reactos.org) 6 * Copyright 2019 Colin Finck (colin@reactos.org) 7 */ 8 9 #include "precomp.h" 10 11 LONG CPipe::m_lPipeCount = 0; 12 13 14 /** 15 * Constructs a CPipe object and initializes read and write handles. 16 */ 17 CPipe::CPipe() 18 { 19 SECURITY_ATTRIBUTES SecurityAttributes; 20 21 SecurityAttributes.nLength = sizeof(SecurityAttributes); 22 SecurityAttributes.bInheritHandle = TRUE; 23 SecurityAttributes.lpSecurityDescriptor = NULL; 24 25 // Construct a unique pipe name. 26 WCHAR wszPipeName[MAX_PATH]; 27 InterlockedIncrement(&m_lPipeCount); 28 swprintf(wszPipeName, L"\\\\.\\pipe\\TestmanPipe%ld", m_lPipeCount); 29 30 // Create a named pipe with the default settings, but overlapped (asynchronous) operations. 31 // Latter feature is why we can't simply use CreatePipe. 32 const DWORD dwDefaultBufferSize = 4096; 33 const DWORD dwDefaultTimeoutMilliseconds = 120000; 34 35 m_hReadPipe = CreateNamedPipeW(wszPipeName, 36 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, 37 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 38 1, 39 dwDefaultBufferSize, 40 dwDefaultBufferSize, 41 dwDefaultTimeoutMilliseconds, 42 &SecurityAttributes); 43 if (m_hReadPipe == INVALID_HANDLE_VALUE) 44 { 45 FATAL("CreateNamedPipe failed\n"); 46 } 47 48 // Use CreateFileW to get the write handle to the pipe. 49 // Writing is done synchronously, so no FILE_FLAG_OVERLAPPED here! 50 m_hWritePipe = CreateFileW(wszPipeName, 51 GENERIC_WRITE, 52 0, 53 &SecurityAttributes, 54 OPEN_EXISTING, 55 FILE_ATTRIBUTE_NORMAL, 56 NULL); 57 if (m_hWritePipe == INVALID_HANDLE_VALUE) 58 { 59 FATAL("CreateFileW failed\n"); 60 } 61 62 // Prepare the OVERLAPPED structure for reading. 63 ZeroMemory(&m_ReadOverlapped, sizeof(m_ReadOverlapped)); 64 m_ReadOverlapped.hEvent = CreateEventW(NULL, TRUE, TRUE, NULL); 65 if (!m_ReadOverlapped.hEvent) 66 { 67 FATAL("CreateEvent failed\n"); 68 } 69 } 70 71 /** 72 * Destructs a CPipe object and closes all open handles. 73 */ 74 CPipe::~CPipe() 75 { 76 if (m_hReadPipe) 77 CloseHandle(m_hReadPipe); 78 if (m_hWritePipe) 79 CloseHandle(m_hWritePipe); 80 } 81 82 /** 83 * Closes a CPipe's read pipe, leaving the write pipe unchanged. 84 */ 85 void 86 CPipe::CloseReadPipe() 87 { 88 if (!m_hReadPipe) 89 FATAL("Trying to close already closed read pipe\n"); 90 CloseHandle(m_hReadPipe); 91 m_hReadPipe = NULL; 92 } 93 94 /** 95 * Closes a CPipe's write pipe, leaving the read pipe unchanged. 96 */ 97 void 98 CPipe::CloseWritePipe() 99 { 100 if (!m_hWritePipe) 101 FATAL("Trying to close already closed write pipe\n"); 102 CloseHandle(m_hWritePipe); 103 m_hWritePipe = NULL; 104 } 105 106 /** 107 * Reads data from a pipe without advancing the read offset and/or retrieves information about available data. 108 * 109 * This function must not be called after CloseReadPipe. 110 * 111 * @param Buffer 112 * An optional buffer to read pipe data into. 113 * 114 * @param BufferSize 115 * The size of the buffer specified in Buffer, or 0 if no read should be performed. 116 * 117 * @param BytesRead 118 * On return, the number of bytes actually read from the pipe into Buffer. 119 * 120 * @param TotalBytesAvailable 121 * On return, the total number of bytes available to read from the pipe. 122 * 123 * @return 124 * True on success, false on failure; call GetLastError for error information. 125 * 126 * @see PeekNamedPipe 127 */ 128 bool 129 CPipe::Peek(PVOID Buffer, DWORD BufferSize, PDWORD BytesRead, PDWORD TotalBytesAvailable) 130 { 131 if (!m_hReadPipe) 132 FATAL("Trying to peek from a closed read pipe\n"); 133 134 return PeekNamedPipe(m_hReadPipe, Buffer, BufferSize, BytesRead, TotalBytesAvailable, NULL); 135 } 136 137 /** 138 * Reads data from the read pipe, advancing the read offset accordingly. 139 * 140 * This function must not be called after CloseReadPipe. 141 * 142 * @param Buffer 143 * Buffer to read pipe data into. 144 * 145 * @param NumberOfBytesToRead 146 * The number of bytes to read into Buffer. 147 * 148 * @param NumberOfBytesRead 149 * On return, the number of bytes actually read from the pipe into Buffer. 150 * 151 * @return 152 * Returns a Win32 error code. Expected error codes include: 153 * - ERROR_SUCCESS: The read has completed successfully. 154 * - WAIT_TIMEOUT: The given timeout has elapsed before any data was read. 155 * - ERROR_BROKEN_PIPE: The other end of the pipe has been closed. 156 * 157 * @see ReadFile 158 */ 159 DWORD 160 CPipe::Read(PVOID Buffer, DWORD NumberOfBytesToRead, PDWORD NumberOfBytesRead, DWORD TimeoutMilliseconds) 161 { 162 if (!m_hReadPipe) 163 { 164 FATAL("Trying to read from a closed read pipe\n"); 165 } 166 167 if (ReadFile(m_hReadPipe, Buffer, NumberOfBytesToRead, NumberOfBytesRead, &m_ReadOverlapped)) 168 { 169 // The asynchronous read request could be satisfied immediately. 170 return ERROR_SUCCESS; 171 } 172 173 DWORD dwLastError = GetLastError(); 174 if (dwLastError == ERROR_IO_PENDING) 175 { 176 // The asynchronous read request could not be satisfied immediately, so wait for it with the given timeout. 177 DWORD dwWaitResult = WaitForSingleObject(m_ReadOverlapped.hEvent, TimeoutMilliseconds); 178 if (dwWaitResult == WAIT_OBJECT_0) 179 { 180 // Fill NumberOfBytesRead. 181 if (GetOverlappedResult(m_hReadPipe, &m_ReadOverlapped, NumberOfBytesRead, FALSE)) 182 { 183 // We successfully read NumberOfBytesRead bytes. 184 return ERROR_SUCCESS; 185 } 186 187 dwLastError = GetLastError(); 188 if (dwLastError == ERROR_BROKEN_PIPE) 189 { 190 // The other end of the pipe has been closed. 191 return ERROR_BROKEN_PIPE; 192 } 193 else 194 { 195 // An unexpected error. 196 FATAL("GetOverlappedResult failed\n"); 197 } 198 } 199 else 200 { 201 // This may be WAIT_TIMEOUT or an unexpected error. 202 return dwWaitResult; 203 } 204 } 205 else 206 { 207 // This may be ERROR_BROKEN_PIPE or an unexpected error. 208 return dwLastError; 209 } 210 } 211 212 /** 213 * Writes data to the write pipe. 214 * 215 * This function must not be called after CloseWritePipe. 216 * 217 * @param Buffer 218 * Buffer containing the data to write. 219 * 220 * @param NumberOfBytesToWrite 221 * The number of bytes to write to the pipe from Buffer. 222 * 223 * @param NumberOfBytesWritten 224 * On return, the number of bytes actually written to the pipe. 225 * 226 * @return 227 * True on success, false on failure; call GetLastError for error information. 228 * 229 * @see WriteFile 230 */ 231 bool 232 CPipe::Write(LPCVOID Buffer, DWORD NumberOfBytesToWrite, PDWORD NumberOfBytesWritten) 233 { 234 if (!m_hWritePipe) 235 FATAL("Trying to write to a closed write pipe\n"); 236 237 return WriteFile(m_hWritePipe, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten, NULL); 238 } 239