C++和C#进程之间通过命名管道通信,附源码—下

接上篇:

我采用的是C#开发的一个windows应用程序(pipe_server_csharp)作为服务器端,而MFC开发的应用程序(NamedPipeClient_vc)作为客户端。客户端和服务器端要进行频繁的大量的通信,常见的是文本信息和曲线数据,例如,一共有10条曲线,每条曲线有1000000条double数据。

服务器端:

服务器端是用在VS2005中用C#开发的一个名为pipe_server_csharp的应用程序,只有一个名为frmServer的主界面。

由于管道的相关API函数都是属于kernel32.dll函数,C#中不能直接调用,所以必须将所要用到的API函数全部封装在一个类NamedPipeNative中。 至于如何调用这些API函数,有兴趣的朋友可以上网搜索,或者看我的另一篇文章《C#中怎么调用命名管道的WinAPI》。

NamedPipeNative类中几个主要函数如下(大家可以对比这些重写的API函数和原来的函数有什么变化):

[DllImport("kernel32.dll", SetLastError=true)]

public static extern IntPtr CreateNamedPipe

(

String lpName, // pipe name

uint dwOpenMode, // pipe open mode

uint dwPipeMode, // pipe-specific modes

uint nMaxInstances, // maximum number of instances

uint nOutBufferSize, // output buffer size

uint nInBufferSize, // input buffer size

uint nDefaultTimeOut, // time-out interval

IntPtr pipeSecurityDescriptor // SD

);

[DllImport("kernel32.dll", SetLastError=true)]

public static extern bool ConnectNamedPipe

(

IntPtr hHandle, // handle to named pipe

Overlapped lpOverlapped // overlapped structure

);

[DllImport("kernel32.dll", SetLastError=true)]

public static extern IntPtr CreateFile

(

String lpFileName, // file name

uint dwDesiredAccess, // access mode

uint dwShareMode, // share mode

SecurityAttributes attr, // SD

uint dwCreationDisposition, // how to create

uint dwFlagsAndAttributes, // file attributes

uint hTemplateFile); // handle to template file

[DllImport("kernel32.dll", SetLastError=true)]

public static extern bool ReadFile

(

IntPtr hHandle, // handle to file

byte[] lpBuffer,// data buffer字节流

uint nNumberOfBytesToRead,// number of bytes to read

byte[] lpNumberOfBytesRead,// number of bytes read

uint lpOverlapped// overlapped buffer

);

[DllImport("kernel32.dll", SetLastError=true)]

public static extern bool WriteFile

(

IntPtr hHandle, // handle to file

byte[] lpBuffer,// data buffer字节流

uint nNumberOfBytesToWrite, // number of bytes to write

byte[] lpNumberOfBytesWritten, // number of bytes written

uint lpOverlapped // overlapped buffer

);

还有其他一些常量:

public const uint PIPE_ACCESS_DUPLEX = 0x00000003;

public const uint PIPE_ACCESS_OUTBOUND = 0x00000002;

public const uint PIPE_TYPE_BYTE = 0x00000000;

public const uint PIPE_TYPE_MESSAGE = 0x00000004;

在此不一一列举了。

封装之后,在C#中就可以直接调用这些函数和常量了。

另外,创建了一个类CMyPipe,封装了服务器管道的属性和方法,如创建管道,读写数据等:

namespace pipe_server_csharp

{

public class CMyPipe

{

private string m_PipeName;//管道名全称

public string PipeName

{

get { return m_PipeName; }

set { m_PipeName = value; }

}

private IntPtr m_HPipe;//管道句柄

public IntPtr HPipe

{

get { return m_HPipe; }

set { m_HPipe = value; }

}

public CMyPipe()//无参构造函数

{

m_HPipe = (IntPtr)NamedPipeNative.INVALID_HANDLE_VALUE;

m_PipeName = "\\\\.\\pipe\\robinTest";

}

public CMyPipe(string pipeName)//有参构造函数

{

m_HPipe = (IntPtr)NamedPipeNative.INVALID_HANDLE_VALUE;

m_PipeName = pipeName;

}

~CMyPipe()//析构函数

{

Dispose();

}

public void Dispose()

{

lock (this)

{

if (m_HPipe != (IntPtr)NamedPipeNative.INVALID_HANDLE_VALUE)

{

NamedPipeNative.CloseHandle(m_HPipe);

m_HPipe = (IntPtr)NamedPipeNative.INVALID_HANDLE_VALUE;

}

}

}

public void CreatePipe()//创建管道

{

m_HPipe = NamedPipeNative.CreateNamedPipe(m_PipeName,

NamedPipeNative.PIPE_ACCESS_DUPLEX, // 数据双工通信

NamedPipeNative. PIPE_TYPE_MESSAGE | NamedPipeNative.PIPE_WAIT, 100, // 最大实例个数

128, // 流出数据缓冲大小

128, // 流入数据缓冲大小

150, // 超时,毫秒

IntPtr.Zero // 安全信息

);

if (m_HPipe.ToInt32() == NamedPipeNative.INVALID_HANDLE_VALUE)

{

frmServer.ActivityRef.AppendText("创建命名管道失败" );

frmServer.ActivityRef.AppendText(Environment.NewLine);

return;

}

frmServer.ActivityRef.AppendText("创建命名管道完毕" );

frmServer.ActivityRef.AppendText(Environment.NewLine);

}

public void ReadCurveData()//读取曲线数据

{

int nCurvesToRead = 0;

nCurvesToRead = ReadInt(HPipe);//先读取需要读取的曲线条数,如

frmServer.CurveDataList.Clear();

for (int j = 1; j <= nCurvesToRead; j++)

{

string curveName = ReadString(HPipe);//读取该曲线名称

int nCount = 0;

nCount = ReadInt(HPipe);//读取该曲线的数据总数(数组大小)

double[] readData = new double[nCount];

for (int i = 0; i < nCount; i++)

{

readData[i] = ReadDouble(HPipe);//顺序读取曲线的数据

}

CCurve newCurve = new CCurve();

newCurve.CurveName = curveName;

newCurve.CurveData = readData;

frmServer.CurveDataList.Add(newCurve);

}

}

public void ReadTextInfo()//读取文本信息

{

string textInfo = ReadString(HPipe);//读取该曲线名称

frmServer.ActivityRef.AppendText(textInfo + Environment.NewLine);

frmServer.ActivityRef.AppendText(Environment.NewLine);

}

public void ReadData()

{

//read data

int flag = -1;

flag = ReadInt(HPipe);//获取当前要读取的数据的信息

if (flag == 0)//flag==0表示读取曲线数据

{

ReadCurveData();

}

else if (flag == 1)//flag==1表示读取文本信息

{

ReadTextInfo();

}

}

//写字符串,由于字符串的大小不同,所以先将字符串的大小写入,然后写字符串内容,对应的,

//在c++端,读字符串时先读取字符数组大小,从而给字符数组开辟相应的空间,然后读取字符串内容。

public bool WriteString(IntPtr HPipe, string str)

{

byte[] Val = System.Text.Encoding.UTF8.GetBytes(str);

byte[] dwBytesWrite = new byte[4];

int len = Val.Length;

byte[] lenBytes = System.BitConverter.GetBytes(len);

if (NamedPipeNative.WriteFile(HPipe, lenBytes, 4, dwBytesWrite, 0))

return (NamedPipeNative.WriteFile(HPipe, Val, (uint)len, dwBytesWrite, 0));

else

return false;

}

public string ReadString(IntPtr HPipe)

{

string Val = "";

byte[] bytes = ReadBytes(HPipe);

if (bytes != null)

{

Val = System.Text.Encoding.UTF8.GetString(bytes);

}

return Val;

}

public byte[] ReadBytes(IntPtr HPipe)

{

//传字节流

byte[] szMsg = null;

byte[] dwBytesRead = new byte[4];

byte[] lenBytes = new byte[4];

int len;

if (NamedPipeNative.ReadFile(HPipe, lenBytes, 4, dwBytesRead, 0))//先读大小

{

len = System.BitConverter.ToInt32(lenBytes, 0);

szMsg = new byte[len];

if (!NamedPipeNative.ReadFile(HPipe, szMsg, (uint)len, dwBytesRead, 0))

{

//frmServer.ActivityRef.AppendText("读取数据失败");

return null;

}

}

return szMsg;

}

public float ReadFloat(IntPtr HPipe)

{

float Val = 0;

byte[] bytes = new byte[4]; //单精度需4个字节存储

byte[] dwBytesRead = new byte[4];

if (!NamedPipeNative.ReadFile(HPipe, bytes, 4, dwBytesRead, 0))

{

//frmServer.ActivityRef.AppendText("读取数据失败");

return 0;

}

Val = System.BitConverter.ToSingle(bytes, 0);

return Val;

}

public double ReadDouble(IntPtr HPipe)

{

double Val = 0;

byte[] bytes = new byte[8]; //双精度需8个字节存储

byte[] dwBytesRead = new byte[4];

if (!NamedPipeNative.ReadFile(HPipe, bytes, 8, dwBytesRead, 0))

{

//frmServer.ActivityRef.AppendText("读取数据失败");

return 0;

}

Val = System.BitConverter.ToDouble(bytes, 0);

return Val;

}

public int ReadInt(IntPtr HPipe)

{

int Val = 0;

byte[] bytes = new byte[4]; //整型需4个字节存储

byte[] dwBytesRead = new byte[4];

if (!NamedPipeNative.ReadFile(HPipe, bytes, 4, dwBytesRead, 0))

{

//frmServer.ActivityRef.AppendText("读取数据失败");

return 0;

}

Val = System.BitConverter.ToInt32(bytes, 0);

return Val;

}

}

}

主程序,读取和显示通信信息:

public partial class frmServer : Form

{

public static System.Windows.Forms.TextBox ActivityRef;//显示信息

public static ArrayList CurveDataList = new ArrayList();//存储数据

public static string PipeName = "robinTest";

public static string ServeName = ".";

public static string FullPipeName = "\\\\"+ServeName+"\\pipe\\"+PipeName;//管道全名

public static CMyPipe ThePipe;//管道实例

private Thread hThread;//线程

public frmServer()

{

InitializeComponent();

frmServer.ActivityRef = this.txtMessage;

Control.CheckForIllegalCrossThreadCalls = false;//不捕获线程错误调用,这样线程就可以别的线程中创建的东西,这样做也会破坏了线程的安全

ThePipe = new CMyPipe(FullPipeName);

ThePipe.CreatePipe();//创建管道

hThread = new Thread(new ThreadStart(PipeProcess));

hThread.IsBackground = true;

hThread.Name = "Main Pipe Thread";

hThread.Start();// 启动线程.监听管道,进行通信

Thread.Sleep(1000);

}

public void PipeProcess()

{

bool ret = false;

try

{

while (true)

{

ret = NamedPipeNative.ConnectNamedPipe(ThePipe.HPipe, null);//连接到管道

if (!ret)

{

if (NamedPipeNative.GetLastError() == NamedPipeNative.ERROR_PIPE_CONNECTED) // 客户还在连接中

{

frmServer.ActivityRef.AppendText("连接正常,客户还在连接中……" + Environment.NewLine);//此处会出错,因为线程调用的问题

ret = true;

}

if (NamedPipeNative.GetLastError() == NamedPipeNative.ERROR_NO_DATA) // 客户已经关闭

{

frmServer.ActivityRef.AppendText("客户已经关闭,等待客户…………" + Environment.NewLine);

NamedPipeNative.DisconnectNamedPipe(ThePipe.HPipe);

continue;

}

}

else

{

frmServer.ActivityRef.AppendText("有客户接入" + Environment.NewLine);

}

ThePipe.ReadData();//读取数据

}

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

finally

{

frmServer.ActivityRef.AppendText("线程退出!");

}

}

}

  客户机端:

客户端是在VS6.0中用MFC开发的一个单界面应用程序,名为NamedPipeClient_vc。在vc6.0中可以直接调用API函数,不需要封装。

当然,按照惯例,还是将客户端的命名管道相关的属性和方法封装成类CMyPipe,如下:

CString PipeName = "robinTest";

CString ServerName = ".";

CString FullPipeName="\\\\"+ServerName+"\\pipe\\"+PipeName;

CMyPipe::CMyPipe()

{

m_pThread=NULL;

}

CMyPipe::~CMyPipe()

{

if(m_pThread)

{

if(TerminateThread(m_pThread->m_hThread,0))

{

if(m_pThread)

delete m_pThread;

m_pThread = NULL;

}

}

}

//客户端和服务器端建立连接

void CMyPipe::ClientCreateFile()

{

BOOL ret = ::WaitNamedPipe(FullPipeName, 5000);

if (!ret)

{

//ClientMsg="管道忙或者没有启动……\n";

return;

}

m_hPipe = ::CreateFile(FullPipeName,

GENERIC_READ|GENERIC_WRITE,

FILE_SHARE_READ|FILE_SHARE_WRITE,

NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

NULL);

if (m_hPipe == INVALID_HANDLE_VALUE)

{

char szErr[256] = "";

DWORD dwErr = GetLastError();

sprintf(szErr, "%l", dwErr);

//ClientMsg=szErr;

return;

}

//ClientMsg="打开管道了\n";

}

//写字符串

void CMyPipe::WriteString(char *szMsg)

{

DWORD dwSize = strlen(szMsg)+1, dwBytesWritten = 0;

BOOL ret =WriteFile(m_hPipe, &dwSize, 4, &dwBytesWritten, NULL);

if (ret)

{

BOOL ret2=WriteFile(m_hPipe, szMsg, dwSize, &dwBytesWritten, NULL);

}

}

//写单精度

void CMyPipe::WriteFloat(float szMsg)

{

DWORD dwSize = 4, dwBytesWritten = 0;

BOOL ret=WriteFile(m_hPipe, &szMsg, dwSize, &dwBytesWritten, NULL);

}

//写双精度

void CMyPipe::WriteDouble(double szMsg)

{

DWORD dwSize = 8, dwBytesWritten = 0;

BOOL ret=WriteFile(m_hPipe, &szMsg, dwSize, &dwBytesWritten, NULL);

}

//写整型

void CMyPipe::WriteInt(int szMsg)

{

DWORD dwSize = 4, dwBytesWritten = 0;

BOOL ret=WriteFile(m_hPipe, &szMsg, dwSize, &dwBytesWritten, NULL);

}

void CMyPipe::ClosePipe()

{

CloseHandle(m_hPipe);

}

//读字符串

char * CMyPipe::ReadString()

{

char *readStr;

DWORD drSize=0, dwBytesRead = 0;

BOOL ret =ReadFile(m_hPipe, &drSize, 4, &dwBytesRead, NULL);

if (ret)

{

readStr=new char[drSize];

BOOL ret2=ReadFile(m_hPipe, readStr, drSize, &dwBytesRead, NULL);

}

return readStr;

}

int CMyPipe::ReadInt()

{

int intRead=0;

DWORD drSize=4, dwBytesRead = 0;

BOOL ret =ReadFile(m_hPipe, &intRead, drSize, &dwBytesRead, NULL);

return intRead;

}

float CMyPipe::ReadFloat()

{

float floatRead=0;

DWORD drSize=4, dwBytesRead = 0;

BOOL ret =ReadFile(m_hPipe, &floatRead, drSize, &dwBytesRead, NULL);

return floatRead;

}

double CMyPipe::ReadDouble()

{

double floatRead=0;

DWORD drSize=8, dwBytesRead = 0;

BOOL ret =ReadFile(m_hPipe, &floatRead, drSize, &dwBytesRead, NULL);

return floatRead;

}

而主程序NamedPipeClient_vcDlg.cpp如下:

CMyPipe thePipe;

//连接服务器管道

void CNamedPipeClient_vcDlg::OnOpen()

{

// TODO: Add your control notification handler code here

thePipe.ClientCreateFile();

}

//发送数据

void CNamedPipeClient_vcDlg::OnWrite()

{

// TODO: Add your control notification handler code here

WriteData();

}

//关闭管道

void CNamedPipeClient_vcDlg::OnClose()

{

// TODO: Add your control notification handler code here

thePipe.ClosePipe();

}

//写曲线数据(注意写入的顺序,读的时候也要按照这个顺序)

void CNamedPipeClient_vcDlg::WriteCurveData()

{

int nCurvesToWrite=10;

thePipe.WriteInt(nCurvesToWrite);//先写入要传递的曲线总条数

for (int j = 1; j <= nCurvesToWrite; j++)

{

char *curveName= "curve";//

thePipe.WriteString(curveName);//写该曲线名称

int nCount = 10000;//该曲线的数据总数(数组大小)

thePipe.WriteInt(nCount);

double *writeData=new double[nCount];

for (int i = 0; i < nCount; i++)

{

writeData[i]=i;//给每个曲线数据赋初值

thePipe.WriteDouble(writeData[i]);//顺序写曲线的数据

}

}

}

//写文本信息

void CNamedPipeClient_vcDlg::WriteTextInfo(char *textInfo)

{

thePipe.WriteString(textInfo);

}

void CNamedPipeClient_vcDlg::WriteData()

{

int flag=1;//传递的信息的标识,0表示曲线数据,1表示文本信息

if (flag==0)

{

thePipe.WriteInt(flag);

WriteCurveData();

}

else if (flag==1)

{

thePipe.WriteInt(flag);

char * charWrite="hedafasfddsafdsafdsafdsafdsafsafdsafdsafdsafsaaaa";

WriteTextInfo(charWrite);

}

}