Extending windows named pipe to handle clients information - c

Handling & processing data from named pipes.
I am trying to implement a service provider to connect with a hardware device.
request some pointers on my approach to implement a robust system.
Mentioned are the raised requirements
Receive data from other EXE process
To process received Q information and send response information in clients response channel.
Asynchronously send information on some failure to client response channel.
TO implement the mentioned system:
Selected 2 named pipe (ClntcommandRecv & ClntRespSend) .bcz of between process (IPC)
ClntcommandRecv pipe will be used as "Named Pipe Server Using Overlapped" I/O"
ClntRespSend pipe will be used for sending the processed information.
ClntRespSend will also need to send all the async messages from service provider to connected application.
From here my implementation is straight forward.
Using "Named Pipe Server Using Overlapped I/O" by documentation I will be able to address multiple client connection request and its data processing using single thread.
On init system will create a thread to hold connection instance of clients ClntRespSend pipe.
Since, it requires for device to tell its failures to connected clients asynchronously.
Is it advisable for system to have timeout operation on "WaitForMultipleObjects" or
can we have readfile timeout counts after n timeout can we check for health info.WHich is advised
But, stuck in finding the best way to sync my ClntRespSend & ClntcommandRecv (MAPPIN).
Need to get process id of the connected process.Since the system is developed under MINGW - WIN32 - server will not be able to get the process id directly by using (GetNamedPipeClientProcessId).
Need to form a message structure on getting a client connection.
This is the code which i am trying to extend:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
//#include <strsafe.h>
//#include <glib.h>
#define CONNECTING_STATE 0
#define READING_STATE 1
#define WRITING_STATE 2
#define INSTANCES 4
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
TCHAR chRequest[BUFSIZE];
DWORD cbRead;
TCHAR chReply[BUFSIZE];
DWORD cbToWrite;
DWORD dwState;
BOOL fPendingIO;
int processId;
} PIPEINST, *LPPIPEINST;
typedef struct
{
char appName[256];
int processId;
}PIPEHANDSHAKE;
VOID DisconnectAndReconnect(DWORD);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetAnswerToRequest(LPPIPEINST);
PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];
HANDLE responsePipeHandle[INSTANCES];
DWORD WINAPI InstanceThread(LPVOID);
HANDLE hPipeHandles[10];
PULONG s;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
LPTSTR lpszResponsePipe = TEXT("\\\\.\\pipe\\mynamedpipe1");
//GHashTable* hash;
int responsePipeConnectionHandler(VOID)
{
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
int cbBytesRead;
INT threadCount=0;
//hash = g_hash_table_new(g_str_hash, g_str_equal);
char bufferSize[512];
for (;;)
{
_tprintf( TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszResponsePipe);
hPipe = CreateNamedPipe(
lpszResponsePipe, // 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){
PIPEHANDSHAKE processData;
fConnected = ReadFile(
hPipe, // handle to pipe
bufferSize, // buffer to receive data
sizeof(PIPEHANDSHAKE), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
memset(&processData,0,sizeof(PIPEHANDSHAKE));
memcpy(&processData,&bufferSize,sizeof(PIPEHANDSHAKE));
printf("APP Process id: %d , app name: %s",processData.processId,processData.appName);
}
/* 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);*/
}
return 0;
}
int _tmain(VOID)
{
DWORD i, dwWait, cbRet, dwErr,hThread;
BOOL fSuccess;
int dwThreadId;
// The initial loop creates several instances of a named pipe
// along with an event object for each instance. An
// overlapped ConnectNamedPipe operation is started for
// each instance.
// Create response pipe thread
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
responsePipeConnectionHandler, // thread proc
NULL, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
printf("Response server creation failed with %d.\n", GetLastError());
return 0;
}
for (i = 0; i < INSTANCES; i++)
{
// Create an event object for this instance.
hEvents[i] = CreateEvent(
NULL, // default security attribute
TRUE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hEvents[i] == NULL)
{
printf("CreateEvent failed with %d.\n", GetLastError());
return 0;
}
Pipe[i].oOverlap.hEvent = hEvents[i];
Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX | // read/write access
FILE_FLAG_OVERLAPPED, // overlapped mode
PIPE_TYPE_MESSAGE | // message-type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
INSTANCES, // number of instances
BUFSIZE*sizeof(TCHAR), // output buffer size
BUFSIZE*sizeof(TCHAR), // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // default security attributes
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with %d.\n", GetLastError());
return 0;
}
// Call the subroutine to connect to the new client
Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);
Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : // still connecting
READING_STATE; // ready to read
}
while (1)
{
dwWait = WaitForMultipleObjects(
INSTANCES, // number of event objects
hEvents, // array of event objects
FALSE, // does not wait for all
INFINITE); // waits indefinitely
// dwWait shows which pipe completed the operation.
i = dwWait - WAIT_OBJECT_0; // determines which pipe
if (i < 0 || i > (INSTANCES - 1))
{
printf("Index out of range.\n");
return 0;
}
// Get the result if the operation was pending.
if (Pipe[i].fPendingIO)
{
fSuccess = GetOverlappedResult(
Pipe[i].hPipeInst, // handle to pipe
&Pipe[i].oOverlap, // OVERLAPPED structure
&cbRet, // bytes transferred
FALSE); // do not wait
switch (Pipe[i].dwState)
{
// Pending connect operation
case CONNECTING_STATE:
if (! fSuccess)
{
printf("Error %d.\n", GetLastError());
return 0;
}
Pipe[i].dwState = READING_STATE;
break;
// Pending read operation
case READING_STATE:
if (! fSuccess || cbRet == 0)
{
DisconnectAndReconnect(i);
continue;
}
Pipe[i].cbRead = cbRet;
Pipe[i].dwState = WRITING_STATE;
break;
// Pending write operation
case WRITING_STATE:
if (! fSuccess || cbRet != Pipe[i].cbToWrite)
{
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = READING_STATE;
break;
default:
{
printf("Invalid pipe state.\n");
return 0;
}
}
}
// The pipe state determines which operation to do next.
switch (Pipe[i].dwState)
{
case READING_STATE:
fSuccess = ReadFile(
Pipe[i].hPipeInst,
Pipe[i].chRequest,
BUFSIZE*sizeof(TCHAR),
&Pipe[i].cbRead,
&Pipe[i].oOverlap);
if (fSuccess && Pipe[i].cbRead != 0)
{
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = WRITING_STATE;
continue;
}
dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING))
{
Pipe[i].fPendingIO = TRUE;
continue;
}
DisconnectAndReconnect(i);
break;
case WRITING_STATE:
GetAnswerToRequest(&Pipe[i]);
fSuccess = WriteFile(
Pipe[i].hPipeInst,
Pipe[i].chReply,
Pipe[i].cbToWrite,
&cbRet,
&Pipe[i].oOverlap);
if (fSuccess && cbRet == Pipe[i].cbToWrite)
{
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = READING_STATE;
continue;
}
dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING))
{
Pipe[i].fPendingIO = TRUE;
continue;
}
DisconnectAndReconnect(i);
break;
default:
{
printf("Invalid pipe state.\n");
return 0;
}
}
}
return 0;
}
VOID DisconnectAndReconnect(DWORD i)
{
if (! DisconnectNamedPipe(Pipe[i].hPipeInst) )
{
printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
}
Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);
Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : // still connecting
READING_STATE; // ready to read
}
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
{
BOOL fConnected, fPendingIO = FALSE;
fConnected = ConnectNamedPipe(hPipe, lpo);
if (fConnected)
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
switch (GetLastError())
{
// The overlapped connection in progress.
case ERROR_IO_PENDING:
fPendingIO = TRUE;
break;
case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break;
default:
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
}
return fPendingIO;
}
int rxProccesIdMsg(HANDLE pipe)
{
PIPEHANDSHAKE pipeInfo;
CHAR bufferSize[512] = {'\0'};
INT cbBytesRead;
BOOL fSuccess;
PIPEHANDSHAKE processData;
fSuccess = ReadFile(
pipe, // handle to pipe
bufferSize, // buffer to receive data
sizeof(PIPEHANDSHAKE), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
memset(&processData,0,sizeof(PIPEHANDSHAKE));
memcpy(&processData,&bufferSize,sizeof(PIPEHANDSHAKE));
if ( (!fSuccess))
{
printf("Client: READ Server Pipe Failed(%d)\n",GetLastError());
CloseHandle(pipe);
return -1;
}
else
{
printf("Client: READ Server Pipe Success(%d)\n",GetLastError());
printf("APP Process id: %d , app name: %s",processData.processId,processData.appName);
//Sleep(3*100);
}
return processData.processId;
}
VOID GetAnswerToRequest(LPPIPEINST pipe)
{
_tprintf( TEXT("[%d] %s\n"), pipe->hPipeInst, pipe->chRequest);
// StringCchCopy( pipe->chReply, BUFSIZE, TEXT("Default answer from server") );
strncpy(pipe->chReply, "Default answer from server",BUFSIZE);
pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);
}

Related

Child process (via CreateProcess) stalls on getch() with redirected stdout and stdin

I am trying to launch a process with CreateProcess() with stdin and stdout redirected to pipes. When the child process consists of just printf() statements, I see them piped up to the parent and displayed just fine. If my child process does a printf() and a _getch() statement, then things fail. I have considered a possible deadlock between the pipes in several ways to no avail:
changing the order of things,
applying PeekNamedPipe() and Sleep() statements, and
overlapped I/O using a named pipe.
I'm left suspecting a subtle configuration issue somewhere. This is part of an issue in a larger program but I've reduced it to this simple test case. I started with the Microsoft example for "Creating a Child Process with Redirected Input and Output". That worked, so maybe the child process using ReadFile() works, but my problem is _getch() (among other programs that seem to have related failures). I replaced the child process with my test program and it stalls. I try solving deadlocks as above, with the overlapped I/O achieved following this example for using named pipes to this purpose (in my reading someone mentioned that the Windows implementation of named and anonymous pipes are reasonably unified).
Again, works if the child issues only printfs but fails with _getch(). Of note is that if a _getch() is present in the child program then even the printfs don't show up - even printfs() issued before the _getch(). I've read that pipes have buffering and as above they have potential deadlocks waiting on the other end of the pipe, but I can't think what else I can do to avoid that besides what's done below.
Just in case, I also made sure I had a large heap buffer for the command-line buffer since CreateProcess() is known to modify it.
Here is my parent test code, with those first booleans configuring overlapped/not overlapped behavior:
#include <string>
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <conio.h>
#include <assert.h>
TCHAR szCmdline[] = TEXT("child.exe");
bool OverlappedStdOutRd = true;
bool OverlappedStdInWr = true;
#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;
using namespace std;
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
static ULONG PipeSerialNumber = 1;
static BOOL APIENTRY MyCreatePipeEx(
OUT LPHANDLE lpReadPipe,
OUT LPHANDLE lpWritePipe,
IN LPSECURITY_ATTRIBUTES lpPipeAttributes,
IN DWORD nSize,
DWORD dwReadMode,
DWORD dwWriteMode
)
/*++
Routine Description:
The CreatePipeEx API is used to create an anonymous pipe I/O device.
Unlike CreatePipe FILE_FLAG_OVERLAPPED may be specified for one or
both handles.
Two handles to the device are created. One handle is opened for
reading and the other is opened for writing. These handles may be
used in subsequent calls to ReadFile and WriteFile to transmit data
through the pipe.
Arguments:
lpReadPipe - Returns a handle to the read side of the pipe. Data
may be read from the pipe by specifying this handle value in a
subsequent call to ReadFile.
lpWritePipe - Returns a handle to the write side of the pipe. Data
may be written to the pipe by specifying this handle value in a
subsequent call to WriteFile.
lpPipeAttributes - An optional parameter that may be used to specify
the attributes of the new pipe. If the parameter is not
specified, then the pipe is created without a security
descriptor, and the resulting handles are not inherited on
process creation. Otherwise, the optional security attributes
are used on the pipe, and the inherit handles flag effects both
pipe handles.
nSize - Supplies the requested buffer size for the pipe. This is
only a suggestion and is used by the operating system to
calculate an appropriate buffering mechanism. A value of zero
indicates that the system is to choose the default buffering
scheme.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
HANDLE ReadPipeHandle, WritePipeHandle;
DWORD dwError;
CHAR PipeNameBuffer[MAX_PATH];
//
// Only one valid OpenMode flag - FILE_FLAG_OVERLAPPED
//
if ((dwReadMode | dwWriteMode) & (~FILE_FLAG_OVERLAPPED)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// Set the default timeout to 120 seconds
//
if (nSize == 0) {
nSize = 4096;
}
sprintf_s(PipeNameBuffer,
"\\\\.\\Pipe\\TruthPipe.%08x.%08x",
GetCurrentProcessId(),
PipeSerialNumber++ // TODO: Should use InterlockedIncrement() here to be thread-safe.
);
ReadPipeHandle = CreateNamedPipeA(
PipeNameBuffer,
PIPE_ACCESS_INBOUND | dwReadMode,
PIPE_TYPE_BYTE | PIPE_WAIT,
1, // Number of pipes
nSize, // Out buffer size
nSize, // In buffer size
1000, // Timeout in ms
lpPipeAttributes
);
if (!ReadPipeHandle) {
return FALSE;
}
WritePipeHandle = CreateFileA(
PipeNameBuffer,
GENERIC_WRITE,
0, // No sharing
lpPipeAttributes,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | dwWriteMode,
NULL // Template file
);
if (INVALID_HANDLE_VALUE == WritePipeHandle) {
dwError = GetLastError();
CloseHandle(ReadPipeHandle);
SetLastError(dwError);
return FALSE;
}
*lpReadPipe = ReadPipeHandle;
*lpWritePipe = WritePipeHandle;
return(TRUE);
}
bool OutstandingWrite = false;
OVERLAPPED WriteOverlapped;
CHAR chWriteBuf[BUFSIZE];
DWORD dwBytesWritten;
DWORD dwBytesToWrite;
bool OutstandingRead = false;
OVERLAPPED ReadOverlapped;
CHAR chReadBuf[BUFSIZE];
DWORD dwBytesRead;
void OnReadComplete();
void StartOverlappedRead();
void WaitForIO(bool Wait)
{
HANDLE hEvents[2];
int iEvent = 0;
int iReadEvent = -1;
int iWriteEvent = -1;
if (OutstandingRead) {
hEvents[iEvent] = ReadOverlapped.hEvent;
iReadEvent = iEvent;
iEvent++;
}
if (OutstandingWrite) {
hEvents[iEvent] = WriteOverlapped.hEvent;
iWriteEvent = iEvent;
iEvent++;
}
DWORD dwStatus = WaitForMultipleObjects(iEvent, hEvents, FALSE, Wait ? INFINITE : 250 /*ms*/);
int Index = -2;
switch (dwStatus)
{
case WAIT_OBJECT_0: Index = 0; break;
case WAIT_OBJECT_0 + 1: Index = 1; break;
case WAIT_TIMEOUT: return;
default:
ErrorExit(TEXT("WaitForMultipleObjects"));
}
if (Index == iReadEvent)
{
if (!GetOverlappedResult(
g_hChildStd_OUT_Rd, // handle to pipe
&ReadOverlapped, // OVERLAPPED structure
&dwBytesRead, // bytes transferred
FALSE)) // do not wait
ErrorExit(TEXT("GetOverlappedResult"));
OutstandingRead = false;
if (dwBytesRead > 0) OnReadComplete();
StartOverlappedRead();
}
else if (Index == iWriteEvent)
{
if (!GetOverlappedResult(
g_hChildStd_IN_Wr, // handle to pipe
&WriteOverlapped, // OVERLAPPED structure
&dwBytesWritten, // bytes transferred
FALSE)) // do not wait
ErrorExit(TEXT("GetOverlappedResult"));
if (dwBytesWritten != dwBytesToWrite) ErrorExit(TEXT("Write incomplete."));
OutstandingWrite = false;
}
else ErrorExit(TEXT("WaitForMultipleObjects indexing"));
}
void WriteToPipe(string text)
{
BOOL bSuccess = FALSE;
printf("Writing: %s\n", text.c_str());
if (!OverlappedStdInWr)
{
bSuccess = WriteFile(g_hChildStd_IN_Wr, text.c_str(), (DWORD)text.length(), &dwBytesWritten, NULL);
if (!bSuccess) ErrorExit(TEXT("WriteToPipe"));
return;
}
else
{
while (OutstandingWrite) WaitForIO(true); // Can only have one outstanding write at a time.
WriteOverlapped.Offset = 0;
WriteOverlapped.OffsetHigh = 0;
WriteOverlapped.Pointer = nullptr;
if (text.length() > BUFSIZE) ErrorExit(TEXT("Attempt to write too long a message!"));
CopyMemory(chWriteBuf, text.c_str(), text.length());
dwBytesToWrite = text.length();
bSuccess = WriteFile(g_hChildStd_IN_Wr, chWriteBuf, dwBytesToWrite, &dwBytesWritten, &WriteOverlapped);
if (bSuccess) return;
if (!bSuccess)
{
if (GetLastError() == ERROR_IO_PENDING) {
OutstandingWrite = true;
return;
}
ErrorExit(TEXT("WriteToPipe"));
}
}
}
void OnReadComplete()
{
chReadBuf[dwBytesRead] = '\0';
printf("Rx: ");
for (DWORD ii = 0; ii < dwBytesRead; ii++)
{
if (chReadBuf[ii] >= 0x20 && chReadBuf[ii] <= 0x7e) printf("%c", chReadBuf[ii]);
else
{
printf("\\0x%02X", chReadBuf[ii]);
}
if (chReadBuf[ii] == '\n') printf("\n");
}
printf("\n");
}
void StartOverlappedRead()
{
int loops = 0;
for (;; loops++)
{
if (loops > 10) ErrorExit(TEXT("Read stuck in loop"));
assert(!OutstandingRead);
ReadOverlapped.Offset = 0;
ReadOverlapped.OffsetHigh = 0;
ReadOverlapped.Pointer = nullptr;
BOOL Success = ReadFile(g_hChildStd_OUT_Rd, chReadBuf, BUFSIZE - 1, &dwBytesRead, &ReadOverlapped);
if (!Success && GetLastError() != ERROR_IO_PENDING)
ErrorExit(TEXT("ReadFile"));
if (Success)
{
if (dwBytesRead > 0)
OnReadComplete();
continue;
}
else {
OutstandingRead = true; return;
}
}
}
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.
{
BOOL bSuccess = FALSE;
if (!OverlappedStdOutRd)
{
for (;;)
{
DWORD total_available_bytes;
if (FALSE == PeekNamedPipe(g_hChildStd_OUT_Rd,
0,
0,
0,
&total_available_bytes,
0))
{
ErrorExit(TEXT("ReadFromPipe - peek"));
return;
}
else if (total_available_bytes == 0)
{
// printf("No new pipe data to read at this time.\n");
return;
}
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chReadBuf, BUFSIZE - 1, &dwBytesRead, NULL);
if (!bSuccess) ErrorExit(TEXT("ReadFromPipe"));
if (dwBytesRead == 0) return;
OnReadComplete();
}
}
else
{
if (!OutstandingRead) StartOverlappedRead();
WaitForIO(false);
}
}
void Create()
{
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;
if (!OverlappedStdOutRd)
{
// As per the MS example, create anonymous pipes
// 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"));
}
else
{
// Create overlapped I/O pipes (only one side is overlapped).
if (!MyCreatePipeEx(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, 0))
ErrorExit(TEXT("Stdout MyCreatePipeEx"));
ZeroMemory(&ReadOverlapped, sizeof(ReadOverlapped));
ReadOverlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); // Manual-reset event, unnamed, initially signalled.
if (ReadOverlapped.hEvent == NULL)
ErrorExit(TEXT("CreateEvent Read"));
}
// 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"));
if (!OverlappedStdInWr)
{
// 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"));
}
else
{
// Create overlapped I/O pipes (only one side is overlapped).
if (!MyCreatePipeEx(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0, 0, FILE_FLAG_OVERLAPPED))
ErrorExit(TEXT("Stdin MyCreatePipeEx"));
ZeroMemory(&WriteOverlapped, sizeof(WriteOverlapped));
WriteOverlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); // Manual-reset event, unnamed, initially signalled.
if (WriteOverlapped.hEvent == NULL)
ErrorExit(TEXT("CreateEvent Write"));
}
// 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.
TCHAR* szMutableCmdline = new TCHAR[1024];
ZeroMemory(szMutableCmdline, 1024 * sizeof(TCHAR));
CopyMemory(szMutableCmdline, szCmdline, _tcslen(szCmdline) * sizeof(TCHAR));
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,
szMutableCmdline, // 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);
}
}
int main()
{
printf("Launching...\n");
Create();
Sleep(500);
ReadFromPipe();
Sleep(250);
WriteToPipe("A\r\n");
Sleep(250);
ReadFromPipe();
WriteToPipe("\r\n");
Sleep(250);
ReadFromPipe();
WriteToPipe("X\r\n");
Sleep(250);
ReadFromPipe();
Sleep(250);
ReadFromPipe();
printf("Press any key to exit.\n");
_getch();
// TODO: Not doing proper cleanup in this test app. Overlapped I/O, CloseHandles, etc. are outstanding. Bad.
return 0;
}
And child code can be as simple as:
#include <conio.h>
int main()
{
printf("Hello!\n");
_getch();
printf("Bye!\n");
return 0;
}
Edit: As #Rbmm points out, _getch() uses ReadConsoleInput(). I assume it uses CONIN$ as opposed to STDIN. So the question becomes: can I redirect CONIN$ or have the parent process write to it?
In child process after printf you can add fflush(stdout);. This will immediately transfer data from stdout buffer to pipe. In some configurations stdout buffer data is automatically flushed on end of line character \n, but I'm not sure if it is in this case - probably not.
If your child should read data from pipe (not from console) use getchar, fgets, fread, fscanf giving them stdin as stream argument.
int main()
{
printf("Hello!\n");
fflush(stdout);
getchar();
printf("Bye!\n");
fflush(stdout);
return 0;
}
And you don't have dead lock. Your child just waits for char from console. Press Enter key to revive it.

Sending AT commands to USR modem in C

I was wondering if any of you know what I'm doing wrong here? So I have this program in C, that sends AT commands to a modem. These commands have been tested on hyperterminal and work fine, but when sendind them through the modem I get, first, and "OK" for the first "AT" command, which is good, but then, upon sending the next command, the modem answers with "AT+CC"...which I have no idea what it means. Any help is appreciated.
Source:
void sendSMS(const char* port, const char* number, const char* baud)
{
HANDLE hComm;
DCB dcb;
BOOL fSuccess;
hComm = CreateFile(port, //port name
GENERIC_READ | GENERIC_WRITE, //Read/Write
0, // No Sharing
NULL, // No Security
OPEN_EXISTING,// Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
if (hComm == INVALID_HANDLE_VALUE)
{
printf("ERROR: Cannot open serial port\r\n");
return;
}
else
printf("STATUS: Serial port opened\r\n");
// Configure PORT
// Initialize the DCB structure.
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
// Build on the current configuration by first retrieving all current
// settings.
fSuccess = GetCommState(hComm, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("GetCommState failed with error %d.\n", GetLastError());
return;
}
// Fill in some DCB values and set the com state:
// 57,600 bps, 8 data bits, no parity, and 1 stop bit.
dcb.BaudRate = atoi(baud); // baud rate
dcb.ByteSize = 8; // data size, xmit and rcv
dcb.Parity = NOPARITY; // parity bit
dcb.StopBits = ONESTOPBIT; // stop bit
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fOutX = FALSE;
fSuccess = SetCommState(hComm, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("SetCommState failed with error %d.\n", GetLastError());
return;
}
// Get the comm config again.
fSuccess = GetCommState(hComm, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("GetCommState failed with error %d.\n", GetLastError());
return;
}
// End Config
sendATCommands(hComm, number, "This is NOT a test.");
CloseHandle(hComm);//Closing the Serial Port
}
void sendATCommands(HANDLE hComm, const char* number, const char message[])
{
char str[256];
if (!writeToPort(hComm, "AT\r\n")) //Hello modem
return;
if (!readFromPort(hComm)) // Must be OK
return;
if (!writeToPort(hComm, "AT+CMGF=1\r\n")) //Modem, prepare to send text messages
return;
if (!readFromPort(hComm)) // Must be OK again
return;
memset(str, 0, 256);
strcpy_s(str, "AT+CMGS=\"");
strcat_s(str, 256, number);
strcat_s(str, 256, "\"\r\n");
if (!writeToPort(hComm, str)) //Modem, here's the number to send the message to
return;
if (!readFromPort(hComm)) // Must be ">"
return;
memset(str, 0, 256);
strcpy_s(str, message);
strcat_s(str, 256, "^Z");
if (!writeToPort(hComm, str)) //Modem, communicate this to the number I gave you.
return;
if (!readFromPort(hComm)) // Must be CMGS: ##
return;
}
int writeToPort(HANDLE hComm, const char lpBuffer[])
{
DWORD dNoOFBytestoWrite; // No of bytes to write into the port
DWORD dNoOfBytesWritten = 0; // No of bytes written to the port
dNoOFBytestoWrite = sizeof(lpBuffer);
int Status = WriteFile(hComm, // Handle to the Serial port
lpBuffer, // Data to be written to the port
dNoOFBytestoWrite, //No of bytes to write
&dNoOfBytesWritten, //Bytes written
NULL);
if (Status == FALSE)
{
printf("ERROR: Cannot write to serial port\r\n");
}
else
printf("STATUS: Command %s \n written to port.\r\n", lpBuffer);
return Status;
}
int readFromPort(HANDLE hComm)
{
char TempChar; //Temporary character used for reading
char SerialBuffer[256];//Buffer for storing Rxed Data
DWORD NoBytesRead = 0;
int i = 0;
int status;
memset(SerialBuffer, 0, 256);
printf("STATUS: Waiting response...\r\n");
do
{
status = ReadFile(hComm, //Handle of the Serial port
&TempChar, //Temporary character
sizeof(TempChar),//Size of TempChar
&NoBytesRead, //Number of bytes read
NULL);
if (!status)
{
printf("ERROR: Cannot read from serial port\r\n");
break;
}
SerialBuffer[i] = TempChar;// Store Tempchar into buffer
i++;
}
while (NoBytesRead > 0);
if (status)
printf("PORT RESPONSE: %s \r\n", SerialBuffer);
return status;
}

Reading asynchronous pipe - loosing data [duplicate]

Modified code from
Named Pipe Server Using Overlapped I/O
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365603(v=vs.85).aspx
The server code is as follows:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <iostream>
#define CONNECTING_STATE 0
#define READING_STATE 1
#define INSTANCES 4
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
TCHAR chRequest[BUFSIZE];
DWORD cbRead;
TCHAR chReply[BUFSIZE];
DWORD cbToWrite;
DWORD dwState;
BOOL fPendingIO;
} PIPEINST, *LPPIPEINST;
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];
int _tmain(VOID)
{
DWORD i, dwWait, cbRet, dwErr;
BOOL fSuccess;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
for (i = 0; i < INSTANCES; i++)
{
hEvents[i] = CreateEvent(
NULL, // default security attribute
FALSE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hEvents[i] == NULL)
{
printf("CreateEvent failed with %d.\n", GetLastError());
return 0;
}
Pipe[i].oOverlap.hEvent = hEvents[i];
DWORD dwOpenMode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;
Pipe[i].oOverlap.Offset = 0;
Pipe[i].oOverlap.OffsetHigh = 0;
Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename,
dwOpenMode,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
INSTANCES,
BUFSIZE*sizeof(TCHAR),
BUFSIZE*sizeof(TCHAR),
PIPE_TIMEOUT,
NULL);
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with %d.\n", GetLastError());
return 0;
}
BOOL rc = ConnectNamedPipe(Pipe[i].hPipeInst, &Pipe[i].oOverlap); // Overlapped ConnectNamedPipe should return FALSE.
if (!rc && GetLastError() == ERROR_PIPE_CONNECTED) {
std::cout<<"pipe connected setting event " << std::endl;
rc = SetEvent(&Pipe[i].oOverlap.hEvent);
} else if (rc || GetLastError() != ERROR_IO_PENDING) {
std::cout<<"exiting... " << std::endl;
rc = CloseHandle(Pipe[i].hPipeInst);
return 0;
}
}// for INSTANCES
while (1)
{
dwWait = WaitForMultipleObjects(
INSTANCES, // number of event objects
hEvents, // array of event objects
FALSE, // does not wait for all
INFINITE); // waits indefinitely
i = dwWait - WAIT_OBJECT_0; // determines which pipe
if (i < 0 || i > (INSTANCES - 1))
{
printf("Index out of range.\n");
return 0;
}
fSuccess = GetOverlappedResult(
Pipe[i].hPipeInst, // handle to pipe
&Pipe[i].oOverlap, // OVERLAPPED structure
&cbRet, // bytes transferred
FALSE); // do not wait
std::cout<<"GetOverlappedResult " << cbRet;
std::cout<<" success " << fSuccess;
std::cout<<" state " << Pipe[i].dwState;
std::cout<<" GetLastError " << GetLastError() << std::endl;
fSuccess = ReadFile(
Pipe[i].hPipeInst,
Pipe[i].chRequest,
BUFSIZE*sizeof(TCHAR),
&Pipe[i].cbRead,
&Pipe[i].oOverlap);
if(!fSuccess)
std::wcout<<L" Error: "<< GetLastError() <<std::endl;
if (fSuccess && Pipe[i].cbRead != 0)
{
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = READING_STATE;
std::wcout<<L"Message " << Pipe[i].chRequest << std::endl;
continue;
}
dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING))
{
std::cout<<"Error IO is still pending" << std::endl;
Pipe[i].fPendingIO = TRUE;
continue;
}
break;
}
return 0;
}
The client code is as follows:
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <string>
#include <sstream>
#include <time.h>
#include <iostream>
#define BUFSIZE 4096
int _tmain(int argc, TCHAR *argv[])
{
HANDLE hPipe;
TCHAR chBuf[BUFSIZE];
BOOL fSuccess = FALSE;
DWORD cbRead, cbToWrite, cbWritten, dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
BOOL rc;
do {
hPipe = CreateFileW(lpszPipename, GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (hPipe == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_PIPE_BUSY) {
// wait for the pipe to become available
rc = WaitNamedPipeW(lpszPipename, NMPWAIT_USE_DEFAULT_WAIT);
if (!rc) return false;
} else {
return false;
}
}
} while (hPipe == INVALID_HANDLE_VALUE);
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
if ( ! fSuccess)
{
_tprintf( TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError() );
return -1;
}
//===================================================================
while(1)
{
std::cout<<"press a key to send " << std::endl;
_getch();
fSuccess = WriteFile(
hPipe,
"A",
1,
&cbWritten,
NULL);
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
fSuccess = WriteFile(
hPipe,
"B",
1,
&cbWritten,
NULL);
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
//Sleep(1000);
fSuccess = WriteFile(
hPipe,
"C",
1,
&cbWritten,
NULL);
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
fSuccess = WriteFile(
hPipe,
"D",
1,
&cbWritten,
NULL);
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
}// loop
printf("\n<End of message, press ENTER to terminate connection and exit>");
_getch();
CloseHandle(hPipe);
return 0;
}
The server never receives message A but B, C, D are received OK.
If you uncomment //Sleep(1000) in the client, only B and D are received.
Any idea as to why this happens? The server output with out the sleep is as follows:
GetOverlappedResult 0 success 1 state 0 GetLastErr
or 997
Error: 997
Error IO is still pending
GetOverlappedResult 1 success 1 state 0 GetLastErr
or 997
Message B
GetOverlappedResult 1 success 1 state 1 GetLastErr
or 997
Message C
GetOverlappedResult 1 success 1 state 1 GetLastErr
or 997
Message D
GetOverlappedResult 1 success 1 state 1 GetLastErr
or 997
Error: 997
Error IO is still pending
In the server's read loop, you are discarding any data that arrives asynchronously.
After GetOverlappedResult() has reported that the pending I/O operation is complete, the buffer contains the data from that operation. You're ignoring that data and issuing a new read operation into the same buffer.
The only reason you get any of the messages is that (on most runs) all four messages will be written into the pipe's internal buffer at the same time. The first message arrives asynchronously, so you miss it, but the remaining three messages are already in the pipe so those reads can complete immediately.

How to make non-blocking OpenSSL connection?

I want make a non-blocking OpenSSL connection
On this connection - if no data available for read, then entire program execution flow make stop on SSL_read(). I want so that if no data available for read it give me the returns values like WANT_READ and i know no more data available.
char *sslRead (connection *c)
{
const int readSize = 1024;
char *rc = NULL;
int r;
int received, count = 0;
int ReallocSize = 0;
char buffer[1024];
if (c)
{
while (1)
{
if (!rc)
{
rc = malloc (readSize + 1);
if (rc == NULL)
printf("the major error have happen. leave program\n");
}
else
{
ReallocSize = (count + 1) * (readSize + 1);
rc = realloc (rc, ReallocSize);
}
// if i have no data available for read after reading data,
// this call will not return anything and wait for more data
// i want change this non blocking connections
received = SSL_read (c->sslHandle, buffer, readSize);
buffer[received] = '\0';
if (received <= 0)
{
printf(" received equal to or less than 0\n");
switch (SSL_get_error(c->sslHandle, r))
{
case SSL_ERROR_NONE:
printf("SSL_ERROR_NONE\n");
break;
case SSL_ERROR_ZERO_RETURN:
printf("SSL_ERROR_ZERO_RETURN\n");
break;
case SSL_ERROR_WANT_READ:
printf("SSL_ERROR_WANT_READ\n");
break;
default:
printf("error happens %i\n", r);
}
break;
}
count++;
}
}
return rc;
}
here is how i make connection
connection *sslConnect (void)
{
connection *c;
c = malloc (sizeof (connection));
c->sslHandle = NULL;
c->sslContext = NULL;
c->socket = tcpConnect ();
if (c->socket)
{
// Register the error strings for libcrypto & libssl
SSL_load_error_strings ();
// Register the available ciphers and digests
SSL_library_init ();
// New context saying we are a client, and using SSL 2 or 3
c->sslContext = SSL_CTX_new (SSLv23_client_method ());
if (c->sslContext == NULL)
ERR_print_errors_fp (stderr);
// Create an SSL struct for the connection
c->sslHandle = SSL_new (c->sslContext);
if (c->sslHandle == NULL)
ERR_print_errors_fp (stderr);
// Connect the SSL struct to our connection
if (!SSL_set_fd (c->sslHandle, c->socket))
ERR_print_errors_fp (stderr);
// Initiate SSL handshake
if (SSL_connect (c->sslHandle) != 1)
ERR_print_errors_fp (stderr);
}
else
{
perror ("Connect failed");
}
return c;
}
thanks you very much.
Creating a non-blocking socket is a pre-requisite to a non-blocking connect...
The following steps summarize: (see complete description in site linked below)
1) Call the fcntl() API to retrieve the socket descriptor's current flag settings into a local variable.
2) In that local variable, set the O_NONBLOCK (non-blocking) flag on. (being careful not to tamper with the other flags)
3) Call the fcntl() API to set the flags for the descriptor to the value in our local variable.
( read more on non-blocking sockets techniques here )
Assuming an existing socket, the following implements the steps outlined above:
BOOL SetSocketBlockingEnabled(SOCKET fd, BOOL blocking)
{
if (fd < 0) return FALSE;
#ifdef WIN32
unsigned long mode = blocking ? 0 : 1;
return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? TRUE : FALSE;
#else
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) return false;
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
return (fcntl(fd, F_SETFL, flags) == 0) ? TRUE : FALSE;
#endif
}
Once you have a non-blocking socket, then see this post explaining how to do a non-blocking connect

What does it mean for ReadFile to be "completing asynchronously", and why is it an error?

I'm (synchronously) reading serial input in Windows using ReadFile(), but instead of waiting for the serial port to have input then returning that as I thought it should, ReadFile() instead returns immediately with a value of FALSE, and a GetLastError() of 0. (Yes, I'm certain I have the right error code and am not making syscalls in between).
The ReadFile() documentation says that when the function "is completing asynchronously, the return value is zero (FALSE)." How is it that a synchronous read can be completing asychronously? Why would this be an error? It's worth noting that the data read is garbage data, as one might expect.
More generally, how can I force ReadFile() to behave like a simple synchronous read of a serial port, or at least behave something like the UNIX read()?
Edit: Here is some source code:
HANDLE my_connect(char *port_name)
{
DCB dcb;
COMMTIMEOUTS timeouts;
HANDLE hdl = CreateFile(port_name,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
GetCommState(port_name, &dcb);
dcb.BaudRate = 115200;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
if(SetCommState(hdl, &dcb) == 0)
{
fprintf(stderr, "SetCommState failed with error code %d.\n",
GetLastError());
return (HANDLE) -1;
}
/* TODO: Set a variable timeout. */
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 5000; /* wait 5s for input */
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 5000;
if(SetCommTimeouts(hdl, &timeouts) == 0)
{
fprintf(stderr, "SetCommTimeouts failed with error code %d.\n",
GetLastError());
return (HANDLE) -1;
}
return hdl;
}
int my_disconnect(HANDLE hdl)
{
return CloseHandle(hdl);
}
int my_send(HANDLE hdl, char *cmd)
{
DWORD nb = 0;
if(WriteFile(hdl, cmd, strlen(cmd), &nb, NULL) == 0)
{
fprintf(stderr, "WriteFile failed with error code %d.\n",
GetLastError());
return -1;
}
return (int) nb;
}
int my_receive(HANDLE hdl, char *dst, int dstlen)
{
int i;
DWORD r;
BOOL err;
char c = '\0';
for (i = 0; i < dstlen; err = ReadFile(hdl, &c, 1, &r, NULL))
{
if (err == 0)
{
fprintf(stderr, "ReadFile failed with error code %d.\n",
GetLastError());
return -1;
}
if (r > 0)
{
dst[i++] = c;
if (c == '\n') break;
}
}
if (i == dstlen)
{
fprintf(stderr, "Error: read destination buffer not large enough.\
Recommended size: 256B. Your size: %dB.\n", dstlen);
return -1;
}
else
{
dst[i] = '\0'; /* null-terminate the string. */
}
return i;
}
And my test code:
HANDLE hdl = my_connect("COM4");
char *cmd = "/home\n"; /* basic command */
char reply[256];
my_send(hdl, cmd);
my_receive(hdl, reply, 256);
puts(reply);
It's not completing asynchronously. If it were, GetLastError would return ERROR_IO_PENDING.
To do synchronous I/O, open the file without FILE_FLAG_OVERLAPPED.
It should not be possible for ReadFile to fail without a valid GetLastError code. ReadFile only returns false when the driver sets a non-success status code.

Resources