进程间通信有多种方式,这篇博客简单介绍,并且会详细介绍共享内存方式的进程间通信,其四要素为:设定共享内存区域、找出共享内存、同步处理、清理。本篇博客提供一个例子,详细使用参见这两篇博客:用法参考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!