My question is about linking Qt and win32 application
The problem is this:
My colleague wrote a special program which uses pipeline. This pipeline is used to send special messages to GUI. And GUI must show these messages on display. The pipe line program has a thread inside it. This thread always is running and listening to internal messages. Whenever a message received it create a new thread and this thread must comminucate with GUI and display the message, then killed (He said this routine must be followed).
This is his code for such using
DWORD WINAPI PipeThread( void* pContext )
{
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\InterfacePipe");
SECURITY_ATTRIBUTES sa;
sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!InitializeSecurityDescriptor(sa.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
{
DWORD er = ::GetLastError();
}
if (!SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, (PACL)0, FALSE))
{
DWORD er = ::GetLastError();
}
sa.nLength = sizeof sa;
sa.bInheritHandle = TRUE;
for (;;)
{
//_tprintf( TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);
hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFFER_SIZE, // output buffer size
BUFFER_SIZE, // input buffer size
0, // client time-out
&sa); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
//_tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
return -1;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
InstanceThread, // thread proc
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
return -1;
}
else CloseHandle(hThread);
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
return ERROR_SUCCESS;
}
And the code which uses to display message on MFC dialog is this:
DWORD WINAPI InstanceThread(LPVOID lpvParam)
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming
// client connections.
{
HANDLE hHeap = GetProcessHeap();
TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, BUFFER_SIZE*sizeof(TCHAR));
TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFFER_SIZE*sizeof(TCHAR));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
// Do some extra error checking since the app will keep running even if this
// thread fails.z
if (lpvParam == NULL)
{
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
if (pchRequest == NULL)
{
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
return (DWORD)-1;
}
if (pchReply == NULL)
{
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
// Print verbose messages. In production code, this should be for debugging only.
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE) lpvParam;
// Loop until done reading
while (1)
{
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length.
fSuccess = ReadFile(
hPipe, // handle to pipe
pchRequest, // buffer to receive data
BUFFER_SIZE*sizeof(TCHAR), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (!fSuccess || cbBytesRead == 0)
{
break;
}
MyData* data = new MyData;
data->client_Param = pchRequest;
HWND handle = FindWindow(NULL,CStringW("MFCApplication1"));
PostMessage(handle, WM_YOU_HANVE_DATA, reinterpret_cast<WPARAM>(data) ,NULL);
// Process the incoming message.
//GetAnswerToRequest(pchRequest, pchReply, &cbReplyBytes);
if (wcscmp(pchRequest,L"msg1")==0)
StringCchCopy( pchReply, BUFFER_SIZE, TEXT("Hi"));
else
StringCchCopy( pchReply, BUFFER_SIZE, TEXT("default echo answer"));
cbReplyBytes = (lstrlen(pchReply)+1)*sizeof(TCHAR);
// Write the reply to the pipe.
fSuccess = WriteFile(
hPipe, // handle to pipe
pchReply, // buffer to write from
cbReplyBytes, // number of bytes to write
&cbWritten, // number of bytes written
NULL); // not overlapped I/O
if (!fSuccess || cbReplyBytes != cbWritten)
{
break;
}
}
// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance.
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchRequest);
HeapFree(hHeap, 0, pchReply);
return 1;
}
As you see he use function instead of method. I create a class and push his methods and use signal and slot to get the goal.
But it breaks down. The reason is that createThread method must get the name of function not the name of method. So I create the method as static method but another error raise. Signal and slot should use a real object(in moc file it use “this” keyword which is not compatible with this method)
How should I overcome this problem
PipeCreator::PipeCreator(QObject *parent = 0) :
QObject(parent)
{
HANDLE hPipeThread = CreateThread( NULL, 0, PipeCreator::PipeThread, NULL, 0, NULL );
}
DWORD PipeCreator::PipeThread(void *pContext)
{
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\InterfacePipe");
// The main loop creates an instance of the named pipe and
// then waits for a client to connect to it. When the client
// connects, a thread is created to handle communications
// with that client, and this loop is free to wait for the
// next client connect request. It is an infinite loop.
SECURITY_ATTRIBUTES sa;
sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!InitializeSecurityDescriptor(sa.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
{
DWORD er = ::GetLastError();
}
if (!SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, (PACL)0, FALSE))
{
DWORD er = ::GetLastError();
}
sa.nLength = sizeof sa;
sa.bInheritHandle = TRUE;
for (;;)
{
hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFFER_SIZE, // output buffer size
BUFFER_SIZE, // input buffer size
0, // client time-out
&sa); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
return -1;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
this->instance =new InstanceCreator();
connect(this->instance,SIGNAL(NewDataAvailable(MyData)),this,SLOT(NewDataAvailableForGUI(MyData)));
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
this->instance->InstanceThread, // thread proc
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
return -1;
}
else
{
//Here I should recieve the emmited signal
emit NewDataAvailableForGUI(this->newData);
CloseHandle(hThread);
}
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
return ERROR_SUCCESS;
}
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming
// client connections.
DWORD InstanceCreator::InstanceThread(LPVOID lpvParam)
{
HANDLE hHeap = GetProcessHeap();
TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, BUFFER_SIZE*sizeof(TCHAR));
TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFFER_SIZE*sizeof(TCHAR));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
// Do some extra error checking since the app will keep running even if this
// thread fails.z
//PostMessage((HWND)CMFCApplication1Dlg->textBox3->Handle.ToPointer(), WM_SETTEXT, 0, (LPARAM)L"TestDLL Try");
if (lpvParam == NULL)
{
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
if (pchRequest == NULL)
{
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
return (DWORD)-1;
}
if (pchReply == NULL)
{
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
// Print verbose messages. In production code, this should be for debugging only.
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE) lpvParam;
// Loop until done reading
while (1)
{
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length.
fSuccess = ReadFile(
hPipe, // handle to pipe
pchRequest, // buffer to receive data
BUFFER_SIZE*sizeof(TCHAR), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (!fSuccess || cbBytesRead == 0)
{
break;
}
MyData data;
data.data=QString::fromStdWString(pchRequest);
emit NewDataAvailable(data);
// Process the incoming message.
//GetAnswerToRequest(pchRequest, pchReply, &cbReplyBytes);
if (wcscmp(pchRequest,L"msg1")==0)
StringCchCopy( pchReply, BUFFER_SIZE, TEXT("Hi"));
else
StringCchCopy( pchReply, BUFFER_SIZE, TEXT("default echo answer"));
cbReplyBytes = (lstrlen(pchReply)+1)*sizeof(TCHAR);
// Write the reply to the pipe.
fSuccess = WriteFile(
hPipe, // handle to pipe
pchReply, // buffer to write from
cbReplyBytes, // number of bytes to write
&cbWritten, // number of bytes written
NULL); // not overlapped I/O
if (!fSuccess || cbReplyBytes != cbWritten)
{
break;
}
}
// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance.
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchRequest);
HeapFree(hHeap, 0, pchReply);
return 1;
}
Related
I've got a child thread that calls WaitForMultipleObjects() and blocks waiting for, among other things, an incoming message over COM4. Attached to the other side of COM4 is a board sending a message once every five seconds.
The problem is, WaitForMultipleObjects() is only notified that a message was received when the read buffer I allocated for it is full, rather than becoming notified whenever a single message is transmitted.
The weird part is that this behavior changes if I connect to the board with putty, close putty, and then reopen my application - WaitForMultipleObjects() will thereafter return whenever a message is received, buffer filled or not. This behavior is preferable, and I would like to know what putty is doing so that I may have it enabled all the time.
I checked the DCB object returned from GetCommState() both with and without using Putty, and it doesn't appear that Putty is changing the structure in a way that I am not already doing.
Here is the code initializing the HANDLE object for the com port (referred to as "hCom"):
bool init(struct ReadComArg readComArg) {
BOOL fSuccess;
log(comPort);
// Open a handle to the specified com port.
hCom = CreateFile(comPort,
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // can't write in one thread while blocked for a read in another unless port is declared overlapped
NULL); // hTemplate must be NULL for comm devices
if (hCom == INVALID_HANDLE_VALUE) {
// Handle the error.
log("Failed to create hCom handle");
return false;
}
DWORD lptEvtMask;
fSuccess = GetCommMask(hCom, &lptEvtMask);
if (!fSuccess) {
log("GetCommMask failed");
CloseHandle(hCom);
return false;
}
fSuccess = SetCommMask(hCom, lptEvtMask);
printAll(&lptEvtMask);
// Initialize the DCB structure.
DCB dcb;
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
// Build on the current configuration by first retrieving all current
// settings.
/*fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess) {
log("GetCommState failed");
CloseHandle(hCom);
return false;
}
*/
dcb.BaudRate = readComArg.baudRate; // 115200 baud rate
dcb.ByteSize = readComArg.dataBits; // 8 data size, xmit and rcv
/*
dcb.Parity = readComArg.parity; // parity bit
dcb.StopBits = readComArg.stopBits; // stop bit
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
*/
fSuccess = SetCommState(hCom, &dcb);
if (!fSuccess) {
log("SetCommState failed");
CloseHandle(hCom);
return false;
}
// Get the comm config again.
fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess) {
log("GetCommState failed");
CloseHandle(hCom);
return false;
}
log("Serial Port successfully reconfigured");
hFile = CreateFileW(fileName, // name of the write
FILE_APPEND_DATA, // open for appending
0, // no sharing
NULL, // default security
OPEN_ALWAYS, // open existing file or create new file
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile == NULL) {
log("Failed to create hFile handle");
CloseHandle(hCom);
return false;
}
PostMessage(readComArg.MainWindow, WM_THREAD_UP, 0, 0);
return true;
}
Putty doesn't touch any of the DCB fields except DCBlength, BaudRate, ByteSize, Parity, and StopBits, and setting the fields that aren't these five to arbitrary values doesn't seem to affect the code's behavior, so I left the rest at zero.
Here is the main loop code:
void loop(struct ReadComArg readComArg) {
//TODO: close all handles when function returns
const unsigned long BUFFSIZE = 256;
BYTE buffer[BUFFSIZE];
DWORD bytesRead, bytesWritten, result;
OVERLAPPED osReader;
OVERLAPPED osWriter;
HANDLE eventArray[4];
eventArray[0] = readComArg.killEvent;
eventArray[2] = readComArg.writeRequestEvent;
BOOL continueLooping = true;
BOOL fWaitingOnRead = FALSE;
BOOL fWaitingOnWrite = FALSE;
while (continueLooping) {
//There is no outstanding read request and we must create a new one:
if (!fWaitingOnRead) {
//First, check to make sure we haven't received a killEvent.
result = WaitForSingleObject(readComArg.killEvent, 0); //returns immediately
if (result == WAIT_OBJECT_0) {
//received killEvent, exiting
log("killEvent was signaled. Exiting loop");
if (!ResetEvent(readComArg.killEvent))
log("failed to reset killEvent");
break;
}
else if (result != WAIT_TIMEOUT) {
//some error occured
log("WaitForSingleObject returned an error");
break;
}
//Otherwise, there was no kill request, continue as normal.
//Attempt to create new read request
osReader = { 0 };
osReader.hEvent = CreateEvent(NULL, true, false, NULL);
//read event failed to allocate, return:
if (osReader.hEvent == NULL) {
log("failed to create readEvent");
break;
}
eventArray[1] = osReader.hEvent;
//Execute the asynchronous read:
if (!ReadFile(hCom, buffer, BUFFSIZE, &bytesRead, &osReader)) {
//The asynchronous read request succeeded and will be completed later:
if (GetLastError() == ERROR_IO_PENDING) {
log("Read request queued and pending");
fWaitingOnRead = TRUE;
}
//The asynchronous read request failed:
else {
log("ReadFile returned an error");
CloseHandle(osReader.hEvent);
break;
}
}
//The asynchronous read request succeeded and completed immediately:
else {
log("Read request queued and returned immediately");
CloseHandle(osReader.hEvent);
handleRead(readComArg, buffer, bytesRead);
}
}
//We are waiting on an outstanding read request:
else {
//block until a signal arrives
//if we are waiting on a write, then there is an additional signal we must block for
if (fWaitingOnWrite) {
log("blocking for kills, reads, writeRequests, and writeResponses.");
result = WaitForMultipleObjects(4, eventArray, FALSE, INFINITE);
}
else {
log("blocking for kills, reads, and writeRequests. No outstanding write Request.");
result = WaitForMultipleObjects(3, eventArray, FALSE, INFINITE);
}
continueLooping = false;
switch (result) {
//The killEvent handle received a signal. This has priority over every other signal.
case WAIT_OBJECT_0:
log("Received killEvent. Exiting loop");
if (!ResetEvent(readComArg.killEvent))
log("failed to reset killEvent");
break;
//The com port handle received a signal
case WAIT_OBJECT_0+1:
log("received signal");
//Unsuccessful read
if (!GetOverlappedResult(hCom, &osReader, &bytesRead, FALSE)) {
log("GetOverlappedResult returned an error");
break;
}
//Successful read, continue looping
log("Outstanding read request fulfilled");
handleRead(readComArg, buffer, bytesRead);
fWaitingOnRead = FALSE;
CloseHandle(osReader.hEvent);
continueLooping = true;
break;
//The writeRequestEvent handle received a signal. Create an asynchronous write request:
case WAIT_OBJECT_0 + 2:
//reset writeRequestEvent signal.
if (!ResetEvent(readComArg.writeRequestEvent)) {
log("failed to reset writeRequestEvent");
break;
}
//attempt to create writeResponseEvent:
osWriter = { 0 };
osWriter.hEvent = CreateEvent(NULL, true, false, NULL);
if (osWriter.hEvent == NULL) {
log("failed to create writeResponseEvent");
break;
}
eventArray[3] = osWriter.hEvent;
//execute the asynchronous write:
if (!WriteFile(hCom, readComArg.writeBuffer, readComArg.numCharsToWrite, &bytesWritten, &osWriter)) {
//The asynchronous write request succeeded and will be completed later:
if (GetLastError() == ERROR_IO_PENDING) {
log("Write request queued and pending");
fWaitingOnWrite = true;
continueLooping = true;
break;
}
//The asynchronous write request failed:
else {
log("WriteFile returned an error");
CloseHandle(osWriter.hEvent);
break;
}
}
//The asynchronous write request succeeded and completed immediately
else {
log("Write request queued and returned immediately");
CloseHandle(osWriter.hEvent);
continueLooping = true;
PostMessage(readComArg.MainWindow, WM_THREAD_SENT, 0, 0);
break;
}
break;
//The writeResponseEvent handle received a signal
case WAIT_OBJECT_0+3:
//Unsuccessful write
if (!GetOverlappedResult(hCom, &osWriter, &bytesWritten, FALSE)) {
log("GetOverlappedResult returned an error");
break;
}
//Successful write, continue looping
PostMessage(readComArg.MainWindow, WM_THREAD_SENT, 0, 0);
log("Outstanding write request fulfilled");
fWaitingOnWrite = FALSE;
CloseHandle(osWriter.hEvent);
continueLooping = true;
break;
// Error in WaitForMultipleObjects()
default:
log("WaitForMultipleObjects returned an error");
break;
}
}
}
CancelIo(hCom);
if(fWaitingOnRead)
CloseHandle(osReader.hEvent);
if (fWaitingOnWrite)
CloseHandle(osWriter.hEvent);
}
I am not very familiar with StackOverflow ettiquette, so if there is something incorrect with how I am asking my question, I apologize in advance and will correct it as soon as I am able. Thank you.
I am using a DLL injection on to start the client end of a file pipe, which talks to a server that logs the messages. The problem is that the server only receives a buffer filled with question mark ('?') characters.
Client / Injectable DLL:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define BUFSIZE 1024*1024
HANDLE hPipe;
BOOL fSuccess = FALSE;
DWORD cbToWrite, cbWritten, dwMode;
const wchar_t* lpszPipename = TEXT("\\\\.\\pipe\\listen");
char write_buffer[BUFSIZE];
void init()
{
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
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
}
#pragma warning(disable:4996)
void report(const char* frmt, ...)
{
va_list args;
va_start(args, frmt);
vsnprintf(write_buffer, BUFSIZE, frmt, args);
va_end(args);
// Send a message to the pipe server.
fSuccess = WriteFile(
hPipe, // pipe handle
write_buffer, // message
strlen(write_buffer), // message length
&cbWritten, // bytes written
NULL); // not overlapped
return;
}
Server:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define BUFSIZE 1024*1024
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
const wchar_t* lpszPipename = TEXT("\\\\.\\pipe\\listen");
// The main loop creates an instance of the named pipe and
// then waits for a client to connect to it. When the client
// connects, a thread is created to handle communications
// with that client, and this loop is free to wait for the
// next client connect request. It is an infinite loop.
for (;;)
{
_tprintf(TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);
hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
_tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
return -1;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
printf("Client connected, creating a processing thread.\n");
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
InstanceThread, // thread proc
(LPVOID)hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
_tprintf(TEXT("CreateThread failed, GLE=%d.\n"), GetLastError());
return -1;
}
else CloseHandle(hThread);
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
DWORD WINAPI InstanceThread(LPVOID lpvParam)
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming
// client connections.
{
HANDLE hHeap = GetProcessHeap();
TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(TCHAR));
TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(TCHAR));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
// Print verbose messages. In production code, this should be for debugging only.
printf("InstanceThread created, receiving and processing messages.\n");
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE)lpvParam;
// Loop until done reading
while (1)
{
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length.
fSuccess = ReadFile(
hPipe, // handle to pipe
pchRequest, // buffer to receive data
BUFSIZE, // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
// Process the incoming message.
_tprintf(TEXT("Client Request String:\"%s\"\n"), pchRequest);
}
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchRequest);
HeapFree(hHeap, 0, pchReply);
printf("InstanceThread exitting.\n");
return 1;
}
P.S. If is some how possible to use a debugger with a injectable DLL please let me know!
There are a few problems with this code, which I will come to at the end. First, some code that works. Please note that I have simplified things slightly by making it all into a single application (so that I could easily test it) and by getting rid of the thread, but none of that matters in the context of your question.
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define BUFSIZE 1024*1024
const TCHAR* lpszPipename = TEXT("\\\\.\\pipe\\listen");
char write_buffer [BUFSIZE];
HANDLE init()
{
HANDLE 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
if (hPipe == INVALID_HANDLE_VALUE)
{
printf ("CreateFile returned error %d\n", GetLastError ());
return INVALID_HANDLE_VALUE;
}
// The pipe connected; change to message-read mode.
DWORD dwMode = PIPE_READMODE_MESSAGE;
BOOL fSuccess = SetNamedPipeHandleState(
hPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if (!fSuccess)
{
printf ("SetNamedPipeHandleState returned error %d\n", GetLastError ());
CloseHandle (hPipe);
return INVALID_HANDLE_VALUE;
}
return hPipe;
}
void report(HANDLE hPipe, const char *frmt, ...)
{
va_list args;
va_start(args, frmt);
_vsnprintf(write_buffer, BUFSIZE, frmt, args);
va_end(args);
// Send a message to the pipe server.
DWORD cbWritten;
BOOL fSuccess = WriteFile(
hPipe, // pipe handle
write_buffer, // message
(DWORD) strlen (write_buffer) + 1, // message length, including EOS
&cbWritten, // bytes written
NULL); // not overlapped
if (!fSuccess)
printf ("WriteFile returned error %d\n", GetLastError ());
}
int _tmain (int argc, TCHAR **argv)
{
if (argc > 1 && _tcscmp (argv [1], __T ("send")) == 0)
{
// send
HANDLE hPipe = init ();
if (hPipe != INVALID_HANDLE_VALUE)
{
report (hPipe, "A message to you, Rudi");
CloseHandle (hPipe);
}
return 0;
}
// receive
for (;;)
{
_tprintf(TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);
HANDLE hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
printf ("CreateNamedPipe failed, GLE=%d.\n", GetLastError());
return -1;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
BOOL fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (!fConnected)
{
printf ("Error %d connecting named pipe\n", GetLastError());
return 255;
}
printf ("Client connected\n");
HANDLE hHeap = GetProcessHeap();
char* pchRequest = (char*) HeapAlloc(hHeap, 0, BUFSIZE);
// Loop until done reading
while (1)
{
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length.
DWORD cbBytesRead = 0;
BOOL fSuccess = ReadFile(
hPipe, // handle to pipe
pchRequest, // buffer to receive data
BUFSIZE, // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (!fSuccess)
break;
// Process the incoming message.
printf("Client Request String:\"%s\"\n", pchRequest);
}
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchRequest);
}
return 0;
}
To run this in "send" mode, specify send on the command line. Otherwise it runs as a server. My server runs for ever. Kill it with Ctrl+C.
So what was wrong with your code? Well, mostly, it was a bit of a mishmash of ANSI and UNICODE strings. You need to be much more careful with this kind of thing and you also need to calculate buffer sizes appropriately. This is all fixed in the above code, which is why I posted it. Also, in terms of good programming practise:
You should check for errors much more thoroughly.
As written, the server assumes that the string sent to it is NUL-terminated but the client doesn't send it that way (so I fixed the client).
The server needs to break out of its receive loop when the sender closes its end of the pipe.
Declare local variables locally! (And pass them around as parameters, when appropriate.) Don't use unnecessary globals.
Using #define _CRT_SECURE_NO_WARNINGS is better than explicitly disabling the warning that you get if you don't.
My code fixes all of these issues. HTH.
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'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.
I'm trying to make my named pipe server skip the block-wait of the ConnectNamedPipe function until my client tries to connect. So I want is for my code to continue past the ConnetNamedPipe line until the end but keep the connection for my pipe open in the backgroun. If I use the PIPE_NOWAIT mode when creating the pipe it just returns imediatelly and the pipe closes before my client can connect.
I know that I tried doing this using a thread but even when I create a thread and execute the ConnectNamedPipe part of the code within the thread it still waits on this line instead of continuing with my code. Once it reaches the end of my server cpp file code I want to connect to the pipe with my client.
My Pipe:
hPipe = CreateNamedPipe(
lpszPipename,
PIPE_ACCESS_OUTBOUND, // one way access, only send data
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
512,
512,
0,
NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{
Out->msg(ERR,"Creating the pipe failed.");
}
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
InstanceThread, // thread proc
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
Out->msg(ERR,"Creating the thread failed.");
}
else CloseHandle(hThread);
// Close the pipe.
CloseHandle(hPipe);
My Thread:
DWORD WINAPI InstanceThread(LPVOID lpvParam)
{
Out->msg(output::SEV_INFO,"Waiting for client to connect.");
fConnected = ConnectNamedPipe(hPipe, NULL) ? //This is where the execution hangs
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
HANDLE hHeap = GetProcessHeap();
TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(TCHAR));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE) lpvParam;
uint32_t startTime = time(NULL);
uint32_t elapsedTime;
// Loop until done reading
while ((elapsedTime < 60))
{
elapsedTime = difftime(time(NULL), startTime);
// Write to the pipe.
fSuccess = WriteFile(
hPipe, // handle to pipe
pchReply, // buffer to write from
cbReplyBytes, // number of bytes to write
&cbWritten, // number of bytes written
NULL); // not overlapped I/O
if (!fSuccess || cbReplyBytes != cbWritten)
{
Out->msg(ERR,"InstanceThread WriteFile failed.");
break;
}
}
// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance.
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchReply);
Out->msg(output::SEV_INFO,"InstanceThread exitting.\n");
return 1;
}
I'm trying to make my named pipe server skip the block-wait of the ConnectNamedPipe function until my client tries to connect.
So you're talking about the server.
So I want my code to continue past the ConnectNamedPipe line while still being able to connect to my server.
So you're talking about the client.
It doesn't make sense. ConnectNamedPipe() is a server-side function, and you don't have anything useful to do in a thread dedicated to it except block until a client connects. So do that.