【问题标题】:Preprocessor macro based code yields a C2400 error基于预处理器宏的代码产生 C2400 错误
【发布时间】:2010-06-29 03:10:19
【问题描述】:
#define CANCEL_COMMON_DIALOG_HOOK(name)  \
void __declspec(naked) ##name##CancelCommonDialogHook(void)  \
{  \
    __asm  \
    {  \
        add     esp, [k##name##CancelCommonDialogStackOffset]  \
        jz      RESTORE  \
        jmp     [k##name##CancelCommonDialogNewFileRetnAddr]  \
    RESTORE:  \
        pushad  \
        call    DoSavePluginCommonDialogHook  \
        test    eax, eax  \
        jnz     REMOVE  \
        popad  \
        jmp     [k##name##CancelCommonDialogRestoreRetnAddr]  \
    REMOVE:  \
        popad  \
        jmp     [k##name##CancelCommonDialogRemoveRetnAddr]      \
    }  \
}

使用上面的宏会导致编译器抛出这个错误:

错误 C2400:内联汇编语法 “第二个操作数”中的错误;成立 '恢复'

我做错了什么?

编辑:

void __declspec(naked) #name##CancelCommonDialogHook(void)               \ 
{                                                                        \
    __asm   add     esp, [k##name##CancelCommonDialogStackOffset]        \
    __asm   jz      RESTORE                                              \
    __asm   jmp     [k##name##CancelCommonDialogNewFileRetnAddr]         \
    RESTORE:                                                             \
    __asm   pushad                                                       \
    __asm   call    DoSavePluginCommonDialogHook                         \
    __asm   test    eax, eax                                             \
    __asm   jnz     REMOVE                                               \
    __asm   popad                                                        \
    __asm   jmp     [k##name##CancelCommonDialogRestoreRetnAddr]         \
    REMOVE:                                                              \
    __asm   popad                                                        \
    __asm   jmp     [k##name##CancelCommonDialogRemoveRetnAddr]          \
}

上面的代码也不行:

错误 C2447:“{”:缺少函数 标题(旧式正式列表?)错误 C2014: 预处理器命令必须启动 作为第一个非空白空间

【问题讨论】:

  • 看看预处理器的结果有什么突出的吗?
  • 嗯,结果中似乎没有任何换行符。

标签: c++ visual-studio assembly c-preprocessor


【解决方案1】:

至少在我上次尝试时,您无法在 VC++ 的内联汇编块内创建标签。使用 C 风格的标签虽然有效:

void __declspec(naked) ##name##CancelCommonDialogHook(void)  \
{  \
    __asm  \
    {  \
        add     esp, [k##name##CancelCommonDialogStackOffset]  \
        jz      RESTORE  \
        jmp     [k##name##CancelCommonDialogNewFileRetnAddr]  \
    }           \
    RESTORE:    \
    _asm {      \
        pushad  \
        call    DoSavePluginCommonDialogHook  \
        test    eax, eax  \
        jnz     REMOVE  \
        popad  \
        jmp     [k##name##CancelCommonDialogRestoreRetnAddr]  \
    }          \
    REMOVE:    \
    __asm {    \
        popad  \
        jmp     [k##name##CancelCommonDialogRemoveRetnAddr]      \
    }  \
}

我已经有一段时间没有在 VC++ 中编写任何内联汇编了,所以我不能保证它会起作用,但我猜这是一个相当公平的机会。

【讨论】:

  • 刚刚用 VS2010 进行了测试,我能够毫无问题地创建标签,并且似乎记得这已经有一段时间了。
  • 我明白了。会试试这个。编辑:嗯,不好。 jz 调用仍然会引发错误,以及其他新错误。澄清一下,我使用的是 VS 2008。
【解决方案2】:

我一直无法访问计算机,所以希望您已经解决了这个问题。我认为问题在于使用“\”结束该行实际上告诉C预处理器将下一行与该行合并。见this page上的评论3,和线拼接here。这对大多数 C 语句都有效,但对于汇编来说问题更大,因为新行是它定义语句的方式。

我可以想到两种解决方法。首先是找到类似C的“;”在汇编中可以用作语句分隔符,我不知道是否存在这种东西。第二种方法是将所有内容包装在单独的 __asm 语句中。采用第二种方法,如下:

void __declspec(naked) ##name##CancelCommonDialogHook(void)          
{                                                                    
    __asm{add     esp, [k##name##CancelCommonDialogStackOffset]}      \
    __asm{jz      RESTORE}                                            \
    __asm{jmp     [k##name##CancelCommonDialogNewFileRetnAddr]}       \
    RESTORE:                                                          \
    __asm{pushad}                                                     \
    __asm{call    DoSavePluginCommonDialogHook}                       \
    __asm{test    eax, eax}                                           \
    __asm{jnz     REMOVE}                                             \
    __asm{popad}                                                      \
    __asm{jmp     [k##name##CancelCommonDialogRestoreRetnAddr]}       \
    REMOVE:                                                           \
    __asm{popad}                                                      \
    __asm{jmp     [k##name##CancelCommonDialogRemoveRetnAddr]}        \
}

注意:我将标签留在了 asm 语句之外,因为:

  1. 我想你可以
  2. 我不确定__asm 块中定义的标签的范围规则

【讨论】:

  • 第 3 行中的标记粘贴运算符未被识别为一个 - 编译器认为它是一个指令。使用修改后的代码编辑问题。
  • @shadeMe:您可以尝试将令牌粘贴从程序集中取出并改用函数指针。
【解决方案3】:

只是一个疯狂的猜测:宏会将所有文本扩展为一行,以 add esp, [k...] jz RESTORE jmp k... 结尾。也许在每条汇编指令的末尾放一个分号会有所帮助。

该假设的一个迹象是错误发生在您的第二个“行”上。第一个是可以的,但是第二个会合并到第一个,所以这是编译器第一次混淆的机会。如果错误出现在以后的某个地方,可能就不会是这样了。

【讨论】:

    【解决方案4】:

    通过将函数体封闭在另一个范围内来修复它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-03
      • 1970-01-01
      • 2018-01-19
      • 2012-07-22
      • 2017-01-31
      • 1970-01-01
      • 1970-01-01
      • 2014-04-02
      相关资源
      最近更新 更多