如何链接包含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();
}
答案 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建议的这个技巧提供了一个可管理的解决方法。