MFC:进程间通信方式[通俗易懂]

MFC:进程间通信方式[通俗易懂]windows下进程间通信方式:1、剪贴板(Ctrl+C;Ctrl+V)2、匿名管道3、命名管道4、邮槽/****************************************************************************************/一、剪贴板:voidCClipboardDlg::OnBtnSend()//向剪贴板中放置

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用


windows下进程间通信方式:

1、剪贴板(Ctrl+C;Ctrl+V) 2、匿名管道 3、命名管道 4、邮槽
/****************************************************************************************/
一、剪贴板:
void CClipboardDlg::OnBtnSend() //向剪贴板中放置数据
{

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); //锁定内存对象,维护一个锁计数加一,不可移动除非设置GlobalReAlloc

strcpy(pBuf,str);

GlobalUnlock(hClip);//解锁内存对象,GlobalUnlock将锁计数减一

SetClipboardData(CF_TEXT,hClip);//以指定的剪贴板格式向剪贴板中放置数据

CloseClipboard(); //关闭剪贴板

}

}

void CClipboardDlg::OnBtnRecv() //从剪贴板中获取数据
{

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();

}

}

}

MFC:进程间通信方式[通俗易懂]

/****************************************************************************************/
二、匿名管道(CreatePipe):
//匿名管道只能在本地机器上、在父子进程中通信,且子进程必须由父进程启动
1、父进程中:
//构造析构中
hRead = NULL;hWrite = NULL; 

if(hRead) CloseHandle(hRead);

if(hWrite) CloseHandle(hWrite);

void CParentView::OnPipeCreate() //创建匿名管道,创建子进程
{

SECURITY_ATTRIBUTES sa;
sa.bInheritHandle=TRUE; //可以被一个新的进程继承其读写句柄
sa.lpSecurityDescriptor=NULL;//默认的安全描述符
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
if(!CreatePipe(&hRead,&hWrite,&sa,0)) //创建匿名管道
{

MessageBox(“创建匿名管道失败!”);

return;

}

STARTUPINFO sui; //这个结构体成员真TM的多
PROCESS_INFORMATION pi;
ZeroMemory(&sui,sizeof(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; //置为NULL,析构就不必执行了

hWrite=NULL;

MessageBox(“创建子进程失败!”);

return;

}
else
{

CloseHandle(pi.hProcess);//在不需要使用内核对象句柄时关闭句柄,引用计数变为1

CloseHandle(pi.hThread);//切记,勿忘!

}

}
void CParentView::OnPipeRead() //从管道中读数据
{

char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL)) //可从文件管道控制台读数据
{

MessageBox(“读取数据失败!”);

return;

}
MessageBox(buf);

}

void CParentView::OnPipeWrite() //向管道中写数据
{

char buf[]=”这是父进程通过管道发送的数据:\nhttp://weibo.com/231237992/home”;
DWORD dwWrite; //实际写入的字节数
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{

MessageBox(“写入数据失败!”);

return;

}

}
2、子进程中://添加到当前项目,且和父进程在同一目录下
//构造析构同父进程
void CChildView::OnInitialUpdate() //窗口完全创建成功后第一个调用的函数
{

CView::OnInitialUpdate();
//获取子进程的标准输入和标准输出句柄,即得到管道的读取和写入句柄
hRead=GetStdHandle(STD_INPUT_HANDLE);
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);

}
void CChildView::OnPipeRead() 
{

char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{

MessageBox(“读取数据失败!”);

return;

}
MessageBox(buf);

}
void CChildView::OnPipeWrite() 
{

char buf[]=”这是子进程通过管道写入的数据”;
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{

MessageBox(“写入数据失败!”);

return;

}

}
/****************************************************************************************/
三、命名管道(CreateNamedPipe):
//若要创建命名管道的多个实例,就要多次调用CreateNamedPipe;
//命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节,充分利用了WinNT和Win2000的安全机制。
//将命名管道作为一种网络编程方案时,它实际上建立了一个客户机/服务器通信体系,并可靠传输数据
可以指定哪一组用户可以访问管道,哪一组不可访问,就不需编写用户身份验证的代码了;若用socket,需编码实现
命名管道服务器和客户机的区别:1、服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。
而客户机只能同一个现成的命名管道服务器建立连接。2、命名管道服务器只能在NT或2000上创建,客户机不然。
命名管道提供了两种基本通信模式:字节模式和消息模式。在字节模式中,数据以一个连续的字节流的形式在客户机和服务器
之间流动。而在消息模式中,客户机和服务器则通过一系列不连续的数据单位进行数据的收发,每次在管道上发出一条消息后,
它必须作为一条完整的消息读入。
1、服务器端:
void CNamedPipeSrvView::OnPipeCreate() 
{

//.表示本地机器(远程服务器的名字),固定格式,MYPipe:自定义的管道名
//管道是双向的|重叠模式(读写执行要花一段时间才能完成的函数会立刻返回,耗费时间的操作在后台执行)
//eg:可同时在一个管道句柄上进行读取和写入操作
//参数0:字节模式;此参数决定是字节模式还是消息模式
//参数1:可创建的最大实例数(若想同时连接5个,需调用五次CreateNamedPipe)
//输入输出缓冲区的buffer大小,缺省的超时值,安全属性
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; 
//若创建命名管道用了FILE_FLAG_OVERLAPPED,必须指定此结构体,包含一个人工重置的事件对象句柄
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;

}

}
2、客户端:
//首先构造析构:…
void CNamedPipeCltView::OnPipeConnect() 
{

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 CNamedPipeCltView::OnPipeRead() 
{

char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{

MessageBox(“读取数据失败!”);

return;

}
MessageBox(buf);

}
void CNamedPipeCltView::OnPipeWrite() 

{

char buf[]=”命名管道测试程序”;
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{

MessageBox(“写入数据失败!”);

return;

}

}
/****************************************************************************************/
四、邮槽(CreateMailslot):
//邮槽是基于广播通信体系设计出来的,采用无连接的不可靠的数据传输。
//油槽是一种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据。
//为保证邮槽能在各种win平台下正常工作,传输信息时,应将消息长度限制在424字节以下。
//服务器进程:
void CMailslotSrvView::OnMailslotRecv() 
{

HANDLE hMailslot;
hMailslot=CreateMailslot(“\\\\.\\mailslot\\MyMailslot”,0, //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);//关闭邮槽句柄

}
//客户机进程:
void CMailslotCltView::OnMailslotSend() 
{

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[]=”I love you.”;
DWORD dwWrite;
if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL)) //向邮槽写数据
{

MessageBox(“写入数据失败!”);

CloseHandle(hMailslot);

return;

}
CloseHandle(hMailslot);

}


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/183078.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号