文章目录
- 剪切板
- 管道
- 匿名管道
- 父进程写入数据子进程读出数据
- 命名管道
- 邮槽
- 邮槽服务器
- 邮槽客户端
剪切板
设置界面
发送
//设置剪切板数据
void CClipboardDlg::OnBnClickedBtnSend()
{
UpdateData(TRUE);
if (m_strSend.IsEmpty())
{
MessageBox(L"请输入需要设置的文本", L"提示");
return;
}
//打开剪切板
if (FALSE == OpenClipboard())
{
CString str;
str.Format(L"打开剪切板失败,错误代号:%d", GetLastError());
MessageBox(str);
return;
}
//清空剪切板
EmptyClipboard();
//在全局堆申请一块内存
HGLOBAL hClip=GlobalAlloc(GMEM_MOVEABLE,m_strSend.GetLength()*2+1);
//对全局堆内存枷锁
TCHAR* pBuffer=(TCHAR*)GlobalLock(hClip);
//清空内存
ZeroMemory(pBuffer, m_strSend.GetLength() * 2 + 1);
memcpy(pBuffer, m_strSend.GetBuffer(), m_strSend.GetLength() * 2 + 1);
//解锁
GlobalUnlock(hClip);
//设置剪切板数据
SetClipboardData(CF_UNICODETEXT, hClip);
//关闭剪切板,一定要关闭剪切板
CloseClipboard();
}
从剪切版里面接收数据
void CClipboardDlg::OnBnClickedBtnReceive()
{
//打开剪切板
if (FALSE == OpenClipboard())
{
CString str;
str.Format(L"打开剪切板失败,错误代号:%d", GetLastError());
MessageBox(str);
return;
}
//检查截切版中是否有我们指定格式的数据
if (IsClipboardFormatAvailable(CF_UNICODETEXT))
{
//得到剪切板数据
HANDLE hClip=GetClipboardData(CF_UNICODETEXT);
//把句柄转换为指针
TCHAR* pBuffer=(TCHAR*)GlobalLock(hClip);
m_strRecv = pBuffer;
//解锁
GlobalUnlock(hClip);
UpdateData(FALSE);
}
//关闭剪切板,一定要关闭剪切板
CloseClipboard();
}
管道
命名管道(Named Pipe)和匿名管道(Anonymous Pipe)都是用于进程间通信的机制,但它们有一些区别。
命名管道(Named Pipe):
命名管道是具有唯一名称的管道,可以通过该名称在不同进程之间进行通信。
命名管道可以在本地机器上的不同进程之间进行通信,也可以在网络上的不同计算机之间进行通信。
命名管道支持全双工通信,可以同时进行读取和写入操作。
命名管道可以被多个进程打开和使用,允许多个读取者和写入者同时进行操作。
命名管道通常用于需要持久性连接和长期通信的场景,例如服务器和客户端之间的通信。
匿名管道(Anonymous Pipe):
匿名管道是一种无名称的管道,仅限于在父子进程或者兄弟进程之间进行通信。
匿名管道只能在同一个计算机上的相关进程之间进行通信,不能跨网络使用。
匿名管道是单向的,只能支持单向的数据流传输,例如从父进程到子进程。
匿名管道一般用于简单的进程间通信需求,例如父进程将数据传递给子进程进行处理。
匿名管道
父进程
子进程
子进程的*.exe放到父进程中
父进程启动子进程
//创建子进程
void CParentPipDlg::OnBnClickedBtnCreate()
{
//创建匿名管道
//第1,2个参数:读写句柄
//第3个参数:安全描述符
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = TRUE;//子进程继承父进程的句柄
sa.lpSecurityDescriptor = NULL;//安全描述符
sa.nLength = sizeof(SECURITY_ATTRIBUTES);//结构体大小
//第4个参数:缓冲区大小
if (!CreatePipe(&m_hRead, &m_hWrite, &sa, 0))
{
MessageBox(L"创建匿名管道失败",L"提示");
return;
}
TCHAR szCmdLine[MAX_PATH] = L"subPip.exe";
//启动信息
STARTUPINFO si = { 0 };
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;//标准输入输出
si.hStdInput = m_hRead;//标准输入
si.hStdOutput = m_hWrite;//写入
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);//标准输入输出错误句柄
//进程信息
PROCESS_INFORMATION pi = { 0 };
if (!CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si,&pi))
{
CloseHandle(m_hRead);
CloseHandle(m_hWrite);
m_hRead = NULL;
m_hWrite = NULL;
MessageBox(L"创建子进程失败", L"提示");
return;
}
}
父进程写入数据子进程读出数据
父进程写入数据
//写入数据
void CParentPipDlg::OnBnClickedBntWrite()
{
wchar_t buffer[100]=L"我是父进程";
//向文件或者其他输出设备写入数据的函数
//第一个参数:写入句柄
//第二个参数:待写入的数据缓冲区
//第三个参数:待写入数据的字节数
//第四个参数:实际写入的字节数
//第五个参数;异步操作的重叠结构(可选)
DWORD dwWrite;
if (!WriteFile(m_hWrite, buffer, wcslen(buffer) * 2 + 1, &dwWrite, NULL))
{
MessageBox(L"写入数据失败");
return;
}
}
子进程读入数据
获取读取句柄和写入句柄m_hRead = GetStdHandle(STD_INPUT_HANDLE); m_hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
void CSubPipDlg::OnBnClickedBtnRead()
{
wchar_t buffer[100] = { 0 };
DWORD dwRead = 0;
if (!ReadFile(m_hRead, buffer, sizeof(buffer), &dwRead, NULL))
{
MessageBox(L"读取数据失败");
return;
}
MessageBox(buffer);
}
子进程写入数据父进程读取数据原理一致
命名管道
命名管道服务器
命名管道客户端
服务器命名管道的创建
//命名管道
void CServerDlg::OnBnClickedBtnCreate()
{
//第一个参数:名字,名字格式:\\.\pipe\pipename
//第二个参数:打开模式 PIPE_ACCESS_DUPLEX双工模式,FILE_FLAG_OVERLAPPED 在读写过程中会发送一个信号
//第三个参数:管道模式 PIPE_TYPE_MESSAGE 消息的方式
//第四个参数:最大实例个数
//第五个参数:输出的缓冲大小
//第六个参数:输入的缓冲区大小
//第七个参数:默认的超时时间
//第八个参数:安全描述
m_hPipe=CreateNamedPipe(L"\\\\.\\pipe\\test", PIPE_ACCESS_DUPLEX| FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE,1,1024,1024,0,NULL);
if (m_hPipe == INVALID_HANDLE_VALUE)
{
MessageBox(L"创建命名管道失败");
return;
}
//创建匿名事件对象
HANDLE hEvent=CreateEvent(NULL, TRUE, FALSE,NULL);
//异步 I/O 操作的参数和状态信息
OVERLAPPED ol = {0};
ol.hEvent = hEvent;
//等待客户端的链接
//ConnectNamedPipe 等待客户端有没有连接管道
if (FALSE == ConnectNamedPipe(m_hPipe, &ol))
{
if (GetLastError() != ERROR_IO_PENDING)
{
MessageBox(L"等待客户端链接失败");
return;
}
}
//判断有没有信号
if (WaitForSingleObject(hEvent, INFINITE) == WAIT_FAILED)
{
MessageBox(L"等待对象失败");
CloseHandle(m_hPipe);
CloseHandle(hEvent);
m_hPipe = NULL;
return;
}
CloseHandle(hEvent);
}
客户端管道的连接
void CClientDlg::OnBnClickedBtnConnect()
{
//判断有没有可以用的管道
if (FALSE == WaitNamedPipe(L"\\\\.\\pipe\\test", NMPWAIT_WAIT_FOREVER))
{
MessageBox(L"当前没有可以利用的命名管道");
return;
}
//打开管道
//第一个参数:管道名
//第二个参数:读写模式
//第三个参数:共享模式 0表示不共享
//第四个参数:安全属性
//第五个参数:创建方式
//第六个参数:文件属性
//第七个参数:提供文件的拓展属性
CClientDlg::m_hPipe=CreateFile(L"\\\\.\\pipe\\test", GENERIC_READ | GENERIC_WRITE, 0,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
if (m_hPipe == INVALID_HANDLE_VALUE)
{
MessageBox(L"打开管道失败");
m_hPipe = NULL;
return;
}
}
邮槽
邮槽是一种基于文件的通信机制,通过创建一个具有唯一名称的邮槽对象,进程可以向该邮槽写入消息,而其他进程可以从该邮槽读取消息。邮槽支持广播方式,即多个进程都可以从同一个邮槽读取相同的消息。需要注意的是,邮槽是单向的,只支持从邮槽中读取消息或者向邮槽写入消息,不能同时进行读写操作。此外,邮槽只能用于同一台计算机上的进程间通信,不能用于跨网络的通信。 邮槽是一种简单而可靠的进程间通信机制,适用于需要在同一台计算机上的多个进程之间传递消息的场景。使用邮槽可以实现进程之间的解耦和异步通信,提高系统的可扩展性和响应性。
邮槽服务器
设置ui
初始化邮槽
BOOL CMailSlotDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
//创建油槽
//第一个参数:名字
//第二个参数:最大消息的最大大小
//第三个参数:超时时间
//第四个参数:安全属性
m_hMailSlot=CreateMailslot(L"\\\\.\\mailslot\\test", 0, MAILSLOT_WAIT_FOREVER, NULL);
if (m_hMailSlot == INVALID_HANDLE_VALUE)
{
MessageBox(L"创建邮槽失败");
return TRUE;
}
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
读取内容
//读取
void CMailSlotDlg::OnBnClickedBtnRead()
{
wchar_t buffer[100] = { 0 };
DWORD dwRead=0;
//第一个参数:窗口句柄
//第二个参数:数据缓存
//第三个参数:读取的大小
//第四个参数:实际读了多少
//第五个参数:溢出量
if (!ReadFile(m_hMailSlot, buffer, sizeof(buffer), &dwRead, NULL))
{
MessageBox(L"读取数据失败");
return;
}
MessageBox(buffer);
}
邮槽客户端
ui设置
void CMailSlotClientDlg::OnBnClickedBtnWrite()
{
HANDLE hMainSlot=CreateFile(L"\\\\.\\mailslot\\test", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hMainSlot == INVALID_HANDLE_VALUE)
{
MessageBox(L"打开邮槽失败");
return;
}
wchar_t buffer[100] = L"邮槽客户端";
DWORD dwWrite = 0;
if (!WriteFile(hMainSlot, buffer, wcslen(buffer) * 2 + 1, &dwWrite, NULL))
{
MessageBox(L"写入数据失败");
return;
}
}