引入
我接着上篇博客讲,如果没有构建项目的童鞋请移步到操作系统——MFC实现进程创建和通信1
用PostMessage实现通信请移步到操作系统——MFC实现进程创建和通信2
用CopyDATA消息实现进程间通信请到操作系统——MFC实现进程创建和通信3
这篇博客使用共享内存的方式实现进程间通信,这适用于有很多内容要传输的情况,这种方法没有前两种方法安全,因为存在着内存交互有临界资源,可能出现死锁问题(用户自己慢慢发是不存在同时访问锁住的,除非你同时点击的速度到了ms级(* _ *),所以这里我就没有通过PV操作设置同步)
通过共享内存实现通信
通信原理
共享内存就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。
不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
优点:共享内存进行进程之间的通信是非常方便的,而且函数的接口也比较简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,加快了程序的效率。
缺点:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,使用信号量机制实现PV操作使用不当,易产生死锁。
1、MainProcessDlg.cpp中添加共享内存
在MainProcessDlg.cpp中的初始化函数**OnlinitDialog()**添加共享内存
// 在窗口创建的时候创建共享内存
//在构造函数中创建共享内存,名称为"Data Mutex"
HANDLE hFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 1000000, _T("Data Mutex"));
if (!hFile)//判断是否成功创建
{
AfxMessageBox(_T("共享内存创建失败!"));
return TRUE;
}
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
AfxMessageBox(_T("创建共享内存成功,准备写入数据"));
return TRUE;
}
LPBYTE lpData = (LPBYTE)MapViewOfFile(hFile, FILE_MAP_WRITE, 0, 0, 0);//写模式访问共享内存
if (!lpData)
{
AfxMessageBox(_T("写入数据失败"));
CloseHandle(hFile);
return TRUE;
}
TCHAR buf[1024] = _T("这是要写入的内容");//要写入的内容
memcpy(lpData, buf, sizeof(buf));//写入
共享内存的生命周期可以不同,这里我直接在创建Main窗口的时候分配一段内存用于共享访问,在构造函数中添加代码,创建为逻辑地址从0到1000000的共享内存;在创建内存的时候最好同时进行写入测试,因为可能分配不到内存,进行一下测试是比较好的(100万也才不到2kb,一个内存块就有4k了,嫌不够的童鞋可以多写几个0,或者写16进制0x…)
2、在MainProcessDlg.cpp中修改点击事件
修改写内存文件Button
在资源视图中双击写内存文件Button跳转到点击事件中重写函数。
void CMFCApplication1Dlg::OnBnClickedButton1()
{
HANDLE hFile = OpenFileMapping(FILE_MAP_WRITE, FALSE, _T("Data Mutex"));
if (!hFile)
{
AfxMessageBox(_T("打开共享内存失败"));
return;
}
LPBYTE lpData = (LPBYTE)MapViewOfFile(hFile, FILE_MAP_WRITE, 0, 0, 0);//写模式访问共享内存
if (!lpData)
{
AfxMessageBox(_T("写入数据失败"));
CloseHandle(hFile);
return;
}
CString strtext;
GetDlgItemText(IDC_EDIT1, strtext);//获取EDIT控件内容
TCHAR buf[1024];
wcscpy_s(buf, strtext);//格式转换
memcpy(lpData, buf, sizeof(buf));//写入共享内存
}
修改读内存文件Button
在资源视图中双击读内存文件Button跳转到点击事件中重写函数。
void CMFCApplication1Dlg::OnBnClickedOk5()
{
HANDLE hFile = OpenFileMapping(FILE_MAP_READ, FALSE, _T("Data Mutex"));
if (!hFile)
{
AfxMessageBox(_T("打开共享内存失败"));
return;
}
LPBYTE lpData = (LPBYTE)MapViewOfFile(hFile, FILE_MAP_READ, 0, 0, 0);
if (!lpData)
{
AfxMessageBox(_T("读取共享内存失败"));
CloseHandle(hFile);
return;
}
TCHAR buf[1024];
memset(buf, 0, sizeof(buf));
memcpy(buf, lpData, sizeof(buf));
SetDlgItemText(IDC_EDIT1, buf);
if (lpData)
UnmapViewOfFile(lpData);
if (hFile)
CloseHandle(hFile);
}
3、在ChildProcessDlg.cpp中修改点击事件
修改写内存文件Button
在资源视图中双击写内存文件Button跳转到点击事件中重写函数。
void CMFCApplication1Dlg::OnBnClickedButton1()
{
HANDLE hFile = OpenFileMapping(FILE_MAP_WRITE, FALSE, _T("Data Mutex"));
if (!hFile)
{
AfxMessageBox(_T("打开共享内存失败"));
return;
}
LPBYTE lpData = (LPBYTE)MapViewOfFile(hFile, FILE_MAP_WRITE, 0, 0, 0);//写模式访问共享内存
if (!lpData)
{
AfxMessageBox(_T("写入数据失败"));
CloseHandle(hFile);
return;
}
CString strtext;
GetDlgItemText(IDC_EDIT1, strtext);//获取EDIT控件内容
TCHAR buf[1024];
wcscpy_s(buf, strtext);//格式转换
memcpy(lpData, buf, sizeof(buf));//写入共享内存
}
修改读内存文件Button
在资源视图中双击读内存文件Button跳转到点击事件中重写函数。
void CMFCApplication1Dlg::OnBnClickedOk5()
{
HANDLE hFile = OpenFileMapping(FILE_MAP_READ, FALSE, _T("Data Mutex"));
if (!hFile)
{
AfxMessageBox(_T("打开共享内存失败"));
return;
}
LPBYTE lpData = (LPBYTE)MapViewOfFile(hFile, FILE_MAP_READ, 0, 0, 0);
if (!lpData)
{
AfxMessageBox(_T("读取共享内存失败"));
CloseHandle(hFile);
return;
}
TCHAR buf[1024];
memset(buf, 0, sizeof(buf));
memcpy(buf, lpData, sizeof(buf));
SetDlgItemText(IDC_EDIT1, buf);
if (lpData)
UnmapViewOfFile(lpData);
if (hFile)
CloseHandle(hFile);
}
运行结果
注
点击事件的处理是一样的,都是先进行安全性检查,能打开内存文件并且lpData已经分配了的话,就可以开始读写操作了,对于用户级的操作我都没有加上信号量的控制,上面也说了基本不可能出现死锁问题,具体的PV操作我将在下次博客中再描述(线程创建与线程同步问题)。
这次对于进程通信问题就到这里了,进程用管道通信,我创建管道之后在读取管道文件的时候有忙等待问题,经常锁死在那,就不写出来丢人了,啥时候有时间研究一下再来分享给大家。大家如果有好的思路请在下面留言(~ _ ~)