Я не могу использовать __thiscall
давая мне следующую ошибку:
error C4234: nonstandard extension used : '__thiscall' keyword reserved for future use
Я вызываю функцию класса из DLL:
typedef void(*SETDIRPROC)(void*,D3DXVECTOR3&);
void ZMyCharacter_SetDirection_Rev( void )
{
if( pZMyCharacter == 0 )
{
printf( "cannot set direction now\n" );
return;
}
SETDIRPROC SetDir_Proc = (SETDIRPROC)0x004A2AF0;
D3DXVECTOR3 pos = D3DXVECTOR3(4.0f,2.0f,1.0f);
SetDir_Proc( pZMyCharacter, pos );
}
pZMyCharacter
это void*
указатель на реальный class ZMyCharacter
на реальном приложении. Это работает, но я получаю ошибки отладки (которые можно игнорировать), предупреждающие, что соглашение о вызовах отличается. Так и есть, так как SETDIRPROC
является __cdecl
по умолчанию, и я не могу изменить его на __thiscall
,
typedef void(__thiscall *SETDIRPROC)(void*,D3DXVECTOR3&);
// ошибка C4234
как мне обойти это?
Я собираюсь отвезти вас по темной и ужасающей дороге в страну неопределенного поведения …
Проблема здесь в том, что вам нужно вызывать указатель на функцию-член, фактически не имея указателя на функцию-член. Вы можете использовать немного темной магии из земли UB, чтобы выполнить это. Эта темная магия позволит вам преобразовать простое целочисленное значение в полностью пригодный указатель на функцию-член. Для этого мы можем создать union
…
// Magical beans. Replace with your own beans if you have them.
struct ZMyCharacter {};
// Here be dark magic!
union FunctionAddress
{
typedef void (ZMyCharacter::*MEMBER_FUNC)(D3DXVECTOR3);
FunctionAddress(uintptr_t addr) : address(reinterpret_cast<void*>(addr)) {}
MEMBER_FUNC function;
private:
void* address;
};
Это волшебный union
позволит вам установить указатель на функцию-член из простого целочисленного значения через его конструктор …
FunctionAddress pZMyFunction(0x004A2AF0);
Чтобы использовать это, вам нужно будет внести пару изменений в ZMyCharacter_SetDirection_Rev
вызвать функцию-член напрямую, вместо того, чтобы передавать ее как указатель на другую функцию (что, как я полагаю, основано на вашем предыдущем вопросе, является встроенной сборкой) …
void ZMyCharacter_SetDirection_Rev( void )
{
if( pZMyCharacter == NULL )
{
printf( "cannot set direction now\n" );
return;
}
D3DXVECTOR3 pos = D3DXVECTOR3(4.0f,2.0f,1.0f);
// directly call using pointer to member function
(pZMyCharacter->*pZMyFunction.function)( pos );
}
Имейте в виду, что если функция виртуальная, вы обходите стороной виртуальную функцию. Если это так, вы должны быть готовы к ситуациям, когда это нарушает полиморфизм. Если вы хотите полностью поддерживать диспетчеризацию виртуальных функций, вам необходимо полностью перепроектировать макет виртуальной таблицы. Для этого вы можете сделать что-то еще проще, не используя много темной магии.
// reverse engineered virutal function layour/ordering
struct ZMyCharacter
{
virtual void func1();
virtual void func2();
virtual void func3();
virtual void target_function(D3DXVECTOR3&);
virtual void func4();
};
void ZMyCharacter_SetDirection_Rev( void )
{
if( pZMyCharacter == NULL )
{
printf( "cannot set direction now\n" );
return;
}
D3DXVECTOR3 pos = D3DXVECTOR3(4.0f,2.0f,1.0f);
pZMyCharacter->target_function( pos );
}
Я понимаю, что это меняет несколько вещей для вашей текущей кодовой базы. Вы должны быть в состоянии интегрировать код или концепции в то, что вы пытаетесь достичь.
Неопределенное поведение, о котором я упоминаю, касается доступа к неактивному члену профсоюза. Обычно это считается плохим.
Я думаю, это должно выглядеть так:
typedef void(__thiscall &ZMyCharacter::*SETDIRPROC)(D3DXVECTOR3&);
SETDIRPROC SetDir_Proc = (SETDIRPROC)0x004A2AF0;
static_cast<ZMyCharacter*>(pZMyCharacter)->SetDir_Proc( pos );
__thiscall
предназначен для использования в указателе на функцию-член, а не на свободную функцию, как вы объявили. Вышесказанное должно быть ближе к тому, что нужно компилятору — тип функции-члена приведен и вызван.
Это не проверено, но должно дать вам начало. Обратите внимание, что он основан на деталях реализации MSVC, поэтому он не является стандартным и не будет работать в других компиляторах:
#pragma warning(push)
#pragma warning(disable: 4608)
template < typename Src, typename Dest >
Dest force_cast( Src src )
{
union _convertor { Dest d; Src s; _convertor() : d(0), s(0) {} } convertor;
convertor.s = src;
return convertor.d;
}
#pragma warning(pop)
void func(ZMyCharacter* pZMyCharacter)
{
typedef void (ZMyCharacter::*F_SetDirection)(D3DXVECTOR3&);
F_SetDirection pfSetDirection = force_cast< FARPROC, F_SetDirection >(0x004A2AF0);
D3DXVECTOR3 pos = D3DXVECTOR3(4.0f,2.0f,1.0f);
(pZMyCharacter->*pfSetDirection)(pos);
}
Этот маленький (неясный) фрагмент здесь не рекомендуется совсем, но, как вы можете себе представить, это работает:
class ZMyCharacterHook
{
public:
virtual void SetDir(D3DXVECTOR3&);
};
typedef void (*SETDIRPROC)(D3DXVECTOR3&);
typedef void (ZMyCharacterHook::*SETDIR_METHOD)(D3DXVECTOR3&);
SETDIRPROC SetDir_Proc = (SETDIRPROC)0x004A2AF0;
D3DXVECTOR3 pos = D3DXVECTOR3(4.0f, 2.0f, 1.0f);
((ZMyCharacterHook *)pZMyCharacter->*(SETDIR_METHOD &)SetDir_Proc)(pos);
Он никак не поддерживается и должен использоваться только в том случае, если вы действительно хотите рискнуть, так как он имеет неопределенное поведение (взгляните на обходной путь приведения ссылок).
Если вы хотите быть более дискретным, но все же против какой-либо ремонтопригодности и переносимости, вы можете использовать встроенную сборку MSVC x86 для явного задания ecx
(который содержит this
указатель при использовании thiscall
), а затем вызвать «простую» функцию по ее адресу:
__asm
{
pusha // save all registers
mov ecx, pZMyCharacter // "this" pointer
lea eax, pos // eax = &pos (parameter passed by reference)
push eax // parameters pushed onto the stack
mov eax, 0x004A2AF0
call eax // the function call itself
popa // restore registers, no messed up code from here on
}