【问题标题】:Variable arguments in _stdcall, C++ / Inline ASM_stdcall、C++/内联 ASM 中的变量参数
【发布时间】:2011-08-02 12:08:54
【问题描述】:

我的情况是,我必须使用 C++ 和内联 ASM 模拟一个 _stdcall 函数,但它使用可变数量的参数。通常,当它将控制权返回其父级时,它不知道要从堆栈中弹出多少参数,因此不起作用,但我希望通过全局变量告诉它它应该有多少参数,然后得到它像那样弹出它们。

这真的可能吗?如果是这样,有人可以让我朝着正确的方向前进吗?我特别纠结于我需要的 Epilog 代码。

我的目标是创建一个函数,它可以用作任何需要回调的函数(如 EnumWindows),只要用户在运行时告诉它 args 列表必须有多长。这个想法是让它与其他地方的一些代码集成,所以它基本上每次调用回调时都会运行一个触发器,并提供一个链接到一个地方,用户可以读取和查看返回的变量。

这有意义吗?

【问题讨论】:

  • 全局变量会使其线程不安全。
  • 恐怕这没有意义,至少对我来说是这样。
  • 你不能将值(或指向值的指针)打包到一个数组中,并将指针传递给该数组以及其他相关信息(类型、大小),而不是尝试用 __stdcall 来模仿它不适合?!
  • 你想和什么交互?由于您强调的​​原因,大多数实现者将__stdcall 和变量参数函数视为不兼容。来自__stdcall 的文档:“被调用者清理堆栈,因此编译器生成可变参数函数 __cdecl。”
  • 谢谢大家。我的情况基本上是我必须创建一个函数,其地址将传递给 EnumWindows() 或 DirectSoundEnumerate() 之类的函数,因此它被称为回调。但是运行它的函数可能会有所不同,所以它可能是前一刻是 EnumWindows,下一刻是 DirectSoundEnumerate。调用者假设我的函数是_stdcall,但是不可能只准备一个裸函数,就像它是一个stdcall一样简单地清理自己吗?如果可能的话,我只希望它足够接近地模仿 _stdcall 以保持稳定。

标签: c++ windows programming-languages inline-assembly


【解决方案1】:

没有意义。 __stdcall 不允许使用可变参数,因为所有参数的总大小被修饰为函数名(from msdn):

名称装饰约定

名称前加下划线 (_)。名称后跟 at 符号 (@)后跟参数列表中的字节数(十进制)。因此,声明为int func( int a, double b )的函数修饰如下:_func@12

这句话告诉你可变参数__stdcall 函数是如何实现的:

__stdcall 调用约定用于调用 Win32 API 函数。 被调用者清理堆栈,因此编译器生成可变参数函数 __cdecl。使用这种调用约定的函数需要一个函数原型。

(强调我的)
因此,没有带有可变参数的__stdcall 函数,它们默默地更改为__cdecl。 :)

【讨论】:

    【解决方案2】:

    您可以执行以下操作(破解代码):

    static int NumberOfParameters = 0;
    
    __declspec(naked) void GenericCallback()
    {
        // prologue
        __asm push ebp
        __asm mov ebp, esp
    
        // TODO: do something with parameters on stack
    
        // manual stack unwinding for 2 parameters
        // obviously you would adjust for the appropriate number of parameters
        // (e.g. NumberOfParameters) instead of hard-coding it for 2
        // fixup frame pointer
        __asm mov eax, [ebp + 0]
        __asm mov [ebp + 8], eax // NumberOfParameters * 4 (assuming dword-sized parameters)
        // fixup return address
        __asm mov eax, [ebp + 4]
        __asm mov [ebp + 12], eax // (NumberOfParameters + 1) * 4
        // return TRUE
        __asm mov eax, 1
        // epilogue
        __asm mov esp, ebp
        __asm pop ebp
        // fixup stack pointer
        __asm add esp, 8 // NumberOfParameters * 4
        __asm ret 0
    }
    
    int main(int argc, _TCHAR* argv[])
    {
        NumberOfParameters = 2;
        EnumWindows((WNDENUMPROC)GenericCallback, NULL);
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-19
      • 2015-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多