6

为 dll 设计 C API 的最佳方法是处理传递依赖于 C 运行时的“对象”(文件 *、malloc 返回的指针等)的问题。例如,如果两个 dll 与不同版本的运行时链接,我的理解是您无法将 FILE* 从一个 dll 安全地传递给另一个。

是使用依赖于 Windows 的 API(保证跨 dll 工作)的唯一解决方案吗?C API 已经存在并且很成熟,但主要是从 unix POV 设计的(当然,仍然必须在 unix 上工作)。

4

4 回答 4

2

您要求的是 C,而不是 C++ 解决方案。

在 C 中做这种事情的常用方法是:

  • 将模块 API 设计为不需要 CRT 对象。获取以原始 C 类型传递的东西——即让消费者加载文件并简单地将指针传递给您。或者,让消费者传递一个完全限定的文件名,即在内部打开、读取和关闭。

  • 想到其他 c 模块、MS cabinet SD 和部分 OpenSSL 库 iirc 使用的方法,让消费应用程序将指向函数的指针传递给初始化函数。因此,在初始化期间的某个时刻,您将 FILE* 传递给的任何 API 都会获取一个指向具有与 fread、fopen 等签名匹配的函数指针的结构的指针。在处理外部 FILE*s 时,dll 始终使用传入的函数而不是 CRT 函数。

通过像这样的一些简单技巧,您可以使您的 C DLL 接口完全独立于主机 CRT - 或者实际上需要使用 C 或 C++ 编写主机。

于 2009-06-29T07:39:05.203 回答
1

Neither existing answer is correct: Given the following on Windows: you have two DLLs, each is statically linked with two different versions of the C/C++ standard libraries.

In this case, you should not pass pointers to structures created by the C/C++ standard library in one DLL to the other. The reason is that these structures may be different between the two C/C++ standard library implementations.

The other thing you should not do is free a pointer allocated by new or malloc from one DLL that was allocated in the other. The heap manger may be differently implemented as well.

Note, you can use the pointers between the DLLs - they just point to memory. It is the free that is the issue.

Now, you may find that this works, but if it does, then you are just luck. This is likely to cause you problems in the future.

One potential solution to your problem is dynamically linking to the CRT. For example,you could dynamically link to MSVCRT.DLL. That way your DLL's will always use the same CRT.

Note, I suggest that it is not a best practice to pass CRT data structures between DLLs. You might want to see if you can factor things better.

Note, I am not a Linux/Unix expert - but you will have the same issues on those OSes as well.

于 2009-06-27T16:20:19.940 回答
0

不同运行时的问题无法解决,因为 FILE* 结构属于 Windows 系统上的一个运行时。

但是如果你写一个小的包装接口你就完成了,它并没有真正的伤害。

stdcall IFile* IFileFactory(const char* filename, const char* mode);

class IFile {

  virtual fwrite(...) = 0;
  virtual fread(...) = 0;

  virtual delete() = 0; 
}

这可以保存在任何地方通过 dll 边界传递,并且不会真正受到伤害。

PS:如果开始跨 dll 边界抛出异常,请小心。如果您在 Windows 操作系统上完成一些设计标准,但在其他一些设计标准上会失败,这将很好地工作。

于 2009-06-27T10:22:19.170 回答
0

如果 C API 存在并且已经成熟,那么通过使用纯 Win32 API 的东西在内部绕过 CRT 就可以成功。另一半是确保 DLL 的用户使用相应的 Win32 API 函数。这将使您的 API 在使用和文档方面的可移植性降低。此外,即使您采用这种内存分配方式,CRT 函数和 Win32 函数都处理 void*,您仍然遇到文件问题 - Win32 API 使用句柄,并且对 FILE 结构一无所知。

我不太确定 FILE* 的限制是什么,但我认为问题与跨模块的 CRT 分配相同。MSVCRT 在内部使用 Win32 来处理文件操作,并且可以从同一进程中的每个模块使用底层文件句柄。可能不起作用的是关闭由另一个模块打开的文件,这涉及在可能不同的 CRT 上释放 FILE 结构。

如果仍然可以选择更改 API,我会做的是为 DLL 中创建的任何可能的“对象”导出清理函数。这些清理函数将以与在该 DLL 中创建对象的方式相对应的方式处理给定对象的处置。这也将使 DLL 在使用方面绝对可移植。您唯一需要担心的是确保 DLL 的用户确实使用了您的清理函数,而不是常规的 CRT 函数。这可以使用几个技巧来完成,这值得另一个问题......

于 2009-06-27T16:58:02.730 回答