当前位置 : 主页 > 编程语言 > delphi >

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

来源:互联网 收集:自由互联 发布时间:2021-06-23
如何链接包含WinAPI调用的C代码?链接时我收到以下错误: [dcc32 Error] Project1.dpr(16): E2065 Unsatisfied forward or external declaration: ‘__imp__GetCurrentThreadId@0’ 请考虑以下示例. 德尔福: program Pr
如何链接包含WinAPI调用的C代码?链接时我收到以下错误:

[dcc32 Error] Project1.dpr(16): E2065 Unsatisfied forward or external declaration: ‘__imp__GetCurrentThreadId@0’

请考虑以下示例.

德尔福:

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();
}
发生这种情况是因为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();
}

德尔福

{$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中实现这样的函数,因为它具有难以理解的字符@.

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

您可以通过后处理目标文件来避免所有这些混乱.我不知道是否存在工具,但您可以处理目标文件以使用对GetCurrentThreadId的引用替换对__imp__GetCurrentThreadId @ 0的引用,那么生活将变得简单. 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

德尔福

{$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,并删除co​​nst,那么程序将编译并链接.但是在运行时抛出访问冲突.无论如何,@ user15124建议的这个技巧提供了一个可管理的解决方法.

网友评论