正确的约定调用类方法

时间:2013-06-23 01:33:44

标签: c++ windows visual-studio visual-c++ calling-convention

我无法使用__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*的{​​{1}}指针。它工作正常,但我收到调试错误(可以忽略),警告调用约定不同。确实如此,因为class ZMyCharacter默认为SETDIRPROC,我无法将其更改为__cdecl

__thiscall //错误C4234

我该如何解决这个问题?

4 个答案:

答案 0 :(得分:2)

我认为它应该是这样的:

typedef void(__thiscall &ZMyCharacter::*SETDIRPROC)(D3DXVECTOR3&);
SETDIRPROC SetDir_Proc = (SETDIRPROC)0x004A2AF0;
static_cast<ZMyCharacter*>(pZMyCharacter)->SetDir_Proc( pos );

__thiscall用于成员函数指针,而不是像你声明的那样的自由函数。以上内容应该更接近编译器所需的内容 - 成员函数类型强制转换并调用。

答案 1 :(得分:2)

我将带你沿着黑暗而恐怖的道路前往未定义行为的土地......

这里的问题是你需要调用指向成员函数的指针,而不需要实际指向成员函数的指针。你可以使用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 );
}

我意识到这会改变您当前代码库的一些内容。您应该能够将代码或概念集成到您要完成的任务中。


我提到的未定义行为是关于访问联合的非活动成员。通常认为这很糟糕。

答案 2 :(得分:1)

这是未经测试的,但应该给你一个开始。请注意,它依赖于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);
}

答案 3 :(得分:1)

这里的小(模糊)片段不推荐 >,但正如您所能想象的那样,它有效:

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);

它无法以任何方式维护,只有在你真的想承担风险时才应该使用它,因为它有未定义的行为(请参阅参考强制转换方法)。

如果您希望更加离散,但仍然违反任何可维护性和可移植性,则可以使用x86 MSVC内联汇编来显式设置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
}