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

前言:

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

CAtlFileMapping使用:

定义结构体,包括你所须要共享的数据,这里我们仅仅须要共享主窗体的句柄

//自己定义进程共享数据结构体
struct PROCESS_SHARE_DATA
{
	HWND	hMainWnd;
};
使用</span>VS自带GUID生成工具创建一个GUID,来唯一标识这块共享内存</p><p>const TCHAR szShareGuid[] = _T("4F836C8D-F55E-4D88-A0BF-9ACDC0A33B31");定义共享内存全局变量
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,创建一块共享内存,

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 allocator

		m_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 allocator

		m_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标识,通过API OpenFileMapping来找到这个共享内存的内核对象,然后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将其映射到当前进程的内存空间。保存须要共享的数据到这块内存中去。

其它进程訪问共享内存时。OpenFileMapping通过唯一标识(GUID)获取这块共享内存的内核句柄,调用MapViewOfFileEx映射到自己的内存空间,然后就能够读取里面的数据了,使用完毕后使用UnmapViewOfFile解除映射。CloseHandle关闭内核对象句柄。

尽管使用CAtlFileMapping更加简单、方便。可是我们还是须要了解内存映射的机制的。

时间: 2024-10-25 20:23:04

Windows上CAtlFileMapping共享内存的使用以及内部机制的相关文章

Windows进程通信 -- 共享内存

享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Windows 中是用 FileMapping 实现的,从具体的实现方法上看主要通过以下几步来实现: 1.调用 CreateFileMapping 创建一个内存文件映射对象: HANDLE CreateFileMapping( HANDLE hFile, // handle to file to map

Windows环境下共享内存通信

一.引言 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.WIN32 API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换. 进程间通讯(即:同机通讯)和数据交换有多种方式:消息.共享内存.匿名(命名)管道.邮槽.Windows套接字等多种技术."共享内存"(shared memory)可以定义为对一个以上的进程是可见的内存或存在于多个进程的虚拟地址空间.例如:如果两个进程使用相同的DLL,只把DLL的代码页装入内存

Windows进程通信 -- 共享内存(1)

共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Windows 中是用 FileMapping 实现的,从具体的实现方法上看主要通过以下几步来实现: 1.调用 CreateFileMapping 创建一个内存文件映射对象: HANDLE CreateFileMapping( HANDLE hFile, // handle to file to ma

Windows进程间共享内存通信实例

抄抄补补整出来 采用内存映射文件实现WIN32进程间的通讯:Windows中的内存映射文件的机制为我们高效地操作文件提供了一种途径,它允许我们在WIN32进程中保留一段内存区域,把硬盘或页文件上的目标文件映射到这段虚拟内存中.注意:在程序实现中必须考虑各进程之间的同步问题. 在Windows操作系统下,任何一个进程不允许读取.写入或是修改另一个进程的数据(包括变量.对象和内存分配等),但是在某个进程内创建的文件映射对象的视图却能够为多个其他进程所映射,这些进程共享的是物理存储器的同一个页面. 因

Windows进程通信-共享内存空间

三个模块 1,game.exe,三个方法,控制台输入指令('A','B','R')分别控制三个方法的调用: 2,WGDll.dll,要注入到game进程中的dll文件: 3,myconsole.exe,用来注入dll文件的程序: 先开启game进程,然后用myconsole把dll注入到game,dll模块和myconsole模块利用共享内存实现进程通信,在myconsole的控制台输入指令,dllmokuai接受指令,调用game模块的方法,达到控制game的目的 game模块 #includ

Windows共享内存示例

共享内存主要是通过映射机制实现的. Windows 下进程的地址空间在逻辑上是相互隔离的,但在物理上却是重叠的.所谓的重叠是指同一块内存区域可能被多个进程同时使用.当调用 CreateFileMapping 创建命名的内存映射文件对象时,Windows 即在物理内存申请一块指定大小的内存区域,返回文件映射对象的句柄 hMap.为了能够访问这块内存区域必须调用 MapViewOfFile 函数,促使 Windows 将此内存空间映射到进程的地址空间中.当在其他进程访问这块内存区域时,则必须使用Op

Boost:shared_memory_object --- 共享内存

什么是共享内存 共享内存是最快速的进程间通信机制.操作系统在几个进程的地址空间上映射一段内存,然后这几个进程可以在不需要调用操作系统函数的情况下在那段内存上进行读/写操作.但是,在进程读写共享内存时,我们需要一些同步机制. 考虑一下服务端进程使用网络机制在同一台机器上发送一个HTML文件至客户端将会发生什么: 服务端必须读取这个文件至内存,然后将其传至网络函数,这些网络函数拷贝那段内存至操作系统的内部内存. 客户端使用那些网络函数从操作系统的内部内存拷贝数据至它自己的内存. 如上所示,这里存在两

Linux共享内存使用常见陷阱与分析 - 51CTO.COM http://os.51cto.com/art/201311/418977_all.htmIPC---共享内存

共享内存就是允许两个或多个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据时,不需要在客户进程和服务器进程之间幅值,因此是最快的一种IPC.不同进程之间共享的内存通常安排为同一段物理内存.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样.而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程. 注意:共享内存并未提供同步机制,也就是说,

linux程序设计——共享内存(第十四章)

14.2    共享内存 共享内存是3个IPC机制中的第二个,它允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式.大多数共享内存的具体实现,都把由不同进程之间共享的内存安排为同一段物理内存. 共享内存是由IPC为进程创建的一个特殊的地址访问,它将出现在该进程的地址空间中,其他进程可以将同一段共享内存连接到它们自己的地址空间中.所有进程都可以访问共享内存中的地址,就好像它们是由malloc分配的一样.如果某个进程向共享内存写入了数据,所做的改动