利用共享内存实现进程之间的通讯

本文介绍Windows环境下通过映射文件实现进程间数据共享的方法,并提供两个示例:一个简单示例展示如何在两个进程间共享学生对象;另一个示例展示了如何在两个应用程序(InfoSender和InfoReceiver)之间发送和接收字符串数据。

进程之间数据的共享可以用共用内存实现,在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"); // 找到TitleInfoReceiver的窗口指针

pWnd->PostMessage(WM_INFORMATIONSENT); // TitleInfoReceiver的窗口发送自定义消息

// 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. 使用自定义的消息是,启动InfoSenderInfoReceiver。当在InfoSender填写相关数据后,点击Send Message按钮,InfoReceiver即收到InfoSender发过来的数据。

下面是运行情况:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值