Reputation: 299
How can I link C code which contains calls to WinAPI? While linking I get the following error:
[dcc32 Error] Project1.dpr(16): E2065 Unsatisfied forward or external declaration: '__imp__GetCurrentThreadId@0'
Consider the following example.
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();
}
Upvotes: 4
Views: 577
Reputation: 613511
This is happening because the Windows header files typically use __declspec(dllimport)
when they declare functions. For the function in question, its definition in WinBase.h
is:
WINBASEAPI
DWORD
WINAPI
GetCurrentThreadId(
VOID
);
When you expand all macros, and re-format that becomes:
__declspec(dllimport) DWORD __stdcall GetCurrentThreadId(void);
Now, the use of __declspec(dllimport)
, and __stdcall
, tells the linker that the decorated name of the function is __imp__GetCurrentThreadId@0
. You are expected to provide that function in the import library provided with the SDK. You cannot do that in Delphi because it does not accept it. You have a variety of options. The most obvious is to implement the function in the Delphi code. But that is pretty tricky to do because the name is unspeakable. You cannot give a Delphi function that name.
You could shun the Windows header files in your C code and replace them with your own variants that include just what you need in way of types and functions. And define the functions without using __declspec(dllimport)
. For example:
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.
This is not much fun. But I don't see much alternative. The __stdcall
decoration is going to place @XX
suffices on your function names, and to the very best of my knowledge you cannot implement such functions in Delphi, because of the unspeakable character @
.
Obviously in your real code you'd place the type and function declarations into a header file that could be used in place of the Windows header files.
You could avoid all of this mess by post-processing the object files. I don't know if a tool exists, but you could process the object file to replace references to __imp__GetCurrentThreadId@0
with references to GetCurrentThreadId
then life would be simple. The Delphi linker would look for that function name and find it in Winapi.Windows
.
In the comments you have shown how to use Agner Fog's objconv tool to do exactly this. It runs like this:
C
#include <Windows.h>
DWORD MyGetCurrentThreadId(void)
{
return GetCurrentThreadId();
}
Compilation of C code
cl /c MyGetCurrentThreadId.c
Post-processing of .obj file to undecorate names
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.
For reasons unknown to me, I cannot persuade the linker to pick up Winapi.Windows.GetCurrentThreadId
directly. If I undecorate to GetCurrentThreadId
rather than _GetCurrentThreadId
, and remove the const
, then the program compiles and links. But throws an access violation at runtime. Anyway, this trick as suggested by @user15124 provides a manageable workaround.
Upvotes: 5