Using Windows named pipes with ffmpeg pipes - c++

I'm trying to use Windows Pipes to write data to input pipes in FFmpeg. I'm using the following command for FFmpeg:
ffmpeg -r 24 -pix_fmt rgba -s 1280x720 -f rawvideo -y -i \\.\pipe\videopipe -f s16le -ac 1 -ar 44100 -i \\.\pipe\audiopipe -acodec pcm_s16le -ac 1 -b:a 320k -ar 44100 -vf vflip -vcodec mpeg1video -qscale 4 -bufsize 500KB -maxrate 5000KB OUTPUT_FILE
I tried connecting to it using the CreateFile() method but that doesn't seem to work. I've also tried with CreateNamedPipe() after starting to run the ffmpeg command but then it seems to wait for something else to connect.
I don't know in which order I have to call those two. Do I first need to create a Windows pipe and use the same name in FFmpeg or do I need to first invoke FFmpeg with the named pipes and then connect to it using CreateFile()?

I've developed code to do it, maybe it will help. In my case i'm streaming an image and playing it in ffplay. I launch 3 threads one that creates pipe (in demo pipes the one who executes 'initializePipe') one that listens to pipe creation and launch ffplay and the writer thread who writes to the pipe the image.
demoPipes.cpp
#include "demoPipes.h"
using namespace std;
#define MAX_THREADS 100
//#define BUF_SIZE 255
void ErrorHandler(LPTSTR lpszFunction);
// Sample custom data structure for threads to use.
// This is passed by void pointer so it can be any data type
// that can be passed using a single void pointer (LPVOID).
/*typedef struct MyData {
int val1;
int val2;
} MYDATA, *PMYDATA;
*/
typedef struct
{
HANDLE pipe;
LPCSTR MEDIA_PIPE;
} Session;
int _tmain()
{
Session* s = (Session*)malloc(sizeof(Session));
s->pipe = NULL;
s->MEDIA_PIPE = "\\\\.\\pipe\\screenRec";
DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
// Create MAX_THREADS worker threads.
for( int i=0; i<MAX_THREADS; i++ )
{
//create pipe initializer thread
if(i==0){
hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
initializePipe, // thread function name
(LPVOID) s, // argument to thread function
0, // use default creation flags
&dwThreadIdArray[i]); // returns the thread identifier
}
//create writer thread
if(i>1){
PipeMetaData* pipe_data1 = fetch_file("D:\\images.png", s->pipe);
//PipeMetaData* pipe_data = createPipeMetaData(pipe_data1->file_vector,pipe_data1->byte_size);
//pipe_data1->pipe =s->pipe;
hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
send_media_to_recorder, // thread function name
pipe_data1, // argument to thread function
0, // use default creation flags
&dwThreadIdArray[i]); // returns the thread identifier
}
//create recorder thread
if(i==1){
hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
recorder_pipe_function, // thread function name
0, // argument to thread function
0, // use default creation flags
&dwThreadIdArray[i]); // returns the thread identifier
}
// Check the return value for success.
// If CreateThread fails, terminate execution.
// This will automatically clean up threads and memory.
if (dwThreadIdArray[i] == NULL)
{
ErrorHandler(TEXT("CreateThread"));
ExitProcess(3);
}
} // End of main thread creation loop.
//for close RTP
// Wait until all threads have terminated.
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);
// Close all thread handles and free memory allocations.
CloseHandle(s->pipe);
for(int i=0; i<MAX_THREADS; i++)
{
CloseHandle(hThreadArray[i]);
}
getchar();
return 0;
}
void ErrorHandler(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code.
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 );
// Display the error message.
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);
// Free error-handling buffer allocations.
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
clientpipe.cpp
#include "client_pipe.h"
using namespace std;
DWORD WINAPI recorder_pipe_function( LPVOID lpParam){
const char* MEDIA_PIPE = "\\\\.\\pipe\\screenRec";
//LPCWSTR MEDIA_PIPE_wchar = L"\\\\.\\pipe\\screenRec";
//waits for server to initialize the media pipe.
//wait_for_media_pipe(MEDIA_PIPE,10);
WaitNamedPipeA(MEDIA_PIPE,1000);
start_recording_session(MEDIA_PIPE);
return 0;
}
void start_recording_session(const char* media_pipe){
//initialize recording command
char* command = (char*)malloc(sizeof(char)*100+1);
command[0] ='\0';
strcpy_s(command,sizeof(char)*100+1,"start ffplay -i ");
//strcpy_s(command,sizeof(char)*100+1,"ffmpeg -i ");
strcat_s(command,sizeof(char)*100+1, media_pipe);
//strcat_s(command,sizeof(char)*100+1, " -f matroska D:\\djhfifj.mkv");
std::cout << "Executing ffmpeg command\n";
int i=system (command);
std::cout << "The value returned was: " << i << std::endl;
}
ServerPipe.cpp
#include "server_pipe.h"
using namespace std;
typedef struct
{
HANDLE pipe;
LPCSTR MEDIA_PIPE;
} Session;
DWORD WINAPI initializePipe( LPVOID lpParam ) {
printf("Creating an instance of a named pipe...");
Session* s = (Session*) lpParam;
LPCSTR MEDIA_PIPE = s->MEDIA_PIPE;
// Create a pipe to send data
s->pipe = CreateNamedPipeA(
MEDIA_PIPE, // name of the pipe
PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
PIPE_TYPE_BYTE, // send data as a byte stream
1, // only allow 1 instance of this pipe
0, // no outbound buffer
0, // no inbound buffer
0, // use default wait time
NULL // use default security attributes
);
if (s->pipe == NULL ||s->pipe == INVALID_HANDLE_VALUE) {
wcout << "Failed to create outbound pipe instance.";
// look up error code here using GetLastError()
return NULL;
}
wcout << "Waiting for a client to connect to the pipe..." << endl;
// This call blocks until a client process connects to the pipe
BOOL result = ConnectNamedPipe(s->pipe, NULL);
if (!result) {
wcout << "Failed to make connection on named pipe." << endl;
// look up error code here using GetLastError()
CloseHandle(s->pipe); // close the pipe
system("pause");
return NULL;
}
}
PipeMetaData* fetch_file(char* file_path,HANDLE pipe){
PipeMetaData* data = (PipeMetaData*) malloc(sizeof(PipeMetaData));
//opening file
ifstream infile;
infile.open(file_path,std::ios::binary);
infile.seekg(0,std::ios::end);
//get file byte size
data->byte_size = infile.tellg();
//fetch data to send
data->file_vector =(char*) malloc(sizeof(char)*data->byte_size);
infile.seekg(0,std::ios::beg);
infile.read(&(data->file_vector)[0],data->byte_size);
data->packet = &(data->file_vector)[0];
data->pipe = pipe;
wcout<<data->byte_size<<endl;
return data;
}
DWORD WINAPI send_media_to_recorder(LPVOID lpParam){
PipeMetaData* p = (PipeMetaData*) lpParam;
// This call blocks until a client process reads all the data
DWORD numBytesWritten = 0;
BOOL result = WriteFile(
p->pipe, // handle to our outbound pipe
p->packet,//&(p->file_vector)[0], // data to send
p->byte_size, // length of data to send (bytes)
&numBytesWritten, // will store actual amount of data sent
NULL // not using overlapped IO
);
if (result) {
wcout << "Number of bytes sent: " << numBytesWritten << endl;
} else {
wcout << "Failed to send data." << endl;
// look up error code here using GetLastError()
GetLastError();
}
free(p);
return 0;
}

Related

Can't create named pipe for remote: "The handle is invalid."

I am trying to create named pipe server for remote using 127.0.0.1 but after I run CreateNamedPipe it failed with error 6 which means:
The handle is invalid
I also tried to add PIPE_ACCEPT_REMOTE_CLIENTS but it doesn't work.
This is my server code:
LPCWSTR pwsPipePrefix = L"\\\\127.0.0.1\\pipe\\";
LPCWSTR pwsPipeName = NULL;
HANDLE hServerPipe;
BOOL bSuccess;
DWORD bytesWritten = 0;
DWORD messageLenght;
BOOL bAttemptImpersonation = TRUE;
// Read name pipe name to from STDIN
wchar_t wcInputPipeName[200];
wprintf(L"[*] Pipe name to connect to (max 200 chars): ");
std::wcin.getline(wcInputPipeName, 200);
int iInputLen = wcslen(wcInputPipeName);
if (iInputLen > 200) {
wprintf(L"[-] More than 200 chars (%d).. exiting.\n", iInputLen);
return 1;
}
std::wstring wsConcat = pwsPipePrefix + (std::wstring)wcInputPipeName;
pwsPipeName = wsConcat.c_str();
wprintf(L"[*] Creating named pipe: %ls\n", pwsPipeName);
hServerPipe = CreateNamedPipe(
pwsPipeName, // name of our pipe, must be in the form of \\.\pipe\<NAME>
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, // open mode, specifying a duplex pipe so server and client can send and receive data
PIPE_TYPE_MESSAGE | PIPE_ACCEPT_REMOTE_CLIENTS, // MESSAGE mode to send/receive messages in discrete units (instead of a byte stream)
PIPE_UNLIMITED_INSTANCES, // number of instanced for this pipe, 1 is enough for our use case
2048, // output buffer size
2048, // input buffer size
0, // default timeout value, equal to 50 milliseconds
NULL // use default security attributes
);
wprintf(L"[*] Waiting for incoming connections...");
bSuccess = ConnectNamedPipe(hServerPipe, NULL);
if (bSuccess) {
wprintf(L"Got one.\n");
}
else wprintf(L"Error: %d", GetLastError());
*This piece of code was taken from this project.

Microsoft VC++, vsnprintf, and Pipes (IO) Bug

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.

Windows named pipe client WriteFile fails with error 232 ERROR_NO_DATA only with first client [duplicate]

I've got a C++ program that is creating a named pipe to write data to. Some customers have reported a situation where the client connects to the named pipe but the server end fails to write the data (with ERROR_NO_DATA).
This error code isn't really explained in any MSDN page that I could find; does anyone have any ideas on how to fix this? Or what the cause is?
Open code:
ostringstream pipeName;
pipeName << "\\\\.\\pipe\\unique-named-pipe-" << GetCurrentProcessId();
pipeHandle = CreateNamedPipeA(
pipeName.str().c_str(), // pipe name
PIPE_ACCESS_DUPLEX, // open mode
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, // pipe mode
PIPE_UNLIMITED_INSTANCES, // max instances
512, // output buffer size
512, // input buffer size
0, // use default timeouts
NULL); // security attributes
if (INVALID_HANDLE_VALUE == pipeHandle)
{
THROW("Failed to create named pipe", GetLastError());
}
cout << "Pipe ready" << endl;
// Wait for a client to connect to the pipe
BOOL status = ConnectNamedPipe(pipeHandle, NULL);
if (!status)
{
DWORD lastError = GetLastError();
if (ERROR_PIPE_CONNECTED != lastError)
{
THROW("Failed to wait for client to open pipe", lastError);
}
else
{
// Ignore, see MSDN docs for ConnectNamedPipe() for details.
}
}
Writing code:
// response is a std::string
int writeOffset = 0;
int length = response.length();
while ((int) response.length() > writeOffset)
{
DWORD bytesWritten;
BOOL status = WriteFile(
pipeHandle,
response.c_str() + writeOffset,
length - writeOffset,
&bytesWritten,
NULL);
if (!status)
{
// This sometimes fails with ERROR_NO_DATA, why??
THROW("Failed to send via named pipe", GetLastError());
}
writeOffset += bytesWritten;
}
Throw macro
#define THROW(message, errorCode) \
{ \
fprintf(stderr, "%s: line: %d file: %s error:0x%x\n", \
message, __LINE__, __FILE__, errorCode); \
fflush(stderr); \
throw message; \
} \
Thanks!
Looking at WinError.h, which is where this and other error codes are defined:
//
// MessageId: ERROR_NO_DATA
//
// MessageText:
//
// The pipe is being closed.
//
#define ERROR_NO_DATA 232L
Sounds like the client has already closed their end of the pipe - perhaps the client code thinks it has already got the full string, closes their end, while the code above continues to try to write?

PipeServer not sending text every second

This is the "PipeServer" in the 1st application:
// Open Pipe and wait until ControlProgram is connecting
Pipe.Out = INVALID_HANDLE_VALUE;
const wchar_t *data = L"*** Hello Pipe World ***";
DWORD numBytesWritten = 0;
DWORD timerinit = GetTickCount();;
while (1)
{
DWORD timer = GetTickCount();
if ((timer - timerinit) > 1000)
{
timerinit = timer;
if (ConnectNamedPipe(Pipe.Out, NULL))
{
WriteFile(
Pipe.Out, // handle to our outbound pipe
data, // data to send
wcslen(data) * sizeof(wchar_t), // length of data to send (bytes)
&numBytesWritten, // will store actual amount of data sent
NULL // not using overlapped IO
);
}
else
{
CloseHandle(Pipe.Out);
Pipe.Out = INVALID_HANDLE_VALUE;
do
{
Pipe.Out = CreateNamedPipeW(
L"\\\\.\\pipe\\mypipe", // name of the pipe
PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
PIPE_TYPE_BYTE, // send data as a byte stream
1, // only allow 1 instance of this pipe
0, // no outbound buffer
0, // no inbound buffer
0, // use default wait time
NULL // use default security attributes
);
}
while (Pipe.Out == INVALID_HANDLE_VALUE);
}
}
}
This is the "PipeClient" Application:
///// CLIENT PROGRAM /////
#include <iostream>
#include <windows.h>
using namespace std;
int main(int argc, const char **argv)
{
wcout << "Connecting to pipe..." << endl;
// Open the named pipe
// Most of these parameters aren't very relevant for pipes.
HANDLE pipe = CreateFileW(
L"\\\\.\\pipe\\mypipe",
GENERIC_READ, // only need read access
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (pipe == INVALID_HANDLE_VALUE)
{
wcout << "Failed to connect to pipe." << endl;
// look up error code here using GetLastError()
system("pause");
return 1;
}
wcout << "Reading data from pipe..." << endl;
while (1)
{
// The read operation will block until there is data to read
wchar_t buffer[128];
DWORD numBytesRead = 0;
BOOL result = ReadFile(
pipe,
buffer, // the data from the pipe will be put here
127 * sizeof(wchar_t), // number of bytes allocated
&numBytesRead, // this will store number of bytes actually read
NULL // not using overlapped IO
);
if (result)
{
buffer[numBytesRead / sizeof(wchar_t)] = '\0'; // null terminate the string
wcout << "Number of bytes read: " << numBytesRead << endl;
wcout << "Message: " << buffer << endl;
FlushFileBuffers(pipe);
}
else
{
wcout << "Failed to read data from the pipe." << endl;
wcout << result << endl;
CloseHandle(pipe);
break;
}
}
// Close our pipe handle
wcout << "Done." << endl;
system("pause");
return 0;
}
The server should wait for the client to connect and then send the defined message every 1 second.
The client should be able to start/restart whenever it wants.
But whenever i start the client it receives the message one time, then exits with error while reading from the pipe.
result returns 0.
UPDATE (for those who want to know how it's working now)
This is the "updated" PipeServer code:
// Open Pipe and wait until ControlProgram is connecting
Pipe.Out = INVALID_HANDLE_VALUE;
DWORD numBytesWritten = 0;
DWORD timerinit = GetTickCount();
bool connected = false;
bool writesucc = false;
bool initial = true;
while (1)
{
DWORD timer = GetTickCount();
wchar_t data[100];
if (!initial)
{
swprintf_s(data, 100, L"Time: %d", timer); // use L"" prefix for wide chars
}
else
{
swprintf_s(data, 100, L"Welcome from your Pipe Server"); // use L"" prefix for wide chars
}
if ((timer - timerinit) > 1000)
{
timerinit = timer;
if (!connected)
{
connected = ConnectNamedPipe(Pipe.Out, NULL);
}
if (connected)
{
writesucc = WriteFile(
Pipe.Out, // handle to our outbound pipe
data, // data to send
wcslen(data) * sizeof(wchar_t), // length of data to send (bytes)
&numBytesWritten, // will store actual amount of data sent
NULL // not using overlapped IO
);
if (writesucc) initial = false;
}
if ((!writesucc) || (Pipe.Out == INVALID_HANDLE_VALUE) || (!connected))
{
initial = true;
CloseHandle(Pipe.Out);
Pipe.Out = INVALID_HANDLE_VALUE;
do
{
Pipe.Out = CreateNamedPipeW(
L"\\\\.\\pipe\\mypipe", // name of the pipe
PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
PIPE_TYPE_BYTE, // send data as a byte stream
1, // only allow 1 instance of this pipe
0, // no outbound buffer
0, // no inbound buffer
0, // use default wait time
NULL // use default security attributes
);
}
while (Pipe.Out == INVALID_HANDLE_VALUE);
}
}
}
Your client fails because the server is calling ConnectNamedPipe() every second and then closing the connection if ConnectNamedPipe() fails because there is already a client connected and you are only allowing 1 client to be connected at a time. This is stated in the ConnectNamedPipe() documentation:
A named pipe server process can use ConnectNamedPipe with a newly created pipe instance. It can also be used with an instance that was previously connected to another client process; in this case, the server process must first call the DisconnectNamedPipe function to disconnect the handle from the previous client before the handle can be reconnected to a new client. Otherwise, ConnectNamedPipe returns zero, and GetLastError returns ERROR_NO_DATA if the previous client has closed its handle or ERROR_PIPE_CONNECTED if it has not closed its handle.
You are only writing your message one time - when ConnectNamedPipe() succeessfully creates a new connection. You are not looping the writing after that point, your next iteration calls ConnectNamedPipe() again and fails.
You are not checking any error codes from GetLastError(), otherwise you would have noticed that situation after reading the documentation to find out what the error codes mean.
Try something more like this instead:
const wchar_t *data = L"*** Hello Pipe World ***";
DWORD numBytesWritten;
Pipe.Out = CreateNamedPipeW(
L"\\\\.\\pipe\\mypipe", // name of the pipe
PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
PIPE_TYPE_BYTE, // send data as a byte stream
1, // only allow 1 instance of this pipe
0, // no outbound buffer
0, // no inbound buffer
0, // use default wait time
NULL // use default security attributes
);
if (Pipe.Out == INVALID_HANDLE_VALUE)
{
// error creating pipe
}
else
{
while (should wait for a client to connect)
{
if (!ConnectNamedPipe(Pipe.Out, NULL))
{
// if ERROR_PIPE_CONNECTED then a client is actually connected!
if (GetLastError() != ERROR_PIPE_CONNECTED)
{
// error connecting a client
break;
}
}
DWORD timerinit = GetTickCount();
while (should write to the client)
{
DWORD timer = GetTickCount();
// GetTickCount() wraps back to 0 every 49.7 days, so account for that...
DWORD elapsed = (timer >= timerinit) ? (timer - timerinit) : ((MAXDWORD - timerinit) + timer);
if (elapsed >= 1000)
{
timerinit = timer;
if (!WriteFile(
Pipe.Out, // handle to our outbound pipe
data, // data to send
wcslen(data) * sizeof(wchar_t), // length of data to send (bytes)
&numBytesWritten, // will store actual amount of data sent
NULL // not using overlapped IO
)
{
// error writing to client
break;
}
}
}
DisconnectNamedPipe(Pipe.Out);
}
CloseHandle(Pipe.Out);
}

WriteFile on a named pipe sometimes returns ERROR_NO_DATA

I've got a C++ program that is creating a named pipe to write data to. Some customers have reported a situation where the client connects to the named pipe but the server end fails to write the data (with ERROR_NO_DATA).
This error code isn't really explained in any MSDN page that I could find; does anyone have any ideas on how to fix this? Or what the cause is?
Open code:
ostringstream pipeName;
pipeName << "\\\\.\\pipe\\unique-named-pipe-" << GetCurrentProcessId();
pipeHandle = CreateNamedPipeA(
pipeName.str().c_str(), // pipe name
PIPE_ACCESS_DUPLEX, // open mode
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, // pipe mode
PIPE_UNLIMITED_INSTANCES, // max instances
512, // output buffer size
512, // input buffer size
0, // use default timeouts
NULL); // security attributes
if (INVALID_HANDLE_VALUE == pipeHandle)
{
THROW("Failed to create named pipe", GetLastError());
}
cout << "Pipe ready" << endl;
// Wait for a client to connect to the pipe
BOOL status = ConnectNamedPipe(pipeHandle, NULL);
if (!status)
{
DWORD lastError = GetLastError();
if (ERROR_PIPE_CONNECTED != lastError)
{
THROW("Failed to wait for client to open pipe", lastError);
}
else
{
// Ignore, see MSDN docs for ConnectNamedPipe() for details.
}
}
Writing code:
// response is a std::string
int writeOffset = 0;
int length = response.length();
while ((int) response.length() > writeOffset)
{
DWORD bytesWritten;
BOOL status = WriteFile(
pipeHandle,
response.c_str() + writeOffset,
length - writeOffset,
&bytesWritten,
NULL);
if (!status)
{
// This sometimes fails with ERROR_NO_DATA, why??
THROW("Failed to send via named pipe", GetLastError());
}
writeOffset += bytesWritten;
}
Throw macro
#define THROW(message, errorCode) \
{ \
fprintf(stderr, "%s: line: %d file: %s error:0x%x\n", \
message, __LINE__, __FILE__, errorCode); \
fflush(stderr); \
throw message; \
} \
Thanks!
Looking at WinError.h, which is where this and other error codes are defined:
//
// MessageId: ERROR_NO_DATA
//
// MessageText:
//
// The pipe is being closed.
//
#define ERROR_NO_DATA 232L
Sounds like the client has already closed their end of the pipe - perhaps the client code thinks it has already got the full string, closes their end, while the code above continues to try to write?