7#include <qcoreapplication.h>
13using namespace Qt::StringLiterals;
18
19
20
21
22QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
24 handle(INVALID_HANDLE_VALUE),
25 eventHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
26 syncHandle(CreateEvent(NULL, TRUE, FALSE, NULL)),
29 actualReadBufferSize(0),
31 lastError(ERROR_SUCCESS),
33 readSequenceStarted(
false),
35 readyReadPending(
false),
36 winEventActPosted(
false)
38 ZeroMemory(&overlapped,
sizeof(OVERLAPPED));
39 overlapped.hEvent = eventHandle;
40 waitObject = CreateThreadpoolWait(waitCallback,
this, NULL);
41 if (waitObject == NULL)
42 qErrnoWarning(
"QWindowsPipeReader: CreateThreadpollWait failed.");
45QWindowsPipeReader::~QWindowsPipeReader()
51 WaitForThreadpoolWaitCallbacks(waitObject, FALSE);
52 CloseThreadpoolWait(waitObject);
53 CloseHandle(eventHandle);
54 CloseHandle(syncHandle);
58
59
60
61void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
64 actualReadBufferSize = 0;
65 readyReadPending =
false;
67 handle = hPipeReadEnd;
69 lastError = ERROR_SUCCESS;
73
74
75
76void QWindowsPipeReader::stop()
78 cancelAsyncRead(Stopped);
83
84
85
86void QWindowsPipeReader::drainAndStop()
88 cancelAsyncRead(Draining);
93
94
95
96void QWindowsPipeReader::stopAndClear()
98 cancelAsyncRead(Stopped);
100 actualReadBufferSize = 0;
104 lastError = ERROR_SUCCESS;
108
109
110void QWindowsPipeReader::cancelAsyncRead(State newState)
112 if (state != Running)
117 if (readSequenceStarted) {
121 if (!CancelIoEx(handle, &overlapped)) {
122 const DWORD dwError = GetLastError();
123 if (dwError != ERROR_NOT_FOUND) {
124 qErrnoWarning(dwError,
"QWindowsPipeReader: CancelIoEx on handle %p failed.",
132 waitForNotification();
134 }
while (readSequenceStarted);
145
146
147void QWindowsPipeReader::setMaxReadBufferSize(qint64 size)
149 QMutexLocker locker(&mutex);
150 readBufferMaxSize = size;
154
155
156
157bool QWindowsPipeReader::isReadOperationActive()
const
159 QMutexLocker locker(&mutex);
160 return readSequenceStarted || readyReadPending
161 || (lastError != ERROR_SUCCESS && !pipeBroken);
165
166
167qint64 QWindowsPipeReader::bytesAvailable()
const
169 return actualReadBufferSize;
173
174
175qint64 QWindowsPipeReader::read(
char *data, qint64 maxlen)
177 QMutexLocker locker(&mutex);
181 if (maxlen == 1 && actualReadBufferSize > 0) {
182 *data = readBuffer.getChar();
183 actualReadBufferSize--;
186 readSoFar = readBuffer.read(data, qMin(actualReadBufferSize, maxlen));
187 actualReadBufferSize -= readSoFar;
191 startAsyncReadHelper(&locker);
200
201
202
203
204qint64 QWindowsPipeReader::readLine(
char *data, qint64 maxlen)
206 QMutexLocker locker(&mutex);
207 qint64 readSoFar = 0;
209 if (actualReadBufferSize > 0) {
210 readSoFar = readBuffer.readLine(data, qMin(actualReadBufferSize + 1, maxlen));
211 actualReadBufferSize -= readSoFar;
215 startAsyncReadHelper(&locker);
224
225
226qint64 QWindowsPipeReader::skip(qint64 maxlen)
228 QMutexLocker locker(&mutex);
230 const qint64 skippedSoFar = readBuffer.skip(qMin(actualReadBufferSize, maxlen));
231 actualReadBufferSize -= skippedSoFar;
234 startAsyncReadHelper(&locker);
235 if (skippedSoFar == 0)
243
244
245bool QWindowsPipeReader::canReadLine()
const
247 QMutexLocker locker(&mutex);
248 return readBuffer.indexOf(
'\n', actualReadBufferSize) >= 0;
252
253
254void QWindowsPipeReader::startAsyncRead()
256 QMutexLocker locker(&mutex);
257 startAsyncReadHelper(&locker);
260void QWindowsPipeReader::startAsyncReadHelper(QMutexLocker<QMutex> *locker)
262 if (readSequenceStarted || lastError != ERROR_SUCCESS)
266 startAsyncReadLocked();
269 if (!readyReadPending && lastError == ERROR_SUCCESS)
272 if (!winEventActPosted) {
273 winEventActPosted =
true;
275 QCoreApplication::postEvent(
this,
new QEvent(QEvent::WinEventAct));
280 SetEvent(syncHandle);
284
285
286
287void QWindowsPipeReader::startAsyncReadLocked()
290 qint64 bytesToRead = qMax(checkPipeState(), state == Running ? minReadBufferSize : 0);
293 if (bytesToRead == 0)
296 while (lastError == ERROR_SUCCESS) {
297 if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
298 bytesToRead = readBufferMaxSize - readBuffer.size();
299 if (bytesToRead <= 0) {
306 char *ptr = readBuffer.reserve(bytesToRead);
311 DWORD numberOfBytesRead;
312 DWORD errorCode = ERROR_SUCCESS;
313 if (!ReadFile(handle, ptr, bytesToRead, &numberOfBytesRead, &overlapped)) {
314 errorCode = GetLastError();
315 if (errorCode == ERROR_IO_PENDING) {
316 Q_ASSERT(state == Running);
318 readSequenceStarted =
true;
319 SetThreadpoolWait(waitObject, eventHandle, NULL);
324 if (!readCompleted(errorCode, numberOfBytesRead))
329 if (state == Draining) {
330 Q_ASSERT(bytesToRead == qint64(numberOfBytesRead));
338 bytesToRead = qMax(checkPipeState(), minReadBufferSize);
343
344
345
346
347void QWindowsPipeReader::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
348 PTP_WAIT wait, TP_WAIT_RESULT waitResult)
352 Q_UNUSED(waitResult);
353 QWindowsPipeReader *pipeReader =
reinterpret_cast<QWindowsPipeReader *>(context);
356 DWORD numberOfBytesTransfered = 0;
357 DWORD errorCode = ERROR_SUCCESS;
358 if (!GetOverlappedResult(pipeReader->handle, &pipeReader->overlapped,
359 &numberOfBytesTransfered, FALSE))
360 errorCode = GetLastError();
362 pipeReader->mutex.lock();
364 pipeReader->readSequenceStarted =
false;
371 if (pipeReader->lastError == ERROR_SUCCESS && pipeReader->state != Stopped) {
374 if (pipeReader->state == Draining && errorCode == ERROR_OPERATION_ABORTED)
375 errorCode = ERROR_SUCCESS;
377 if (pipeReader->readCompleted(errorCode, numberOfBytesTransfered))
378 pipeReader->startAsyncReadLocked();
380 if (pipeReader->state == Running && !pipeReader->winEventActPosted) {
381 pipeReader->winEventActPosted =
true;
382 pipeReader->mutex.unlock();
383 QCoreApplication::postEvent(pipeReader,
new QEvent(QEvent::WinEventAct));
385 pipeReader->mutex.unlock();
388 pipeReader->mutex.unlock();
393 SetEvent(pipeReader->syncHandle);
397
398
399
400bool QWindowsPipeReader::readCompleted(DWORD errorCode, DWORD numberOfBytesRead)
405 if (errorCode == ERROR_SUCCESS || errorCode == ERROR_MORE_DATA) {
406 readyReadPending =
true;
407 pendingReadBytes += numberOfBytesRead;
408 readBuffer.truncate(actualReadBufferSize + pendingReadBytes);
412 lastError = errorCode;
417
418
419bool QWindowsPipeReader::event(QEvent *e)
421 if (e->type() == QEvent::WinEventAct) {
422 consumePendingAndEmit(
true);
425 return QObject::event(e);
429
430
431
432bool QWindowsPipeReader::consumePendingAndEmit(
bool allowWinActPosting)
434 ResetEvent(syncHandle);
438 if (allowWinActPosting)
439 winEventActPosted =
false;
441 const bool emitReadyRead = consumePending();
442 const DWORD dwError = lastError;
450 const bool emitPipeClosed = (dwError != ERROR_SUCCESS && !pipeBroken);
457 if (state != Running)
460 if (!emitPipeClosed) {
464 QPointer<QWindowsPipeReader> alive(
this);
467 if (alive && dwError != ERROR_BROKEN_PIPE && dwError != ERROR_PIPE_NOT_CONNECTED)
468 emit winError(dwError,
"QWindowsPipeReader::consumePendingAndEmit"_L1);
473 return emitReadyRead;
477
478
479
480bool QWindowsPipeReader::consumePending()
482 if (readyReadPending) {
483 readyReadPending =
false;
484 actualReadBufferSize += pendingReadBytes;
485 pendingReadBytes = 0;
493
494
495DWORD QWindowsPipeReader::checkPipeState()
498 if (PeekNamedPipe(handle,
nullptr, 0,
nullptr, &bytes,
nullptr))
501 lastError = GetLastError();
505bool QWindowsPipeReader::waitForNotification()
509 waitRet = WaitForSingleObjectEx(syncHandle, INFINITE, TRUE);
510 if (waitRet == WAIT_OBJECT_0)
514 }
while (waitRet == WAIT_IO_COMPLETION);
521#include "moc_qwindowspipereader_p.cpp"
Combined button and popup list for selecting options.
static const DWORD minReadBufferSize