如何获取类成员函数的内存地址,我使用以前动态设置的静态库,因为我将游戏移植到不支持动态库的平台。除了一个问题外,它运作良好。
恢复已保存的游戏时,NPC会变为静态,因为他们不会继续运行保存游戏时处于活动状态的功能。它通过在保存时查找函数地址以获取全局偏移表和符号表中的名称来实现这一点,当恢复它时使用GOT和ST中的名称获取地址。
由于这不适用于静态链接,我试图弄清楚如何获取导出函数的地址,以便在应用程序首次启动时将其存储在std :: map中,并带有地址和唯一名称
由于某些原因,以下似乎不适合我。任何帮助将非常感激 :)。
使用DEFINE_FIELD宏在基类中存储保存游戏文件的地址:
// Global Savedata for Delay
TYPEDESCRIPTION CBaseEntity::m_SaveData[] =
{
DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION )
};
使用动态链接时的派生类导出:
extern "C" _declspec( dllexport ) void CMultiManager( entvars_t *pev );
以前使用动态链接时导出ManagerThink()的派生类:
class CMultiManager : public CBaseEntity
{
public:
CMultiManager();
void /*EXPORT*/ ManagerThink ( void );
}
我在派生类中的新类构造函数尝试获取成员函数的地址(因此我可以将它存储在带有名称的std:map中)。是否可以在全局范围内而不是构造函数?
CMultiManager::CMultiManager()
{
//Try the ManagerThink function directly
void (CBaseEntity ::*ptrTemp)(void);
ptrTemp = static_cast <void (CBaseEntity::*)(void)> (ManagerThink);
void* iMemoryAddressFunc = &ptrTemp;
ALERT( at_error, "__TEST__ FUCNTION __ EXPORT ADDRESS: CMultiManager::ManagerThink (%08lx)\n", (unsigned long)iMemoryAddressFunc );
//-------------------------------------------------------------------------------
//Try the inherited m_pfnThink variable ManagerThink() is stored in as well.
void* iMemoryAddressVar = &m_pfnThink;
ALERT( at_error, "__TEST__ M_PFNTHINK __ EXPORT ADDRESS:
CMultiManager::ManagerThink (%08lx)\n", (unsigned long)iMemoryAddressVar );
}
恢复游戏前调用的函数检查方法,它使用存储在基类中保存游戏数据的函数地址(上面的函数)。
void FunctionCheck( void *pFunction, char *name )
{
if (pFunction && !NAME_FOR_FUNCTION((unsigned long)(pFunction)) )
ALERT( at_error, "No EXPORT: %s:%s (%08lx)\n", STRING(pev->classname), name, (unsigned long)pFunction );
}
运行游戏时调试日志:
显然它不会找到导出,因为我还没有设置任何东西,因为我需要先存储地址,但为什么地址不是全部匹配? ManagerThink()也被分配给m_pfnThink,那么为什么日志中的前两个至少不在同一个地址?
From CMultiManager():
__TEST__ FUCNTION __ EXPORT ADDRESS: CMultiManager::ManagerThink (d008759c)
__TEST__ M_PFNTHINK __ EXPORT ADDRESS: CMultiManager::ManagerThink (01eb0e08)
From FunctionCheck();
No EXPORT: multi_manager:ManagerThink (00226c0a)
ManagerThink()函数:
// Designers were using this to fire targets that may or may not exist --
// so I changed it to use the standard target fire code, made it a little simpler.
void CMultiManager :: ManagerThink ( void )
{
float time;
time = gpGlobals->time - m_startTime;
while ( m_index < m_cTargets && m_flTargetDelay[ m_index ] <= time )
{
FireTargets( STRING( m_iTargetName[ m_index ] ), m_hActivator, this, USE_TOGGLE, 0 );
m_index++;
}
if ( m_index >= m_cTargets )// have we fired all targets?
{
SetThink( NULL );
if ( IsClone() )
{
UTIL_Remove( this );
return;
}
SetUse ( ManagerUse );// allow manager re-use
}
else
pev->nextthink = m_startTime + m_flTargetDelay[ m_index ];
}
我也很困惑它在动态设置时如何知道EXPORT ManagerThink(void);函数来调用创建的对象的每个实例,因为它只是在GOT /符号表中存储了一个EXPORT地址吗?
任何帮助/建议/建议都会很棒。谢谢:)
修改
感谢您的回复。我设法找到了解决问题的方法。
我查看了一个较新版本的游戏,他们更新了代码,不使用GOT和ST,因为他们需要在不支持dll且面临同样问题的平台上进行游戏。他们使用以下宏来解决它,以声明一个结构并将定义的指针存储到结构中的成员函数。
//-----------------------------------------------------------------------------
//
// Macros used to implement datadescs
//
#define DECLARE_SIMPLE_DATADESC() \
static datamap_t m_DataMap; \
static datamap_t *GetBaseMap(); \
template <typename T> friend void DataMapAccess(T *, datamap_t **p); \
template <typename T> friend datamap_t *DataMapInit(T *);
#define BEGIN_SIMPLE_DATADESC( className ) \
datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \
datamap_t *className::GetBaseMap() { return NULL; } \
BEGIN_DATADESC_GUTS( className )
#define BEGIN_SIMPLE_DATADESC_( className, BaseClass ) \
datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \
datamap_t *className::GetBaseMap() { datamap_t *pResult; DataMapAccess((BaseClass *)NULL, &pResult); return pResult; } \
BEGIN_DATADESC_GUTS( className )
#define DECLARE_DATADESC() \
DECLARE_SIMPLE_DATADESC() \
virtual datamap_t *GetDataDescMap( void );
#define BEGIN_DATADESC_NO_BASE( className ) \
datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \
datamap_t *className::GetDataDescMap( void ) { return &m_DataMap; } \
datamap_t *className::GetBaseMap() { return NULL; } \
BEGIN_DATADESC_GUTS( className )
#define BEGIN_DATADESC( className ) \
datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \
datamap_t *className::GetDataDescMap( void ) { return &m_DataMap; } \
datamap_t *className::GetBaseMap() { datamap_t *pResult; DataMapAccess((CBaseEntity *)NULL, &pResult); return pResult; } \
BEGIN_DATADESC_GUTS( className )
#define BEGIN_DATADESC_GUTS( className ) \
template <typename T> datamap_t *DataMapInit(T *); \
template <> datamap_t *DataMapInit<className>( className * ); \
namespace className##_DataDescInit \
{ \
datamap_t *g_DataMapHolder = DataMapInit( (className *)NULL ); /* This can/will be used for some clean up duties later */ \
} \
\
template <> datamap_t *DataMapInit<className>( className * ) \
{ \
typedef className classNameTypedef; \
static CDatadescGeneratedNameHolder nameHolder(#className); \
className::m_DataMap.baseMap = className::GetBaseMap(); \
static typedescription_t dataDesc[] = \
{ \
{ FIELD_VOID, 0, 0, 0, 0 }, /* so you can define "empty" tables */
#define END_DATADESC() \
}; \
\
if ( sizeof( dataDesc ) > sizeof( dataDesc[0] ) ) \
{ \
classNameTypedef::m_DataMap.dataNumFields = SIZE_OF_ARRAY( dataDesc ) - 1; \
classNameTypedef::m_DataMap.dataDesc = &dataDesc[1]; \
} \
else \
{ \
classNameTypedef::m_DataMap.dataNumFields = 1; \
classNameTypedef::m_DataMap.dataDesc = dataDesc; \
} \
return &classNameTypedef::m_DataMap; \
}
// replaces EXPORT table for portability and non-DLL based systems (xbox)
#define DEFINE_FUNCTION_RAW( function, func_type ) { FIELD_VOID, nameHolder.GenerateName(#function), /*{ NULL, NULL },*/ 1, FTYPEDESC_FUNCTIONTABLE, /*NULL, NULL,*/ (inputfunc_t)((func_type)(&classNameTypedef::function))},
//------------------------------------------------------------------------------