【问题标题】:How to free allocated memory in C++ DLL如何在 C++ DLL 中释放分配的内存
【发布时间】:2013-10-08 12:25:14
【问题描述】:

我有以下代码来加密 C++ DLL 中的字符串

EXPORT WCHAR* EncryptString(WCHAR* stringToEncrypt) {
    aes_context ctx;

    WCHAR* in = stringToEncrypt;
    WCHAR* out;
    WCHAR* key = L"TestKey";

    BYTE* buffEnc = (BYTE*)malloc(16);
    BYTE* keyBuffEnc = (BYTE*)malloc(32);

    memset(buffEnc, 0, 16);
    memset(keyBuffEnc, 0, 32);

    memcpy(buffEnc, in, wcslen(in) * 2);
    memcpy(keyBuffEnc, key, wcslen(key) * 2);
    aes_set_key(&ctx, keyBuffEnc, 256);

    aes_encrypt(&ctx, buffEnc, buffEnc);
    out = (WCHAR*)buffEnc;

    // free(buffEnc);   
    // free(keyBuffEnc);

    return out;
}

我的问题是我无法释放分配的内存,否则结果会被破坏。我想知道如何在不丢失结果的情况下释放使用的内存?我要更改返回值的类型吗?

提前感谢您的帮助。 问候亨氏

【问题讨论】:

  • 您的代码示例不是标准 C++(因为 WCHAR 不是标准类型,而是某些系统特定的东西)。您是否考虑过使用std::stringstd::wstring
  • 你应该复制数据,而不是返回一个指针
  • out 正在从 buffEnc 借用内存。最安全的做法是返回一个 std::wstring;在构造中使用 buffEnc。构造 std::wstring 后释放内存,然后返回。
  • 或者你应该定义一个约定,你的EncryptString的调用者需要free它(或者delete它,如果你返回一个指向某个对象的堆指针)。

标签: c++ winapi memory-management dll


【解决方案1】:

这确实是一个有问题的情况 - 您返回一个指向已分配内存的指针,但不清楚谁应该释放内存。您有以下选择:

  1. 告诉调用者使用free()释放内存——这只有在他们使用相同的堆时才有效,这很难保证。这是非常不可靠的,不推荐。
  2. 引入一个内存管理接口(例如在您的库中实现的freeEncrypted() 函数)并告诉调用者使用它 - 然后内存将返回到正确的堆。
  3. 使用众所周知的CoTaskMemAlloc() 进行分配,并告诉调用者使用CoTaskMemFree() 等匹配函数来释放内存。这与第 2 点类似,只是使用了众所周知的通用内存管理器。
  4. 更改接口,使其接受指向已分配数据的指针及其大小,以便调用者分配和释放内存。

【讨论】:

    【解决方案2】:

    在 Windows 上,内存管理器(其中包括跟踪进程中分配和空闲的内存)在 C 运行时库中工作。这意味着如果您有两个模块(例如:您的实际可执行文件和一个 DLL,或两个 DLL)并且您希望允许一个模块分配一些内存而另一个应该拥有它(即负责释放它或传递维护权)基本上有三个选项:

    1. 您让调用者分配一块内存并将指向该内存的指针传递给被调用者。在您的示例中,这将归结为调用者分配一个(希望足够大)缓冲区,然后将指向该缓冲区的指针传递给 EncryptString 函数。这种方法的优点是调用者可以选择只在堆栈上分配一块内存,然后传递一个指向它的指针,类似于

      WCHAR buf[1024];
      EncryptString( "Hello", buf ); // Won't compile, "Hello" is a const string
      

      缺点是调用者必须先计算出合适的缓冲区大小。您可以只强加一个最大限制并记录它,或者您可以公开一个计算所需大小的专用函数,或者您可以说如果 NULL 作为输出缓冲区传递,则该函数返回所需字符的数量。后者通常由 Windows API 使用。

    2. 您让被调用者分配内存(就像您的函数现在所做的那样),例如mallocnew 但随后要求调用者必须使用特殊函数再次释放内存,例如 FreeEncryptedString(char *s)。这个想法是这个释放函数也被你的 DLL 公开,它只是调用适当的释放函数(即freedeletedelete[] 或类似的),以便分配和释放都发生在同一个模块中.

    3. 您确保两个模块动态地链接到同一个 C 运行时库,即进程中只有一个 C 运行时 DLL 副本,并且您的两个模块都使用它。在这种情况下,您可以随意使用mallocfree(或newdelete 等)。这样做的好处是它非常简单,缺点是它意味着您对模块的构建方式提出了要求(如果您在一个加载由其他人编写的插件的程序上工作,这可能是不可能的 - 这些插件可能选择静态链接到 C 运行时)。

    【讨论】:

    • 如果两个模块由不同的人开发和构建,很难保证它们链接到完全相同的 C 运行时。
    • 它被标记为C++,所以你应该使用newdelete,而不是mallocfree
    • @parrowdice:OP 使用了malloc,这就是我坚持使用它的原因。
    • @parrowdice:抱歉,这只是将焦点从真正的问题上转移了。
    • 或者因为这是与 Windows API 相关的,所以可以使用 HeapAlloc/HeapFree。
    猜你喜欢
    • 2021-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    相关资源
    最近更新 更多