【问题标题】:How to safely return objects from DLL calls如何从 DLL 调用中安全地返回对象
【发布时间】:2020-10-23 03:54:18
【问题描述】:

我对 C++ 和使用 DLL 相当陌生。我有一个主要应用程序,它汇总了不同测量的结果。由于测量值因情况而异,我决定将它们放入外部 DLL 中,以便它们可以在运行时加载(它们都只是导出相同的函数)。我们的想法是像这样加载它们,以便可以根据运行时需要扩展聚合器:

typedef int (*measure)(measurement &dataHolder);

int callM() {
  [...]
  measurement dataHolder;
  lib = LoadLibraryA("measureDeviceTypeA.dll");
  measure measureFunc = (measure)GetProcAddress(lib, "measureFunc");
  pluginFunc(dataHolder);
  [...] // close the lib and load the next one depending on found Devices
}

这对于简单的数据类型(取决于结构“测量”的实际定义)非常有效,例如:

typedef struct measurement {
    DWORD realPBS;
    DWORD imaginaryPBS;
    int a;
} measurement;

现在也可能有任意长度的字符串(结果的字符表示)。我也想将它们放入测量结构中,并将它们填充到 DLL 中的实际工作函数中。我的第一个假设是只使用 std::string 会很容易,它有时有效,有时无效(因为它会在 std::string().append() 上重新分配内存,这可能会中断(访问冲突),具体取决于程序的实际运行时环境和dll)。我读到 herehere 说从函数返回字符串是个坏主意。

那么从这样的调用中返回任意长度字符串的“正确”C++ 方式是什么?将结构传递给 DLL 是否有帮助,还是应该将其拆分为单独的调用?当我再次关闭 DLL 时,我不想让指针悬空或释放内存。

【问题讨论】:

  • C++ 不支持任何平台上的不兼容运行时。如果是这种情况,您应该避免在外部接口中使用任何 C++ 对象。
  • 问题是,字符串数据通常分配在堆上,所以必须以某种方式释放/管理。

标签: c++ dll dllimport


【解决方案1】:

问题是,字符串数据通常分配在堆上,因此必须以某种方式释放/管理它。 你可能会想,嘿 std::string 是按值返回的——所以为什么我需要关心内存管理。问题是通常只有非常小的字符串存储在类的“内部”。对于较大的字符串,字符串类包含指向某个“堆存储”的指针。

Dll 可以通过不同的编程语言使用 - 这就是 dll 不共享“内存管理器”的原因,在 dll 中释放会失败。

要解决这个问题,您需要有两个函数调用,一个返回指向数据的指针/句柄,另一个释放它。或者调用者可以给被调用者一些它想要存储数据的指针。你也需要一个最大字节数。

如您所见,您应该避免使用这些 API 有一些原因 - 但并非总是可行。例如,请参阅 Windows API(您可以在其中找到这两种方法)。

另一种方法是确保共享内存管理器,但这有点棘手,因为它必须尽早完成!

【讨论】:

    【解决方案2】:

    这不适用于std::string,正如 Dani 在 cmets 中所指出的那样。问题是std::string是属于你实现的类型,不同的C++实现有不同的std::strings。

    对于 DLL(微软),您确实有另一种选择。 COM 是一项古老的技术,但它今天仍然有效,而且不太可能永远消失。它有自己的字符串类型BSTR。 Visual Studio 为您自己的代码提供了一个辅助 C++ 类 bstr_t,但在界面上您将使用来自 _bstr_t::GetBSTR 的普通 BSTR

    BSTR 依赖于来自 OleAut32.dll 的 Windows 分配器 SysAllocString

    【讨论】:

      猜你喜欢
      • 2019-08-22
      • 2020-08-14
      • 1970-01-01
      • 2014-01-05
      • 2011-02-06
      • 1970-01-01
      • 2015-10-26
      • 2014-05-12
      • 1970-01-01
      相关资源
      最近更新 更多