1.继续完成进程间通信
1.1 完成 剪贴板Ctrl+c 按钮的消息处理函数
1.从 Edit 控件上往下取字符串: UpdateData(TRUE) ;
2.首先打开剪贴板: OpenClipboard() ;
3.若剪贴板有复制好的东西则我们将其进行清空: EmptyClipboard() ;
4.申请一个全局堆空间: HGLOBAL h_global = GlobalAlloc(GPTR, m_size) ;申请全局堆空间需要我们给定一个大小,我们给定的大小为我们从 Edit 控件上取下来的字符串的宽字符集大小: int m_size = (m_str_send.GetLength()+1)2 ;
5.给全剧堆加锁: wchar_t psz_content = (wchar_t*)::GlobalLock(h_global) ;
6.把数据放到剪贴板中: memcpy(psz_content, m_str_send.GetBuffer(), m_size) ;
7.给全局堆解锁: GlobalUnlock ;
8.将全局堆交给剪贴板: SetClipboardData(CF_UNICODETEXT, h_global) ;
9.关闭剪贴板: CloseClipboard ;
void CSendDlg::OnBnClickedButton2()
{
UpdateData(TRUE);
int m_size = (m_str_send.GetLength()+1)*2;
// 1.1 打开剪贴板
if(OpenClipboard() == 0)
{
this->MessageBox(L"打开剪贴板失败");
return;
}
// 1.2 清空剪贴板
if(EmptyClipboard() == 0)
{
CloseClipboard();
MessageBox(L"清空剪贴板失败");
return;
}
// 1.3 分配一个全局堆空间
HGLOBAL h_global = GlobalAlloc(GPTR, m_size);
if(h_global == 0)
{
CloseClipboard();
MessageBox(L"分配全局堆失败");
return;
}
// 1.4 给全局堆加锁
wchar_t* psz_content = (wchar_t*)::GlobalLock(h_global);
if(psz_content == 0)
{
CloseClipboard();
CloseHandle(h_global);
h_global = 0;
MessageBox(L"给全局堆加锁失败");
return;
}
// 1.5 把数据放到剪贴板中
memcpy(psz_content, m_str_send.GetBuffer(), m_size);
// 1.6 给全局堆解锁
if(::GlobalUnlock(h_global) == 0)
{
CloseClipboard();
MessageBox(L"全局堆解锁失败");
return;
}
// 1.7 将全局堆交给剪贴板
if(SetClipboardData(CF_UNICODETEXT, h_global) == NULL)
{
CloseClipboard();
MessageBox(L"剪贴板控制全局堆失败");
return;
}
// 1.8 关闭剪贴板
CloseClipboard();
}
1.2 完成 Recv 对话框上的 剪贴板Ctrl+v 按钮
1.打开剪贴板: OpenClipboard ;
2.获取剪贴板里的数据: HANDLE h_content = GetClipboardData(CF_UNICODETEXT) ;
3.加锁: wchar_t* psz_content = (wchar_t*)::GlobalLock(h_content) ;
4.从剪贴板取数据: m_str_recv = psz_content ;
5.解锁: ::GlobalUnlock(h_content) ;
6.关闭剪贴板: CloseClipboard() ;
7.将从剪贴板中获取的数据上传到 Edit 控件中: UpdateData(FALSE) ;
void CRecvDlg::OnBnClickedButton1()
{
// 接收端 剪贴板Ctrl+V
// 2.1 打开剪贴板
if(OpenClipboard() == 0)
{
MessageBox(L"打开剪贴板失败");
return;
}
// 2.2 获取剪贴板里的数据
HANDLE h_content = GetClipboardData(CF_UNICODETEXT);
if(h_content == 0)
{
CloseClipboard();
MessageBox(L"获取剪贴板里数据失败");
return;
}
// 2.3 加锁
wchar_t* psz_content = (wchar_t*)::GlobalLock(h_content);
if(psz_content == 0)
{
CloseClipboard();
MessageBox(L"加锁失败");
return;
}
// 2.4 从剪贴板取数据
m_str_recv = psz_content;
// 2.5 解锁
if(::GlobalUnlock(h_content) == 0)
{
CloseClipboard();
MessageBox(L"全局堆解锁失败");
return;
}
// 2.6 关闭剪贴板
CloseClipboard();
// 2.7 更新edit中的数据
UpdateData(FALSE);
}
1.3 完成上述两个步骤之后我们打开两个项目,在 Send 对话框中输入一串字符,点击 剪贴板Ctrl+c ,在 Recv 对话框中点击 剪贴板Ctrl+v 来进行接受字符串。
1.4 在 Send 对话框中发送一张位图,在 Recv 对话框中进行接受
1.我们使用剪贴板来进行位图的传送;
2.在 Send 对话框中添加一个 发送图片 的按钮,并创建一张位图;
3.在 发送图片 按钮中,首先加载创建好的位图: CBitmap h_bitmap ; h_bitmap.LoadBitmap(IDB_BITMAP1) ;
4.打开剪贴板: OpenClipboard ;
5.清空剪贴板: EmptyClipboard ;
6.设置剪贴板数据: SetClipboardData(CF_BITMAP, h_bitmap.m_hObject) ;
7.关闭剪贴板: CloseClipboard ;
void CSendDlg::OnBnClickedButton3()
{
// 发送图片
CBitmap h_bitmap;
h_bitmap.LoadBitmap(IDB_BITMAP1);
// 打开剪贴板
if(OpenClipboard() == 0)
{
MessageBox(L"打开剪贴板失败");
return;
}
// 清空剪贴板
if(EmptyClipboard() == 0)
{
CloseClipboard();
MessageBox(L"清空剪贴板失败");
return;
}
// 设置剪贴板数据
if(SetClipboardData(CF_BITMAP, h_bitmap.m_hObject) == 0)
{
CloseClipboard();
MessageBox(L"设置数据失败");
return;
}
else
{
// 分离图片和句柄
h_bitmap.Detach();
}
CloseClipboard();
}
1.5 在 Recv 对话框中添加一个 接受图片 按钮和一个 Picture Control 控件
1.给 Picture Control 控件添加一个 CStatic 变量;
2.打开剪贴板: OpenClipboard ;
3.获得剪贴板里的数据: HBITMAP h_bitmap = (HBITMAP)GetClipboardData(CF_BITMAP) ;
4.显示图片: m_picture.SetBitmap(h_bitmap) ;
5.关闭剪贴板: CloseClipboard() ;
void CRecvDlg::OnBnClickedButton2()
{
// 接受图片
// 打开剪贴板
if(OpenClipboard() == FALSE)
{
MessageBox(L"打开剪贴板失败");
return;
}
// 获取剪贴板里的数据
HBITMAP h_bitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
if(h_bitmap == 0)
{
CloseClipboard();
MessageBox(L"获取数据失败");
return;
}
// 显示图片
m_picture.SetBitmap(h_bitmap);
// 关闭剪贴板
CloseClipboard();
}
1.6 延迟拷贝技术
1.给 Send 对话框添加 延迟拷贝 按钮;
2.在 延迟拷贝 按钮的处理函数中,首先打开剪贴板: OpenClipboard ;
3.清空剪贴板: EmptyClipboard ;
4.设置空数据: SetClipboardData(CF_UNICODETEXT, 0) ;
void CSendDlg::OnBnClickedButton4()
{
// 延迟拷贝
// 打开剪贴板
if(OpenClipboard() == 0)
{
MessageBox(L"打开剪贴板失败");
return;
}
// 清空剪贴板
if(EmptyClipboard() == 0)
{
CloseClipboard();
MessageBox(L"清空剪贴板失败");
return;
}
// 设置空数据
SetClipboardData(CF_UNICODETEXT, 0);
CloseClipboard();
}
5.给 Send 对话框添加一个 WM_RENDERDORMAT 消息: CSendDlg::OnRenderFormat ;
6.由于在处理该消息时,剪贴板是出于打开状态的,因此在 CSendDlg::OnRenderFormat 函数中不需要打开剪贴板;
7.取数据: UpdateData(TRUE) ; int m_size = (m_str_send.GetLength()+1)2 ;
8.分配一个全局堆空间: HGLOBAL h_global = GlobalAlloc(GPTR, m_size) ;
9.给全局堆加锁: wchar_t psz_content = (wchar_t*)::GlobalLock(h_global) ;
10.把数据放到剪贴板中: memcpy(psz_content, m_str_send.GetBuffer(), m_size) ;
11.给全局堆解锁: ::GlobalUnlock(h_global) ;
12.将全局堆交给剪贴板: SetClipboardData(CF_UNICODETEXT, h_global) ;
void CSendDlg::OnRenderFormat(UINT nFormat)
{
if(CF_UNICODETEXT == nFormat)
{
UpdateData(TRUE);
int m_size = (m_str_send.GetLength()+1)*2;
// 1.3 分配一个全局堆空间
HGLOBAL h_global = GlobalAlloc(GPTR, m_size);
if(h_global == 0)
{
CloseClipboard();
MessageBox(L"分配全局堆失败");
return;
}
// 1.4 给全局堆加锁
wchar_t* psz_content = (wchar_t*)::GlobalLock(h_global);
if(psz_content == 0)
{
CloseClipboard();
CloseHandle(h_global);
h_global = 0;
MessageBox(L"给全局堆加锁失败");
return;
}
// 1.5 把数据放到剪贴板中
memcpy(psz_content, m_str_send.GetBuffer(), m_size);
// 1.6 给全局堆解锁
if(::GlobalUnlock(h_global) == 0)
{
CloseClipboard();
MessageBox(L"全局堆解锁失败");
return;
}
// 1.7 将全局堆交给剪贴板
if(SetClipboardData(CF_UNICODETEXT, h_global) == NULL)
{
CloseClipboard();
MessageBox(L"剪贴板控制全局堆失败");
return;
}
}
CDialogEx::OnRenderFormat(nFormat);
}
2.内存文件映射(另一种进程间通信的方法)
2.1 文件缓冲区:文件缓冲区是用以暂时存放读写期间的文件数据而在内存区预留的一定空间。通过磁盘缓存来实现,磁盘缓存本身并不是一种实际存在的存储介质,它依托于固定磁盘,提供对主存储器存储空间的扩充(辅存)。
2.2 文件缓冲区的作用
1.缓和 CPU 与 I/O 设备间速度不匹配的矛盾;
2.减少对 CPU 的中断频率,放宽对 CPU 中断响应时间的限制;
3.提高 CPU和 I/O 设备之间的并行性。
2.3 给 Send 对话框和 Recv 对话框分别添加一个按钮, Send 上的用来 创建内存文件映射
1.给 Send 对话框上的 创建内存文件映射 按钮添加消息处理函数;
2.在 CSendDlg::OnBnClickedButton5 函数中,首先创建文件: HANDLE h_file = ::CreateFile ;
3.创建内存文件映射: HANDLE h_file_mapping = ::CreateFileMapping ;
4.向文件的哪一块区域写入内容: wchar_t* pwsz_content = (wchar_t*)MapViewOfFile ;
5.UnmapViewOfFile(pwsz_content) ;
6.关闭文件: CloseHandle ;
void CSendDlg::OnBnClickedButton5()
{
UpdateData(TRUE);
int m_size = (m_str_send.GetLength()+1)*2;
// 创建文件
HANDLE h_file = ::CreateFile(
L"../Msg/aa.txt", // 创建的文件名
GENERIC_READ|GENERIC_WRITE, // 对文件操作的权限
FILE_SHARE_WRITE|FILE_SHARE_READ, // 共享者的权限
0, // 安全属性
CREATE_ALWAYS, // 一直创建
FILE_ATTRIBUTE_NORMAL, // 文件属性
0 // 文件模版
);
if(h_file == INVALID_HANDLE_VALUE)
{
MessageBox(L"文件创建失败");
return;
}
// 创建内存文件映射
HANDLE h_file_mapping = ::CreateFileMapping(
h_file, // 建立与哪个文件的映射
0, // 安全属性
PAGE_READWRITE, // 操作权限
0, 4096, // 创建内存映射的大小
L"HH" // 内核对象名
);
if(h_file_mapping == 0)
{
MessageBox(L"文件映射创建失败");
return;
}
// 向文件的哪一块区域写入内容
wchar_t* pwsz_content = (wchar_t*)MapViewOfFile(
h_file_mapping,
FILE_MAP_ALL_ACCESS,
0, 0, m_size
);
if(pwsz_content != 0)
{
::memcpy(pwsz_content, m_str_send.GetBuffer(), m_size);
}
UnmapViewOfFile(pwsz_content);
// 关闭文件
::CloseHandle(h_file);
h_file = 0;
// 关闭内存映射(这里不关闭文件映射的原因是我们需要在另一个进程中打开这个内存映射,若在这里关闭了,那么在另一个进程中无法打开)
//::CloseHandle(h_file_mapping);
//h_file_mapping = 0;
}
7.完成上述操作后我们运行 Send 对话框,在 Edit 控件上输入一串字符,点击 创建内存文件映射 按钮就会在 Send 项目同级的目录下的 Msg 文件夹里创建一个 aa.txt 文件,这个文件里的数据就是我们在 Edit 控件上输入的字符串。