进程间通信
进程间通信
1.剪贴板
新建一个MFC基于单文档的应用程序,取名:Clipboard,然后编辑对话框资源,如下图:
双击发送按钮,添加消息响应函数,编辑:
void CClipboardDlg::OnBtnSend()
{
// TODO: Add your control notification handler code here
if(OpenClipboard())//如果打开剪贴板
{
CString str;//用于存放编辑框中的数据
HANDLE hClip;
char *pBuf;
EmptyClipboard();//清空剪贴板
GetDlgItemText(IDC_EDIT_SEND,str);
hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);//分配一个内存对象
pBuf=(char*)GlobalLock(hClip);//将句柄转换成指针,并加锁
strcpy(pBuf,str);//把str中的数据拷贝到内存中
GlobalUnlock(hClip);//解锁
SetClipboardData(CF_TEXT,hClip);//将数据设置到剪贴板上
CloseClipboard();//关闭剪贴板
}
}
双击接收按钮,添加消息响应函数,编辑:
void CClipboardDlg::OnBtnRecv()
{
// TODO: Add your control notification handler code here
if(OpenClipboard())
{
if(IsClipboardFormatAvailable(CF_TEXT))
{
HANDLE hClip;
char *pBuf;
hClip=GetClipboardData(CF_TEXT);//获取剪贴板数据
pBuf=(char*)GlobalLock(hClip); //给句柄加锁后,存到pBuf中
GlobalUnlock(hClip); //解锁
SetDlgItemText(IDC_EDIT_RECV,pBuf); //将数据设置到pBuf中
CloseClipboard();
}
}
}
2.匿名管道
父进程:
新建一个MFC的单文档应用程序,取名:Parent,编辑资源,如下图:
并分别对这三个菜单添加命令消息响应函数,再在CParentView类上添加两个成员变量,
private:
HANDLE hWrite;
HANDLE hRead;
再在构造函数中初始化,在析构函数中关闭句柄,如下:
CParentView::CParentView()
{
// TODO: add construction code here
hRead=NULL;
hWrite=NULL;
}
CParentView::~CParentView()
{
if(hRead)
CloseHandle(hRead);//关闭读句柄
if(hWrite)
CloseHandle(hWrite);//关闭写句柄
}
编辑三个菜单添加命令消息响应函数:
void CParentView::OnPipeCreate()
{
// TODO: Add your command handler code here
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle=TRUE;
sa.lpSecurityDescriptor=NULL;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
if(!CreatePipe(&hRead,&hWrite,&sa,0))//创建匿名管道
{
MessageBox("创建匿名管道失败");
return ;
}
STARTUPINFO sui;
PROCESS_INFORMATION pi;//进程信息结构体
ZeroMemory(&sui,sizeof(STARTUPINFO));//把结构体STARTUPINFO所有成员赋值为0
sui.cb=sizeof(STARTUPINFO);//对此结构体本身大小的成员赋值
sui.dwFlags=STARTF_USESTDHANDLES;//为子进程设置标准的输入、输出和错误句柄
sui.hStdInput=hRead;//将子进程的标准输入句柄设置成管道的读句柄
sui.hStdOutput=hWrite;//将子进程的标准输入句柄设置成管道的写句柄
sui.hStdError=GetStdHandle(STD_ERROR_HANDLE);//标准错误句柄
if(!CreateProcess("..\\Child\\Debug\\Child.exe",NULL,NULL,NULL,
TRUE,0,NULL,NULL,&sui,&pi))//启动子进程
{
CloseHandle(hRead);//关闭句柄
CloseHandle(hWrite);
hRead=NULL;
hWrite=NULL;
MessageBox("创建子进程失败");
return;
}
else
{
CloseHandle(pi.hProcess);//关闭子进程句柄
CloseHandle(pi.hThread);//关闭子进程中主线程的句柄
}
}
void CParentView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
}
void CParentView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[]="http://www.baidu.com";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
子进程:
在Workspace 'Parent'上点右键 -> Add New Project To Wordspace… ,新建一个MFC单文档应用程序,取名Child,编辑菜单资源,如下图:
然后分别对这两个菜单项添加消息响应函数,并CChildView类上添加一个OnInitialUpdate虚函数,再添加两个成员变量:
private:
HANDLE hRead;
HANDLE hWrite;
在构造函数和析构函数中:
CChildView::CChildView()
{
// TODO: add construction code here
hRead=NULL;
hWrite=NULL;
}
CChildView::~CChildView()
{
if(hRead)
CloseHandle(hRead);//关闭读句柄
if(hWrite)
CloseHandle(hWrite);//关闭写句柄
}
然后在ChildView.cpp中编辑:
void CChildView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
}
void CChildView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[]="匿名管道测试程序";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
void CChildView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
hRead=GetStdHandle(STD_INPUT_HANDLE);//得到标准输入句柄
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);//得到标准输出句柄
}
3.命名管道
服务端:
新建一个MFC的单文档应用程序,取名:NamedPipeSrv,编辑资源,如下图:
并分别对这三个菜单添加命令消息响应函数,再在CNamedPipeSrvView类上添加一个成员变量:
private:
HANDLE hPipe;
在构造函数和析构函数中编辑:
CNamedPipeSrvView::CNamedPipeSrvView()
{
hPipe=NULL;
}
CNamedPipeSrvView::~CNamedPipeSrvView()
{
if(hPipe)
CloseHandle(hPipe);//关闭这个句柄
}
在NamedPipeSrvView.cpp中编辑:
void CNamedPipeSrvView::OnPipeCreate()
{
// TODO: Add your command handler code here
hPipe=CreateNamedPipe("\\\\.\\pipe\\MyPipe",
PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,0,
1,1024,1024,0,NULL);//创建命名管道
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("创建命名管道失败");
hPipe=NULL;
return;
}
HANDLE hEvent;
hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//创建一个人工重置的事件对象
if(!hEvent)
{
MessageBox("创建对象事件失败");
CloseHandle(hPipe);
hPipe=NULL;
return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap,sizeof(OVERLAPPED));
ovlap.hEvent=hEvent;
if(!ConnectNamedPipe(hPipe,&ovlap))
//允许服务器端等待客户端连接到命名管道实例上,并判断是否失败
{
if(ERROR_IO_PENDING!=GetLastError())//这种情况真的代表出错了
{
MessageBox("等待客户端连接失败");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
}
if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
//等待事件对象,直到它变为有信号状态
{
MessageBox("等待对象失败!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
CloseHandle(hEvent);//关闭事件对象
}
void CNamedPipeSrvView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
}
void CNamedPipeSrvView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[]="匿名管道测试程序";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
客户端:
在Workspace 'NamedpipeSrv'上点右键 -> Add New Project To Wordspace… ,新建一个MFC单文档应用程序,取名NamedPipeClt,编辑菜单资源,如下图:
然后分别对这两个菜单项添加消息响应函数,并CNamedPipeCltView类上添加一个成员变量:
private:
HANDLE hPipe;
并在构造函数和析构函数中编辑:
CNamePipeCltView::CNamePipeCltView()
{
hPipe=NULL;
}
CNamePipeCltView::~CNamePipeCltView()
{
if(hPipe)
CloseHandle(hPipe);//关闭句柄
}
编辑NamePipeCltView.cpp:
void CNamePipeCltView::OnPipeConnect()
{
// TODO: Add your command handler code here
if(!WaitNamedPipe("\\\\.\\pipe\\MyPipe",NMPWAIT_WAIT_FOREVER))
{
MessageBox("当前没有可以利用的命名管道实例");
return;
}
hPipe=CreateFile("\\\\.\\pipe\\MyPipe",GENERIC_READ|GENERIC_WRITE,
0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);//打开命名管道实例
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("打开管道失败!");
hPipe=NULL;
return;
}
}
void CNamePipeCltView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
}
void CNamePipeCltView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[]="命名管道测试程序";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
4.利用邮槽完成进程间通信
服务器端:
新建一个MFC单文档应用程序,取名:MailSlotSrv,编辑菜单资源,添加一个接收数据菜单项,并取消弹出菜单项,如下图:
再给接收菜单添加命令消息响应,编辑:
void CMailSlotSrvView::OnMailslotRecv()
{
// TODO: Add your command handler code here
HANDLE hMailslot;
hMailslot=CreateMailslot("\\\\.\\mailslot\\MyMailslot",0,
MAILSLOT_WAIT_FOREVER,NULL);//创建邮槽
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("创建邮槽失败");
return;
}
char buf[100];
DWORD dwRead;
if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
CloseHandle(hMailslot);
return;
}
MessageBox(buf);
CloseHandle(hMailslot);
}
客户端:
在Workspace 'MailSlotSrv'上点右键 -> Add New Project To Wordspace… ,新建一个MFC单文档应用程序,取名MailslotClt,编辑菜单资源,添加一个发送数据菜单项,并取消弹出菜单项,如下图:
并这个菜单项添加命令消息响应函数,编辑:
void CMailslotCltView::OnMailslotSend()
{
// TODO: Add your command handler code here
HANDLE hMailslot; //定义句柄变量
hMailslot=CreateFile("\\\\.\\mailslot\\MyMailslot",GENERIC_WRITE,
FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("打开邮槽失败!");
return;
}
char buf[]="邮槽测试程序";
DWORD dwWrite;
if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
CloseHandle(hMailslot);
return;
}
CloseHandle(hMailslot);
}
运行, OK !