2017-02-14 18 views
0
/* 

Насколько я понимаю, процесс Windows, который загружает одну или несколько библиотек динамических ссылок (DLL) в его адресное пространство, будет разделять это адресное пространство со всеми загруженными DLL - это означает, что эти DLL могут читать и записывать в любую память в адресном пространстве процессов. Однако, когда объекты распределены в куче, каждый модуль (будь то .exe или одна из загруженных DLL процессов) выделяет из своей собственной кучи. По этой причине крайне важно освободить память от той же кучи, которая выполняла выделение.Проблема очистки кучи выделенных ресурсов через границу модуля DLL Windows

Это все имеет смысл для меня, и я подумал, что могу использовать std::unique_ptr, чтобы помочь организовать все. Это подход, который я использовал. (Я не имею мой компилятор под рукой в ​​данный момент, но я думаю, что эти фрагменты кода/псевдо-код будет достаточно ясно выразить свои намерения:

*/ 

Library.h

class ILibrary 
{ 
    public: 
    virtual void DoStuff() = 0; 
}; 

struct Deleter 
{ 
    void operator()(ILibrary *p) 
    { 
    delete p; 
    } 
}; 

typedef std::unique_ptr<ILibrary, Deleter> Ptr; 

//*MyLibrary.dll* 
//Includes Library.h 
//Exports: 

void GetMyLibrary(Ptr & library) 
{ 
    library = Ptr(new MyLibrary); // point (1) 
} 

//**Program.exe** 
//Includes Library.h 
//Imports MyLibrary.dll (GetMyLibrary export) 

int main() 
{ 
    Ptr local; 
    MyLibrary->GetMyLibrary(local); 
    local->DoStuff(); 
} // heap corruption on cleanup 

Вы может видеть, что и моя библиотека, и основная программа используют один и тот же заголовок Library.h. Я создаю переменную local, чтобы удерживать указатель на мою библиотеку. Метод GetMyLibrary (вызывается в DLL) выделяет и назначает new unique_ptr ссылке, Я использовал назначение в «точке 1», потому что я хочу, чтобы Deleter от cont ext DLL, который будет использоваться для очистки, а не Deleter, который был первоначально назначен переменной local в моей основной программе. Значение, когда локальное выходит из области видимости, я хочу, чтобы его очистка вызывала Deleter DLL, а не тот, который был первоначально назначен ему (т.е. я использовал library = Ptr(new MyLibrary) вместо library.reset(new MyLibrary), потому что я хочу, чтобы Deleter вызывался из контекста DLL)

В любом случае программа работает, за исключением того, что во время очистки, когда локальный unique_ptr уничтожает, я получаю исключение кучи (в отладке), которое заставляет меня поверить, что я удаляю неправильную кучу (то есть, unique_ptr не ведет себя так, как я ожидал)

В конечном счете, я решил решить проблему другим способом, который кажется намного более чистым, но мне просто было любопытно, почему подход выше был неудачным?

+1

«** ** куча» - это большая часть вашей проблемы. Что касается Windows, ** ** куча возвращается ['GetProcessHeap'] (https://msdn.microsoft.com/en-us/library/windows/desktop/aa366569 (v = vs.85) .aspx), и что куча _is_ делится между EXE и DLL (отсюда и название «куча процесса»). – MSalters

+0

Спасибо за ссылку, я прочитаю об этом. – charunnera

ответ

1

Ваш дед является частью unique_ptr и вызывается из main, когда указатель выходит из сферы действия.

Вы должны либо предоставить GetMyLibrary()/FreeMyLibrary() в DLL и обрабатывать выделение памяти/открепление там (с использованием RAII на стороне приложения), или передать аллокатор в GetMyLibrary() и сделать распределение памяти и ответственность открепления приложения.

+0

Спасибо за ваш ответ ... Я все еще немного смущен. Я получаю то, что вы говорите об использовании экспорта Get/Free ... вот как я обычно делал это в прошлом, но я не совсем понимаю, почему мой подход выше разрушается. Когда я вызываю экспорт GetMyLibrary, выполняется код в DLL для создания экземпляра unique_ptr и нового экземпляра библиотеки. У этого unique_ptr есть пользовательский Deleter (который находится в DLL-коде), чтобы освободить соответствующий объект.Поэтому, когда я копирую unique_ptr (через ref) из DLL в main_ уникальной_ptr, я ожидаю, что Deleter в DLL будет вызван – charunnera

+0

(Аналогично вызову функции FreeMyLibrary(), которую вы упомянули) Что мне не хватает? – charunnera

+1

Тип 'Deleter' здесь является частью типа' Ptr'. 'unique_ptr', я полагаю, создает экземпляр этого объекта (типа' Deleter') либо по построению, либо для первого вызова 'get_deleter()', оба из которых происходят в вашем 'main()'. Вы могли бы использовать простую функцию как deleter, но не было бы лучшей идеей использовать 'unique_ptr' на границе dll/app. Представьте, что dll был скомпилирован с использованием другого компилятора или стандартной библиотеки или даже других версий. – w1ck3dg0ph3r