进程间通信有多种方式,这篇博客简单介绍,并且会详细介绍共享内存方式的进程间通信,其四要素为:设定共享内存区域、找出共享内存、同步处理、清理。本篇博客提供一个例子,详细使用参见这两篇博客:用法参考1,用法参考2。
进程间通信的方式大致有:
1.匿名管道,底层通过网络实现通信,提供字节模式和消息模式
2.消息管道,使用Windows消息,一段发送,一段接受
3.套接字,Socket网络编程
4.剪切板,CWnd提供支持
5.动态数据交换
6.邮件槽,基于广播通信体系,是一种单向通信机制
7.共享内存,这种方法建立一片共享内存区,两个程序都可以访问这篇区域
此外,还有其它方式,接下来会详细介绍共享内存方式的进程间通信。
1.在程序A中,创建一片内存共享区域,并设置同步变量
这里注意,指定了内存区域的大小
HANDLE m_hFileMapping;
LPTSTR p;
/////////////////////
void CbaseDlg::InitInterprocess()
{
m_hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 2048*4096*4, _T("BaseImage"));
m_hMutex = CreateEvent(nullptr, false, false, _T("BaseImageMutex"));
p = (LPTSTR) MapViewOfFile(m_hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
// WaitForSingleObject(m_hMutex, 0); // in the situation that algorithm running before wait after createmutex
// the algorithm would achieve a frame of error image
}
2.在程序B中,可以通过设置的同步量抓取数据
FetchImage::FetchImage() :
buffer_(nullptr),
fileMapping_(nullptr)
{
fileName_ = new char[30];
memcpy(fileName_, "BaseImage", 10);
mutex_ = OpenEvent(MUTEX_ALL_ACCESS, false, "BaseImageMutex");
fileMapping_ = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, fileName_);
pMsg = (LPTSTR)MapViewOfFile(fileMapping_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
}
3.当要发送数据时,程序A中写入共享内存区域,同时要保证同步
将要传送的数据写入共享内存区,给B程序抓取
第二段代码为A程序创建B程序的进行,如果B进程还未被创建,CreateProcess 就根据可执行文件名创建了一个进程,这不是必须的
m_bCapture = true; // communicate inter process
SendInterprocess();
/////////////////////////////////////////////////////
void CbaseDlg::SendInterprocess()
{
LPTSTR p = m_pMapping;
if (m_bCapture == true)
{
m_bCapture = false;
// WaitForSingleObject(m_hMutex, INFINITE); // not need to lock
((int*)p)[0] = m_image.m_imgLength;
((int*)p)[1] = m_image.m_imgWidth;
((int*)p)[2] = m_image.m_imgHeight;
((int*)p)[3] = m_image.m_nBitCount;
memcpy(((char*)p) + 16, m_image.m_pImgData, m_image.m_imgLength);
CString str;
GetDlgItemText(IDC_EDIT_EXE_FILENAME, str);
if (str.GetLength() != 0)
{
// test whether the thread is running
DWORD ExitCode;
GetExitCodeProcess(m_pInfo.hProcess,&ExitCode);
if( ExitCode != STILL_ACTIVE )
{
STARTUPINFO si = { sizeof(si) };
si.dwFlags = STARTF_USESHOWWINDOW; // 指定wShowWindow成员有效
si.wShowWindow = TRUE; // 此成员设为TRUE的话则显示新建进程的主窗口,
// 为FALSE的话则不显示
BOOL bRet = ::CreateProcess (
str, // 不在此指定可执行文件的文件名
nullptr, // 命令行参数
NULL, // 默认进程安全性
NULL, // 默认线程安全性
FALSE, // 指定当前进程内的句柄不可以被子进程继承
CREATE_NEW_CONSOLE, // 为新进程创建一个新的控制台窗口
NULL, // 使用本进程的环境变量
NULL, // 使用本进程的驱动器和目录
&si,
&m_pInfo);
}
}
SetEvent(m_hMutex); // set the event for alg
}
}
4.B程序根据同步时间,来抓取数据
读取时,首先锁住共享内存区域,然后使用MapViewOfFile获取共享内存区的首地址,然后取出数据即可
在A中,SetEvent触发事件,在这里,ResetEvent进行复位
LPTSTR pMsg;
unsigned char* FetchImage::getImage()
{
if (mutex_ == nullptr || fileMapping_ == nullptr)
{
throw "base工程未启动";
return nullptr;
}
WaitForSingleObject(mutex_, INFINITE); // lock filemapping
if (imageSize_ < ((int*)pMsg)[0]) // buffer size is not enough
{
delete buffer_;
buffer_ = nullptr;
}
imageSize_ = ((int*)pMsg)[0];
if (buffer_ == nullptr)
{
buffer_ = new unsigned char[imageSize_ + HEAD_SIZE];
}
memcpy(buffer_, pMsg, imageSize_ + HEAD_SIZE); // save the data
ResetEvent(mutex_); // unlock
// ((int*)buffer_)[0] is image length (byte)
imageWidth_ = ((int*)buffer_)[1];
imageHeight_ = ((int*)buffer_)[2];
imageType_ = ((int*)buffer_)[3];
return buffer_ + HEAD_SIZE; // return head of image data
}
5.清理
在A程序和B程序中都要进行清理。
FetchImage::~FetchImage()
{
if (!UnmapViewOfFile(pMsg_)){
//assert(false);
}
CloseHandle(fileMapping_);
CloseHandle(mutex_);
delete fileName_;
delete buffer_;
}
特别说明,每调用一次MapViewOfFile,就会分配一片与共享内存大小相同的内存区域,一般一个进程中只用调用一次,如果重复调用会出现内存不足。
OK,See You Next Chapter!