2013-10-02 2 views
2

я получил следующий код, чтобы зашифровать строку в C++ DLLКак освободить выделенную память в 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; 
} 

Моя проблема заключается в том, что я не могу освободить выделенную память, так как в противном случае результат нарушается. Интересно, как я могу освободить использованную память, не потеряв результат? У меня изменить тип возвращаемого значения?

Заранее за вашу помощь. Greets Heinz

+0

Ваш образец кода не является стандартным C++ (поскольку 'WCHAR' не является стандартным типом, но имеет определенную системную специфику). Считаете ли вы использование 'std :: string' или' std :: wstring'? –

+1

Вы должны дублировать данные, вместо того чтобы возвращать указатель – accfews

+0

'out' заимствует память из buffEnc. Самой безопасной задачей было бы вернуть std :: wstring; используя buffEnc в конструкции. Освободите память, когда std :: wstring будет построена, а затем вернется. – Bathsheba

ответ

8

Это действительно проблематичная ситуация - вы возвращаете указатель на выделенную память, и неясно, кто должен освобождать память. У вас есть следующие варианты:

  1. вызывающему абоненту освободить память с помощью free() - это будет работать, только если они используют ту же кучу, которую трудно гарантировать. Это очень ненадежно и не рекомендуется.
  2. введите интерфейс управления памятью (например, freeEncrypted(), который реализован в вашей библиотеке) и сообщите, что вызывающий абонент использует его - тогда память будет возвращена в правую кучу.
  3. используйте что-то хорошо известное как CoTaskMemAlloc() для выделения и сообщите вызывающему абоненту, чтобы использовать функцию соответствия, такую ​​как CoTaskMemFree() для освобождения памяти. Это похоже на пункт 2, просто использует хорошо известный общий менеджер памяти.
  4. изменить интерфейс таким образом, чтобы он принимал указатель на уже распределенные данные и его размер, чтобы вызывающий и выделял и освобождал память.
+0

+1 за то, что он гораздо более лаконичен, чем мои блуждания: -} –

6

В Windows менеджер памяти (который, среди прочего, отслеживает выделенную и свободную память в вашем процессе) работает в библиотеке времени выполнения C. Это означает, что если у вас есть два модуля (скажем: ваш фактический исполняемый файл и DLL или две библиотеки DLL), и вы хотите разрешить одному модулю выделять некоторую память, а другой должен владеть им (т.е. отвечать за освобождение или передачу maintainership на) Автошоу в основном три варианта:

  1. Вы позволяете звонящему выделить кусок памяти и передать указатель на что вызываемый. В вашем примере это сводится к тому, что вызывающий абонент выделяет (и, надеюсь, достаточно большой) буфер, а затем передает указатель на функцию EncryptString. Преимущество этого подхода состоит в том, что абонент может выбрать просто выделить часть памяти на стеке, а затем передать указатель на что-то вроде

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

    Недостаток заключается в том, что абонент должен выяснить соответствующий размер буфера сначала. Вы можете просто наложить максимальный лимит и документ, или вы можете открыть выделенную функцию, которая вычисляет требуемый размер, или вы могли бы сказать, что если NULL передается в качестве выходного буфера, функция возвращает количество обязательных символов. Последнее обычно используется Windows API.

  2. Вы позволяете вызываемому выделить память (как ваша функция делает прямо сейчас), используя, например, malloc или new, но затем укажите, что вызывающий абонент должен использовать специальную функцию для освобождения памяти снова, что-то вроде FreeEncryptedString(char *s). Идея состоит в том, что эта функция освобождения также раскрывается вашей DLL, и она просто вызывает соответствующую функцию деаллокации (то есть free или delete или delete[] или тому подобное), так что распределение и освобождение происходит в одном модуле.

  3. Вы убедитесь, что оба модуля ссылаются на то же C библиотеки времени выполнения динамически, т.е. есть только одна копия C во время выполнения DLL в процессе, и оба ваших модулей использовать его. В этом случае вы можете просто использовать malloc и free (или new и delete и т. Д.), Как вы хотите. Преимущество этого в том, что это очень просто, недостатком является то, что это означает, что вы накладываете требования о том, как строятся ваши модули (что может быть невозможно, если вы работаете над программой, которая загружает плагины, написанные другими людьми или около того - эти плагины могут решил связать себя со временем старта C).

+0

Очень сложно гарантировать, что два модуля связаны с одной и той же средой C, если они разработаны и созданы разными людьми. – sharptooth

+0

Он помечен как C++, поэтому вы должны использовать 'new' и' delete', а не 'malloc' и' free'. – parrowdice

+0

@parrowdice: OP использовал 'malloc', поэтому я придерживался этого. –

Смежные вопросы