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