在Exe和Dll之间共享变量

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/fishjam/article/details/8648202

今天解决了我开发的FTL里面一个困扰我很久的Bug,把方法共享一下。

首先说明:这不是用 pragma_data 设置的在多个 Exe 公用同一个DLL时,使用DLL中相同的变量,而是同一个进程的EXE和各个DLL之间共享变量。


背景介绍:

   FTL是本人参考ATL、WTL等开发的一个模版库,其中是我常用到的各种可重用的功能代码(比如日志、线程、线程池、流水线等)。因为全部都是头文件(.h 和内嵌的.hpp),在头文件中通过 __declspec(selectany)  定义了全局变量。但不同的 Exe、Dll 等模块包含了头文件后,会在各个模块内部定义各自的全局变量,从而造成逻辑错误。比如我使用了TLS 来将不同线程的日志写入不同的文件,尽量减少写日志时影响程序性能。因为前面的原因,造成同一个线程在 Exe 和 Dll 中写的日志会被判断成两个文件。

当然,因为大部分的人都不会编写模版库,而是编写DLL库,所以估计大部分人都用不到这种方式。这种方法只能是很难找到龙的屠龙技了吐舌头


更改方法:通过文件映射的方式,将指定的变量设置为进程内全局。这样,不同的模块,包含同一个文件后,使用的变量就统一了。代码实现起来也比较简单。

以下的代码都写在头文件(.h) 中,业务代码包含头文件后直接使用 g_GlobalShareInfo.GetShareValue().xxxx 即可。

//在 Exe 和 DLL 中共享变量的内存区域 -- T 必须是简单类型,能支持 CopyMemory, sizeof 等操作
template<typename T>
class CFSharedVariable
{
public:
	typedef BOOL (CALLBACK* InitializeSharedVariableProc)(T& rValue);
	typedef BOOL (CALLBACK* FinalizeSharedVariableProc)(T& rValue);

	//pszShareName 如果是NULL,会自动根据 Exe 的名字创建进程相关的共享区,这样同一进程中的各个模块能够共享变量
	FTLINLINE CFSharedVariable(InitializeSharedVariableProc pInitializeProc,
		FinalizeSharedVariableProc pFinalizeProc,
		LPCTSTR pszShareName = NULL)
	{
		DWORD dwShareInfoSize = sizeof(T);

		m_pShareValue = NULL;
		m_hMapping = NULL;
		m_bFirstCreate = FALSE;
		m_pFinalizeProc = pFinalizeProc;

		TCHAR szMapName[MAX_PATH] = {0};
		if (NULL == pszShareName)
		{
			TCHAR szModuleFileName[MAX_PATH] = {0};
			GetModuleFileName(NULL, szModuleFileName, _countof(szModuleFileName));
			PathRemoveExtension(szModuleFileName);
			LPCTSTR pszNamePos = PathFindFileName(szModuleFileName); // &szModuleFileName[nLength + 1];
			StringCchPrintf(szMapName, _countof(szMapName), TEXT("FTLShare_%s_%d"), pszNamePos, GetCurrentProcessId());
			pszShareName =  szMapName;
		}

		m_hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
			0, dwShareInfoSize, pszShareName);
		FTLASSERT(m_hMapping != NULL);
		if (m_hMapping)
		{
			m_bFirstCreate = (GetLastError() == 0); //Not ERROR_ALREADY_EXISTS
			m_pShareValue = reinterpret_cast<T*>
				(::MapViewOfFileEx(m_hMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, dwShareInfoSize, NULL));
		}
		FTLASSERT(m_pShareValue);

		if (m_bFirstCreate && pInitializeProc)
		{
			//如果是第一个定义该变量的模块,则初始化
			if (m_pShareValue)
			{
				pInitializeProc(*m_pShareValue);
			}
		}
	}

	FTLINLINE ~CFSharedVariable()
	{
		if (m_pShareValue)
		{
			if (m_bFirstCreate && m_pFinalizeProc)
			{
				m_pFinalizeProc(*m_pShareValue);
			}

			UnmapViewOfFile(m_pShareValue);
			m_pShareValue = NULL;
		}
		if (m_hMapping)
		{
			CloseHandle(m_hMapping);
			m_hMapping = NULL;
		}
	}

	FTLINLINE T& GetShareValue(){
		FTLASSERT(m_pShareValue);
		return *m_pShareValue;
	}
private:
	HANDLE		m_hMapping;
	BOOL		m_bFirstCreate;
	T*			m_pShareValue;
	FinalizeSharedVariableProc	m_pFinalizeProc;
};

使用方式:先定义需要共享的结构体类型和初始化、终止化方法,然后通过 __declspec(selectany) 声明全局变量即可

	struct FTLGlobalShareInfo
	{
		DWORD	dwTraceTlsIndex;			//FastTrace中保存线程局部储存的index
		LONG    nTraceSequenceNumber;			//FastTrace中每一个日志的全局序列号(分析工具中排序用)
		DWORD   dwBlockElapseTlsIndex;			//CFBlockElapse 中保存线程局部存储的Index
		LONG	nBlockElapseId;				//CFBlockElapse 中保存Id
	};
	FTLINLINE BOOL CALLBACK _FtlGlobalShareInfoInitialize(FTLGlobalShareInfo& rShareInfo);
	FTLINLINE BOOL CALLBACK _FtlGlobalShareInfoFinalize(FTLGlobalShareInfo& rShareInfo);

	//定义FTL中会用到全局共享变量 -- 可以在使用FTL的 Exe/Dll 之间共享变量
	__declspec(selectany)	CFSharedVariable<FTLGlobalShareInfo>	g_GlobalShareInfo(
		_FtlGlobalShareInfoInitialize, 
		_FtlGlobalShareInfoFinalize,
		NULL);

下面是初始化、终止化的代码:

	BOOL CALLBACK _FtlGlobalShareInfoInitialize(FTLGlobalShareInfo& rShareInfo)
	{
		BOOL bRet = TRUE;
		FTLASSERT(rShareInfo.dwTraceTlsIndex == 0);
		FTLASSERT(rShareInfo.dwBlockElapseTlsIndex == 0);

		rShareInfo.dwTraceTlsIndex = TlsAlloc();
		FTLASSERT(TLS_OUT_OF_INDEXES != rShareInfo.dwTraceTlsIndex);
		if (TLS_OUT_OF_INDEXES != rShareInfo.dwTraceTlsIndex)
		{
			TlsSetValue(rShareInfo.dwTraceTlsIndex, NULL);		
		}
		rShareInfo.dwBlockElapseTlsIndex = TlsAlloc();
		FTLASSERT(TLS_OUT_OF_INDEXES != rShareInfo.dwBlockElapseTlsIndex);
		if (TLS_OUT_OF_INDEXES != rShareInfo.dwBlockElapseTlsIndex)
		{
			TlsSetValue(rShareInfo.dwBlockElapseTlsIndex, NULL);
		}

		rShareInfo.nTraceSequenceNumber = 0;
		rShareInfo.nBlockElapseId = 0;
		return bRet;
	}

	BOOL CALLBACK _FtlGlobalShareInfoFinalize(FTLGlobalShareInfo& rShareInfo)
	{
		if (TLS_OUT_OF_INDEXES != rShareInfo.dwTraceTlsIndex)
		{
			//CFFastTrace::CFTFileWriter*
			FTLASSERT(NULL == TlsGetValue(rShareInfo.dwTraceTlsIndex));			//相关的资源必须已经释放
			TlsFree(rShareInfo.dwTraceTlsIndex);
			rShareInfo.dwTraceTlsIndex = TLS_OUT_OF_INDEXES;
		}
		if (TLS_OUT_OF_INDEXES != rShareInfo.dwBlockElapseTlsIndex)
		{
			//BlockElapseInfo*
			FTLASSERT(NULL == TlsGetValue(rShareInfo.dwBlockElapseTlsIndex));	//相关的资源必须已经释放
			TlsFree(rShareInfo.dwBlockElapseTlsIndex);
			rShareInfo.dwBlockElapseTlsIndex = TLS_OUT_OF_INDEXES;
		}
		return TRUE;
	}


展开阅读全文

没有更多推荐了,返回首页