Windows上CAtlFileMapping共享内存的使用以及内部机制

前言:

使用CEF加载网页,做JS与C++交互时,需要向主窗口发送一些消息来通知界面做相应的处理。但是,由于CEF使用chrome内核是多进程架构的,渲染引擎与主程序都不在同一个进程里面。因此,理所当然的就想到了使用共享内存了。为了更容易地使用,我们选择的是ATL里面封装的共享内存操作类:CAtlFileMapping。

CAtlFileMapping使用:

定义结构体,包含你所需要共享的数据,这里我们只需要共享主窗口的句柄

//自定义进程共享数据结构体struct PROCESS_SHARE_DATA{HWNDhMainWnd;};<p><span style="white-space: pre;">使用</span>VS自带GUID生成工具创建一个GUID,来唯一标识这块共享内存</p><p>const TCHAR szShareGuid[] = _T("4F836C8D-F55E-4D88-A0BF-9ACDC0A33B31");</p>定义共享内存全局变量extern CAtlFileMapping<PROCESS_SHARE_DATA> g_ShareData;

初始化这块共享内存,并给数据赋值

g_ShareData.MapSharedMem(sizeof(PROCESS_SHARE_DATA), szShareGuid);((PROCESS_SHARE_DATA*)g_ShareData)->hMainWnd=pMainWnd->GetHWND();好了,这样共享内存数据已经初始化完成了。CEF进程在创建后也可以直接使用这个数据了。

打开共享内存,取出需要的数据g_ShareData.OpenMapping(szShareGuid, sizeof(PROCESS_SHARE_DATA));HWND hWnd = ((PROCESS_SHARE_DATA*)g_ShareData)->hMainWnd;然后,把这块共享内存句柄关闭

g_ShareData.Unmap();挺简单的吧,我们无需知道内部的API调用及实现,就可以轻松在多个进程间共享数据了。

但是,,作为程序员,我们应该知其然知其所以然。那就进去看看实现吧,反正ATL的代码都可以看到。

内部实现:

首先是MapSharedMem,创建一块共享内存,<span style="font-family: Arial, Helvetica, sans-serif;">HRESULT MapSharedMem(</span>

_In_ SIZE_T nMappingSize,_In_z_ LPCTSTR szName,_Out_opt_ BOOL* pbAlreadyExisted = NULL,_In_opt_ LPSECURITY_ATTRIBUTES lpsa = NULL,_In_ DWORD dwMappingProtection = PAGE_READWRITE,_In_ DWORD dwViewDesiredAccess = FILE_MAP_ALL_ACCESS) throw(){ATLASSUME(m_pData == NULL);ATLASSUME(m_hMapping == NULL);ATLASSERT(nMappingSize > 0);ATLASSERT(szName != NULL); // if you just want a regular chunk of memory, use a heap allocatorm_nMappingSize = nMappingSize;ULARGE_INTEGER nSize;nSize.QuadPart = nMappingSize;m_hMapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, lpsa, dwMappingProtection, nSize.HighPart, nSize.LowPart, szName);if (m_hMapping == NULL)return AtlHresultFromLastError();if (pbAlreadyExisted != NULL)*pbAlreadyExisted = (GetLastError() == ERROR_ALREADY_EXISTS);m_dwViewDesiredAccess = dwViewDesiredAccess;m_nOffset.QuadPart = 0;m_pData = ::MapViewOfFileEx(m_hMapping, m_dwViewDesiredAccess, m_nOffset.HighPart, m_nOffset.LowPart, m_nMappingSize, NULL);if (m_pData == NULL){HRESULT hr;hr = AtlHresultFromLastError();::CloseHandle(m_hMapping);m_hMapping = NULL;return hr;}return S_OK;}通过CreateFileMapping传入一个INVALID_HANDLE_VALUE创建一个与物理文件无关的内存映射,传入映射内存的大小。然后调用MapViewOfFileEx将其映射到内存中,返回内存首地址m_pData,这是一个CAtlFileMappingBase的成员变量。

然后我们查看CAtlFileMapping的定义

template <typename T = char>class CAtlFileMapping : public CAtlFileMappingBase{public:operator T*() const throw(){return reinterpret_cast<T*>(GetData());}};派生自CAtlFileMappingBase,然后对*进行了重载,使用reinterpret_cast将内存映射地址转换成我们的数据结构的地址,这样我们就可以通过CAtlFileMapping指针来直接访问我们定义的数据结构的数据了。

其他进程使用共享内存,再看OpenMapping函数的实现

HRESULT OpenMapping(_In_z_ LPCTSTR szName,_In_ SIZE_T nMappingSize,_In_ ULONGLONG nOffset = 0,_In_ DWORD dwViewDesiredAccess = FILE_MAP_ALL_ACCESS) throw(){ATLASSUME(m_pData == NULL);ATLASSUME(m_hMapping == NULL);ATLASSERT(szName != NULL); // if you just want a regular chunk of memory, use a heap allocatorm_nMappingSize = nMappingSize;m_dwViewDesiredAccess = dwViewDesiredAccess;m_hMapping = ::OpenFileMapping(m_dwViewDesiredAccess, FALSE, szName);if (m_hMapping == NULL)return AtlHresultFromLastError();m_dwViewDesiredAccess = dwViewDesiredAccess;m_nOffset.QuadPart = nOffset;m_pData = ::MapViewOfFileEx(m_hMapping, m_dwViewDesiredAccess, m_nOffset.HighPart, m_nOffset.LowPart, m_nMappingSize, NULL);if (m_pData == NULL){HRESULT hr;hr = AtlHresultFromLastError();::CloseHandle(m_hMapping);m_hMapping = NULL;return hr;}return S_OK;}因为我们的共享内存创建时,使用了一个唯一的GUID标识,通过APIOpenFileMapping来找到这个共享内存的内核对象,然后MapViewOfFileEx同样的将其映射到当前进程的内存空间里面,这样我们就可以访问共享内存里面的数据了。

使用完成后,内核对象都要释放掉,再看Unmap的实现代码

HRESULT Unmap() throw(){HRESULT hr = S_OK;if (m_pData != NULL){if (!::UnmapViewOfFile(m_pData))hr = AtlHresultFromLastError();m_pData = NULL;}if (m_hMapping != NULL){if (!::CloseHandle(m_hMapping) && SUCCEEDED(hr))hr = AtlHresultFromLastError();m_hMapping = NULL;}return hr;}首先解除当前进程的内存映射,然后关闭映射内核对象的句柄。

总结:

使用CreateFileMapping创建一个唯一标识(GUID)的内存映射,MapViewOfFileEx将其映射到当前进程的内存空间,保存需要共享的数据到这块内存中去。

爬上那座山,听最圣洁的经。

Windows上CAtlFileMapping共享内存的使用以及内部机制

相关文章:

你感兴趣的文章:

标签云: