Related
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.
I have the following code as part of another module that sends messages to the client. This was for IPC. Two dll's are loaded by the exe and these two need to communicate
In DLL-1 I have the following line of code as the server named pipe.
pipe = CreateNamedPipe("\\\\.\\pipe\\S2D8",PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED /**1-way, send only with overlapped IO*/,
PIPE_TYPE_MESSAGE,1,0,0, 0, NULL);
if( INVALID_HANDLE_VALUE != pipe )
{
log("Created Named Pipe as Serverl\n");
}
else
{
log("Cannot create Named Pipe as Server\n");
}
And somewhere else in the DLL-1 I have the following for the server
bool result = ConnectNamedPipe(pipe, NULL);
if (!result)
{
CloseHandle(pipe); // close the pipe
}
else
{
DWORD numWritten;
WriteFile(pipe,KeyBoardBuffer,strlen(KeyBoardBuffer) * sizeof(char),&numWritten,0);
log("Bytes writtern to pipe:%d\n",numWritten);
}
When I look at the logs, I can see the that named pipe. Good so far.
While in DLL-2 I have the following as the client part
log("Connecting to named pipe at client\n");
if(pipe2 == NULL || pipe2 == INVALID_HANDLE_VALUE)
{
pipe2 = CreateFile("\\\\.\\pipe\\S2D8", GENERIC_READ ,
FILE_SHARE_READ | FILE_SHARE_WRITE,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
if (pipe2 == INVALID_HANDLE_VALUE)
{
log("Cannot connect to named pipe at client%x\n", GetLastError());
CloseHandle(pipe2);
}
else
{
log("Connected to named pipe at client! Going to read!!!\n");
char buffer[256] = {'\0'};
DWORD numBytesRead = 0;
BOOL result = ReadFile(
pipe2,
buffer, // the data from the pipe will be put here
sizeof(buffer) * sizeof(char), // number of bytes allocated
&numBytesRead, // this will store number of bytes actually read
NULL // not using overlapped IO
);
if (result)
{
kbBuffer[numBytesRead / sizeof(char)] = '\0'; // null terminate the string
log( "Number of bytes read: %d\n",numBytesRead);
log(kbBuffer );
}
else
{
log("Failed to read data from the pipe.\n");
}
}
}
And in my logs, I can see the line "Connecting to named pipe at client" and then "Connected to named pipe at client! Going to read!!!", after that there is nothing in the log, everything seems stuck.
Is the naming convention of pipe correct? Or is there any security settings I have to define?
I am using VS2010, Win7 x64.
Any guidance is much appreciated.
You're calling the wrong method. The pipe is supposed to pre-exist, so you should be calling OpenFile(), not CreateFile().
Ah, I found the answer to the hang, I had to do a PeekNamedPipe(pipe2, NULL, 0, NULL, &bytesAvailable, NULL); and then check for the bytesAvailable to be greater than zero before I did a ReadFile()
Ok so below I have the code for named pipe server/client. I have the server placed on my Windows 8 computer and it creates a pipe and then waits for the client to connect. Then I start the client on my Windows 7 but it returns error 5 (Access denied, I think??) Can someone explain to me why it is giving me error 5 please?
Thnx in advance for all the answers
Server Code
#include "stdafx.h"
#include "windows.h"
#include <iostream>
using namespace std;
#define g_szPipeName "\\\\.\\pipe\\pipename" //Name given to the pipe
#define BUFFER_SIZE 1024 //1k
#define ACK_MESG_RECV "Message received successfully"
#define _CRT_SECURE_NO_WARNINGS
HANDLE hPipe;
int repeate() {
char szBuffer[BUFFER_SIZE];
DWORD cbBytes;
//We are connected to the client.
//To communicate with the client we will use ReadFile()/WriteFile()
//on the pipe handle - hPipe
//Read client message
BOOL bResult = ReadFile(
hPipe, // handle to pipe
szBuffer, // buffer to receive data
sizeof(szBuffer), // size of buffer
&cbBytes, // number of bytes read
NULL); // not overlapped I/O
if ((!bResult) || (0 == cbBytes))
{
printf("\nError occurred while reading from the client: %d", GetLastError());
CloseHandle(hPipe);
system("Pause");
return 1; //Error
}
else
{
printf("\nReadFile() was successful.");
}
printf("\nClient sent the following message: %s", szBuffer);
strcpy(szBuffer, ACK_MESG_RECV);
//Reply to client
bResult = WriteFile(
hPipe, // handle to pipe
szBuffer, // buffer to write from
strlen(szBuffer) + 1, // number of bytes to write, include the NULL
&cbBytes, // number of bytes written
NULL); // not overlapped I/O
if ((!bResult) || (strlen(szBuffer) + 1 != cbBytes))
{
printf("\nError occurred while writing to the client: %d", GetLastError());
CloseHandle(hPipe);
system("Pause");
return 1; //Error
}
else
{
printf("\nWriteFile() was successful.");
}
repeate();
}
int main(int argc, char* argv[])
{
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
hPipe = CreateNamedPipe(
g_szPipeName, // 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
NMPWAIT_USE_DEFAULT_WAIT,
&sa);
if (INVALID_HANDLE_VALUE == hPipe)
{
printf("\nError occurred while creating the pipe: %d", GetLastError());
system("Pause");
return 1; //Error
}
else
{
printf("\nCreateNamedPipe() was successful.");
}
printf("\nWaiting for client connection...");
//Wait for the client to connect
BOOL bClientConnected = ConnectNamedPipe(hPipe, NULL);
if (FALSE == bClientConnected)
{
printf("\nError occurred while connecting to the client: %d", GetLastError());
CloseHandle(hPipe);
system("Pause");
return 1; //Error
}
else
{
printf("\nConnectNamedPipe() was successful.");
}
repeate();
}
Client Code
#include "stdafx.h"
#include "windows.h"
#include <iostream>
#define g_szPipeName "\\\\MyComputerName\\pipe\\pipename" //Name given to the pipe
#define BUFFER_SIZE 1024 //1k
#define ACK_MESG_RECV "Message received successfully"
HANDLE hPipe;
int repeate() {
char szBuffer[BUFFER_SIZE];
printf("\nEnter a message to be sent to the server: ");
gets(szBuffer);
DWORD cbBytes;
//Send the message to server
BOOL bResult = WriteFile(
hPipe, // handle to pipe
szBuffer, // buffer to write from
strlen(szBuffer) + 1, // number of bytes to write, include the NULL
&cbBytes, // number of bytes written
NULL); // not overlapped I/O
if ((!bResult) || (strlen(szBuffer) + 1 != cbBytes))
{
printf("\nError occurred while writing to the server: %d", GetLastError());
CloseHandle(hPipe);
system("Pause");
return 1; //Error
}
else
{
printf("\nWriteFile() was successful.");
}
//Read server response
bResult = ReadFile(
hPipe, // handle to pipe
szBuffer, // buffer to receive data
sizeof(szBuffer), // size of buffer
&cbBytes, // number of bytes read
NULL); // not overlapped I/O
if ((!bResult) || (0 == cbBytes))
{
printf("\nError occurred while reading from the server: %d", GetLastError());
CloseHandle(hPipe);
system("Pause");
return 1; //Error
}
else
{
printf("\nReadFile() was successful.");
}
printf("\nServer sent the following message: %s", szBuffer);
repeate();
}
int main(int argc, char* argv[])
{
//Connect to the server pipe using CreateFile()
hPipe = CreateFile(
g_szPipeName, // 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 (INVALID_HANDLE_VALUE == hPipe)
{
printf("\nError occurred while connecting to the server: %d", GetLastError());
//One might want to check whether the server pipe is busy
//This sample will error out if the server pipe is busy
//Read on ERROR_PIPE_BUSY and WaitNamedPipe() for that
system("Pause");
return 1; //Error
}
else
{
printf("\nCreateFile() was successful.");
}
//We are done connecting to the server pipe,
//we can start communicating with the server using ReadFile()/WriteFile()
//on handle - hPipe
repeate();
}
Configure both computers by adding ip address, subnet mask, default gateway. From Network and Sharing Center, turn on all sharing and turn off password protected sharing. Test it by pinging ip address from cmd. User account of the both computers should not be password protected. That's why i fixed the Client Error 5 and set up named pipe communication between two remote computers
The client should use OpenFile(), not CreateFile(). The client doesn't want to create anything new, it wants to talk to an existing pipe.
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);
}
I need to pass some string by pipe. we can't pass a pointer by pipe and we must pass data. for passing a string we can send an array of chars. but I don't want to use array. I need a way to send strings in variable size.
I used msdn samples for creating pipe server and pipe client:
but inestead of only one writeFile and readFile function in pipe client and pipe server I used them three time in this way:
I used a structure for saving my strings size. first of all this structure is sent. then my two string will be sent. so in pipe server at first, the size of strings will be read and after that the two strings will be received.
i defined a structure like this in both client and server programs:
typedef struct
{
int fileNameLen;
int commandArgLen;
}pipeData,*PpipeData;
pipeData dataToWrite;
pipeData *pdataToWrite = &dataToWrite;
in pipe client I want to send this strings:
LPTSTR s1 = TEXT("file1");
LPTSTR s2 = TEXT("startCmd");
dataToWrite.commandArgLen = sizeof(s1);
dataToWrite.fileNameLen = sizeof(s2);
I sent the structure by pipe client in this way.
fSuccess = WriteFile(
hPipe, // pipe handle
pdataToWrite, // message
sizeof(dataToWrite), // message length
&cbWritten, // bytes written
NULL); // not overlapped
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
fSuccess = WriteFile(
hPipe, // pipe handle
s1, // message
sizeof(s1), // message length
&cbWritten, // bytes written
NULL); // not overlapped
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
fSuccess = WriteFile(
hPipe, // pipe handle
s2, // message
sizeof(s2), // message length
&cbWritten, // bytes written
NULL); // not overlapped
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
in pipe server for read the pipe i use 3 readFile like this:
fSuccess = ReadFile(
hPipe, // handle to pipe
pdataToWrite, // buffer to receive data
sizeof(pdataToWrite), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (!fSuccess || cbBytesRead == 0)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
_tprintf(TEXT("InstanceThread: client disconnected.\n"), GetLastError());
break;
}
else
{
_tprintf(TEXT("InstanceThread ReadFile failed, GLE=%d.\n"), GetLastError());
break;
}
}
// Process the incoming message.
GetAnswerToRequest(TEXT("structure recieved"), pchReply, &cbReplyBytes);
fSuccess = ReadFile(
hPipe, // handle to pipe
s1, // buffer to receive data
dataToWrite.commandArgLen, // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (!fSuccess || cbBytesRead == 0)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
_tprintf(TEXT("InstanceThread: client disconnected.\n"), GetLastError());
}
else
{
_tprintf(TEXT("InstanceThread ReadFile failed, GLE=%d.\n"), GetLastError());
}
break;
}
GetAnswerToRequest(s1, pchReply, &cbReplyBytes);
fSuccess = ReadFile(
hPipe, // handle to pipe
s2, // buffer to receive data
dataToWrite.fileNameLen, // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (!fSuccess || cbBytesRead == 0)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
_tprintf(TEXT("InstanceThread: client disconnected.\n"), GetLastError());
}
else
{
_tprintf(TEXT("InstanceThread ReadFile failed, GLE=%d.\n"), GetLastError());
}
break;
}
GetAnswerToRequest(s2, pchReply, &cbReplyBytes);
this way doesn't work properly. when pipe server read data in first readFile, it may return this error : ERROR_MORE_DATA (if I used PIPE_TYPE_MESSAGE in createNamedPipe)
I don't know how can i use multiple writeFile and readFile in pipe client and server.
If a named pipe is being read in message mode and the next message is longer than the nNumberOfBytesToRead parameter specifies, ReadFile returns FALSE and GetLastError returns ERROR_MORE_DATA. The remainder of the message can be read by a subsequent call to the ReadFile or PeekNamedPipe function. By MSDN
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
FYI