编写要从 gcc 应用程序调用的 Delphi/FreePascal DLL

发布于 2024-11-09 11:46:50 字数 959 浏览 0 评论 0原文

我需要将我的 Win32 Delphi 应用程序的一部分可供另一家公司的 Linux gcc 程序使用。

吞吐量和部署要求使得任何类型的远程服务都不适合,因此我正在考虑使用 FreePascal 构建 gcc 应用程序可以调用的 .SO(相当于 DLL 的 Linux)。

我已经很长时间没有使用 C/C++ 了,而且从未在 Linux 上使用过,所以我有点不确定如何最好地构造 DLL/SO 接口以与 gcc 调用者兼容。

这是我的数据结构的表示

TFoo = record
  x, y : double;
  a : smallint;
  b : string;
end;

TBar = record
  a : double;
  b : longint;
  c : string;
end;

TFooBar = record
  foo : array of TFoo;
  bar : array of TBar;
end;

procedure Process(const inFooBar : TFooBar);

要通过 FreePascal 使此 Process 方法在外部可用。那么我需要如何修改这些声明?我正在思考类似

TFoo = record
  x, y : double;
  a : smallint;
  b : PChar;
end;

TBar = record
  a : double;
  b : longint;
  c : PChar;
end;

TFooBar = record
  foo : ^TFoo;
  foo_count : longint;
  bar : ^TBar;
  bar_count : longint;
end;

procedure Process(const inFooBar : TFooBar);

我走在正确的轨道上吗?我不必完全正确,其他公司的程序员很可能会修复我的错误。我只是不想让他们看到我发给他们的内容时笑得太厉害。

I need to make parts of my Win32 Delphi app available to another company's Linux gcc program.

Throughput and deployment requirements make any sort of remote service unsuitable so I'm looking at using FreePascal to build a .SO (Linux equivalent of a DLL) that the gcc app can call.

It's a long time since I used C/C++ and never on Linux so I'm a little unsure as to how best to structure the DLL/SO interface for compatibility with a gcc caller.

Here's a representation of my data structures

TFoo = record
  x, y : double;
  a : smallint;
  b : string;
end;

TBar = record
  a : double;
  b : longint;
  c : string;
end;

TFooBar = record
  foo : array of TFoo;
  bar : array of TBar;
end;

procedure Process(const inFooBar : TFooBar);

To make this Process method externally available via a FreePascal .SO how do I need to modify these declarations? I'm thinking something along the lines of

TFoo = record
  x, y : double;
  a : smallint;
  b : PChar;
end;

TBar = record
  a : double;
  b : longint;
  c : PChar;
end;

TFooBar = record
  foo : ^TFoo;
  foo_count : longint;
  bar : ^TBar;
  bar_count : longint;
end;

procedure Process(const inFooBar : TFooBar);

Am I on the right track? I don't have to get this exactly right, the other company's programmers will very likely fix up my mistakes. I just don't want them to laugh too hard when they see what I've sent them.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

允世 2024-11-16 11:46:50

使用 PAnsiChar 代替 PChar,使用 integer 代替 longint/smallint (因为您使用对齐记录,所以有使用smallint 字段没有任何好处)。

定义一些指针类型,如PFooPBar,这会比^TFoo写得更好,例如

如果你需要访问一些数组,你可以定义TFooArrayTBarArray,如下面的代码所示。

不要忘记任何函数/过程的 cdeclstdcall 调用约定。

type
TFoo = record
  x, y : double;
  a : integer;
  b : PAnsiChar;
end;

TBar = record
  a : double;
  b : integer;
  c : PAnsiChar;
end;

TFooArray = array[0..maxInt div sizeof(TFoo)-1] of TFoo;
TBarArray = array[0..maxInt div sizeof(TBar)-1] of TBar;

PBar = ^TBar;
PFoo = ^TFoo;
PFooArray = ^TFooArray;
PBarArray = ^TBarArray;

TFooBar = record
  foo : PFooArray;
  foo_count : integer;
  bar : PBarArray;
  bar_count : integer;
end;

procedure Process(const inFooBar : TFooBar); cdecl; external 'ProcessGCCLibrary.so';

您的进程将是只读的吗?

如果C部分需要添加一些项目,则必须向外部库提供一些内存重新分配方法,至少映射reallocmem()

请注意,Delphi 动态数组可以轻松映射到 C 兼容结构,如下所示:

type
  TFooDynArray: array of TFoo;
  TBarDynArray: array of TBar;

procedure CallProcess(const aFoo: TFooDynArray; const aBar: TBarDynArray);
var tmp: TFooBar;
begin
  tmp.foo := pointer(aFoo);
  tmp.foo_count := length(aFoo);
  tmp.bar := pointer(aBar);
  tmp.bar_count := length(aBar);
  Process(tmp);
end;

这可以使您的 Delphi 代码更具可读性。看看我们的 TDynArray 包装器 如果您希望使用类似 TList 的方法对此类动态记录数组进行高级访问。

由于 AnsiString 类型映射 PAnsiChar,从二进制的角度来看,您甚至可以这样定义记录:

type
TFoo = record
  x, y : double;
  a : integer;
  b : AnsiString;
end;

TBar = record
  a : double;
  b : integer;
  c : AnsiString;
end;

当映射到 gcc 应用程序时,它将被读取为常规的*char

在这里使用AnsiString,您将不需要处理 Delphi 代码中的内存分配。通过关联的动态数组,它可以使您的 Delphi 代码更易于维护。请注意,我们的 TDynArray 包装器将按预期处理记录中的嵌套 AnsiString,即使对于最高级别的方法(例如二进制序列化或哈希)也是如此。

Use PAnsiChar instead of PChar, and integer instead of longint/smallint (since you use aligned records, there is no benefit of using a smallint field).

Define some pointer types like PFoo and PBar, which will be better written than ^TFoo e.g.

If you need to access some arrays, you could define TFooArray and TBarArray, as in the code below.

Don't forget the cdecl or stdcall calling convention for any function/procedure.

type
TFoo = record
  x, y : double;
  a : integer;
  b : PAnsiChar;
end;

TBar = record
  a : double;
  b : integer;
  c : PAnsiChar;
end;

TFooArray = array[0..maxInt div sizeof(TFoo)-1] of TFoo;
TBarArray = array[0..maxInt div sizeof(TBar)-1] of TBar;

PBar = ^TBar;
PFoo = ^TFoo;
PFooArray = ^TFooArray;
PBarArray = ^TBarArray;

TFooBar = record
  foo : PFooArray;
  foo_count : integer;
  bar : PBarArray;
  bar_count : integer;
end;

procedure Process(const inFooBar : TFooBar); cdecl; external 'ProcessGCCLibrary.so';

Will be your process read-only?

If the C part need to add some items, you'll have to provide some memory reallocation methods to the external library, mapping at least reallocmem().

Note that Delphi dynamic arrays can be mapped easily into C compatible structure, as such:

type
  TFooDynArray: array of TFoo;
  TBarDynArray: array of TBar;

procedure CallProcess(const aFoo: TFooDynArray; const aBar: TBarDynArray);
var tmp: TFooBar;
begin
  tmp.foo := pointer(aFoo);
  tmp.foo_count := length(aFoo);
  tmp.bar := pointer(aBar);
  tmp.bar_count := length(aBar);
  Process(tmp);
end;

This could make your Delphi code much more readable. Take a look at our TDynArray wrapper if you want high-level access to such a dynamic array of records, with TList-like methods.

Since the AnsiString type maps a PAnsiChar, from the binary point of view, you can even define your records as such:

type
TFoo = record
  x, y : double;
  a : integer;
  b : AnsiString;
end;

TBar = record
  a : double;
  b : integer;
  c : AnsiString;
end;

When mapped to the gcc application, it will be read as a regular *char.

Using AnsiString here you won't need to handle memory allocation from your Delphi code. With an associated dynamic array, it could make your Delphi code much easier to maintain. Note that our TDynArray wrapper will handle nested AnsiString in records as expected, even for the highest level methods (e.g. binary serialization or hashing).

情魔剑神 2024-11-16 11:46:50

为了确保记录打包/对齐/填充符合 GCC 的预期,请将 {$packrecords c} 添加到您的 Pascal 源中。请注意,该指令特定于 Free Pascal 编译器,Delphi 不支持它。

To ensure that the record packing/alignment/padding is as GCC expects, add {$packrecords c} to your Pascal source. Note that this directive is specific to the Free Pascal Compiler, Delphi does not support it.

还如梦归 2024-11-16 11:46:50

看起来不错。想法:

  • 您的数组(声明为指针和计数)将完全手动管理 - 这对您来说没问题吗?

  • 你应该改变:

    过程 Process(const inFooBar : TFooBar);
    

    包含 C 兼容的调用约定。我不确定 FreePascal 和 GCC 都支持什么,但是像 cdecl 这样的东西应该可以正常工作。

  • 您还应该(为了安全)指定结构对齐/打包。

  • PChar 应该明确地窄或宽(现在宽是正常的。)

Looks pretty good. Thoughts:

  • Your arrays (declared as a pointer and count) are going to be fully manually managed - that's fine for you?

  • You should change:

    procedure Process(const inFooBar : TFooBar);
    

    to include a C-compatible calling convention. I'm not sure what FreePascal and GCC both support, but something like cdecl should work fine.

  • You should also (for safety) specify the structure alignment / packing.

  • PChar should be explicitly narrow or wide (wide is normal these days.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文