发生这种情况是因为 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 头文件。
您可以通过对目标文件进行后处理来避免所有这些混乱。我不知道是否存在工具,但是您可以处理目标文件以将对__imp__GetCurrentThreadId@0 的引用替换为对GetCurrentThreadId 的引用,那么生活会很简单。 Delphi 链接器将查找该函数名并在Winapi.Windows 中找到它。
在 cmets 中,您已经展示了如何使用 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,并删除const,则程序编译并链接。但在运行时引发访问冲突。无论如何,@user15124 建议的这个技巧提供了一种易于管理的解决方法。