【问题标题】:Win32::API giving wrong prototype errorWin32::API 给出错误的原型错误
【发布时间】:2017-07-12 13:37:50
【问题描述】:

面对

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);
}

谁能指出这里要进行的更正吗?

【问题讨论】:

  • 在 C 函数的定义中尝试 WINAPI__stdcall

标签: c perl winapi


【解决方案1】:

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

Windows API 函数使用不同的调用约定,微软将其命名为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)

【讨论】:

  • 当C代码中没有指定调用约定时,使用编译器的默认约定,通常cdecl,但也可能是别的,取决于用户的编译器设置。所以你不能总是假设cdecl,除非你从不改变默认值。
  • @ikegami 感谢编辑,我不熟悉Win32::API,所以不知道它也支持cdecl
  • @FelixPalmen 谢谢。 _cdecl 工作。但即使我使用_cdecl,我也会遇到同样的错误。我们不能使用 WINAPI,因为这是 windows dll。下面是代码 my $CreateProcess = new Win32::API("kernel32.dll", "CreateProcess", [ 'P', 'P', 'P', 'P', 'N', 'N', 'P', 'P', 'P', 'P' ], 'N'); $rc = $CreateProcess-&gt;Call($dbengineCodeFile, $cmdLine, 0, 0, 0, NORMAL_PRIORITY_CLASS, 0, ".", $startInfo, $procInfo); 对此有何建议?
  • 另外值得注意的是:当面向 64 位时,只有一个调用约定。因此,在编写 64 位代码时,问题会自动消失。
  • @Prateek 我真的理解这条评论,尤其是“我们不能使用 WINAPI,因为这是 windows dll”...跨度>
猜你喜欢
  • 1970-01-01
  • 2016-12-27
  • 1970-01-01
  • 1970-01-01
  • 2015-03-10
  • 2021-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多