学习笔记:第17课 进程间的通信
来源:互联网 发布:生物医学专业数据库 编辑:程序博客网 时间:2024/06/04 00:36
◆ 进程间通信的四种方式
剪贴板:所有的进程都可以访问。
匿名管道
命名管道
邮槽
剪贴板:所有的进程都可以访问。
匿名管道
命名管道
邮槽
◆BOOL OpenClipboard()如果打开剪贴版,则打开是非0。
打开剪贴版后,别的应用程序就不能再调用剪贴版了。直到调用了 CloseClipboard();
◆ EmptyClipboard();清空剪贴版,并释放剪贴版的句柄
◆ SetClipboardData在剪贴版上放置程序
◆ GlobalAlloc从堆上分配指定数目的字节。WIN32内存管理没有提供一个本地堆和全局堆。他和动态数据交换和剪贴版一起使用。
◆ 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); //解锁前拷贝
◆ 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);
◆ GlobalUnlock(hClip);
◆ SetDlgItemText(IDC_EDIT_RECV,pBuf);
◆ CloseClipboard();
◆ }
◆ }
◆ }
◆ 创建匿名管道:CreatePipe他只能够在父子进程之间进行通信
◆ BOOL CreatePipe(
PHANDLE hReadPipe,//读的句并
PHANDLE hWritePipe,//写的句并
LPSECURITY_ATTRIBUTES lpPipeAttributes,//安全描述符
DWORD nSize
);
PHANDLE hReadPipe,//读的句并
PHANDLE hWritePipe,//写的句并
LPSECURITY_ATTRIBUTES lpPipeAttributes,//安全描述符
DWORD nSize
);
◆ 启动进程:
BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
◆
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));// 将机构体中所有成员置为零可以使用函数
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.sunxin.org";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
◇ 子进程的编写
◇ 为得到子进程的标准输入和标准输出句柄,需要在View类窗口完全创建成功之后去获取,增加一个虚函数OnInitialUpdate();此函数是当我们的窗口创建成功之后第一个调用的函数
代码如下:
代码如下:
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);
}
◆ 命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信。
◆ 命名管道充分利用了Windows NT和Windows2000内建的安全机制。
◆ 名管道是围绕Windows文件系统设计的一种机制,采用“命名管道文件系统(Named Pipe File System,NPFS)”接口,因此,客户机和服务器可利用标准的Win32文件系统内核(例如ReadFile和WriteFile)来进行数据的收发。
◆ 命名管道服务器和客户机的区别在于:服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。而客户机只能同一个现成的命名管道服务器建立连接。
◆ 名管道服务器只能在Windows NT或Windows2000上创建,所以,我们无法在两台Windows95或Windows98计算机之间利用管道进行通信。不过,客户机可以是Windows95或Windows98计算机,与Windows NT或Windows2000计算机进行连接通信。
◆ 命名管道提供了两种基本通信模式:字节模式和消息模式。在字节模式中,数据以一个连续的字节流的形式,在客户机和服务器之间流动。而在消息模式中,客户机和服务器则通过一系列不连续的数据单位,进行数据的收发,每次在管道上发出一条消息后,它必须作为一条完整的消息读入。
◆服务器进程使用
HANDLE CreateNamedPipe(
LPCTSTR lpName, //唯一标拾管道 //./pipe/pipename
DWORD dwOpenMode, //访问的方式
DWORD dwPipeMode, //指定是字节模式还是消息模式的管道
DWORD nMaxInstances, //最大可以创建几个命名管道的实例,对于同一个名字的管道来说
DWORD nOutBufferSize,
DWORD nInBufferSize,
DWORD nDefaultTimeOut,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
HANDLE CreateNamedPipe(
LPCTSTR lpName, //唯一标拾管道 //./pipe/pipename
DWORD dwOpenMode, //访问的方式
DWORD dwPipeMode, //指定是字节模式还是消息模式的管道
DWORD nMaxInstances, //最大可以创建几个命名管道的实例,对于同一个名字的管道来说
DWORD nOutBufferSize,
DWORD nInBufferSize,
DWORD nDefaultTimeOut,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
对于字节模式的命名管道,他不能和消息读的命名管道一起使用
◆ void CNamePipeView::OnPipeCreate()
{
// TODO: 在此添加命令处理程序代码
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 ;
}
{
// TODO: 在此添加命令处理程序代码
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 ;
}
hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!hEvent)
{
MessageBox("创建事件对象失败!");
CloseHandle(hPipe);
hPipe=NULL;
return ;
}
OVERLAPPED lp;
ZeroMemory(&lp,sizeof(OVERLAPPED));
lp.hEvent=hEvent; //人工从置的事件对象
ZeroMemory(&lp,sizeof(OVERLAPPED));
lp.hEvent=hEvent; //人工从置的事件对象
}
if(!ConnectNamedPipe(hPipe,&lp))
{
if(ERROR_IO_PENDING != GetLastError())
//如果相等表示可能在随后的时间内这个操作会完成
{
MessageBox("等待客户段连接失败!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
{
if(ERROR_IO_PENDING != GetLastError())
//如果相等表示可能在随后的时间内这个操作会完成
{
MessageBox("等待客户段连接失败!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
if(WAIT_FAILED= =WaitForSingleObject(hEvent,INFINITE))
{// INFINITE表示永远等待
MessageBox("等待对象失败!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
{// INFINITE表示永远等待
MessageBox("等待对象失败!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
CloseHandle(hEvent);
}
}
◆服务器等待连接请求到来,使用函数:
BOOL ConnectNamedPipe(
HANDLE hNamedPipe,
LPOVERLAPPED lpOverlapped);
BOOL ConnectNamedPipe(
HANDLE hNamedPipe,
LPOVERLAPPED lpOverlapped);
如果ConnectNamedPipe返回一个零值而ERROR_IO_PENDING= =GetLastError()表示这个操作没有失败,可能在随后的时间内这个操作会完成
调用CreateEvent创建一个人工或自动重置的事件对象
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
◆ WaitForSingleObject等待事件对象变为有信号状态
◆ 客户机端:WaitNamedPipe等待,直到一个超时值的发生
◆ 客户端程序要相对简单,只需要等待网络上命名管道实例,如果连上就打开命名空间
代码如下:
void CNamedPipeClientView::OnPipeConnect()
{
// TODO: 在此添加命令处理程序代码
if(!WaitNamedPipe("////.//pipe//MyTest",NMPWAIT_WAIT_FOREVER))
{
MessageBox("当前没有可利用的命名管道实例!");
return ;
}
hPipe=CreateFile("////.//pipe//MyTest",FILE_SHARE_READ | FILE_SHARE_WRITE,
0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
//点那部分表示服务器名字,用“.”表示本地主机,如果要连接其他主机就要把点//换成主机名。注意双引号中的反斜杠,四个表示两个
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("打开命名空间失败!");
hPipe=NULL;
return ;
}
}
代码如下:
void CNamedPipeClientView::OnPipeConnect()
{
// TODO: 在此添加命令处理程序代码
if(!WaitNamedPipe("////.//pipe//MyTest",NMPWAIT_WAIT_FOREVER))
{
MessageBox("当前没有可利用的命名管道实例!");
return ;
}
hPipe=CreateFile("////.//pipe//MyTest",FILE_SHARE_READ | FILE_SHARE_WRITE,
0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
//点那部分表示服务器名字,用“.”表示本地主机,如果要连接其他主机就要把点//换成主机名。注意双引号中的反斜杠,四个表示两个
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("打开命名空间失败!");
hPipe=NULL;
return ;
}
}
void CNamedPipeClientView::OnPipeRead()
{
// TODO: 在此添加命令处理程序代码
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{
MessageBox ("读取数据失败!");
return ;
}
else MessageBox (buf);
}
{
// TODO: 在此添加命令处理程序代码
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{
MessageBox ("读取数据失败!");
return ;
}
else MessageBox (buf);
}
void CNamedPipeClientView::OnPipeWrite()
{
// TODO: 在此添加命令处理程序代码
char buf[]="命名管道测试程序!!";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox ("写入数据失败!");
return ;
}
}
{
// TODO: 在此添加命令处理程序代码
char buf[]="命名管道测试程序!!";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox ("写入数据失败!");
return ;
}
}
◆邮槽
邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。
邮槽是一种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据,为保证邮槽在各种Windows平台下都能正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下。
要创建邮槽,可以使用函数
HANDLE CreateMailslot(
LPCTSTR lpName, ////./mailslot/MyMail",
DWORD nMaxMessageSize,
DWORD lReadTimeout, //读超时的间隔
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。
邮槽是一种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据,为保证邮槽在各种Windows平台下都能正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下。
要创建邮槽,可以使用函数
HANDLE CreateMailslot(
LPCTSTR lpName, ////./mailslot/MyMail",
DWORD nMaxMessageSize,
DWORD lReadTimeout, //读超时的间隔
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
服务器端代码:
void CMailsoltSrvView::OnMailRecv()
{
// TODO: 在此添加命令处理程序代码
// HANDLE hMail;
void CMailsoltSrvView::OnMailRecv()
{
// TODO: 在此添加命令处理程序代码
// HANDLE hMail;
hMail=CreateMailslot("////.//mailslot//MyMail",0,MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE==hMail)
{
MessageBox("创建邮槽失败!");
return;
}
char buf[100]="0";
DWORD dwRead;
if(!ReadFile(hMail,buf,100,&dwRead,NULL))
{
MessageBox ("读取数据失败!");
CloseHandle(hMail);
return ;
}
MessageBox (buf);
CloseHandle(hMail);
}
if(INVALID_HANDLE_VALUE==hMail)
{
MessageBox("创建邮槽失败!");
return;
}
char buf[100]="0";
DWORD dwRead;
if(!ReadFile(hMail,buf,100,&dwRead,NULL))
{
MessageBox ("读取数据失败!");
CloseHandle(hMail);
return ;
}
MessageBox (buf);
CloseHandle(hMail);
}
◆ 在客户端口,CreateFile也可以打开邮槽
◆ 客户端代码:
void CMailsoltCltView::OnMailSend()
{
// TODO: 在此添加命令处理程序代码
HANDLE hMail;
hMail=CreateFile("////.//mailslot//MyMail",GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMail)
{
MessageBox("打开邮槽失败!");
return;
}
char buf[]="邮槽测试程序!";
DWORD dwWrite;
if(!WriteFile(hMail,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox ("写入数据失败!");
CloseHandle(hMail);
return ;
}
CloseHandle(hMail);
}
void CMailsoltCltView::OnMailSend()
{
// TODO: 在此添加命令处理程序代码
HANDLE hMail;
hMail=CreateFile("////.//mailslot//MyMail",GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMail)
{
MessageBox("打开邮槽失败!");
return;
}
char buf[]="邮槽测试程序!";
DWORD dwWrite;
if(!WriteFile(hMail,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox ("写入数据失败!");
CloseHandle(hMail);
return ;
}
CloseHandle(hMail);
}
- 学习笔记:第17课 进程间的通信
- 孙鑫VC学习(第17课--进程间通信)
- 《VC++深入详解》学习笔记[14]——第17章 进程间通信
- 第17课 进程间通信
- 第17课 进程间通信
- Linux 学习第一天之进程间的通信
- Linux进程间通信学习笔记
- Linux系统学习笔记:进程间通信
- 进程间通信学习笔记五(信号量)
- 进程\线程间通信学习笔记(一)
- Linux系统学习笔记:进程间通信
- 学习笔记—进程间通信
- MFC学习笔记-进程间通信
- 学习笔记38-进程间通信
- 孙鑫VC++第17章进程间的通信
- 进程间通信学习笔记一(管道通信)
- 进程间通信学习笔记二(信号通信)
- 进程间通信学习笔记三(共享内存通信)
- .net 资源链接
- ASP.NET 2.0加密Web.config 配置文件 (终极版)
- 外企英语电话面试应急模拟
- 淮上喜会梁州故人
- Ajax原则
- 学习笔记:第17课 进程间的通信
- 多线程开发学习笔记之线程同步——互斥量
- Google Maps API编程资源大全
- Java开源 AJAX框架
- 应聘Java笔试时可能出现问题及其答案(一)
- 对文档库和列表进行的一些基本操作(转)
- Java开源IDE
- 插队,又是插队!
- 应聘Java笔试时可能出现问题及其答案(二)