孙鑫VC学习(第17课--进程间通信)
来源:互联网 发布:加尔默罗修女知乎 编辑:程序博客网 时间:2024/06/14 01:50
进程间通信的四种方式
n 剪贴板
n 匿名管道
n 命名管道
n 邮槽
剪切板是将数据放到内存区域中,然后再取出来。
剪切板程序:(基于对话框的程序,利用剪切板进行通信的例子):
两个EDIT BOX,一个是发送数据,一个是接收数据,再放置两个按钮,一个是发送,一个是接受,当按下发送按钮,数据放到剪切板,接受按钮取出数据并显示。(可以把发送和接受的代码放到不同的进程中进行通信。)
void CClipBoardDlg::OnBtnSend() //将数据送到剪切板里面
{
// TODO: Add your control notification handler code here
if (OpenClipboard( ))
{
CString str;
EmptyClipboard();
GetDlgItemText(IDC_EDIT_SEND,str);
HANDLE hMem=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);//str.GetLength()+1多分配一个空字节
char *pBuf=(char*)GlobalLock(hMem);
strcpy(pBuf,str);
GlobalUnlock(hMem);
SetClipboardData(CF_TEXT,hMem);
CloseClipboard();
}
}
void CClipBoardDlg::OnBtnRecv()
{
// TODO: Add your control notification handler code here
if (OpenClipboard( ))
{
if (IsClipboardFormatAvailable(CF_TEXT))
{
HANDLE hdata=GetClipboardData(CF_TEXT);
char *pBuf=(char*)GlobalLock(hdata);
GlobalUnlock(hdata);
SetDlgItemText(IDC_EDIT_RECV,pBuf);
CloseClipboard();
}
}
}
下面创建一个匿名管道:(匿名管道只能在父子进程间通信)
创建一个单文档的程序(父程序。Parent),写入三个菜单:创建管道,写入数据,读取数据。并在VIEW类中加上命令响应函数。
为CParentView增加
private:
HANDLE hRead;
HANDLE hWrite;
CParentView::CParentView()
{
// TODO: add construction code here
hRead=NULL;
hWrite=NULL;
}
CParentView::~CParentView()
{
if (hRead)
{
CloseHandle(hRead);
}
if (hWrite)
{
CloseHandle(hWrite);
}
}
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 若指定文件名,在当前目录下寻找可执行文件名,不会搜索,若没有找到,失败返回。不会自动加上EXE文件名。本例中的"..//Child//Debug//Child.exe",..是指返回到Parent的上一级目录。Parent和Child是平级.
LPTSTR lpCommandLine, // 若没加目录,自动加EXE扩展名。自动搜索。
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 当调用CreateProcess创建新进程时,系统会自动创建进程内核对象和线程内核对象(用于进程主线程)并将它们的使用计数设置为2(创建的时候本身为1,创建时打开再加1,为2,所以后面要关闭),本参数和下一个是用来设定创建的新进程和线程的安全属性,以及指定父进程将来生成的其它线程是否可以继承他们的句柄。若为NULL,为默认的安全属性。
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);
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 sp;
ZeroMemory(&sp,sizeof(STARTUPINFO));
sp.cb=sizeof(STARTUPINFO);
sp.dwFlags=STARTF_USESTDHANDLES;
sp.hStdInput=hRead;
sp.hStdOutput=hWrite;
sp.hStdError=GetStdHandle(STD_ERROR_HANDLE);//得到父进程的标准错误句柄。
PROCESS_INFORMATION pi;
if(!CreateProcess("..//Child//Debug//Child.exe",NULL,NULL,NULL,true,0,NULL,NULL,&sp,&pi))//启动子进程的函数CreateProcess
{
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://sunxin.org";
DWORD dwwrite;
if(!WriteFile(hWrite,Buf,strlen(Buf)+1,&dwwrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
创建一个单文档的程序(子程序。Child和父程序同级),写入三个菜单:写入数据,读取数据。并在VIEW类中加上命令响应函数。
首先要得到子进程的标准输入输出句柄。可以在VIEW类的窗口完全创建成功之后获取。
为VIEW类增加:
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);
}
}
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);
}
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[]="this is child";
DWORD dwwrite;
if(!WriteFile(hwrite,Buf,strlen(Buf)+1,&dwwrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
启动:子进程由父进程启动(本地机器,父子进程间,不可以跨网络)。自己写自己读也是可以的。
命名管道。(可以在本地机器,还可以跨网络)
n 命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信。
n 命名管道充分利用了Windows NT和Windows 2000内建的安全机制。
n 将命名管道作为一种网络编程方案时,它实际上建立了一个客户机/服务器通信体系,并在其中可靠地传输数据。
n 命名管道是围绕Windows文件系统设计的一种机制,采用“命名管道文件系统(Named Pipe File System,NPFS)”接口,因此,客户机和服务器可利用标准的Win32文件系统函数(例如:ReadFile和WriteFile)来进行数据的收发。
n 命名管道服务器和客户机的区别在于:服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。而客户机只能同一个现成的命名管道服务器建立连接。
n 命名管道服务器只能在Windows NT或Windows 2000上创建,所以,我们无法在两台Windows 95或Windows 98计算机之间利用管道进行通信。不过,客户机可以是Windows 95或Windows 98计算机,与Windows NT或Windows 2000计算机进行连接通信。
n 命名管道提供了两种基本通信模式:字节模式和消息模式。在字节模式中,数据以一个连续的字节流的形式,在客户机和服务器之间流动。而在消息模式中,客户机和服务器则通过一系列不连续的数据单位,进行数据的收发,每次在管道上发出了一条消息后,它必须作为一条完整的消息读入。
命名管道可以在创建的时候指定哪组用户可以访问管道,不需要编写用户身份验证的代码。
先编写命名管道的服务器端程序。(单文档,名字是NamePipeSrv)写入三个菜单:创建管道,写入数据,读取数据。并在VIEW类中加上命令响应函数。
为VIEW类增加:
private:
HANDLE hPipe;
CNamePipeSrvView::CNamePipeSrvView()
{
// TODO: add construction code here
hPipe=0;
}
CNamePipeSrvView::~CNamePipeSrvView()
{
if (hPipe)
{
CloseHandle(hPipe);
}
}
void CNamePipeSrvView::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=CreateEvent(NULL,true,false,NULL);
if (NULL==hevent)
{
MessageBox("创建事件失败!");
CloseHandle(hPipe);
hPipe=NULL;
CloseHandle(hevent);
return;
}
OVERLAPPED ov;
ZeroMemory(&ov,sizeof(OVERLAPPED));
ov.hEvent=hevent;
if(0==ConnectNamedPipe(hPipe,&ov))
{
if(ERROR_IO_PENDING!=GetLastError())//ERROR_IO_PENDING表示
//这个操作是个未决的操作,表示在随后的时间内,会完成。
{
MessageBox("等到客户端连接请求失败!");
CloseHandle(hPipe);
hPipe=NULL;
CloseHandle(hevent);
return;
}
}
if(WAIT_FAILED==WaitForSingleObject(hevent,INFINITE))
{
MessageBox("等到对象失败!");
CloseHandle(hPipe);
hPipe=NULL;
CloseHandle(hevent);
return;
}
//表示有一个事件实例连接到了命名管道。函数成功。
CloseHandle(hevent);
}
void CNamePipeSrvView::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 CNamePipeSrvView::OnPipeWrite()
{
// TODO: Add your command handler code here
char Buf[]="this is NamePipeSrv";
DWORD dwwrite;
if(!WriteFile(hPipe,Buf,strlen(Buf)+1,&dwwrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
下面编写管道的客户端程序。(单文档,与服务器平级),增加3个菜单,连接管道,读取数据,写入数据,并在VIEW增加命令响应。
为VIEW类增加:
private:
HANDLE hpipe;
CNamePipeClientView::CNamePipeClientView()
{
// TODO: add construction code here
hpipe=NULL;
}
CNamePipeClientView::~CNamePipeClientView()
{
if (hpipe)
{
CloseHandle(hpipe);
}
}
void CNamePipeClientView::OnPipeLink()
{
// TODO: Add your command handler code here
//在连接前,先判断当前是否有可以利用的命名管道实例
////servername/pipe/pipename 如果是跨网络通信,要设置servername为通信的机器的主机名
if(0==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 CNamePipeClientView::OnPipeRead()
{
// TODO: Add your command handler code here
// TODO: Add your command handler code here
char Buf[100];
DWORD dwread;
if(!ReadFile(hpipe,Buf,100,&dwread,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(Buf);
}
void CNamePipeClientView::OnPipeWrite()
{
// TODO: Add your command handler code here
// TODO: Add your command handler code here
char Buf[]="this is NamePipeClient";
DWORD dwwrite;
if(!WriteFile(hpipe,Buf,strlen(Buf)+1,&dwwrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
命名管道的服务器和客户端不用有任何的关系。启动后,服务器端先创建管道,客户端连接管道。然后就可以读写数据了。
邮槽
n 邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。
n 邮槽是一种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据。
n 为保证邮槽在各种Windows平台下都能够正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下。
创建油槽程序:单文档(MailSlotSrv);服务器端只能接收数据,添加一个接收数据的按钮,在VIEW中增加消息响应。
void CMailSlotSrvView::OnRecvData()
{
// TODO: Add your command handler code here
HANDLE hmailslot;
hmailslot=CreateMailslot("////.//mailslot//MyMailslot",
0,MAILSLOT_WAIT_FOREVER,NULL );
if(INVALID_HANDLE_VALUE==hmailslot)
{
MessageBox("创建油槽失败!");
CloseHandle(hmailslot);
return;
}
//读取数据
char Buf[100];
DWORD dwread;
if(!ReadFile(hmailslot,Buf,100,&dwread,NULL))
{
MessageBox("读取数据失败!");
CloseHandle(hmailslot);
return;
}
MessageBox(Buf);
CloseHandle(hmailslot);
}
下面是油槽的客户端程序:
MFC的单文档(MailSlotClient)添加写入数据的按钮,添加VIEW的消息响应函数。
void CMailSlotClientView::OnSendData()
{
// TODO: Add your command handler code here
//如果不是在本地机器上,////./mailslot//MyMailslot中的.要替换成油槽的服务器端的进程所在的机器名
HANDLE hslot=CreateFile("////.//mailslot//MyMailslot",
GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE==hslot)
{
MessageBox("打开油槽失败!");
return;
}
char Buf[]="this is MailSlotClient";
DWORD dwwrite;
if(!WriteFile(hslot,Buf,strlen(Buf)+1,&dwwrite,NULL))
{
MessageBox("写入数据失败!");
CloseHandle(hslot);
return;
}
CloseHandle(hslot);
}
剪切板和匿名管道只能实现在一台机器上两个进程间通信,不能跨网络通信。而命名管道和油槽不仅能实现同一台机器上的通信,也可以跨网络实现两个进程通信。油槽可以实现1对多的通信,而命名管道只能实现点到点的单一的通信。利用一个实例通信。油槽通信量比较小,(424字节以下)。
注:
1.在剪切板中取数据时,不必要调用EmptyClipboard()(此函数是让当前进程拥有剪切板)。
2.匿名管道:CreatePipe,CreateProcess然后进行读写,CHILD端:hread=GetStdHandle(STD_INPUT_HANDLE);
hwrite=GetStdHandle(STD_OUTPUT_HANDLE);来进行读写。父子进行通信,父创建管道。不能跨网络,只能在同一台机器上通信。
3.命名管道:可以跨网络,服务器来创建管道,客户端只能和服务器端连接。客户端:CreateNamedPipe,ConnectNamedPipe,
WaitForSingleObject。客户端与服务器端级别无所谓,WaitNamedPipe,CreateFile.
4.注意ERROR_IO_PENDING用法。
5.邮槽:服务器端只能接收数据,客户端只能发送数据。服务器端 CreateMailslot,ReadFile。客户端CreateFile
(FILE_SHARE_READ),WriteFile
6.要记得及时关闭句柄。
- 孙鑫VC学习(第17课--进程间通信)
- 孙鑫VC++第17章进程间的通信
- 《VC++深入详解》学习笔记[14]——第17章 进程间通信
- 学习笔记:第17课 进程间的通信
- 孙鑫vc++ 17 进程间通信(1)剪贴板
- 孙鑫vc++ 17 进程间通信(2) 匿名管道
- 孙鑫vc++ 17 进程间通信(3) 命名管道
- 孙鑫vc++ 17 进程间通信(4) 邮槽
- 第17课 进程间通信
- 第17课 进程间通信
- VC 进程间通信
- VC:进程间通信
- VC:进程间通信
- VC 进程间通信
- VC 进程间通信
- vc进程间通信
- vc++17 进程间的通信
- 《VC++深入详解》学习笔记 第十七章 进程间通信
- 新手搭建PHP环境必备知识:windows下PHP5+APACHE+MYSQ完整配置
- 概括学习Fedora Zend Studio配置服务器
- Android深入浅出之Binder机制
- 省赛
- 用C#实现IP的路由跟踪- -
- 孙鑫VC学习(第17课--进程间通信)
- ioctl函数详细说明
- MySQL安装目录结构
- Erlang and Cloud Computing: A Fine Pair Indeed
- 孙鑫VC学习(第19课--动态链接库)
- C++和C#编写调用COM组件
- github
- Linux Date修改系统时间
- LINUX软中断处理如何与BH函数关联的