如何link包含WinAPI的C代码?
How to link C code that contains WinAPI?
如何 link 包含调用 WinAPI 的 C 代码? linking 时出现以下错误:
[dcc32 Error] Project1.dpr(16): E2065 Unsatisfied forward or external declaration: '__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();
}
这是因为 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 中实现这样的函数,因为 [=24] 是不可描述的字符=].
显然,在您的实际代码中,您会将类型和函数声明放入一个头文件中,该头文件可用于代替 Windows 头文件。
您可以通过 post 处理目标文件来避免所有这些混乱。我不知道是否存在工具,但您可以处理目标文件以将对 __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
Post-处理 .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 建议的这个技巧提供了一个易于管理的解决方法。
如何 link 包含调用 WinAPI 的 C 代码? linking 时出现以下错误:
[dcc32 Error] Project1.dpr(16): E2065 Unsatisfied forward or external declaration: '__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();
}
这是因为 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 中实现这样的函数,因为 [=24] 是不可描述的字符=].
显然,在您的实际代码中,您会将类型和函数声明放入一个头文件中,该头文件可用于代替 Windows 头文件。
您可以通过 post 处理目标文件来避免所有这些混乱。我不知道是否存在工具,但您可以处理目标文件以将对 __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
Post-处理 .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 建议的这个技巧提供了一个易于管理的解决方法。