如何链接包含 WinAPI 调用的 C 代码?链接时出现以下错误:
[dcc32错误] Project1.dpr(16): E2065 不满足转发或外部声明: '__imp__GetCurrentThreadId@0'
考虑以下示例。
德尔福:
program Project1;
uses
Windows;
{$L C:\Source.obj}
function Test: DWORD; cdecl; external name '_Test';
begin
WriteLn(Test);
end.
Run Code Online (Sandbox Code Playgroud)
C:
program Project1;
uses
Windows;
{$L C:\Source.obj}
function Test: DWORD; cdecl; external name '_Test';
begin
WriteLn(Test);
end.
Run Code Online (Sandbox Code Playgroud)
发生这种情况是因为 Windows 头文件通常__declspec(dllimport)在声明函数时使用。对于所讨论的函数,其定义为WinBase.h:
WINBASEAPI
DWORD
WINAPI
GetCurrentThreadId(
VOID
);
Run Code Online (Sandbox Code Playgroud)
当您展开所有宏并重新格式化时,将变为:
__declspec(dllimport) DWORD __stdcall GetCurrentThreadId(void);
Run Code Online (Sandbox Code Playgroud)
现在,使用__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();
}
Run Code Online (Sandbox Code Playgroud)
德尔福
{$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.
Run Code Online (Sandbox Code Playgroud)
这可不是什么好玩的事。但我没有看到太多的选择。装饰__stdcall将在您的函数名称上放置@XX足够的内容,并且据我所知,您无法在 Delphi 中实现此类函数,因为难以形容的字符@。
显然,在实际代码中,您会将类型和函数声明放入可用于代替 Windows 头文件的头文件中。
您可以通过对目标文件进行后处理来避免所有这些混乱。我不知道是否存在工具,但是您可以处理对象文件以将引用替换为__imp__GetCurrentThreadId@0引用,GetCurrentThreadId然后生活就会很简单。Delphi 链接器将查找该函数名称并在Winapi.Windows.
在评论中,您已经展示了如何使用Agner Fog 的 objconv 工具来完成此操作。它运行起来是这样的:
C
#include <Windows.h>
DWORD MyGetCurrentThreadId(void)
{
return GetCurrentThreadId();
}
Run Code Online (Sandbox Code Playgroud)
C代码的编译
cl /c MyGetCurrentThreadId.c
对 .obj 文件进行后处理以取消修饰名称
objconv -nr:__imp__GetCurrentThreadId@0:GetCurrentThreadId MyGetCurrentThreadId.obj MyGetCurrentThreadId_undecorated.obj
德尔福
cl /c MyGetCurrentThreadId.c
由于我不知道的原因,我无法说服链接器直接接听Winapi.Windows.GetCurrentThreadId。如果我取消装饰而GetCurrentThreadId不是_GetCurrentThreadId,并删除const,则程序将编译并链接。但在运行时会引发访问冲突。无论如何,@user15124 建议的这个技巧提供了一个可管理的解决方法。
| 归档时间: |
|
| 查看次数: |
544 次 |
| 最近记录: |