C 运行时对象、dll 边界
为 dll 设计 C API 的最佳方法是什么,它处理传递依赖于 C 运行时的“对象”的问题(FILE*、malloc 返回的指针等)。 例如,如果两个 dll 与不同版本的运行时链接,我的理解是您无法将 FILE* 从一个 dll 安全地传递到另一个 dll。
是使用依赖于 Windows 的 API(保证跨 dll 工作)的唯一解决方案吗? C API 已经存在并且很成熟,但大部分是从 unix POV 设计的(当然,仍然必须在 unix 上工作)。
What is the best way to design a C API for dlls which deals with the problem of passing "objects" which are C runtime dependent (FILE*, pointer returned by malloc, etc...). For example, if two dlls are linked with a different version of the runtime, my understanding is that you cannot pass a FILE* from one dll to the other safely.
Is the only solution to use windows-dependent API (which are guaranteed to work across dlls) ? The C API already exists and is mature, but was designed from a unix POV, mostly (and still has to work on unix, of course).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您要求的是 C 解决方案,而不是 C++ 解决方案。
在 C 中执行此类操作的常用方法是:
将模块 API 设计为不需要 CRT 对象。 获取以原始 C 类型传递的内容 - 即让消费者加载文件并简单地向您传递指针。 或者,让使用者传递一个完全限定的文件名,即在内部打开、读取和关闭。
我想到了其他 C 模块、MS Cabinet SD 和 OpenSSL 库 iirc 的部分使用的方法,让使用应用程序将指向函数的指针传递给初始化函数。 因此,您传递 FILE* 的任何 API 都会在初始化期间的某个时刻获取一个指向结构体的指针,该结构体的函数指针与 fread、fopen 等的签名相匹配。在处理外部 FILE* 时,dll 始终使用传入的函数而不是 CRT 函数。
通过一些像这样的简单技巧,您可以使 C DLL 接口完全独立于主机 CRT,或者实际上要求主机完全用 C 或 C++ 编写。
You asked for a C, not a C++ solution.
The usual method(s) for doing this kind of thing in C are:
Design the modules API to simply not require CRT objects. Get stuff passed accross in raw C types - i.e. get the consumer to load the file and simply pass you the pointer. Or, get the consumer to pass a fully qualified file name, that is opened , read, and closed, internally.
An approach used by other c modules, the MS cabinet SD and parts of the OpenSSL library iirc come to mind, get the consuming application to pass in pointers to functions to the initialization function. So, any API you pass a FILE* to would at some point during initialization have taken a pointer to a struct with function pointers matching the signatures of fread, fopen etc. When dealing with the external FILE*s the dll always uses the passed in functions rather than the CRT functions.
With some simple tricks like this you can make your C DLLs interface entirely independent of the hosts CRT - or in fact require the host to be written in C or C++ at all.
现有答案都不正确:在 Windows 上给出以下情况:您有两个 DLL,每个 DLL 都与两个不同版本的 C/C++ 标准库静态链接。
在这种情况下,不应将指向一个 DLL 中 C/C++ 标准库创建的结构的指针传递给另一个 DLL。 原因是这些结构在两个 C/C++ 标准库实现之间可能不同。
您不应该做的另一件事是从一个 DLL 中释放由 new 或 malloc 分配的指针,而该指针是在另一个 DLL 中分配的。 堆管理器也可以以不同的方式实现。
注意,您可以使用 DLL 之间的指针 - 它们只是指向内存。 问题在于免费。
现在,您可能会发现这可行,但如果确实如此,那么您只是运气好。 这很可能会在将来给您带来问题。
解决您的问题的一种可能的解决方案是动态链接到 CRT< /a>. 例如,您可以动态链接到 MSVCRT.DLL。 这样您的 DLL 将始终使用相同的 CRT。
请注意,我建议在 DLL 之间传递 CRT 数据结构不是最佳实践。 您可能想看看是否可以更好地考虑因素。
请注意,我不是 Linux/Unix 专家 - 但您在这些操作系统上也会遇到相同的问题。
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.
不同运行时的问题无法解决,因为 FILE* 结构属于
到 Windows 系统上的一个运行时。
但如果你编写一个小的包装接口,你就完成了,这并没有什么坏处。
这可以保存在各处的 dll 边界上传递,并且不会造成真正的伤害。
PS:如果您开始跨 dll 边界抛出异常,请务必小心。 如果您在 Windows 操作系统上满足某些设计要求,那么这将很好地工作,但在其他一些操作系统上会失败。
The problem with the different runtimes isn't solvable because the FILE* struct belongs
to one runtime on a windows system.
But if you write a small wrapper Interface your done and it does not really hurt.
This is save to be passed accross dll boundaries everywhere and does not really hurt.
P.S.: Be careful if you start throwing exceptions across dll boundaries. This will work quiet well if you fulfill some design creterions on windows OS but will fail on some others.
如果 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 函数。 这可以使用几个技巧来完成,这值得另一个问题......
If the C API exists and is mature, bypassing the CRT internally by using pure Win32 API stuff gets you half the way. The other half is making sure the DLL's user uses the corresponding Win32 API functions. This will make your API less portable, in both use and documentation. Also, even if you go this way with memory allocation, where both the CRT functions and the Win32 ones deal with void*, you're still in trouble with the file stuff - Win32 API uses handles, and knows nothing about the FILE structure.
I'm not quite sure what are the limitations of the FILE*, but I assume the problem is the same as with CRT allocations across modules. MSVCRT uses Win32 internally to handle the file operations, and the underlying file handle can be used from every module within the same process. What might not work is closing a file that was opened by another module, which involves freeing the FILE structure on a possibly different CRT.
What I would do, if changing the API is still an option, is export cleanup functions for any possible "object" created within the DLL. These cleanup functions will handle the disposal of the given object in the way that corresponds to the way it was created within that DLL. This will also make the DLL absolutely portable in terms of usage. The only worry you'll have then is making sure the DLL's user does indeed use your cleanup functions rather than the regular CRT ones. This can be done using several tricks, which deserve another question...