进程之间数据的共享可以用共用内存实现,在Win32中,进程之间共享内存使用的事映射文件。虚拟内存系统具有把实际内存映射到页文件或者交换文件的能力。用户可以把内存映射到任何的映射文件中,包括系统内存页。而利用系统内存页可以实现快捷的内存共享。
利用共有内存实现进程之间的数据共享共有两步:
一. 使用CreateFileMapping函数创建内存映射文件。此函数需要文件句柄,对于大多数的内存共享应用程序,建此文件句柄设置为0xFFFFFFFF即可。这样的句柄指向系统内存页文件。
二. 映射文件文件创建成功以后,以其返回的句柄作为参数,调用MapViewOfFile函数为内存映射文件对象创建视,MapViewOfFile函数将返回指向文件的视指针。可以利用此视指针对内存映射文件进行操作,内存的读写简化到了就像普通变量的操作。
下面是最简单的例子:
//MemoryWriter.cpp
#include "windows.h"
#include "iostream.h"
class student
{
public:
long ID;
char name[20];
};
void main()
{
HANDLE hMemShare;
student stu;
int stu_num = 30;
student *lpstu;
stu.ID = 99041232;
strcpy(stu.name,"SecBug");
hMemShare = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,
0,sizeof(student),"TestMemShare");
if(hMemShare == NULL)
{
cout<<"Failed to Alloocate"<<endl;
return;
}
lpstu = (student *)MapViewOfFile(hMemShare, FILE_MAP_WRITE,0,0,sizeof(student));
if(lpstu == NULL)
{
cout<<"Failed to Map"<<endl;
return;
}
*lpstu = stu;
while(1){}
UnmapViewOfFile(lpstu);
}
//MemoryReader.cpp
#include "windows.h"
#include "iostream.h"
class student
{
public:
long ID;
char name[20];
};
void main()
{
HANDLE hMemShare;
student stu;
student *lpstu;
stu.ID = 0;
strcpy(stu.name ,"tst");
hMemShare = OpenFileMapping(FILE_MAP_READ,FALSE,"TestMemShare");
if(hMemShare == NULL)
{
cout<<"File Created Failed"<<endl;
return;
}
lpstu = (student *)MapViewOfFile(hMemShare, FILE_MAP_READ,0,0,sizeof(student));
if(lpstu == NULL)
{
cout<<"Failed to Map"<<endl;
return;
}
stu = *lpstu;
cout<<stu.ID<<endl;
cout<<stu.name<<endl;
UnmapViewOfFile(lpstu);
}
a. InfoSender项目:
1. 基于CDialog的应用程序。项目名称InfoSender。
2. 在InfoSenderDlg.h中,增加下面带边框语句(用户自定义消息):
#define WM_INFORMATIONSENT WM_USER + 101
class CInfoSenderDlg : public CDialog
{
// Construction
public:
……
3. 如下图设计界面:
OK按钮的Caption改为“Send Message”, Cancel按钮的Caption改为“Quit”;
双击Send Message按钮,注释掉CDialog::OnOK();;
增加一下成员变量:
4. 在CInfoSenderDlg::OnInitDialog()中增加以下代码:
// TODO: Add extra initialization here
m_nBufferSize = 4096;
m_hMap = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
m_nBufferSize,
"pat.memshare"); // 指定映射对象名称为pat.memshare
ASSERT(m_hMap != NULL);
//上面 INVALID_HANDLE_VALUE通知系统不要在硬盘上创建文件映射对象,而是转而创建其物理存储器由系统页文件
// (paging file)支持的文件映射对象。
m_lpvViewOfFile = MapViewOfFile(m_hMap,
FILE_MAP_WRITE,
0,
0,
m_nBufferSize);
ASSERT(m_lpvViewOfFile != NULL);
5. 在CInfoSenderDlg::OnOK()中增加如下代码
char tempStr[4096];
char* separator = "|";
// we can cast a CString object to be an LPCTSTR. The LPCTSTR type conversion operator returns a pointer to a
// read-only C-style null-terminated string from a CString object.
strcpy(tempStr, (LPCTSTR)m_Name);
strcat(tempStr, separator);
strcat(tempStr, (LPCTSTR)m_IDNumber);
strcat(tempStr, separator);
strcat(tempStr, (LPCTSTR)m_Phone);
strcat(tempStr, separator);
strcat(tempStr, (LPCTSTR)m_Resume);
memcpy(m_lpvViewOfFile, tempStr, strlen(tempStr));
CWnd* pWnd = FindWindow(NULL, "InfoReceiver"); // 找到Title为InfoReceiver的窗口指针
pWnd->PostMessage(WM_INFORMATIONSENT); // 向Title为InfoReceiver的窗口发送自定义消息
// WM_INFORMATIONSENT
6. 在CInfoSenderDlg::OnCancel() 中增加语句:
// TODO: Add extra cleanup here
UnmapViewOfFile(m_lpvViewOfFile);
CloseHandle(m_hMap);
…
InfoReceiver项目
1. 基于CDialog的应用程序。项目名称InfoReceiver。
2. 在InfoReceiverDlg.h中,增加下面带下划线语句(用户自定义消息):
#define WM_INFORMATIONSENT WM_USER + 101 //必须和InfoSender中的一致
class CInfoReceiverDlg : public CDialog
{
// Construction
public:
CInfoReceiverDlg(CWnd* pParent = NULL);
……
3. OK按钮的Caption改为Receive Message界面设计和InfoSender完全一样。
4. 在InfoReceiverDlg.h中,增加下面带下划线语句(用户自定义消息):
……
// Generated message map functions
//{{AFX_MSG(CInfoReceiverDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
virtual void OnOK();
virtual void OnCancel();
//}}AFX_MSG
afx_msg LRESULT OnInformationSent(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
……
注意添加OnInformationSent的位置,不能在//{{AFX_MSG(CInfoReceiverDlg)和//}}AFX_MSG之间,因为此而者之间是自动生成的消息响应函数,如果加在其间,当有新的系统消息响应函数加入时,VC会重新生成一次,从而将我们想加入的OnInformationSent清除掉。
5. 在InfoReceiverDlg.cpp中增加下面带下划线的语句:
BEGIN_MESSAGE_MAP(CInfoReceiverDlg, CDialog)
//{{AFX_MSG_MAP(CInfoReceiverDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_INFORMATIONSENT, OnInformationSent)
// 设定自定义消息 WM_INFORMATIONSENT的处理函数
END_MESSAGE_MAP()
也要注意ON_MESSAGE加入的位置。理由和4中提及的一样。
6. 在InfoReceiverDlg.cpp中增加下面成员函数:
LRESULT CInfoReceiverDlg::OnInformationSent(WPARAM wParam, LPARAM lParam)
{
OnOK(); // 直接调用OnOK
return 0L;
}
7. 在CInfoReceiverDlg::OnInitDialog()中增加如下代码
// TODO: Add extra initialization here
m_hMap = OpenFileMapping(FILE_MAP_READ, FALSE, "pat.memshare");
ASSERT(m_hMap != NULL);
m_lpvViewOfFile = MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 4096);
ASSERT(m_lpvViewOfFile != NULL);
8. 将CInfoReceiverDlg::OnOK()改为一下代码:
char* separator;
char* another;
char temp[4096];
memset(temp, 0, 4096);
separator = strchr((const char *)m_lpvViewOfFile, '|');
memcpy(temp, m_lpvViewOfFile, (int)(separator - (char*)m_lpvViewOfFile));
m_Name = temp;
memset(temp, 0, 4096);
another = strchr(separator + 1, '|');
memcpy(temp, separator + 1, (int)(another - separator - 1));
m_IDNumber = temp;
memset(temp, 0, 4096);
separator = strchr(another + 1, '|');
memcpy(temp, another + 1, (int)(separator - another - 1));
m_Phone = temp;
memset(temp, 0, 4096);
memcpy(temp, separator + 1, strlen(separator) - 1);
m_Resume = temp;
UpdateData(FALSE);
9. 在CInfoReceiverDlg::OnCancel() 中增加如下语句:
// TODO: Add extra cleanup here
UnmapViewOfFile(m_lpvViewOfFile);
CloseHandle(m_hMap);
……
说明:
1. 不使用自定义的消息时,当在InfoSender填写相关数据后,点击Send Message按钮;启动InfoReceiver,点击Receive Message按钮,即可接收到InfoSender发过来的数据;
2. 使用自定义的消息是,启动InfoSender和InfoReceiver。当在InfoSender填写相关数据后,点击Send Message按钮,InfoReceiver即收到InfoSender发过来的数据。
下面是运行情况: