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

Pra*_*eek 0 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;
        }
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

谁有人指出要在这里做的更正请?

小智 5

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

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

你得到的消息支持这个:EBP=18fde0并且ESP=18fdd0彼此正好相差16个字节,这是函数参数的大小加上返回值.由于您的函数声明没有任何表示调用约定的属性cdecl,因此它使用,因此它根本不进行任何堆栈清理,但Win32::API预计会这样做.

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

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

另一种解决方案是更改库以使用stdcall约定.

BOOL __stdcall CheckSqlAnyWindow(DWORD pid, char *winTitle, DWORD * hwnd)
Run Code Online (Sandbox Code Playgroud)

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

BOOL WINAPI CheckSqlAnyWindow(DWORD pid, char *winTitle, DWORD * hwnd)
Run Code Online (Sandbox Code Playgroud)