C运行时对象,dll边界

时间:2009-06-27 09:28:50

标签: windows unix dll abi

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

使用依赖于Windows的API的唯一解决方案(保证可以在dll中使用)? C API已经存在且已经成熟,但是主要是使用unix POV设计的(当然还是必须使用unix)。

4 个答案:

答案 0 :(得分:2)

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

在C中执行此类操作的常用方法是:

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

  • 其他c模块使用的方法,MS机柜SD和OpenSSL库的部件iirc浮现在脑海中,让消费应用程序将函数指针传递给初始化函数。所以,你在初始化过程中传递FILE *的任何API都会指向一个结构,其中的函数指针与fread,fopen等的签名相匹配。当处理外部FILE *时,dll总是使用传入的功能而不是CRT功能。

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

答案 1 :(得分:1)

现有答案都不正确:在Windows上给出以下内容:您有两个DLL,每个DLL与两个不同版本的C / C ++标准库静态链接。

在这种情况下,您不应将指针传递给由一个DLL中的C / C ++标准库创建的结构到另一个。原因是这些结构可能在两个C / C ++标准库实现之间是不同的。

你不应该做的另一件事是从一个在另一个DLL中分配的DLL中释放由new或malloc分配的指针。堆管理器也可以不同地实现。

注意,您可以使用DLL之间的指针 - 它们只指向内存。这是免费的问题。

现在,您可能会发现这有效,但如果有,那么您就是运气。这很可能会在将来给你带来麻烦。

您问题的一个可能解决方案是动态链接到CRT。例如,您可以动态链接到MSVCRT.DLL。这样你的DLL将始终使用相同的CRT。

注意,我建议在DLL之间传递CRT数据结构不是最佳做法。您可能想看看是否可以更好地考虑因素。

注意,我不是Linux / Unix专家 - 但你也会在这些操作系统上遇到同样的问题。

答案 2 :(得分:0)

不同运行时的问题是不可解决的,因为FILE * struct属于 到Windows系统上的一个运行时。

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

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

class IFile {

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

  virtual delete() = 0; 
}

这可以保存到各地的dll边界,并没有真正受到伤害。

P.S。:如果你开始在dll边界上抛出异常,请小心。如果你在Windows操作系统上完成一些设计方案,但在其他方面会失败,那么这将很安静。

答案 3 :(得分: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功能。这可以使用几个技巧来完成,值得另一个问题......