在Exe跟Dll之间共享变量
在Exe和Dll之间共享变量
使用方式:先定义需要共享的结构体类型和初始化、终止化方法,然后通过 __declspec(selectany) 声明全局变量即可
下面是初始化、终止化的代码:
今天解决了我开发的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; }