I need to read several lines from file simultaneously, i.e. asynchronously.
Lines in file are of the same size.
For instance, I need to read the second and the fourth lines of file to separate variables or to an array.
I'm more used to c#'s async/await and all these OVERLAPPED things are a bit difficult for understanding to me.
Using msdn examples, I achieved this, it just reads the data from the file as a one string (is that even asynchronous reading?):
BOOL ReadFromFileAsync(PCTSTR path)
{
HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
_tprintf_s(TEXT("INVALID_HANDLE_VALUE\n"));
return FALSE;
}
BOOL bResult;
BYTE bReadBuf[2048];
OVERLAPPED oRead = { 0 };
oRead.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
bResult = ReadFile(hFile, bReadBuf, _countof(bReadBuf), NULL, &oRead);
if (!bResult && GetLastError() != ERROR_IO_PENDING)
{
_tprintf_s(TEXT("ERROR io pending"));
CloseHandle(hFile);
return FALSE;
}
// perform some stuff asynchronously
_tprintf_s(TEXT("HEY\n"));
HANDLE hEvents[2];
hEvents[0] = oRead.hEvent;
hEvents[1] = oRead.hEvent;
DWORD dwWaitRes = WaitForMultipleObjects(_countof(hEvents), hEvents, FALSE, INFINITE);
switch (dwWaitRes - WAIT_OBJECT_0)
{
case 0: // reading finished
_tprintf_s(TEXT("String that was read from file: "));
for (int i = 0; i < oRead.InternalHigh; ++i)
_tprintf_s(TEXT("%c"), bReadBuf[i]);
_tprintf_s(TEXT("\n"));
break;
default:
_tprintf_s(TEXT("Nooo"));
}
CloseHandle(hFile);
return TRUE;
}
Could you help me with reading two lines from file asynchronously?
Should I use SetFilePointer for that to move through the lines?
When you open a file with the FILE_FLAG_OVERLAPPED flag and then use an OVERLAPPED structure with ReadFile(), use the OVERLAPPED.Offset and OVERLAPPED.OffsetHigh fields to specify the byte offset where reading should start from. Also, you must use a separate OVERLAPPED instance for each ReadFile() if you run them simultaneously. This is clearly stated in the documentation:
If hFile is opened with FILE_FLAG_OVERLAPPED, the lpOverlapped parameter must point to a valid and unique OVERLAPPED structure, otherwise the function can incorrectly report that the read operation is complete.
For an hFile that supports byte offsets, if you use this parameter you must specify a byte offset at which to start reading from the file or device. This offset is specified by setting the Offset and OffsetHigh members of the OVERLAPPED structure. For an hFile that does not support byte offsets, Offset and OffsetHigh are ignored.
As your lines are the same length, you can easily calculate the offsets of the second and fourth lines and then issue two asynchronous ReadFile() calls for those offsets, and then wait for the two operations to complete as needed.
On a side note, you really should not be using FILE_FLAG_NO_BUFFERING unless you REALLY know what you are doing:
There are strict requirements for successfully working with files opened with CreateFile using the FILE_FLAG_NO_BUFFERING flag, for details see File Buffering.
Try something more like this instead:
#include <vector>
BOOL ReadFromFileAsync(PCTSTR path)
{
BOOL bResult = FALSE;
HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED /*| FILE_FLAG_NO_BUFFERING*/, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
_tprintf_s(TEXT("Error opening file: %s\n"), path);
return FALSE;
}
DWORD dwLineSize = ...; // size of each line, in bytes
std::vector<BYTE> bSecondLineBuf(dwLineSize);
std::vector<BYTE> bFourthLineBuf(dwLineSize);
OVERLAPPED oReadSecondLine = { 0 };
OVERLAPPED oReadFourthLine = { 0 };
oReadSecondLine.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!oReadSecondLine.hEvent)
{
_tprintf_s(TEXT("Error creating I/O event for reading second line\n"));
goto done;
}
oReadSecondLine.Offset = ...; // offset of second line
oReadFourthLine.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!oReadFourthLine.hEvent)
{
_tprintf_s(TEXT("Error creating I/O event for reading fourth line\n"));
goto done;
}
oReadFourthLine.Offset = ...; // offset of fourth line
if (!ReadFile(hFile, &bSecondLineBuf[0], dwLineSize, NULL, &oReadSecondLine))
{
if (GetLastError() != ERROR_IO_PENDING)
{
_tprintf_s(TEXT("Error starting I/O to read second line\n"));
goto done;
}
}
if (!ReadFile(hFile, &bFourthLineBuf[0], dwLineSize, NULL, &oReadFourthLine))
{
if (GetLastError() != ERROR_IO_PENDING)
{
_tprintf_s(TEXT("Error starting I/O to read fourth line\n"));
CancelIo(hFile);
goto done;
}
}
// perform some stuff asynchronously
_tprintf_s(TEXT("HEY\n"));
HANDLE hEvents[2];
hEvents[0] = oReadSecondLine.hEvent;
hEvents[1] = oReadFourthLine.hEvent;
DWORD dwWaitRes = WaitForMultipleObjects(_countof(hEvents), hEvents, TRUE, INFINITE);
if (dwWaitRes == WAIT_FAILED)
{
_tprintf_s(TEXT("Error waiting for I/O to finish\n"));
CancelIo(hFile);
goto done;
}
_tprintf_s(TEXT("Strings that were read from file: "));
for (int i = 0; i < oReadSecondLine.InternalHigh; ++i)
_tprintf_s(TEXT("%c"), (TCHAR) &bSecondLineBuf[i]);
_tprintf_s(TEXT("\n"));
for (int i = 0; i < oReadFourthLine.InternalHigh; ++i)
_tprintf_s(TEXT("%c"), (TCHAR) &bFourthLineBuf[i]);
_tprintf_s(TEXT("\n"));
done:
if (oReadSecondLine.hEvent) CloseHandle(oReadSecondLine.hEvent);
if (oReadFourthLine.hEvent) CloseHandle(oReadFourthLine.hEvent);
CloseHandle(hFile);
return bResult;
}
Alternatively:
#include <vector>
BOOL ReadFromFileAsync(PCTSTR path)
{
BOOL bResult = FALSE;
HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED /*| FILE_FLAG_NO_BUFFERING*/, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
_tprintf_s(TEXT("Error opening file: %s\n"), path);
return FALSE;
}
DWORD dwLineSize = ...; // size of each line, in bytes
std::vector<BYTE> bSecondLineBuf(dwLineSize);
std::vector<BYTE> bFourthLineBuf(dwLineSize);
OVERLAPPED oReadSecondLine = { 0 };
OVERLAPPED oReadFourthLine = { 0 };
oReadSecondLine.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!oReadSecondLine.hEvent)
{
_tprintf_s(TEXT("Error creating I/O event for reading second line\n"));
goto done;
}
oReadSecondLine.Offset = ...; // offset of second line
oReadFourthLine.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!oReadFourthLine.hEvent)
{
_tprintf_s(TEXT("Error creating I/O event for reading fourth line\n"));
goto done;
}
oReadFourthLine.Offset = ...; // offset of fourth line
if (!ReadFile(hFile, &bSecondLineBuf[0], dwLineSize, NULL, &oReadSecondLine))
{
if (GetLastError() != ERROR_IO_PENDING)
{
_tprintf_s(TEXT("Error starting I/O to read second line\n"));
goto done;
}
}
if (!ReadFile(hFile, &bFourthLineBuf[0], dwLineSize, NULL, &oReadFourthLine))
{
if (GetLastError() != ERROR_IO_PENDING)
{
_tprintf_s(TEXT("Error starting I/O to read fourth line\n"));
CancelIo(hFile);
goto done;
}
}
// perform some stuff asynchronously
_tprintf_s(TEXT("HEY\n"));
HANDLE hEvents[2];
hEvents[0] = oReadSecondLine.hEvent;
hEvents[1] = oReadFourthLine.hEvent;
OVERLAPPED* pOverlappeds[2];
pOverlappeds[0] = &oReadSecondLine;
pOverlappeds[1] = &oReadFourthLine;
BYTE* pBufs[2];
pBufs[0] = &bSecondLineBuf[0];
pBufs[1] = &bFourthLineBuf[0];
DWORD dwNumReads = _countof(hEvents);
do
{
DWORD dwWaitRes = WaitForMultipleObjects(dwNumReads, hEvents, FALSE, INFINITE);
if (dwWaitRes == WAIT_FAILED)
{
_tprintf_s(TEXT("Error waiting for I/O to finish\n"));
CancelIo(hFile);
goto done;
}
if ((dwWaitRes >= WAIT_OBJECT_0) && (dwWaitRes < (WAIT_OBJECT_0+dwNumReads)))
{
DWORD dwIndex = dwWaitRes - WAIT_OBJECT_0;
_tprintf_s(TEXT("String that was read from file: "));
for (int i = 0; i < pOverlappeds[dwIndex]->InternalHigh; ++i)
_tprintf_s(TEXT("%c"), (TCHAR) pBufs[dwIndex][i]);
_tprintf_s(TEXT("\n"));
--dwNumReads;
if (dwNumReads == 0)
break;
if (dwIndex == 0)
{
hEvents[0] = hEvents[1];
pOverlappeds[0] = pOverlappeds[1];
pBufs[0] = pBufs[1];
}
}
}
while (true);
done:
if (oReadSecondLine.hEvent) CloseHandle(oReadSecondLine.hEvent);
if (oReadFourthLine.hEvent) CloseHandle(oReadFourthLine.hEvent);
CloseHandle(hFile);
return bResult;
}
Related
I am working on IO redirection program and i successfully created poc for it. The program spawn the child process and communicate with it using named pipes. I used Event object to get event whenever there is data on the pipe. I set the event to signaled state by default but i am not getting the event for the first time. To get the event i have to write on input pipe. When i write some command on input pipe i get the event and and get the output for the old command, not the current command(Please see the output).
below is the working code.
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <thread>
#include <string>
using namespace std;
#define input_pipe_name L"\\\\.\\pipe\\input"
#define output_pipe_name L"\\\\.\\pipe\\output"
#define process_name L"cmd.exe"
HANDLE input_pipe_handle;
HANDLE output_pipe_handle;
HANDLE input_file_handle;
HANDLE output_file_handle;
OVERLAPPED output_overlapped = { 0 };
BOOL InitHandels()
{
input_pipe_handle = CreateNamedPipe(input_pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 120000, 0);
SetHandleInformation(input_pipe_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
if (input_pipe_handle == INVALID_HANDLE_VALUE)
{
cout << "pipe creation error: " << GetLastError() << endl;
return FALSE;
}
output_pipe_handle = CreateNamedPipe(output_pipe_name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 120000, 0);
SetHandleInformation(output_pipe_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
if (output_pipe_handle == INVALID_HANDLE_VALUE)
{
cout << "pipe creation error: " << GetLastError() << endl;
return FALSE;
}
input_file_handle = CreateFile(input_pipe_name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (input_file_handle == INVALID_HANDLE_VALUE)
{
cout << "file creation error: " << GetLastError() << endl;
return FALSE;
}
output_file_handle = CreateFile(output_pipe_name, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (output_file_handle == INVALID_HANDLE_VALUE)
{
cout << "file creation error: " << GetLastError() << endl;
return FALSE;
}
output_overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
ConnectNamedPipe(output_pipe_handle, &output_overlapped);
}
void CreateChildProcess()
{
TCHAR szCmdline[] = L"cmd.exe";
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = output_pipe_handle;
siStartInfo.hStdOutput = output_pipe_handle;
siStartInfo.hStdInput = input_pipe_handle;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
if (!CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo))
{
cout << "process creation error: " << GetLastError() << endl;
//return FALSE;
}
else
{
HANDLE h_array[] = {output_overlapped.hEvent, piProcInfo.hProcess};
for (;;)
{
DWORD result = WaitForMultipleObjects(2, h_array, FALSE, 1000);
DWORD bwritten = 0, bread = 0;
char buffer[4096];
switch (result)
{
case WAIT_TIMEOUT:
//cout << "TimeOut" << endl;
break;
case WAIT_OBJECT_0:
ReadFile(output_file_handle, buffer, sizeof(buffer), &bread, &output_overlapped);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, bread, &bwritten, 0);
ResetEvent(output_overlapped.hEvent);
break;
case WAIT_OBJECT_0 + 1:
break;
//return FALSE;
}
}
}
}
int main()
{
DWORD bwritten;
InitHandels();
//CreateChildProcess();
std::thread t1(CreateChildProcess);
for (;;Sleep(1000))
{
std::string mystring;
std::cin >> mystring;
mystring.append("\n");
WriteFile(input_file_handle, mystring.c_str(), mystring.length(), &bwritten, &output_overlapped);
//WriteFile(input_file_handle, "dir\n", 4, &bwritten, &output_overlapped);
}
t1.join();
return 0;
}
I get the following output
dir
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
D:\Programming\VS\to_post_on_stack\to_post_on_stack>hello
dir
Volume in drive D has no label.
Volume Serial Number is 54FB-7A94
Directory of D:\Programming\VS\to_post_on_stack\to_post_on_stack
01/13/2018 05:36 PM <DIR> .
01/13/2018 05:36 PM <DIR> ..
01/13/2018 05:36 PM <DIR> Debug
01/12/2018 08:54 PM 608 stdafx.cpp
01/12/2018 08:54 PM 642 stdafx.h
01/12/2018 08:54 PM 630 targetver.h
01/13/2018 05:36 PM 7,434 to_post_on_stack.cpp
01/12/2018 08:54 PM 8,038 to_post_on_stack.vcxproj
01/12/2018 08:54 PM 1,277 to_post_on_stack.vcxproj.filters
6 File(s) 18,629 bytes
3 Dir(s) 39,347,019,776 bytes free
D:\Programming\VS\to_post_on_stack\to_post_on_stack>dir
hello
'hello' is not recognized as an internal or external command,
operable program or batch file.
D:\Programming\VS\to_post_on_stack\to_post_on_stack>dir
As you can see in the output when i send dir command i get the old output. When i send hello i get the output for the dir command which i executed before hello.
SO can anyone point out the mistake that why i am not getting signal for the very first time. And why the output is not getting in sequence?
the code example full of critical bugs:
the first and main:
If hFile was opened with FILE_FLAG_OVERLAPPED, the following
conditions are in effect:
The lpOverlapped parameter must point to a valid and unique
OVERLAPPED structure, otherwise the function can incorrectly report
that the io operation is complete.
and
io operation resets the event specified by the hEvent member of the
OVERLAPPED structure to a nonsignaled state when it begins the I/O
operation. Therefore, the caller does not need to do that.
when io operation is complete - io subsystem write to lpOverlapped final status of operation, number of bytes transferred, and if it containing event - set this event to signal state. if you use the same lpOverlapped in concurrent - they overwrite result each other, and you never know - which operation is really complete - event is one, common !, also if you use event - system reset event before begin io - as result - one operation can complete and set event, then another operation reset it after this
you call in 2 threads in concurrent:
WriteFile(input_file_handle, ..&output_overlapped);
ReadFile(output_file_handle, .. &output_overlapped);
with this you already have UB because the same &output_overlapped used in concurrent. we need allocate unique overlapped to every operation. and if you use events for detect completion - you need create several events - this is not good way at all. much better use iocp completion here - we not need create events, we not need create separate thread.
ReadFile(output_file_handle, buffer, sizeof(buffer), &bread, &output_overlapped);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, bread, &bwritten, 0);
ResetEvent(output_overlapped.hEvent);
at first ReadFile resets the event specified by the hEvent member of the OVERLAPPED structure to a nonsignaled state when it begins the I/O operation. Therefore, the caller does not need to do that. and more - when you call ResetEvent - operation can be already completed - so you reset already signaled event - and as result you lost completion signal. if call ReasetEvent this need do before io operation (ReadFile in concrete case) but not after - which is error. however we not need do this and before - because io subsystem anyway do this.
else one critical bug - we can not use buffer, breadin WriteFile just after asynchronous call to ReadFile - the call is can be not completed yet. and context of buffer yet not defined.
the &bread is undefined always in asynchronous call and must not be used at all
The lpNumberOfBytesRead parameter should be set to NULL. Use the
GetOverlappedResult function to get the actual number of bytes read.
If the hFile parameter is associated with an I/O completion port,
you can also get the number of bytes read by calling the
GetQueuedCompletionStatus function.
else one very common mistake - that we create 2 pipe pairs (input_pipe_handle, output_file_handle) - this is absolute not need - we can use the 1 pipe pair.
the call to SetHandleInformation excess - we need just create handle with inherit properties via SECURITY_ATTRIBUTES.
code example:
//#define _XP_SUPPORT_
struct IO_COUNT
{
HANDLE _hFile;
HANDLE _hEvent;
LONG _dwIoCount;
IO_COUNT()
{
_dwIoCount = 1;
_hEvent = 0;
}
~IO_COUNT()
{
if (_hEvent)
{
CloseHandle(_hEvent);
}
}
ULONG Create(HANDLE hFile);
void BeginIo()
{
InterlockedIncrement(&_dwIoCount);
}
void EndIo()
{
if (!InterlockedDecrement(&_dwIoCount))
{
SetEvent(_hEvent);
}
}
void Wait()
{
WaitForSingleObject(_hEvent, INFINITE);
}
};
class U_IRP : OVERLAPPED
{
enum { connect, read, write };
IO_COUNT* _pIoObject;
ULONG _code;
LONG _dwRef;
char _buffer[256];
~U_IRP()
{
_pIoObject->EndIo();
}
ULONG Read()
{
_code = read;
AddRef();
return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer), 0, this));
}
ULONG CheckIoResult(BOOL fOk)
{
if (fOk)
{
#ifndef _XP_SUPPORT_
OnIoComplete(NOERROR, InternalHigh);
#endif
return NOERROR;
}
ULONG dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
OnIoComplete(dwErrorCode, 0);
}
return dwErrorCode;
}
VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
{
switch (_code)
{
case connect:
switch (dwErrorCode)
{
case ERROR_PIPE_CONNECTED:
case ERROR_NO_DATA:
dwErrorCode = NOERROR;
case NOERROR:
Read();
}
break;
case read:
if (dwErrorCode == NOERROR)
{
if (dwNumberOfBytesTransfered)
{
if (int cchWideChar = MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, 0, 0))
{
PWSTR wz = (PWSTR)alloca(cchWideChar * sizeof(WCHAR));
if (MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, wz, cchWideChar))
{
if (int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, 0, 0, 0, 0))
{
PSTR sz = (PSTR)alloca(cbMultiByte);
if (WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, sz, cbMultiByte, 0, 0))
{
DbgPrint("%.*s", cbMultiByte, sz);
}
}
}
}
}
Read();
}
break;
case write:
break;
default:
__debugbreak();
}
Release();
if (dwErrorCode)
{
DbgPrint("[%u]: error=%u\n", _code, dwErrorCode);
}
}
static VOID WINAPI _OnIoComplete(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
)
{
static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}
public:
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
{
_dwRef = 1;
pIoObject->BeginIo();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}
ULONG Write(const void* pvBuffer, ULONG cbBuffer)
{
_code = write;
AddRef();
return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
}
ULONG Connect()
{
_code = connect;
AddRef();
return CheckIoResult(ConnectNamedPipe(_pIoObject->_hFile, this));
}
static ULONG Bind(HANDLE hFile)
{
return BindIoCompletionCallback(hFile, U_IRP::_OnIoComplete, 0)
#ifndef _XP_SUPPORT_
&& SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)
#endif
? NOERROR : GetLastError();
}
};
ULONG IO_COUNT::Create(HANDLE hFile)
{
_hFile = hFile;
if (_hEvent = CreateEvent(0, TRUE, FALSE, 0))
{
return U_IRP::Bind(hFile);
}
return GetLastError();
}
void ChildTest()
{
static const WCHAR name[] = L"\\\\?\\pipe\\somename";
HANDLE hFile = CreateNamedPipeW(name,
PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, NMPWAIT_USE_DEFAULT_WAIT, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
IO_COUNT obj;
if (obj.Create(hFile) == NOERROR)
{
BOOL fOk = FALSE;
static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);
if (si.hStdError != INVALID_HANDLE_VALUE)
{
si.hStdInput = si.hStdOutput = si.hStdError;
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
if (CreateProcessW(ApplicationName , 0, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
fOk = TRUE;
}
}
CloseHandle(si.hStdError);
}
if (fOk)
{
U_IRP* p;
if (p = new U_IRP(&obj))
{
p->Connect();
p->Release();
}
obj.EndIo();
//++ simulate user commands
static PCSTR commands[] = { "dir\r\n", "ver\r\n", "exit\r\n" };
ULONG n = RTL_NUMBER_OF(commands);
PCSTR* psz = commands;
do
{
if (p = new U_IRP(&obj))
{
PCSTR command = *psz++;
p->Write(command, (ULONG)strlen(command) * sizeof(CHAR));
p->Release();
}
} while (--n);
//--
obj.Wait();
}
}
CloseHandle(hFile);
}
}
Hello I am trying to make a front end GUI for cmd.exe so I can make it wider but I got stuck.
I try to design an API like this
char* Directory = WriteCommand("dir");
printf("- %s\n", Directory);
and the output look exactly like it would in a cmd window, except I have it in a string, so it would be
DATE TIME FILESIZE FILENAME
etc etc etc
and then I can issue
char* Up = WriteCommand ("cd ..");
and it will give me the above directory listing. So I want a terminal control through using pipes to read and write.
I have tried many things based on this MSDN sample code - https://msdn.microsoft.com/en-us/library/ms682499.aspx
But I think this code is only good to issue one command, and read one response, because right after it deadlocks as described here - https://blogs.msdn.microsoft.com/oldnewthing/20110707-00/?p=10223
I see several other questions here, like this one with similar problems - How to read output from cmd.exe using CreateProcess() and CreatePipe() but no solutions posted work for me.
So here is my code.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
void CreateChildProcess(void);
void WriteToPipe(char* Arg1);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
int _tmain(int argc, TCHAR *argv[])
{
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
// Create the child process.
CreateChildProcess();
// Get a handle to an input file for the parent.
// This example assumes a plain text file and uses string output to verify data flow.
/*if (argc == 1)
ErrorExit(TEXT("Please specify an input file.\n"));
g_hInputFile = CreateFile(
argv[1],
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if (g_hInputFile == INVALID_HANDLE_VALUE)
ErrorExit(TEXT("CreateFile"));*/
// Write to the pipe that is the standard input for a child process.
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
// Read from pipe that is the standard output for child process.
ReadFromPipe();
WriteToPipe("ipconfig");
// THIS IS WHERE DEADLOCK OCCURS, FROM HERE
// PROGRAM BECOMES UNRESPONSIVE - HOW TO FIX THIS?
ReadFromPipe();
printf("\n->End of parent execution.\n");
// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application, close handles explicitly.
return 0;
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[] = TEXT("cmd.exe /k");
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
"cmd.exe", // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if (!bSuccess)
ErrorExit(TEXT("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
}
void WriteToPipe(char* Command)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
if (bSuccess == FALSE)
printf("write fail\n");
printf("written = %i\n", dwWritten);
//for (;;)
//{
//bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
//if (!bSuccess || dwRead == 0) break;
//bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
//if (bSuccess == FALSE)
//printf("write fail\n");
//printf("written = %i\n", dwWritten);
//}
// Close the pipe handle so the child process stops reading.
//if (!CloseHandle(g_hChildStd_IN_Wr))
//ErrorExit(TEXT("StdInWr CloseHandle"));
}
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
int i;
for (i = 0; i < 4; i++)
{
/*DWORD dwAvail = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &dwAvail, NULL)) {
// error, the child process might have ended
break;
}
if (!dwAvail) {
// no data available in the pipe
break;
}*/
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if (!bSuccess || dwRead == 0) break;
/*bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
if (!bSuccess) break;*/
chBuf[dwRead] = '\0';
printf("%i - %s\n", i, chBuf);
}
printf("done\n");
}
I issue the initial "cmd.exe" command which gives me the start of the command prompt. I now want to issue "ipconfig" (or any other command) to get networking info. The program deadlocks and becomes unresponsive. I can no longer read output of child process. How can I fix this? Thanks for your help.
the most power and effective solution for avoid any deadlocks - use asynchronous io. never wait for IO (read,write,ioctl) complete in place, but handle this in callbacks.
also note about use pipes for redirect output - very common errancy that we need use different handles for STDIN and STDOUT and need create 2 different pipes pair - one for STDIN and another for STDOUT. this is false. we can use single pipe handle for both STDIN and STDOUT (and STDERROR).
we need create server pipe handle by using CreateNamedPipeW with
PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED
flags. by using PIPE_ACCESS_DUPLEX we create bi-directional pipe,
as result both server and client processes can read from and write
to the pipe. and FILE_FLAG_OVERLAPPED give to as asynchronous
mode. also we not make this handle inheritable, so not need call
SetHandleInformation on it
client handle we create by CreateFileW also with
FILE_GENERIC_READ|FILE_GENERIC_WRITE access - this give ability
assign it both to stdin and stdout. because clients (like
cmd.exe) usually assume synchronous io - we not use
FILE_FLAG_OVERLAPPED here. also by using lpSecurityAttributes we
just make this handle inheritable.
we need bind server handle to some IOCP, for callback called when io
is ended. here we have 3 variants - use
BindIoCompletionCallback - the most simply way or use
CreateThreadpoolIo. also we can create IOCP yourself and own
thread pool, but for redirect child process output, this way usually
not need.
after we create child process - we need close client pipe handle
(which we duplicate to child) and just call ReadFile on our pipe
handle. when this ReadFile complete - we need again call
ReadFile from callback and so on - until we not got error from
ReadFile in completion (usually ERROR_BROKEN_PIPE). so we need
all time have active read request from pipe, until disconnect.
and we free call WriteFile at any time and any place - this never
cause deadlock, because we use asynchronous io.
some time (very very rarely) if we need complex processing on read
data(based on previous results and state) and this much more easy
handle in plain procedure but not in callbacks, we can create fiber
for this task (CreateFiber) and from working thread callback,
when read complete - first call ConvertThreadToFiber (if we
call this more than once for same working thread - will be error
ERROR_ALREADY_FIBER on second and next calls, but this is ok. but
all this work begin from vista only. on xp error here). remember
current fiber, to where need retirn (GetCurrentFiber()) and
call SwitchToFiber (with our dedicated for read fiber)- where
we can handle read result and after this return back by call
SwitchToFiber (with fiber for worked thread). but all this
really can be need in in very rare and specific scenarios. usually
handle all is callbacks with state in object related to pipe handle - more than enough.
simply example with cmd
#define _XP_SUPPORT_
struct IO_COUNT
{
HANDLE _hFile;
HANDLE _hEvent;
LONG _dwIoCount;
IO_COUNT()
{
_dwIoCount = 1;
_hEvent = 0;
}
~IO_COUNT()
{
if (_hEvent)
{
CloseHandle(_hEvent);
}
}
ULONG Create(HANDLE hFile);
void BeginIo()
{
InterlockedIncrement(&_dwIoCount);
}
void EndIo()
{
if (!InterlockedDecrement(&_dwIoCount))
{
SetEvent(_hEvent);
}
}
void Wait()
{
WaitForSingleObject(_hEvent, INFINITE);
}
};
struct U_IRP : OVERLAPPED
{
enum { read, write };
IO_COUNT* _pIoObject;
ULONG _code;
LONG _dwRef;
char _buffer[256];
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
{
_dwRef = 1;
pIoObject->BeginIo();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}
~U_IRP()
{
_pIoObject->EndIo();
}
ULONG CheckIoResult(BOOL fOk)
{
if (fOk)
{
#ifndef _XP_SUPPORT_
OnIoComplete(NOERROR, InternalHigh);
#endif
return NOERROR;
}
ULONG dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
OnIoComplete(dwErrorCode, 0);
}
return dwErrorCode;
}
ULONG Read()
{
_code = read;
AddRef();
return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer), 0, this));
}
ULONG Write(const void* pvBuffer, ULONG cbBuffer)
{
_code = write;
AddRef();
return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
}
VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
{
switch (_code)
{
case read:
if (dwErrorCode == NOERROR)
{
if (dwNumberOfBytesTransfered)
{
if (int cchWideChar = MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, 0, 0))
{
PWSTR wz = (PWSTR)alloca(cchWideChar * sizeof(WCHAR));
if (MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, wz, cchWideChar))
{
if (int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, 0, 0, 0, 0))
{
PSTR sz = (PSTR)alloca(cbMultiByte);
if (WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, sz, cbMultiByte, 0, 0))
{
DbgPrint("%.*s", cbMultiByte, sz);
}
}
}
}
}
Read();
}
break;
case write:
break;
default:
__debugbreak();
}
Release();
if (dwErrorCode)
{
DbgPrint("[%u]: error=%u\n", _code, dwErrorCode);
}
}
static VOID WINAPI _OnIoComplete(
DWORD dwErrorCode,
DWORD_PTR dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
)
{
static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}
};
ULONG IO_COUNT::Create(HANDLE hFile)
{
_hFile = hFile;
// error in declaration LPOVERLAPPED_COMPLETION_ROUTINE :
// second parameter must be DWORD_PTR but not DWORD
return BindIoCompletionCallback(hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)U_IRP::_OnIoComplete, 0) &&
#ifndef _XP_SUPPORT_
SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) &&
#endif
(_hEvent = CreateEvent(0, TRUE, FALSE, 0)) ? NOERROR : GetLastError();
}
void ChildTest()
{
static const WCHAR name[] = L"\\\\?\\pipe\\somename";
HANDLE hFile = CreateNamedPipeW(name,
PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
IO_COUNT obj;
if (obj.Create(hFile) == NOERROR)
{
BOOL fOk = FALSE;
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);
if (si.hStdError != INVALID_HANDLE_VALUE)
{
si.hStdInput = si.hStdOutput = si.hStdError;
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
if (CreateProcessW(ApplicationName, 0, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
fOk = TRUE;
}
}
CloseHandle(si.hStdError);
}
if (fOk)
{
STATIC_ASTRING(help_and_exit, "help\r\nexit\r\n");
U_IRP* p;
if (p = new U_IRP(&obj))
{
p->Read();
p->Release();
}
obj.EndIo();
//++ simulate user commands
static PCSTR commands[] = { "help\r\n", "ver\r\n", "dir\r\n", "exit\r\n" };
ULONG n = RTL_NUMBER_OF(commands);
PCSTR* psz = commands;
do
{
if (MessageBoxW(0,0, L"force close ?", MB_YESNO) == IDYES)
{
DisconnectNamedPipe(hFile);
break;
}
if (p = new U_IRP(&obj))
{
PCSTR command = *psz++;
p->Write(command, (ULONG)strlen(command) * sizeof(CHAR));
p->Release();
}
} while (--n);
//--
obj.Wait();
}
}
CloseHandle(hFile);
}
}
I know is it a bit old so you probably won't need this answer anymore. But for those who came to StackOverflow for a solution for the same problem, I faced the same problem when building a similar project and I found a solution.
Basically, just add "\n" newline character to the end of the command. This is needed to simulate the "ENTER" button is pressed. Otherwise, WriteFile() works but ReadFile() is still waiting because the command was never executed in child process cmd.exe hence there is nothing for ReadFile() to read, causing it to hang there.
So the modified code is (I didn't test run the following code but is just modified based on the example the original author posted):
void WriteToPipe(char* Command)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
// Fix for the issue
strcat_s(command, strlen(command) + 1, "\n", 1);
bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
if (bSuccess == FALSE)
printf("write fail\n");
printf("written = %i\n", dwWritten);
//for (;;)
//{
//bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
//if (!bSuccess || dwRead == 0) break;
//bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
//if (bSuccess == FALSE)
//printf("write fail\n");
//printf("written = %i\n", dwWritten);
//}
// Close the pipe handle so the child process stops reading.
//if (!CloseHandle(g_hChildStd_IN_Wr))
//ErrorExit(TEXT("StdInWr CloseHandle"));
}
I am calling ReadDirectoryChangesW asynchronously to monitor directory changes in a background thread.
This how the directory (basePath) is opened and the "reading" thread is started:
m_hDIR = CreateFileW(
basePath,
FILE_LIST_DIRECTORY | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (m_hDIR == INVALID_HANDLE_VALUE)
throw CrException(CrWin32ErrorString());
//Start reading changes in background thread
m_Callback = std::move(a_Callback);
m_Reading = true;
m_ReadThread = std::thread(&CrDirectoryWatcher::StartRead, this);
This is StartRead(): (Note: m_Reading is atomic<bool>)
void StartRead()
{
DWORD dwBytes = 0;
FILE_NOTIFY_INFORMATION fni{0};
OVERLAPPED o{0};
//Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
o.hEvent = CreateEvent(0, 0, 0, 0);
while(m_Reading)
{
if (!ReadDirectoryChangesW(m_hDIR,
&fni, sizeof(fni),
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
&dwBytes, &o, NULL))
{
CrAssert(0, CrWin32ErrorString());
}
if (!GetOverlappedResult(m_hDIR, &o, &dwBytes, FALSE))
CrAssert(0, CrWin32ErrorString());
if (fni.Action != 0)
{
std::wstring fileName(fni.FileName, fni.FileNameLength);
m_Callback(fileName);
fni.Action = 0;
}
}
}
Basically, I am "polling" for new changes every frame.
Now when I call GetOverlappedResult() it fails and yields the following error:
Overlapped I/O event is not in a signaled state.
Am I missing something? Is ReadDirectoryChangesW meant to be called every "tick"? Or just when new changes were detected?
Note: When I leave out the OVERLAPPED struct (and GetOverlappedResult) it works, but blocks the thread until changes were read. This prevents my application to properly terminate. (i.e. I can't join the thread)
When calling GetOverlappedResult(), if you set the bWait parameter to FALSE and the I/O operation hasn't completed yet, GetOverlappedResult() fails with an ERROR_IO_INCOMPLETE error code:
bWait [in]
If this parameter is TRUE, and the Internal member of the lpOverlapped structure is STATUS_PENDING, the function does not return until the operation has been completed. If this parameter is FALSE and the operation is still pending, the function returns FALSE and the GetLastError function returns ERROR_IO_INCOMPLETE.
That is not a fatal error, so just ignore that error and move on.
And yes, make sure you don't call ReadDirectoryChangesW() again until GetOverlappedResult() has reported the previous I/O operation has completed first.
Now, with that said, there is another problem with your code. Your thread is allocating a single FILE_NOTIFY_INFORMATION instance on the stack. If you look at the definition of FILE_NOTIFY_INFORMATION, its FileName field is variable-length:
typedef struct _FILE_NOTIFY_INFORMATION {
DWORD NextEntryOffset;
DWORD Action;
DWORD FileNameLength;
WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
FileName
A variable-length field that contains the file name relative to the directory handle. The file name is in the Unicode character format and is not null-terminated.
Which means allocating a FILE_NOTIFY_INFORMATION statically is going to be too small, and dwBytes will almost always be 0 since ReadDirectoryChangesW() won't be able to return a full FILE_NOTIFY_INFORMATION to you (unless the FileName is exactly 1 character in length):
When you first call ReadDirectoryChangesW, the system allocates a buffer to store change information. This buffer is associated with the directory handle until it is closed and its size does not change during its lifetime. Directory changes that occur between calls to this function are added to the buffer and then returned with the next call. If the buffer overflows, the entire contents of the buffer are discarded, the lpBytesReturned parameter contains zero, and the ReadDirectoryChangesW function fails with the error code ERROR_NOTIFY_ENUM_DIR.
ERROR_NOTIFY_ENUM_DIR
1022 (0x3FE)
A notify change request is being completed and the information is not being returned in the caller's buffer. The caller now needs to enumerate the files to find the changes.
So, you need to dynamically allocate a large byte buffer for receiving FILE_NOTIFY_INFORMATION data, and then you can walk that buffer whenever GetOverlappedResult() reports that data is available.
Your thread should look something more like this:
void StartRead()
{
DWORD dwBytes = 0;
std::vector<BYTE> buffer(1024*64);
OVERLAPPED o{0};
bool bPending = false;
//Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!o.hEvent) {
CrAssert(0, CrWin32ErrorString());
}
while (m_Reading)
{
bPending = ReadDirectoryChangesW(m_hDIR,
&buffer[0], buffer.size(),
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
&dwBytes, &o, NULL);
if (!bPending)
{
CrAssert(0, CrWin32ErrorString());
}
while (m_Reading)
{
if (GetOverlappedResult(m_hDIR, &o, &dwBytes, FALSE))
{
bPending = false;
if (dwBytes != 0)
{
FILE_NOTIFY_INFORMATION *fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buffer[0]);
do
{
if (fni->Action != 0)
{
std::wstring fileName(fni->FileName, fni->FileNameLength);
m_Callback(fileName);
}
if (fni->NextEntryOffset == 0)
break;
fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(fni) + fni->NextEntryOffset);
}
while (true);
}
break;
}
if (GetLastError() != ERROR_IO_INCOMPLETE) {
CrAssert(0, CrWin32ErrorString());
}
Sleep(10);
}
if (bPending)
{
CancelIo(m_hDIR);
GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE);
}
}
CloseHandle(o.hEvent);
}
An alternative way to implement this without polling the I/O status regularly would be to get rid of m_Reading and use a waitable event instead. Let the OS signal the thread when it should call GetOverlappedResult() or terminate, that way it can sleep the rest of the time it is not busy doing something:
m_hDIR = CreateFileW(
basePath,
FILE_LIST_DIRECTORY | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (m_hDIR == INVALID_HANDLE_VALUE)
throw CrException(CrWin32ErrorString());
m_TermEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!m_TermEvent)
throw CrException(CrWin32ErrorString());
//Start reading changes in background thread
m_Callback = std::move(a_Callback);
m_ReadThread = std::thread(&CrDirectoryWatcher::StartRead, this);
...
SetEvent(m_TermEvent);
m_ReadThread.join();
void StartRead()
{
DWORD dwBytes = 0;
std::vector<BYTE> buffer(1024*64);
OVERLAPPED o{0};
bool bPending = false, bKeepRunning = true;
//Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!o.hEvent) {
CrAssert(0, CrWin32ErrorString());
}
HANDLE h[2] = {o.hEvent, h_TermEvent};
do
{
bPending = ReadDirectoryChangesW(m_hDIR,
&buffer[0], buffer.size(),
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
&dwBytes, &o, NULL);
if (!bPending)
{
CrAssert(0, CrWin32ErrorString());
}
switch (WaitForMultipleObjects(2, h, FALSE, INFINITE))
{
case WAIT_OBJECT_0:
{
if (!GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE)) {
CrAssert(0, CrWin32ErrorString());
}
bPending = false;
if (dwBytes == 0)
break;
FILE_NOTIFY_INFORMATION *fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buffer[0]);
do
{
if (fni->Action != 0)
{
std::wstring fileName(fni->FileName, fni->FileNameLength);
m_Callback(fileName);
}
if (fni->NextEntryOffset == 0)
break;
fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(fni) + fni->NextEntryOffset);
}
while (true);
break;
}
case WAIT_OBJECT_0+1:
bKeepRunning = false;
break;
case WAIT_FAILED:
CrAssert(0, CrWin32ErrorString());
break;
}
}
while (bKeepRunning);
if (bPending)
{
CancelIo(m_hDIR);
GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE);
}
CloseHandle(o.hEvent);
}
I'm trying to implement in my code with pipeline communication.
Build success but my code doesn't work.
My Pipeline communication code is :
int CTssPipeClient::Start(VOID)
{
m_uThreadId = 0;
m_hThread = (HANDLE)_beginthreadex(NULL, 0, ProcessPipe, LPVOID(this), 0, &m_uThreadId);
return 1;
}
VOID CTssPipeClient::Stop(VOID)
{
m_bRunning = FALSE;
if(m_hThread)
{
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
}
if(m_hPipe)
{
CloseHandle(m_hPipe);
}
}
UINT CTssPipeClient::Run()
{
BOOL fSuccess;
DWORD dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\tssnamedpipe");
m_vMsgList.clear();
m_bRunning = TRUE;
// Try to open a named pipe; wait for it, if necessary.
while (m_bRunning)
{
m_hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ // read and write access
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if (m_hPipe == INVALID_HANDLE_VALUE)
{
Sleep(1000);
continue;
}
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
m_hPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if (!fSuccess)
{
continue;
}
while(fSuccess)
{
if(m_vMsgList.size() > 0)
{
DWORD cbWritten;
// Send a message to the pipe server.
fSuccess = WriteFile(
m_hPipe, // pipe handle
m_vMsgList[0].c_str(), // message
(m_vMsgList[0].length() + 1)*sizeof(TCHAR), // message length
&cbWritten, // bytes written
NULL); // not overlapped
m_vMsgList.erase(m_vMsgList.begin());
if (!fSuccess)
{
break;
}
}
Sleep(200);
}
CloseHandle(m_hPipe);
}
_endthreadex(0);
return 0;
}
DWORD CTssPipeClient::WriteMsg(LPCTSTR szMsg)
{
if(!m_bRunning)
{
Start();
}
wstring wstr(szMsg);
m_vMsgList.push_back(wstr);
return 0;
}
I was tried fix this problem. But I didn't found What's wrong?
Please help me. I'm grateful for your help.
Thank you.
The reason is simple. Because you open a file with only read mode.
Please modify like this:
m_hPipe = CreateFile(
lpszPipename, // 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
I hope that helps.
In my Win32 program I implemented executing a console application and reading its std/err output. Basically it's the same code as given in MSDN: Creating a Child Process with Redirected Input and Output
So far, so good. It worked like a charm, reading both std and err streams with all my console applications. But obviously (due to the global HANDLE variables) the code is designed to run console applications one by one, never together. So I've changed it a bit:
The global HANDLE variables replaced with the local ones. They are passed into the helper functions.
A parameter named bWait added. If it's false, no reading from the console pipe after starting and no waiting for a process handle (a flavor of asynchrony).
Instead, the reading handles are returned to a caller (thru given pointers). They can be used for reading from the pipe later.
Why do I need this? I want to start tshark (a console version of Wireshark, a traffic sniffer) with bWait = false, then start my own utility with bWait = true and wait until my utility stops working. Then I want to check, whether my utility pings a server. (Since we have a lot of utilities, this would be important functionality of our auto-testing procedure). So, I'd like to read from the tshark console pipe after that and parse its log.
Here is my modifications:
// Create a child process that uses the previously created pipes
// for STDERR and STDOUT.
PROCESS_INFORMATION CreateChildProcess(HANDLE hChildStd_OUT_Wr, HANDLE hChildStd_ERR_Wr,
const std::wstring& cmd, bool& bSuccess, DWORD& exitCode, DWORD& lastError, bool bWait = true)
{
// Set the text I want to run
//char szCmdline[]="test --log_level=all --report_level=detailed";
bSuccess = false;
wchar_t wrBuffer[BUFSIZE];
::wcscpy_s(wrBuffer, cmd.c_str());
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDERR and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hChildStd_ERR_Wr;
siStartInfo.hStdOutput = hChildStd_OUT_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
wrBuffer, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo) != 0; // receives PROCESS_INFORMATION
if (!bSuccess)
{
lastError = ::GetLastError();
}
else
{
lastError = 0;
}
if (bWait && bSuccess && ::WaitForSingleObject(piProcInfo.hProcess, INFINITE) == WAIT_FAILED)
{
bSuccess = false;
}
if (bWait && FALSE == ::GetExitCodeProcess(piProcInfo.hProcess, &exitCode))
{
bSuccess = false;
}
if (bWait)
{
::CloseHandle(hChildStd_ERR_Wr);
::CloseHandle(hChildStd_OUT_Wr);
}
return piProcInfo;
}
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
void ReadFromPipe(HANDLE hChildStd_OUT_Rd, HANDLE hChildStd_ERR_Rd, std::wstring& stdS, std::wstring& errS)
{
DWORD dwRead;
CHAR chBuf[BUFSIZE];
bool bSuccess = FALSE;
std::string out = "", err = "";
for (;;)
{
bSuccess = ReadFile(hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL) != 0;
if (!bSuccess || dwRead == 0) break;
std::string s(chBuf, dwRead);
out += s;
}
dwRead = 0;
for (;;)
{
bSuccess = ReadFile(hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL) != 0;
if (!bSuccess || dwRead == 0) break;
std::string s(chBuf, dwRead);
err += s;
}
wchar_t utf[10000] = { 0 };
::MultiByteToWideChar(866, 0, (LPCCH) out.c_str(), -1, utf, sizeof(utf));
stdS = utf;
StringReplace(stdS, std::wstring(L"\n"), std::wstring(L"\r\n"));
::MultiByteToWideChar(866, 0, (LPCCH) err.c_str(), -1, utf, sizeof(utf));
errS = utf;
StringReplace(errS, std::wstring(L"\n"), std::wstring(L"\r\n"));
}
bool ExecuteCmd(std::wstring cmd, std::wstring& std, std::wstring& err, std::wstring& code, DWORD& lastError,
bool bWait = true, HANDLE* phChildStd_OUT_Rd = nullptr, HANDLE* phChildStd_ERR_Rd = nullptr)
{
HANDLE hChildStd_OUT_Rd = NULL;
HANDLE hChildStd_OUT_Wr = NULL;
HANDLE hChildStd_ERR_Rd = NULL;
HANDLE hChildStd_ERR_Wr = NULL;
SECURITY_ATTRIBUTES sa;
// Set the bInheritHandle flag so pipe handles are inherited.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDERR.
if (!CreatePipe(&hChildStd_ERR_Rd, &hChildStd_ERR_Wr, &sa, 0))
{
return false;
}
// Ensure the read handle to the pipe for STDERR is not inherited.
if (!SetHandleInformation(hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &sa, 0))
{
return false;
}
// Ensure the read handle to the pipe for STDOUT is not inherited
if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
// Create the child process.
bool bSuccess = false;
DWORD dwExitCode = 9999;
PROCESS_INFORMATION piProcInfo = CreateChildProcess(hChildStd_OUT_Wr, hChildStd_ERR_Wr, cmd, bSuccess, dwExitCode, lastError, bWait);
if (phChildStd_OUT_Rd)
*phChildStd_OUT_Rd = hChildStd_OUT_Rd;
if (phChildStd_ERR_Rd)
*phChildStd_ERR_Rd = hChildStd_ERR_Rd;
if (!bWait)
return true;
wchar_t buffer[10] = { 0 };
code = ::_itow((int) dwExitCode, buffer, 10);
if (!bSuccess)
{
return false;
}
// Read from pipe that is the standard output for child process.
ReadFromPipe(hChildStd_OUT_Rd, hChildStd_ERR_Rd, std, err);
::CloseHandle(hChildStd_OUT_Rd);
::CloseHandle(hChildStd_ERR_Rd);
return true;
}
Now, the problem. When I tried starting tshark in the no-waiting mode, reading from pipe hanged up. Namely, in ReadFile.
if (g_iConnection != -1 && g_Products[i].PingbackDomain.size() > 0)
{
wchar_t buf[5] = { 0 };
std::wstring list, err, code;
DWORD dwErr = 0;
std::wstring cmd = L"C:\\Program Files\\Wireshark\\tshark -a duration:5 -l -i ";
cmd += ::_itow(g_iConnection + 1, buf, 10);
cmd += L" -f \"host ";
cmd += g_Products[i].PingbackDomain;
cmd += L"\"";
ExecuteCmd(cmd, list, err, code, dwErr, false, &hChildStd_OUT_Rd, &hChildStd_ERR_Rd);
::Sleep(500);
}
...
// Starting my utility (if this section is commented out, ReadFile still hangs).
...
if (hChildStd_OUT_Rd && hChildStd_ERR_Rd)
{
std::wstring traffic, tsharkErr;
ReadFromPipe(hChildStd_OUT_Rd, hChildStd_ERR_Rd, traffic, tsharkErr);
::CloseHandle(hChildStd_OUT_Rd);
::CloseHandle(hChildStd_ERR_Rd);
if (tsharkErr.size() > 0)
{
std::wstring msg = L"There has been an issue, while logging with Wireshark:\r\n\r\n";
msg += tsharkErr;
::MessageBox(NULL, msg.c_str(), L"uhelper", MB_ICONERROR | MB_OK);
}
else if (traffic.length() > 0)
{
newOutput += L"\r\nTraffic to ";
newOutput += g_Products[i].PingbackDomain;
newOutput += L":\r\n";
newOutput += traffic;
if (newOutput[newOutput.length() - 1] != L'\n')
newOutput += L"\r\n";
}
}
Did I break the MSDN code with my modifications? Unfortunately, I can't find how (and where).
This solves the problem (before creating a process!):
if (!bWait)
{
DWORD mode = PIPE_READMODE_BYTE | PIPE_NOWAIT;
::SetNamedPipeHandleState(hChildStd_OUT_Rd, &mode, NULL, NULL);
::SetNamedPipeHandleState(hChildStd_ERR_Rd, &mode, NULL, NULL);
}
After applying PIPE_NOWAIT reading stopped hanging.