(1)剪贴板
void CMFCApplication39Dlg::OnBnClickedBtnSend()
{
// TODO: 在此添加控件通知处理程序代码
if(OpenClipboard())//打开剪贴板
{
CString str;//保存发送编辑框控件上的数据
HANDLE hClip;//保存调用GlobalAlloc函数后分配的内存对象的句柄
char *pBuf;//保存调用GlobalLock函数后返回的内存地址
EmptyClipboard();//清空剪贴板上的数据
GetDlgItemText(IDC_EDIT_SEND,str);
hClip = GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
pBuf = (char *)GlobalLock(hClip);
int i;
for(i=0;i<str.GetLength();i++)
{
pBuf[i] = str[i];
}
pBuf[i] = '\0';
GlobalUnlock(hClip);
SetClipboardData(CF_TEXT,hClip);
CloseClipboard();//关闭剪贴板
}
}
void CMFCApplication39Dlg::OnBnClickedBtnRecv()
{
// TODO: 在此添加控件通知处理程序代码
if(OpenClipboard())//打开剪贴板
{
if(IsClipboardFormatAvailable(CF_TEXT))
{
HANDLE hClip;//保存调用GlobalAlloc函数后分配的内存对象的句柄
char *pBuf;//保存调用GlobalLock函数后返回的内存地址
hClip = GetClipboardData(CF_TEXT);
pBuf = (char *)GlobalLock(hClip);
GlobalUnlock(hClip);
CString str(pBuf);
SetDlgItemText(IDC_EDIT_RECV,str);
}
CloseClipboard();
}
}
(2)匿名管道
匿名管道是一个未命名的、单向管道,通常用来在一个父进程和一个子进程之间传输数据,匿名管道只能实现本地机器上两个进程间的通信,而不能实现跨网络的通信。
父进程:
BOOL CParentDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
pMenu=new CMenu() ;
pMenu->LoadMenu(IDR_MENU1);
SetMenu(pMenu);
hWrite = NULL;
hRead = NULL;
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CParentDlg::OnPipeCreate()
{
// TODO: 在此添加命令处理程序代码
SECURITY_ATTRIBUTES sa;//SECURITY_ATTRIBUTES结构体有三个成员
sa.nLength = sizeof(SECURITY_ATTRIBUTES);//nLength指定结构体的大小
sa.lpSecurityDescriptor = NULL;//lpSecurityDescriptor可以传入NULL
sa.bInheritHandle = TRUE;//bInheritHandle指定返回的句柄能否被一个新的进程继承,如果为TRUE,表示能够被新进程继承
if(!CreatePipe(&hRead,&hWrite,&sa,0))//创建匿名管道,hRead返回管道的读取句柄,hWrite返回管道的写入句柄,sa是SECURITY_ATTRIBUTES结构体变量,第四个参数表示缓冲区大小,0表示默认大小。
{
MessageBox(_T("创建匿名管道失败!"));
return;
}
STARTUPINFO sui;
PROCESS_INFORMATION pi;
ZeroMemory(&sui,sizeof(STARTUPINFO));
sui.cb = sizeof(STARTUPINFO);//cb表示STARTUPINFO结构体的大小
sui.dwFlags = STARTF_USESTDHANDLES;//如果dwFlags为STARTF_USESTDHANDLES,那么将使用STARTUPINFO的hStdInput,hStdOutput,hStdError设置所创建的新进程的标准输入,标准输出和标准错误句柄。
sui.hStdInput = hRead;//新进程标准输入句柄
sui.hStdOutput = hWrite;//新进程标准输出句柄
sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);//新进程标准错误句柄
if(!CreateProcess(_T("F:\\代码保存\\Parent\\Debug\\Child.exe"),NULL,NULL,NULL,TRUE,0,NULL,NULL,&sui,&pi))//创建进程,第一个参数表示要创建进程的路径,第四个参数如果为TRUE,
//那么父进程的每个可继承的打开句柄都能被子进程继承。我们设置为TRUE,让Child继续Parent的读写句柄,第七个参数是指向STARTUPINFO结构体的指针,第八个参数是指向PROCESS_INFORMATION的指针
{
CloseHandle(hRead);
CloseHandle(hWrite);
hRead = NULL;
hWrite = NULL;
MessageBox(_T("创建子进程失败!"));
return;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
void CParentDlg::OnPipeRead()
{
// TODO: 在此添加命令处理程序代码
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))//读取数据
{
MessageBox(_T("读取数据失败!"));
return;
}
MessageBox((CString)buf);
}
void CParentDlg::OnPipeWrite()
{
// TODO: 在此添加命令处理程序代码
char buf[100]= "http://www.baidu.com";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))//写入数据
{
MessageBox(_T("写入数据失败!"));
return;
}
}
子进程:
BOOL CChildDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
pMenu = new CMenu;
pMenu->LoadMenuW(IDR_MENU1);
SetMenu(pMenu);
hRead = GetStdHandle(STD_INPUT_HANDLE);//获取子进程的标准输入
hWrite = GetStdHandle(STD_OUTPUT_HANDLE);//获取子进程的标准输出
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CChildDlg::OnPipeRead()
{
// TODO: 在此添加命令处理程序代码
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox(_T("读取数据失败!"));
return;
}
MessageBox((CString)buf);
}
void CChildDlg::OnPipeWrite()
{
// TODO: 在此添加命令处理程序代码
char buf[100]= "匿名管道测试程序";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox(_T("写入数据失败!"));
return;
}
}
这样父进程与子进程就能就行通讯了
(3)命名管道
命名管道作为一种通信方法,有其独特的优越性,这主要表现在它不完全依赖于某一种协议,而是适用于任何协议——只要能够实现通信。
命名管道具有很好的使用灵活性,表现在:
1) 既可用于本地,又可用于网络。
2) 可以通过它的名称而被引用。
3) 支持多客户机连接。
4) 支持双向通信。
5) 支持异步重叠I/O操作。
服务器端:
void CNamePipeSrvDlg::OnPipeCreate()
{
// TODO: 在此添加命令处理程序代码
m_hPipe = CreateNamedPipe(_T("\\\\.\\pipe\\MyPipe"),PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,0,1,1024,1024,0,NULL);//创建命名管道
//第一个参数: \\\\.\\pipe\\MyPipe, 必须为这种格式。中间的“.”表示本地机器,如果要跟远程机器建立连接,则需要设定远程服务器的名字。
//第二个参数:表明管道的访问方式,双向,单向,设置重叠模式等
//第三个参数:指定管道的模式,为0默认是字节读的方式,阻塞模式
//第四个参数:指定管道能创建的最大数目
//第五个参数:指定为输出缓冲区所保留的字节数
//第六个参数:指定为输入缓冲区所保留的字节数
//第七个参数:指定默认的超时值
//第八个参数:命名管道的安全描述符。NULL表示默认。
if(INVALID_HANDLE_VALUE ==m_hPipe)
{
MessageBox(_T("创建命名管道失败!"));
m_hPipe = NULL;
return;
}
//创建匿名的人工重置事件对象
HANDLE hEvent;
hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if(!hEvent)
{
MessageBox(_T("创建事件对象失败!"));
CloseHandle(m_hPipe);
m_hPipe = NULL;
return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap,sizeof(OVERLAPPED));
ovlap.hEvent = hEvent;
//等待客户端请求的到来
if(!ConnectNamedPipe(m_hPipe,&ovlap))//第一个参数表示命名管道服务器的句柄,第二个参数指向一个OVERLAPPED结构的指针
{
if(ERROR_IO_PENDING != GetLastError())
{
MessageBox(_T("等待客户端连接失败!"));
CloseHandle(m_hPipe);
CloseHandle(hEvent);
m_hPipe = NULL;
return;
}
}
if(WAIT_FAILED == WaitForSingleObject(hEvent,INFINITE))
{
MessageBox(_T("等待对象失败!"));
CloseHandle(m_hPipe);
CloseHandle(hEvent);
m_hPipe = NULL;
return;
}
CloseHandle(hEvent);
}
void CNamePipeSrvDlg::OnPipeRead()
{
// TODO: 在此添加命令处理程序代码
char buf[100];
DWORD dwRead;
if(!ReadFile(m_hPipe,buf,100,&dwRead,NULL))
{
MessageBox(_T("读取数据失败"));
return;
}
MessageBox((CString)buf);
}
void CNamePipeSrvDlg::OnPipeWrite()
{
// TODO: 在此添加命令处理程序代码
char buf[] = "http://www.baidu.com";
DWORD dwWrite;
if(!WriteFile(m_hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox(_T("写入数据失败!"));
return;
}
}
客户端:
void CNamePipeCltDlg::OnPipeWrite()
{
// TODO: 在此添加命令处理程序代码
char buf[] = "命名管道测试!";
DWORD dwWrite;
if(!WriteFile(m_hPipe,buf,sizeof(buf)+1,&dwWrite,NULL))
{
MessageBox(_T("写入数据失败!"));
return;
}
}
void CNamePipeCltDlg::OnPipeRead()
{
// TODO: 在此添加命令处理程序代码
char buf[100];
DWORD dwRead;
if(!ReadFile(m_hPipe,buf,100,&dwRead,NULL))
{
MessageBox(_T("读取数据失败!"));
return;
}
MessageBox((CString)buf);
}
void CNamePipeCltDlg::OnPipeConnect()
{
// TODO: 在此添加命令处理程序代码
//判断是否有可利用的命名管道
if(!WaitNamedPipe(_T("\\\\.\\pipe\\MyPipe"),NMPWAIT_WAIT_FOREVER))
{
MessageBox(_T("当前没有可利用的命名管道!"));
return;
}
//打开可利用的命名管道,并与服务器端进行通讯
m_hPipe = CreateFile(_T("\\\\.\\pipe\\MyPipe"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE == m_hPipe)
{
MessageBox(_T("打开命名管道失败!"));
m_hPipe = NULL;
return;
}
}
(4)邮槽
邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。邮槽是一种单向通信机制,创建邮槽的服务器读取数据,打开邮槽的客户机写入数据。邮槽是利用广播通信的,也就是说,邮槽可以实现一对多的单向通信。
服务器端:
BOOL CMailSoltSrvDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
HANDLE hMailslot;
//创建邮槽
hMailslot = CreateMailslot(_T("\\\\.\\mailslot\\MyMailSlot"),0,MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE == hMailslot)
{
MessageBox(_T("创建邮槽失败"));
return TRUE;
}
char buf[100];
DWORD dwRead;
//从邮槽读取数据
if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
{
MessageBox(_T("读取数据失败"));
CloseHandle(hMailslot);
return TRUE;
}
MessageBox((CString)buf);
CloseHandle(hMailslot);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
客户端:
BOOL CMailSlotCltDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
HANDLE hMailSlot;
//打开邮槽
hMailSlot = CreateFile(_T("\\\\.\\mailslot\\MyMailslot"),GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE == hMailSlot)
{
MessageBox(_T("打开邮槽失败!"));
return TRUE;
}
char buf[] = "http://www.baidu.com";
DWORD dwWrite;
//向邮槽写入数据
if(!WriteFile(hMailSlot,buf,sizeof(buf)+1,&dwWrite,NULL))
{
MessageBox(_T("写入数据失败!"));
return TRUE;
}
CloseHandle(hMailSlot);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
小结:这四种通信方式,其中剪贴板和匿名管道只能实现同一台机器上两个进程间的通信,而不能实现跨网络的通信,而命名管道和邮槽不仅可以实现本地的通信,还可以实现跨网络的通信。而命名管道是一对一通信,邮槽是一对多的通信,但是邮槽传输的数据量小,可以根据具体情况选用合适的方式。