如何链接包含WinAPI的C代码?

时间:2015-10-22 10:41:30

标签: delphi delphi-xe8

如何链接包含WinAPI调用的C代码?链接时我收到以下错误:

  

[dcc32错误] Project1.dpr(16):E2065不满意的前向或外部声明:'__ imp__GetCurrentThreadId @ 0'

请考虑以下示例。

的Delphi:

program Project1;

uses
  Windows;

{$L C:\Source.obj}

function Test: DWORD; cdecl; external name '_Test';

begin
  WriteLn(Test);
end.

C:

#include <Windows.h>

DWORD Test(void)
{
   return GetCurrentThreadId();
}

1 个答案:

答案 0 :(得分:3)

这是因为Windows头文件在声明函数时通常使用__declspec(dllimport)。对于有问题的函数,其在WinBase.h中的定义是:

WINBASEAPI
DWORD
WINAPI
GetCurrentThreadId(
    VOID
    );

展开所有宏时,重新格式化为:

__declspec(dllimport) DWORD __stdcall GetCurrentThreadId(void);

现在,使用__declspec(dllimport)__stdcall告诉链接器函数的修饰名称是__imp__GetCurrentThreadId@0。您需要在随SDK提供的导入库中提供该功能。你不能在Delphi中这样做,因为它不接受它。你有多种选择。最明显的是在Delphi代码中实现该功能。但这样做非常棘手,因为这个名字无法形容。你不能给这个名字的Delphi函数。

您可以避开C代码中的Windows头文件,并将其替换为您自己的变体,其中包含您在类型和功能方面所需的内容。并且在不使用__declspec(dllimport)的情况下定义函数。例如:

<强> C

typedef unsigned long DWORD; // taken from the Windows header files

DWORD GetCurrentThreadId(void); // this is implemented in the Delphi code to which you link

DWORD MyGetCurrentThreadId(void)
{
   return GetCurrentThreadId();
}

<强>的Delphi

{$APPTYPE CONSOLE}

uses
  Winapi.Windows;

{$LINK MyGetCurrentThreadId.obj}

function _GetCurrentThreadId: DWORD; cdecl;
begin
  Result := Winapi.Windows.GetCurrentThreadId;
end;

function MyGetCurrentThreadId: DWORD; cdecl; external name '_MyGetCurrentThreadId';

begin
  Writeln(MyGetCurrentThreadId);
  Readln;
end.

这不是很有趣。但我没有看到太多选择。 __stdcall装饰会在函数名称上放置@XX,并且据我所知,由于字符@无法形容,因此无法在Delphi中实现此类函数。< / p>

显然,在您的实际代码中,您将类型和函数声明放入一个头文件中,该文件可用于代替Windows头文件。

您可以通过对目标文件进行后期处理来避免所有这些混乱。我不知道是否存在工具,但您可以处理目标文件以使用__imp__GetCurrentThreadId@0的引用替换对GetCurrentThreadId的引用,那么生活将变得简单。 Delphi链接器将查找该函数名称并在Winapi.Windows中找到它。

在评论中,您已展示了如何使用Agner Fog's objconv tool来完成此操作。它运行如下:

<强> C

#include <Windows.h>

DWORD MyGetCurrentThreadId(void)
{
   return GetCurrentThreadId();
}

编译C代码

cl /c MyGetCurrentThreadId.c

.obj文件的后处理以取消对名称的修改

objconv -nr:__imp__GetCurrentThreadId@0:GetCurrentThreadId MyGetCurrentThreadId.obj MyGetCurrentThreadId_undecorated.obj

<强>的Delphi

{$APPTYPE CONSOLE}

uses
  Winapi.Windows;

{$LINK MyGetCurrentThreadId_undecorated.obj}

const
   _GetCurrentThreadId: function: DWORD; stdcall = Winapi.Windows.GetCurrentThreadId;

function MyGetCurrentThreadId: DWORD; cdecl; external name '_MyGetCurrentThreadId';

begin
  Writeln(MyGetCurrentThreadId);
  Readln;
end.

由于我不知道的原因,我无法说服链接器直接接收Winapi.Windows.GetCurrentThreadId。如果我不适应GetCurrentThreadId而不是_GetCurrentThreadId,并删除const,则程序会编译并链接。但是在运行时抛出访问冲突。无论如何,@ user15124建议的这个技巧提供了一个可管理的解决方法。