您好,欢迎来到吉趣旅游网。
搜索
您的当前位置:首页vc++使用管道实现进程通信

vc++使用管道实现进程通信

来源:吉趣旅游网
自从上次与Steve博士和Russell博士聊过之后,我就决定完成我的虚拟人仿真的计划。由于暑假时间的积累,在对人体建模、人体数据流设计、控制设计上都没有话费太多的时间,一切做起来都还是顺风顺水的。但由于Kinect需要VS2010以上的版本才行,而我的引擎Open Inventor目前免费的只支持VS2008,这就为我的应用带来的巨大的麻烦。于是我用VS2012编写的Kinect捕获人体骨骼数据的程序,用VS2008编写了虚拟人体仿真的程序,这样一来就出现了一个进程间通信的问题,也就是常说的Inter Processes Communication,简称IPC。

为了解决这个问题我足足花了7天时间,最初我尝试了内存文件映射的方法,效果并不理想,经常出现内存不能访问的问题。之后我使用读写文件的形式,两个进程对文件的读写,如何检测数据的跟新以及文件访问的冲突让我头疼不已。说的Socket方法,也让我感到极其繁琐,不敢尝试。俗话说的好“柳暗花明又一村”,就在我无可奈何的时候,贵杰师弟给我出了使用管道的方式来进行进程间通信的方法。大牛就是大牛,在贵杰师弟的帮助下,终于解决了这个通信的问题。如果我这个应用能按期完成,我将把源码公开,大家一起学习。好了现在说说使用管道通信的具体内容吧。 在MSDN上有对管道(Pipes)的详细解释,管道有两种:匿名管道( Anonymous pipes)主要用于父进程和子进程之间的通信,命名管道(Named pipes)主要用来在两个无关的进程间通信,这两个进程也可以位于不同的计算机上面。在我的案例里面我使用了命名管道。首先我用VS2012写了一个控制台程序作为骨骼数据的生产者,暂且用SkeletonOut表示这个程序,用VS2008写了一个使用骨骼数据的MFC程序,暂且记为SkeletonIn,在SkeletonIn中定义一个消息函数OnKinect,只要出发这个事件,SkeletonIn便会打开应用程序SkeletonOut,让后SkeletonOut开始捕获人体骨骼数据,并将其源源不断的传送给SkeletonIn进程。

基于以上的构思,开始使用管道来进行通信。

首先,在SkeletonIn程序的OnKinect函数中写入如下代码:

void SkeletonIn::OnKinect() {

//创建命名管道

//对于管道名微软有严格的命名要求,具体详见MSDN

HANDLE hNamedPipe = CreateNamedPipe(_T(\"\\\\\\\\.\\\\pipe\\\\namedPipe\"),// 管道名 PIPE_ACCESS_DUPLEX | FILE_FLAG_WRITE_THROUGH, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 2, 1024, 1024,

NMPWAIT_WAIT_FOREVER, NULL);

//判断管道是否创建成功

if (INVALID_HANDLE_VALUE == hNamedPipe){ AfxMessageBox(_T(\"CreateNamedPipe failed.\")); return ; }

//打开SkeletonOut应用程序

WinExec(\"E:\\\\Kinect_Human\\\\Kinect Human Version2\\\\SkeletonOut.exe\

//等待连接管道,这是一个阻塞函数,会一直等待程序连接管道 if(!ConnectNamedPipe(hNamedPipe,NULL)) {

AfxMessageBox(_T(\"Cannot connet Named Pipe\")); CloseHandle(hNamedPipe); return; }

//读取数据

SkeletonData* pReadBuf = new SkeletonData; //将pReadBuf的内容都初始化为0

memset(pReadBuf,0,sizeof(SkeletonData));

DWORD dwRead = 0;//用于记录读入管道的数据量

//这里的ReadFile也是一个阻塞函数,会一直等待管道的数据更新,一旦管道中有新的数据立马读入,否则一直等待

if(!ReadFile(hNamedPipe,pReadBuf,sizeof(SkeletonData),&dwRead,NULL)) {

if(NULL!=pReadBuf) {

delete []pReadBuf; pReadBuf = NULL; }

AfxMessageBox(_T(\"Read File Failed.\")); }

//释放资源、关闭连接、关闭句柄 DisconnectNamedPipe(hNamedPipe);

CloseHandle(hNamedPipe);

} //这样SkeletonIn中就创建了一个管道通过这个管道可以冲SkeletonOut中获取骨骼数据

在SkeletonOut中添加如下代码,为了简便起见SkeletonOut我是使用控制台程序写的 CStringstrPipeName = _T(\"\\\\\\\\.\\\\pipe\\\\namedPipe\");\\\\管道名

//等待连接命名管道

cout<<\"Waif for connecting Pipe...\"<if(!waitNamedPipe(strPipeName,NMPWAIT_WAIT_FOREVER)) {

cout<<\"命名管道不存在!\"; return -1; }

//打开命名管道

HANDLE hNamedPipe = CreateFile(strPipeName,

GENRIC_READ|GENRIC_WRITE,0,NULL,

OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

//判断是否打开命名管道成功

if(INVALID_HANDLE_VALUE == hNamedPipe) {

cout<<\"Creat Pipe Failed:\"<//写入数据

DWORD dwWrite = 0;

if(!WriteFile(hNamedPipe,&SkeletonPoints,sizeof(SkeletonData),&dwWrite,NULL)) {

cout<<\"写入数据失败!\"<cout<<\"写入数据成功!\"<//关闭句柄

CloseHandle(hNamedPipe);

至此我们就完成了在SkeletonIn和SkeletonOut之间的数据传递,SkeletonOut实时捕获到的人体骨骼数据保存在SkeletonPoints结构中,而SkeletonOut通过使用命名管道将其传送到SkeletonIn中的pReadBuf中去。但是这样的只能传送Kinect捕获的第一个骨骼数据。而我们需要的是Kinect实时捕获的所有骨骼数据。为此我尝试在其中加了了While死循环,但是在操作管道的时候提示失败。贵杰表示这里需要使用线程来读取从SkeletonOut中读入数据,使用线程来监视数据的更新。这种方法常见于对硬件输出数据的读写。为此在SkeletonOut和SkeletonIn中需要对代码进行修改。 SkeletonIn中的代码修改比较方便,只需将代码: //等待连接命名管道

cout<<\"Waif for connecting Pipe...\"<if(!waitNamedPipe(strPipeName,NMPWAIT_WAIT_FOREVER)) {

cout<<\"命名管道不存在!\"; return -1; }

//打开命名管道

HANDLE hNamedPipe = CreateFile(strPipeName,

GENRIC_READ|GENRIC_WRITE,0,NULL,

OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

//判断是否打开命名管道成功

if(INVALID_HANDLE_VALUE == hNamedPipe) {

cout<<\"Creat Pipe Failed:\"<骨骼流循环之前,剩下的代码依然放在骨骼流输出循环之中便可。

而在SkeletonOut的代码书写则相对会麻烦一点,在修改代码之间,这里写一个今天贵杰教我的一个调试技巧。骨骼数据每秒钟刷新30次,要看我们传送数据是否成功,最好将每次传送过来的数据显示出来,这样传送成功与否一看便知。但是MFC中不能像控制台那样将数据输出在一个黑窗口上,但是我们可以将数据输出到一个文件中去,如下所示: ofstreamos(\"data.txt\");

cout.rdbuf(os.rdbuf());

/***********一些操作*****************/ CoutSkeleton(*pReadBuf);//输出数据 os.flush();

os.close();

这里CoutSkeleton函数依然使用<<操作符对数据进行输出操作,不过因为

cout.rdbuf(os.rdbuf());函数的执行,cout<<的输出操作不再是将数据输出到控制台窗口上,而是将其输出到data.txt文件中。这将为我们程序的调试带来不小的便利性。

接下来我们需要创建一个线程专门用于从管道中读数据。这里需要注意的是线程函数是回调函数,因此它必须是静态成员函数或是在类外部声明的全局函数。下面的代码用于定义线程函数

DWORD WINAPI MyThreadProc (LPVOID lpParam) {

HANDLE hNamedPipe = (HANDLE)lpParam; // 等待连接

if (!ConnectNamedPipe(hNamedPipe, NULL)){ AfxMessageBox(_T(\"connectNamedPide\")); CloseHandle(hNamedPipe); return -1; }

// 输出!!!!!!!!!!! ofstreamos(\"data.txt\"); cout.rdbuf(os.rdbuf());

SkeletonData* pReadBuf = new SkeletonData; memset(pReadBuf, 0, sizeof(SkeletonData)); while(TRUE){ // 读数据 ------ DWORD dwRead = 0;

if (!ReadFile(hNamedPipe, pReadBuf, sizeof(SkeletonData), &dwRead, NULL)){ if (NULL != pReadBuf){ delete [] pReadBuf; pReadBuf = NULL; break; }

AfxMessageBox(_T(\"ReadFile failed.\")); }

CoutSkeleton(*pReadBuf); }

os.flush();

os.close();

// 释放资源关闭连接 关闭句柄 if (NULL != pReadBuf){ delete [] pReadBuf; pReadBuf = NULL; }

DisconnectNamedPipe(hNamedPipe); CloseHandle(hNamedPipe);

// -------------------------------------------------------------------------------------------- return 0;

}

这里的参数pParam内传递的值不再是一个指针。具体可以查询MSDN。

当然这里我们只是定义课线程函数,我们还需要在OnKinect()函数中创建线程。改写之后的OnKinec函数如下: voidSkeletonIn::OnKinec()

{

// TODO: 在此添加命令处理程序代码

// ------------------------ 管道 --------------------------- // 创建管道

HANDLE hNamedPipe = CreateNamedPipe(_T(\"\\\\\\\\.\\\\pipe\\\\namedPipe\"),// 管道名 PIPE_ACCESS_DUPLEX | FILE_FLAG_WRITE_THROUGH, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 2, 1024,

1024,

NMPWAIT_WAIT_FOREVER, NULL);

if (INVALID_HANDLE_VALUE == hNamedPipe){ AfxMessageBox(_T(\"CreateNamedPipe failed.\")); return ; }

// CreateProcess();

WinExec(\"E:\\\\Kinect_Human\\\\Kinect Human

Version2\\\\KinectSkeletonOut.exe\

//创建线程 DWORD MyThredID = 0;

CreateThread(0, 0, MyThreadProc, hNamedPipe, 0, &MyThredID); }

至此,完成进程SkeletonIn和进程SkeletonOut之间的通信。这个问题困扰了我7天时间,现在终于结果了,这里非常感谢贵杰为我改写代码、调试代码,也非常感谢谭军为我提供的读写文件的解决方案,为我提供的Socket通信解决方案,唐学涛老师为我提供的MSMQ解决方案,以及蕾姐帮我咨询MFC高手。

总之以后写程序尽量写MFC程序,不要再用控制台程序了,使用MFC程序会为后期的维护带来不小的便利。还有就是自己经常将一个函数写几百行的习惯也得改改了,这不利于他人读代码,也不利于自己维护代码。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- jqkq.cn 版权所有 赣ICP备2024042794号-4

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务