C++ / C# Communication using Named Pipes
Working on a service that needs to do Named Pipe communication between a C# and a C++ application. Eventually the C# will run as a service in session 0, and the C++ as a GUI in the user session. I have created a Named Pipe server class for the C# application, and a Named Pipe client class for the C++ application, along with the necessary GUI to run tests.
From what I can see, I am able to connect correctly. On connecting, the C++ client communication thread writes an initial "Connecting" message, and then sits waiting for the "Connected" response. When debug into a run of the two applications, I get passed the named pipe connections in both. The client sends its connection message and the return value is true. Then the client sits waiting to read a response. The server meanwhile went straight to waiting to read an initial message. Both of the reads block and never return.
As far as I can see, the connection is good. Both the C# and C++ applications have defined their pipe connections as READ/WRITE (inOut in C#), and both have the connection defined to use the Message mode. The only real difference I can see is that I have defined the connection on the C# side as allowing 1 server instance, while on the C++ side I have not yet been able to find out how this definition is to be done. I can't seem to find the answer, and I've looked online and don't see anyone stating this as an issue for them.
Running the C# client and server as separate apps connected and communicated perfectly, and running the C++ client and server as separate apps did too. But the problem has come up when connecting the C# and C++ applications.
The code is as follows. Sorry, but got it as small as I could without losing the intricacies that might actually be part of the problem. I cut this down from a test project that has a server and client on both C# and C++ sides. Any help here is appreciated. If anyone wants a zip of the project, please let me know how I can get it to you:
C# code:
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
namespace NamedPipeCSharp
{
class NamedPipeServer
{
/*******************************************
* Member constants
*******************************************/
const int numConnects = 2;
const int ConnSourceBE = 0;
const int ConnSourceGUI = 1;
/*******************************************
* Public member variables
*******************************************/
private static bool m_shouldContinue = true;
private Thread m_serverThread;
public static void ServerThread(object data)
{
NamedPipeServerStream beServer = new NamedPipeServerStream("MyNamedPipe",
PipeDirection.InOut, 1, PipeTransmissionMode.Message);
beServer.WaitForConnection();
StreamReader reader = new StreamReader(beServer);
StreamWriter writer = new StreamWriter(beServer);
String startProtocolMsg = "Connecting";
String startProtocolRsp = "Connected";
while (m_shouldContinue)
{
// block on read input
String line = reader.ReadLine();
if (line == startProtocolMsg)
{
writer.WriteLine(startProtocolRsp);
writer.Flush();
}
Thread.Sleep(10);
}
}
public void StartCommunication()
{
// Start a thread to send IPC communications as Backend and GUI
m_serverThread = new Thread(NamedPipeServer.ServerThread);
m_serverThread.IsBackground = true;
m_serverThread.Start();
}
public void StopCommunication()
{
m_shouldContinue = false;
if (m_serverThread != null)
{
m_serverThread.Join();
m_serverThread = null;
}
}
}
}
C++ NamedPipeClient class header:
#ifndef NAMEDPIPESERVER_H
#define NAMEDPIPESERVER_H
#include <QtWidgets/QMainWindow>
#include <Windows.h>
#include <queue>
class NamedPipeClient
{
public:
NamedPipeClient();
~NamedPipeClient();
void startCommunication();
void stopCommunication();
bool shouldContinue();
// message functions
bool hasMessageIn();
bool hasMessageOut();
std::string nextMessageIn();
std::string nextMessageOut();
private:
// Thread function
static DWORD WINAPI clientThread(LPVOID param);
// Thread management
HANDLE createThread(DWORD WINAPI threadFunc(LPVOID), void* objPtr);
private:
HANDLE m_clientThreadHandle;
std::queue<std::string> m_inMessageQueue;
std::queue<std::string> m_outMessageQueue;
unsigned long m_serverId;
bool m_shouldContinue;
};
#endif // NAMEDPIPESERVER_H
C++ NamedPipeClient class definition:
#include "NamedPipeClient.h"
#include "NamedPipeStream.h"
NamedPipeClient::NamedPipeClient() :
m_clientThreadHandle(0),
m_shouldContinue(true)
{
}
NamedPipeClient::~NamedPipeClient()
{
}
void NamedPipeClient::startCommunication()
{
// Start a thread to send IPC communications from Backend
m_clientThreadHandle = createThread(clientThread, this);
if (m_clientThreadHandle && m_clientThreadHandle != INVALID_HANDLE_VALUE)
{
ResumeThread(m_clientThreadHandle);
}
}
void NamedPipeClient::stopCommunication()
{
// set this particular named pipe server's should continue to false
m_shouldContinue = false;
if (m_clientThreadHandle != INVALID_HANDLE_VALUE)
{
WaitForSingleObject(m_clientThreadHandle, INFINITE);
m_clientThreadHandle = 0;
}
}
/**********************************
* Thread function definitions
**********************************/
DWORD WINAPI NamedPipeClient::clientThread(LPVOID param)
{
NamedPipeClientStream* beClient = new NamedPipeClientStream("MyNamedPipe", OPEN_EXISTING);
if (beClient->validPipe())
{
NamedPipeClient* thisPtr = static_cast<NamedPipeClient*>((void*)param);
// start by sending initial message to get things rolling
std::string startProtocolMsg = "Connecting";
std::string startProtocolRsp = "Connected";
std::string message;
std::string response;
char buff[BUFSIZE];
int iterNum = 0;
int numTries = 0;
// initial connection protocol
message = startProtocolMsg;
bool ok = beClient->writeToPipe(message);
// wait for correct response
response = beClient->readFromPipe();
if (response != startProtocolRsp)
return -1;
while (thisPtr->shouldContinue())
{
if (thisPtr->hasMessageIn())
{
//send the next communication
message = thisPtr->nextMessageIn();
while (!beClient->writeToPipe(message))
{
// try sending this message up to 10 times
numTries++;
if (numTries == 10)
{
break;
}
Sleep(2);
continue;
}
numTries = 0;
// then sit and wait for an answer
response = beClient->readFromPipe();
if (response.length() == 0)
{
Sleep(1);
}
}
Sleep(10);
}
return 0;
}
else
return -1;
}
HANDLE NamedPipeClient::createThread(DWORD WINAPI threadFunc(LPVOID), void* objPtr)
{
// create a thread suspended and return the handle
HANDLE hThread = CreateThread(
NULL, // default security
0, // default stack size
(LPTHREAD_START_ROUTINE)threadFunc, // Thread
(LPVOID) objPtr, // arguments
CREATE_SUSPENDED, //0, // default creation flags
NULL);
return hThread;
}
bool NamedPipeClient::shouldContinue()
{
return m_shouldContinue;
}
bool NamedPipeClient::hasMessageIn()
{
return (m_inMessageQueue.size() > 0);
}
bool NamedPipeClient::hasMessageOut()
{
return (m_outMessageQueue.size() > 0);
}
std::string NamedPipeClient::nextMessageIn()
{
std::string msg = m_inMessageQueue.front();
m_inMessageQueue.pop();
return msg;
}
std::string NamedPipeClient::nextMessageOut()
{
std::string msg = m_outMessageQueue.front();
m_outMessageQueue.pop();
return msg;
}
C++ NamedPipeStream class header:
#ifndef NAMEDPIPESTREAM_H
#define NAMEDPIPESTREAM_H
#include <Windows.h>
#include <string>
#define BUFSIZE 512
class NamedPipeStream
{
public:
NamedPipeStream(std::string pipeName, int createMode);
~NamedPipeStream();
virtual bool writeToPipe(std::string message);
virtual std::string readFromPipe();
virtual bool closePipe() = 0;
bool validPipe();
protected:
void createPipe(std::string pipeName);
virtual HANDLE openPipe(wchar_t* pipeName) = 0;
protected:
std::string m_pipeName;
HANDLE m_pipeHandle;
int m_createMode;
bool m_closePipe;
bool m_isValid;
};
class NamedPipeClientStream : public NamedPipeStream
{
public:
NamedPipeClientStream(std::string pipeName, int createMode = CREATE_NEW);
~NamedPipeClientStream();
bool closePipe();
private:
HANDLE openPipe(wchar_t* pipeName);
};
C++ NamedPipeStream class definition:
#include "NamedPipeStream.h"
#include <stdlib.h>
/**********************************************
*
* NamedPipeStream class
*
* Definition: Abstract base class for named pipes
*
**********************************************/
NamedPipeStream::NamedPipeStream(std::string pipeName, int createMode) :
m_pipeName(pipeName),
m_createMode(createMode),
m_closePipe(false),
m_isValid(false)
{
}
NamedPipeStream::~NamedPipeStream()
{
}
bool NamedPipeStream::writeToPipe(std::string message)
{
DWORD numToWrite, numWritten;
BOOL fSuccess = FALSE;
wchar_t* lpvMessage;
// Send a message to the pipe server.
std::wstring wsMessage(message.begin(), message.end());
lpvMessage = const_cast<wchar_t*>(wsMessage.c_str());
numToWrite = (lstrlen(lpvMessage)+1)*sizeof(TCHAR);
fSuccess = WriteFile(
m_pipeHandle, // pipe handle
lpvMessage, // message
numToWrite, // message length
&numWritten, // bytes written
NULL); // not overlapped
if (!fSuccess)
{
return false;
}
return true;
}
std::string NamedPipeStream::readFromPipe()
{
DWORD cbRead;
TCHAR chBuf[BUFSIZE];
BOOL fSuccess = FALSE;
// Read from the pipe.
fSuccess = ReadFile(
m_pipeHandle, // pipe handle
chBuf, // buffer to receive reply
BUFSIZE*sizeof(TCHAR), // size of buffer
&cbRead, // number of bytes read
NULL); // not overlapped
if (!fSuccess && GetLastError() != ERROR_MORE_DATA)
{
// return empty string on error
return std::string();
}
std::wstring instr(chBuf);
return std::string(instr.begin(), instr.end());
}
bool NamedPipeStream::validPipe()
{
return m_isValid;
}
void NamedPipeStream::createPipe(std::string pipeName)
{
BOOL fSuccess = FALSE;
DWORD cbRead, cbToWrite, cbWritten, dwMode;
char* szPipenameFormat = "\.pipe";
std::string pipeNameStr;
size_t formatLen = 0;
wchar_t* lpszPipenameFormat = NULL;
char* szPipename = NULL;
// convert std::string name to wide char pointer and append to format string
pipeNameStr = szPipenameFormat;
pipeNameStr.append(pipeName);
lpszPipenameFormat = new wchar_t[pipeNameStr.length()+1];
szPipename = const_cast<char*>(pipeNameStr.c_str());
mbstowcs_s(&formatLen, lpszPipenameFormat, pipeNameStr.length()+1,
szPipename, pipeNameStr.length());
lpszPipenameFormat[pipeNameStr.length()+1] = NULL;
// Try to open a named pipe; wait for it, if necessary.
// loop until get a valid named pipe
while (1)
{
m_pipeHandle = openPipe(lpszPipenameFormat);
// Break if the pipe handle is valid.
if (m_pipeHandle != INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
break;
}
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
DWORD err = GetLastError();
m_isValid = false;
return;
}
// All pipe instances are busy, so wait for 20 seconds.
if (!WaitNamedPipe(lpszPipenameFormat, 20000))
{
m_isValid = false;
return;
}
}
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
m_pipeHandle, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if (!fSuccess)
{
m_isValid = false;
return;
}
m_isValid = true;
}
/****************************************************
*
* NamedPipeClientStream class
*
* Definition: Class for creating server named pipe
* to open client connection
*
****************************************************/
NamedPipeClientStream::NamedPipeClientStream(std::string pipeName, int createMode)
: NamedPipeStream(pipeName, createMode)
{
createPipe(pipeName);
}
NamedPipeClientStream::~NamedPipeClientStream()
{
}
HANDLE NamedPipeClientStream::openPipe(wchar_t* lpPipeName)
{
HANDLE hPipe = CreateFile(
lpPipeName, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
m_createMode, // specifies whether create new or open existing
0, // default attributes
NULL); // no template file
return hPipe;
}
bool NamedPipeClientStream::closePipe()
{
m_closePipe = true;
return (CloseHandle(m_pipeHandle) == TRUE) ? true : false;
}
链接地址: http://www.djcxy.com/p/48292.html
上一篇: 在套接字超时后删除所有待处理数据以供读取
下一篇: 使用命名管道的C ++ / C#通信