本文着重讲述了如果用WM_COPYDATA消息来实现两个进程之间传递数据. 进程之间通讯的几种方法: 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。常用的方法有 使用内存映射文件 比起前两种的复杂实现来,WM_COPYDATA消息无疑是一种经济实惠的一中方法. WM_COPYDATA消息的主要目的是允许在进程间传递只读数据。Windows在通过WM_COPYDATA消息传递期间,不提供继承同步方式。SDK文档推荐用户使用SendMessage函数,接受方在数据拷贝完成前不返回,这样发送方就不可能删除和修改数据: 这个函数的原型及其要用到的结构如下: SendMessage(hwnd,WM_COPYDATA,wParam,lParam); wParam设置为包含数据的窗口的句柄。lParam指向一个COPYDATASTRUCT的结构: 具体过程如下:
接受方在DefWndProc事件中,来处理这条消息.由于中文编码是两个字节,所以传递中文时候字节长度要搞清楚. 代码中有适量的解释,大家请自己看吧. 用WM_COPYDATA的前提: 1,知道接收消息进程的句柄。 2,接收消息进程重载了WM_COPYDATA消息映射,能对其做出反应(否则不是发送端自作多情了?) 看过前提,的出结论:在自己写的两个进程间用WM_COPYDATA再好不过。 下面CO 获得句柄的方法,最简单的方法就是使用FindWindow,找窗口类,或者名,如果你觉得这样不把握,那就利用SetProp个窗口做个记号....(不说这些,跑踢儿了都) OK,开始写发送端代码: HWND hWnd = FindWindow(NULL,"MyApp"); if(hWnd!=NULL) { COPYDATASTRUCT cpd; /*给COPYDATASTRUCT结构赋值*/ cpd.dwData = 0; cpd.cbData = strlen("字符串"); cpd.lpData = (void*)"字符串"; ::SendMessage(hWnd,WM_COPYDATANULL,(LPARAM)&cpd);//发送! /*完事儿了!!*/ } 接收端重载ON_WM_COPYDATA消息映射函数(下面是手工所要加的,你最好还是用ClassWizard) afx_msg BOOL On ON_WM_COPYDATA()/*消息映射*/ return CWnd::On 进程通信还有其他一些手段,相对来说比较麻烦,但局限性要比WM_COPYDATA小。当然你也可以两端都注册一个消息来通信。 使用WM_COPYDATA进行进程间通信的一个问题
开发中有时需要进程间传递数据,比如对于只允许单实例运行的程序,当已有实例运行时,再次打开程序,可能需要向当前运行的实例传递信息进行特殊处理。对于传递少量数据的情况,最简单的就是用SendMessage发送WM_COPYDATA消息,所带参数wParam和lParam可以携带相关数据。由于SendMessage是阻塞的,在接收数据进程处理完数据之前不会返回,发送方不会删除或修改数据,因此这种方法是简单且安全的,不过数据量不能太大,否则会由于处理时间过长造成阻塞假死。
WM_COPYDATA使用的一个例子: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 进程间通信的方法有多种,其中,对于少量数据可以用WM_COPYDATA方便的实现通信(如果对于大量数据的话,由于SendMessage是阻塞的,只有接收方响应了消息,SendMessage才能返回,否则则一直阻塞,所以,对于大量数据来说,用SendMessage就容易造成窗口假死) 。 本例子分别用WM_COPYDATA 实现了两种数据类型的发送,一种为Cstring,另外一种为自定义的结构体Student: //********************************************************** #pragma pack(1) struct Student { char ID[10]; TCHAR Name[20]; UINT Age; UINT Grade; char Room[5]; char Tel[12]; }; #pragma pack() //********************************************************** 因为需要在接收方的On #define STRING 1 #define STUDENT 2 发送方: void CSendDataDlg::On { UpdateData(TRUE); if (m_szData.IsEmpty()) { m_szData = _T("Hello"); UpdateData(FALSE); } // m_szData += '\0'; HWND hWndRcv = ::FindWindow(NULL,"Receiver"); if (hWndRcv == NULL) { AfxMessageBox(_T("找不到接收窗口,发送不成功")); return ; } COPYDATASTRUCT cpd; cpd.dwData = STRING; //标志为CString类型 cpd.cbData = m_szData.GetLength() + 1; //GetLength()只是取得实际字符的长度,没有包括'\0'. cpd.lpData = (void*)m_szData.GetBuffer(cpd.cbData); ::SendMessage(hWndRcv,WM_COPYDATA,(WPARAM)this->m_hWnd,(LPARAM)&cpd); m_szData.ReleaseBuffer(); AfxMessageBox(_T("发送成功")); } void CSendDataDlg::On { UpdateData(); m_szID += '\0'; m_szName += '\0'; m_szRoom += '\0'; m_szTel += '\0'; m_pStu = new Student(); strcpy(m_pStu->ID,m_szID.GetBuffer(m_szID.GetLength())); _tcscpy(m_pStu->Name,m_szName.GetBuffer(m_szName.GetLength())); strcpy(m_pStu->Room,m_szRoom.GetBuffer(m_szRoom.GetLength())); strcpy(m_pStu->Tel,m_szTel.GetBuffer(m_szTel.GetLength())); m_szID.ReleaseBuffer();m_szName.ReleaseBuffer(); m_szRoom.ReleaseBuffer();m_szTel.ReleaseBuffer(); m_pStu->Age = m_nAge; m_pStu->Grade = m_nGrade; HWND hWndRcv = ::FindWindow(NULL,"Receiver"); if (hWndRcv == NULL) { AfxMessageBox(_T("找不到接收窗口,发送不成功")); return ; } COPYDATASTRUCT cpd; cpd.dwData = STUDENT; // 标志为Student类型 cpd.cbData = sizeof(Student); cpd.lpData = (PVOID)m_pStu; ::SendMessage(hWndRcv,WM_COPYDATA,(WPARAM)this->m_hWnd,(LPARAM)&cpd); delete m_pStu; AfxMessageBox(_T("发送成功")); } 接收方: 在On //*************************************************************** //初始化ListCtrl控件 LVCOLUMN column; column.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH; column.cx = 80; column.iSubItem = 0; column.pszText = _T("ID"); m_ListCtl.InsertColumn(0,&column); column.cx = 80; column.pszText = _T("Name"); column.iSubItem = 1; m_ListCtl.InsertColumn(1,&column); column.cx = 55; column.pszText = _T("Age"); column.iSubItem = 2; m_ListCtl.InsertColumn(2,&column); column.cx = 55; column.pszText = _T("Grade"); column.iSubItem = 3; m_ListCtl.InsertColumn(3,&column); column.cx = 55; column.pszText = _T("Room"); column.iSubItem = 4; m_ListCtl.InsertColumn(4,&column); column.cx = 80; column.pszText = _T("Tel"); column.iSubItem = 5; m_ListCtl.InsertColumn(5,&column);
BOOL CReceiverDlg::On { switch (pCopyDataStruct->dwData) { // 接收到的是CString类型 case STRING: m_szData += (LPCSTR)(pCopyDataStruct->lpData); UpdateData(FALSE); break; case STUDENT: // 接收到的是Student类型 CString id,name,room,tel; UINT age,grade; CString str; Student* pStu = (Student*)(pCopyDataStruct->lpData); id = pStu->ID; name = pStu->Name; room = pStu->Room; tel = pStu->Tel; age = pStu->Age; grade = pStu->Grade; LVITEM item; // 把接收到的数据显示到ListCtrl控件上 item.mask = LVIF_TEXT; int n = m_ListCtl.GetItemCount(); item.iItem = n; item.iSubItem = 0; item.pszText = id.GetBuffer(id.GetLength()); id.ReleaseBuffer(); m_ListCtl.InsertItem(&item); m_ListCtl.SetItemText(n,1,name); str.Format("%d",age); m_ListCtl.SetItemText(n,2,str); str.Format("%d",grade); m_ListCtl.SetItemText(n,3,str); m_ListCtl.SetItemText(n,4,room); m_ListCtl.SetItemText(n,5,tel); UpdateData(FALSE); //delete pStu; break; } // return CDialog::On return TRUE; }
MSDN帮助里面有该消息的例子,说的也很清楚。 |
|