Это происходит потому, что заголовочные файлы 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 такое имя.
Вы можете отказаться от заголовочных файлов Windows в своем коде C и заменить их своими вариантами, которые включают в себя только то, что вам нужно в плане типов и функций. И определите функции без использования __declspec(dllimport)
. Например:
С
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
.
В комментариях вы показали, как использовать инструмент objconv от Agner Fog, чтобы сделать именно это. Это работает следующим образом:
С
#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, обеспечивает управляемый обходной путь.
person
David Heffernan
schedule
22.10.2015