进程之间通讯,发送自定义消息/发送WM_COPYDATA消息/发送线程消息/以及内存映射方式
时间:2010-08-25 来源:散客游
打开VC6,创建两个MFC对话框工程,ProcessA和ProcessB
1.发送自定义消息方式:
优点:简单,方便
缺点:能发送的数据太小,需要接收消息进程有窗口
用到的函数:
FindWindow //查找窗口
PostMessage //发送消息到指定窗口的消息队列,发送后立即返回
SendMessage //发送消息到指定窗口,直到接收窗口程序处理完消息再返回
ProcessA:
//找窗口句柄 HWND hWnd = ::FindWindow(NULL, "ProcessB"); if (hWnd == NULL) { return; } //把10发送给进程B DWORD dwNum = 10; //向进程B消息队列投递一个自定义消息 WM_USER + 1 //::PostMessage(hWnd, WM_USER + 1, dwNum, NULL); //直接向进程B发送一个 WM_USER + 1 ::SendMessage(hWnd, WM_USER + 1, dwNum, NULL);
ProcessB:
CString txt;
txt.Format("数值:%d", wParam);
AfxMessageBox(txt);
2.发送WM_COPYDATA消息
优点:简单,方便,可以发送稍大的数据,由操作系统管理接收方进程的内存申请和释放,不用自己去管理
缺点:数据量太大时,可能会造成进程A卡住,需要接收消息进程有窗口
用到的函数:
FindWindow //查找窗口
SendMessage //发送消息到指定窗口,直到接收窗口程序处理完消息再返回
ProcessA:
//找窗口句柄 HWND hWnd = ::FindWindow(NULL, "ProcessB"); if (hWnd == NULL) { return; } //要发送的字符串 char szBuff[] = "hello world!"; //WM_COPYDATA 需要发送一个结构体 COPYDATASTRUCT cds = {0}; cds.lpData = szBuff; //要发送内容的首地址 cds.cbData = strlen(szBuff) + 1; //要发送多大内容 cds.dwData = 0; //附加参数,如果需要可以随便改变这里的值 //直接向进程B发送一个 WM_COPYDATA 消息 //注意,发送WM_COPYDATA消息,不能用PostMessage,MSDN有明确说明. ::SendMessage(hWnd, //接收消息的窗口句柄 WM_COPYDATA, //消息类型 (WPARAM)GetSafeHwnd(), //发送消息的窗口句柄,既进程A的窗口句柄 (LPARAM)&cds); //要发送的内容
ProcessB:
//因为lpData是一个LPVOID,void*指针,所以需要强转一下 AfxMessageBox((LPTSTR)pCopyDataStruct->lpData);
3.发送线程消息:
优点:简单,方便
缺点:能发送的数据太小
用到的函数:
FindWindow //查找窗口
GetWindowThreadProcessId //获取指定窗口的进程ID和主线程ID
PostThreadMessage//发送消息到指定线程
如果是向主线程发送消息,接收线程消息的进程,可以覆盖虚函数PreTranslateMessage来完成消息的处理
ProcessA:
//找窗口句柄 HWND hWnd = ::FindWindow(NULL, "ProcessB"); if (hWnd == NULL) { return; } //主线程ID DWORD dwThreadID = 0; //获取指定窗口主线程ID, 第二个参数是进程ID out 传出值. dwThreadID = ::GetWindowThreadProcessId(hWnd, NULL); //向主线程发送线程消息 BOOL bRet = ::PostThreadMessage(dwThreadID, WM_USER + 2, 1, 2); if (!bRet) { AfxMessageBox("发送线程消息失败!"); }
ProcessB:
BOOL CProcessBDlg::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if(pMsg->message == WM_USER + 2) { CString txt; txt.Format("参数1:%d,参数2:%d", pMsg->wParam, pMsg->lParam); AfxMessageBox(txt); return TRUE; } return CDialog::PreTranslateMessage(pMsg); }
4.内存映射方式:
优点:多个进程可以通过虚拟内存映射到同一块物理内存存进行操作,实现数据共享,不要求进程有窗口
缺点:操作稍微有点麻烦
用到的函数:
CreateFileMapping //在物理内存中开辟一块指定大小的空间
MapViewOfFile //建立一块本进程的虚拟内存和映射对象管理的物理内存建立映射,但实际上的物理内存页面并没有分配,这个时候,如果你要写这片内存的话,才会出现物理内存页面的分配
UnmapViewOfFile //解除本进程的虚拟内存和映射对象管理的物理内存的映射
CloseHandle //对对象进行引用计数-1
OpenFileMapping //根据映射对象的名称打开内存映射
这种方式用的人很多,个人也比较喜欢,但需要说明的是,CreateFileMapping返回的映射对象控制句柄,最好不要定义成局部变量,当函数执行完毕之后,局部变量就会销毁,应该会导致对象引用计数-1,这样的话别的进程就无法映射开辟的这块物理内存了,最好保存成类的成员变量,在对话框的OnClose()函数的时候进行一次CloseHandle,CloseHandle会进行对象的引用计数-1,当计数到0的时候,由系统最后回收资源
ProcessA:
void CProcessADlg::OnBtnFileMapping() { // TODO: Add your control notification handler code here //创建内存映射对象,建立用于共享的物理内存,第一个参数,如果给文件句柄,则是文件映射 m_hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE, //-1值,代表这是一个内存映射 NULL, //返回的句柄是否可以通过子进程继承 PAGE_READWRITE, //读写属性 0, //高位大小 因为文件可能超过4G,所以有可能会有值 0x1000, //低位大小,创建多大的内存 "MyMap"); //映射对象的名称 if (m_hMap == NULL) { AfxMessageBox("创建内存映射失败!"); return; } //建立一块本进程的虚拟内存 和 映射对象管理的物理内存 建立映射 LPVOID lpMem = MapViewOfFile(m_hMap, //内存映射控制对象 FILE_MAP_ALL_ACCESS, //访问权限 0, //高位偏移 0, //低位偏移 0x1000); //大小 if (lpMem == NULL) { //引用计数-1,销毁对象 CloseHandle(m_hMap); m_hMap = NULL; AfxMessageBox("映射内存失败!"); return; } //往内存中写入内容.lpMem是void* 所以做一下强转 strcpy((LPTSTR)lpMem, "hello world!"); //解除映射 //取消本进程的 虚拟内存 和 映射对象管理的物理内存 的 映射 if (!UnmapViewOfFile(lpMem)) { //引用计数-1,销毁对象 CloseHandle(m_hMap); m_hMap = NULL; AfxMessageBox("解除内存映射失败!"); } }
ProcessB:
void CProcessBDlg::OnBtnFileMapping() { // TODO: Add your control notification handler code here //根据映射对象的名称打开内存映射,会使内存映射控制对象引用计数+1 HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, //访问权限 NULL, //返回的句柄是否可以通过子进程继承 "MyMap"); //映射对象的名称 if (hMap == NULL) { AfxMessageBox("打开内存映射失败!"); return; } //建立一块本进程的虚拟内存 和 映射对象管理的物理内存 建立映射 LPVOID lpMem = MapViewOfFile(hMap, //内存映射控制对象 FILE_MAP_ALL_ACCESS, //访问权限 0, //高位偏移 0, //低位偏移 0x1000); //大小 if (lpMem == NULL) { AfxMessageBox("映射内存失败!"); } else { //把内存中的东西读出来,并显示. AfxMessageBox((LPTSTR)lpMem); //解除映射 //取消本进程的虚拟内存 和 映射对象管理的物理内存 的 映射 if (!UnmapViewOfFile(lpMem)) { AfxMessageBox("解除内存映射失败!"); } } //引用计数-1,因为使用OpenFileMapping,会使内存映射控制对象引用计数+1 CloseHandle(hMap); hMap = NULL; }
完整源码下载地址
http://files.cnblogs.com/sankeyou/%e8%bf%9b%e7%a8%8b%e9%97%b4%e9%80%9a%e8%ae%af.rar