Win32 :: API提供错误的原型错误

时间:2017-07-12 13:37:51

标签: c perl winapi

面对

  

Win32 :: API使用错误的原型调用函数并导致C堆栈一致性EBP = 18fde0 ESP = 18fdd0

以下是perl和C代码

my $CheckSqlAnyWindow = new Win32::API($dllfile,
                   "CheckSqlAnyWindow", [ 'N', 'P', 'P' ], 'N');
    my $hwndSqlany = pack("L", 0);
    my ($i, $sqlanyWinId);
    START:
    for ($i = 0; $i < $numRetries; $i++) 
    {

        $rc = $CheckSqlAnyWindow->Call($procId,"$engine - Adaptive Server Anywhere - 12.0.1 (3817)", $hwndSqlany);
        if ($rc <= 0)
        {
                $Cisco::DbUtils::errstr =  Win32::FormatMessage( Win32::GetLastError());
                return 1;
        }
}

c代码

BOOL CheckSqlAnyWindow(DWORD pid, char *winTitle, DWORD * hwnd)
{
    StartDbInfo startDbBuf;
    BOOL rc;

    startDbBuf.pid = pid;
    startDbBuf.hwnd = NULL;
    strcpy(startDbBuf.winTitle, winTitle);

    rc = EnumWindows((WNDENUMPROC) getWindowId, (LPARAM) &startDbBuf);
    memcpy((DWORD *) hwnd, (DWORD *) &startDbBuf.hwnd, sizeof(DWORD));
    return (TRUE);
}

有人可以指出要在这里进行更正吗?

1 个答案:

答案 0 :(得分:5)

在Windows上,您有不同的调用约定。一个关键的区别是谁清理参数占用的堆栈空间并在调用函数后返回值。 C中使用的标准调用约定(通常称为cdecl)将此工作留给调用者

Windows API函数使用不同的调用约定,由Microsoft命名为stdcall。在此约定中,被调用者有责任清理堆栈。由于perl模块Win32::API旨在调用Windows API函数,因此默认使用它是调用约定。

您得到的消息支持:EBP=18fde0ESP=18fdd0相距16个字节,这是函数参数的大小加上返回值。由于您的函数声明没有任何属性表示调用约定,因此它使用cdecl,因此它根本不进行任何堆栈清理,但Win32::API期望它执行。

一种解决方案是通知Win32::API调用约定。

Win32::API->new($dllfile, "CheckSqlAnyWindow", [ 'N', 'P', 'P' ], 'N', '__cdecl');

另一个解决方案是更改您的库以使用stdcall约定。

BOOL __stdcall CheckSqlAnyWindow(DWORD pid, char *winTitle, DWORD * hwnd)

请注意,__stdcall是MSVC,GCC,clang以及其他一些编译器所采用的符号。其他编译器可能使用不同的语法将函数声明为stdcall。您可以使用以下内容来避免整个问题,因为您包含windows.h

BOOL WINAPI CheckSqlAnyWindow(DWORD pid, char *winTitle, DWORD * hwnd)