asm inline vs 2015 with call RegCreateKeyEx

时间:2017-05-18 08:03:07

标签: c++ visual-studio assembly

我尝试使用asm inline测试RegCreateKeyEx调用。 这是代码:

        long regkey(HKEY lnKey, LPCTSTR lpsub, DWORD rise, LPTSTR lpc, DWORD dwopt, REGSAM sd, LPSECURITY_ATTRIBUTES lpas, HKEY * const &llkey, DWORD * const &dwDisposition)
    {
        __asm
        {
            lea eax, dwDisposition
            push eax   // put eax at the top of the stack
            lea eax, llkey
            push eax
            lea eax, lpas
            push eax
            lea eax, sd
            push eax
            lea eax, dwopt
            push eax
            lea eax, lpc
            push eax
            lea eax, rise
            push eax
            lea eax, lpsub
            push eax
            lea eax, lnKey
            push eax
            call DWORD ptr RegCreateKeyEx
        }
        return;
    }

    int main()
    {
    HKEY lnKey;
        LPCTSTR lpsub;
        DWORD rise;
        LPTSTR lpc;
        DWORD dwopt;
        REGSAM sd;
        LPSECURITY_ATTRIBUTES lpas;
        HKEY llkey;
        DWORD dwDisposition;
    long ret0 = regkey(HKEY_CURRENT_USER, TEXT(linkey.c_str()), 0, NULL, 0, KEY_WRITE, NULL, &llkey, &dwDisposition);
        printf("CREATE %d\n", ret0);
return 0;
}

然而,它返回错误87(无效的论据)。

linkey.c_str()变量包含字符串“Software \\ Microsoft \\ Windows \\ CurrentVersion \\ Run \\”路径并正确传递它,因为我尝试直接调用RegCreateKeyEx(HKEY_CURRENT_USER, TEXT(linkey.c_str()), 0,NULL,0, KEY_WRITE,NULL, &安培; Llkey,& dwDisposition)ad,一切都好。

使用asm inline我错在哪里?

1 个答案:

答案 0 :(得分:1)

如果你用C ++编写代码,就像这样:

LONG regkey(HKEY lnKey, LPCTSTR lpsub, DWORD rise, LPTSTR lpc, DWORD dwopt, REGSAM sd, LPSECURITY_ATTRIBUTES lpas, HKEY * const &llkey, DWORD * const &dwDisposition)
{
    return RegCreateKeyEx(lnKey, lpsub, rise, lpc, dwopt, sd, lpas, llkey, dwDisposition);
}

然后您可以使用/FA开关编译它,如注释中所建议的那样,以使编译器生成它将生成的代码的汇编列表。或者,您可以编译它然后中断,使用调试器显示实际二进制文件的反汇编。无论哪种方式,您都会看到编译器生成以下汇编代码:

mov  eax, DWORD PTR [esp+36]    ; dwDisposition
push DWORD PTR [eax]
mov  eax, DWORD PTR [esp+36]    ; llkey
push DWORD PTR [eax]
push DWORD PTR [esp+36]         ; lpas
push DWORD PTR [esp+36]         ; sd
push DWORD PTR [esp+36]         ; dwopt
push DWORD PTR [esp+36]         ; lpc
push DWORD PTR [esp+36]         ; rise
push DWORD PTR [esp+36]         ; lpsub
push DWORD PTR [esp+36]         ; lnKey
call DWORD PTR RegCreateKeyEx
ret  0

因此,内联汇编应为:

LONG regkey(HKEY lnKey, LPCTSTR lpsub, DWORD rise, LPTSTR lpc, DWORD dwopt, REGSAM sd, LPSECURITY_ATTRIBUTES lpas, HKEY * const &llkey, DWORD * const &dwDisposition)
{
    __asm
    {
       mov  eax, DWORD PTR [dwDisposition]
       push DWORD PTR [eax]
       mov  eax, DWORD PTR [llkey]
       push DWORD PTR [eax]
       push DWORD PTR [lpas]; 
       push DWORD PTR [sd]; 
       push DWORD PTR [dwopt]; 
       push DWORD PTR [lpc]; 
       push DWORD PTR [rise]; 
       push DWORD PTR [lpsub]
       push DWORD PTR [lnKey]
       call DWORD PTR RegCreateKeyEx
    }  // return value is left in EAX
}

这很简单。您不必担心从堆栈指针计算偏移量,因为内联汇编程序支持使用C ++变量。永远不需要LEA指令。事实上,LEA指令是错误的,因为它会导致你将指针作为参数传递给RegCreateKeyEx函数,而不是值本身,这就是你得到的原因错误代码87,"参数无效"。

唯一复杂的是处理dwDispositionllkey参数的方式。首先,必须将地址加载到寄存器(EAX)中,然后在将其推入堆栈时解除引用该地址。这种额外的间接级别是必要的,因为您将这些参数作为对指针的引用传递。我不知道你为什么选择这样做,但因为你做了,所以必须取消引用。 (在引擎盖下,C ++编译器实现像指针一样的引用。)

但是,我不知道你为什么要在内联汇编中实际编写这段代码。绝对没有理由这样做;它不会给你买任何东西,它只会使编写和维护变得更加复杂。它也会让你在性能上花费一些成本。上面,我展示了C ++编译器为函数调用生成的内容。以下是使用内联汇编时编译器生成的内容:

push    ebp
mov     ebp, esp
mov     eax, DWORD PTR _dwDisposition$[ebp]
push    DWORD PTR [eax]
mov     eax, DWORD PTR _llkey$[ebp]
push    DWORD PTR [eax]
push    DWORD PTR _lpas$[ebp]
push    DWORD PTR _sd$[ebp]
push    DWORD PTR _dwopt$[ebp]
push    DWORD PTR _lpc$[ebp]
push    DWORD PTR _rise$[ebp]
push    DWORD PTR _lpsub$[ebp]
push    DWORD PTR _lnKey$[ebp]
call    DWORD PTR RegCreateKey
pop     ebp
ret     0

注意使用内联汇编所需的额外序言和尾声指令,因为编译器不知道你在内联汇编中实际上是在做什么因此必须补偿它通过设置和拆除堆栈框架。它不是一个巨大的性能成本或任何东西,但同样,它完全没有意义。

您的后续问题/问题(来自您发布的a commentan answer)对我没有任何意义。您不应该需要任何类型的异常处理程序,并且因为您没有引用您正在获取的确切错误消息,所以我不知道它可能会说什么。这个问题很可能与strtemp有关,而你的声明并未向我们展示。由于您说您正在调用RegCreateKeyEx的ANSI版本,strtemp应该是指向char缓冲区的指针,以NUL字符终止。此外,您传递给RegSetValueEx的下一个参数是错误的:cbData必须包含终止NUL字符,因此这应该是strlen(strtemp) + 1

您的代码存在的其他问题包括绝对没有错误检查。如果尝试创建注册表项失败,那么您不应该尝试写入它,也不应该尝试关闭它。

此外,2017年从来没有理由调用ANSI API版本的Windows API函数。近二十年来,一切都在内部使用Unicode,您的代码需要使用该程序。这意味着使用由wchar_t个字符组成的字符串。

相关问题