Почему я получаю повреждение кучи при изменении размера вектора внутри dll?

Я пишу XLL (используя библиотеку XLW), которая вызывает функцию DLL. Эта функция DLL получит ссылку на вектор, изменит вектор и вернет его по аргументу.

У меня есть решение VS10 с несколькими проектами C++, некоторыми библиотеками DLL и XLL, которые будут вызывать функции DLL из Excel. Я скомпилировал все с помощью компилятора VS10 с _HAS_ITERATOR_DEBUGGING=0 и _CRT_SECURE_NO_WARNINGS и использовал одну и ту же библиотеку времени выполнения (/MDd) для всех проектов.

Мне также пришлось пересобрать библиотеку XLW, чтобы она соответствовала _HAS_ITERATOR_DEBUGGING=0, которую я должен использовать в своих проектах.

При вызове xll_function я получал ошибки Heap Corruption и не мог понять, почему. После того, как я попытался изменить размер моего вектора перед вызовом функции dll, ошибка исчезла. То есть я могу вызвать функцию и получить правильный вектор, возвращаемый аргументом, без повреждений кучи.

Может ли кто-нибудь пролить свет на это? Поскольку я новичок в использовании DLL, я не уверен, должно ли это происходить или я делаю что-то не так.

Как вы можете видеть в приведенном ниже коде, функция dll попытается изменить размер forwards, и это то, что, как я думаю, генерирует ошибки кучи. Я пытаюсь понять, почему это происходит и как это изменение размера и распределения работает для dll. Возможно, я не могу изменить размер вектора, выделенного в другой куче.

** Код ниже — первая функция является статическим методом в классе из проекта dll, а вторая функция экспортируется в XLL.

void dll_function(double quote, const std::vector<double>& drift, const std::vector<double>& divs, std::vector<double>& forwards)
{
    size_t size = drift.size();
    forwards.resize(size);

    for( size_t t = 0; t < size; t++)
    {
        forwards[t] = (quote - divs[t]) * drift[t];
    }
}


MyArray xll_function(double quote, const MyArray& drift, const MyArray& divs)
{
    // Resizing the vector before passing to function
    std::vector<double> forwards(drift.size());

    dll_function(quote, drift, divs, forwards);

    return forwards;
}

person holox    schedule 20.06.2019    source источник
comment
Может ли кто-нибудь пролить свет на это? Вы смешиваете компиляторы между dll и приложением? Только версии VS 2015 года или выше совместимы друг с другом. Двоичная совместимость не существует между более старыми версиями или любым другим компилятором.   -  person drescherjm    schedule 20.06.2019
comment
Я отредактировал свой вопрос, спасибо за ответы. Я действительно пытаюсь понять, является ли это какой-то ошибкой или это ожидаемое поведение от dll.   -  person holox    schedule 21.06.2019


Ответы (1)


Чтобы передать ссылки на std::vector или другие коллекции C++ через границы DLL, вам нужно сделать следующее.

  1. Используйте один и тот же компилятор C++ для обоих модулей и одну и ту же версию компилятора.

  2. В настройках проекта установите такое же значение для настройки General / Platform Toolset.

  3. В настройках проекта установите для параметра C/C++/Code Generation/Runtime Library значение «Многопоточная DLL (/MD)» или «Многопоточная отладочная DLL» (/MDd) для конфигурации отладки. Если у одного из проектов есть зависимость, которая требует статической настройки CRT, извините, вам не повезло, это не сработает.

  4. Используйте одинаковую конфигурацию с обеих сторон: если вы создали отладочную версию DLL, не связывайте ее с релизной версией потребляющего EXE-файла. Также не меняйте определения препроцессора, такие как _ITERATOR_DEBUG_LEVEL или _SCL_SECURE_NO_WARNINGS, или, если вы это сделали, измените их на одно и то же значение для обоих проектов.

Причина этих сложностей в том, что C++ не имеет стандартизированного ABI. Структура памяти std::vector и других классов меняется в зависимости от многих факторов. Операторы new и delete тоже есть в стандартной библиотеке C++, т.е. нельзя выделить память с C++ в одном модуле, свободную в другом.

Если вы не можете выполнить эти условия, есть несколько обходных путей, вот хорошее резюме: https://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL

person Soonts    schedule 20.06.2019
comment
Совет: используйте для этого файл листа общих свойств (.vsprops). - person MSalters; 20.06.2019
comment
Обычно я не голосую, когда нам нужно угадать вопрос, потому что ОП молчит, однако это хороший ответ, поэтому я получаю +1. - person drescherjm; 20.06.2019
comment
Я бы сказал, что 1. не совсем верно. Достаточно использовать компилятор с тем же ABI (что обычно и бывает) и совместимую с ABI стандартную библиотеку (что было сложнее в Windows, но в целом должно быть хорошо, начиная с VS 2015+). Хотя последнее часто подразумевает первое и наоборот в мире MSVC. - person Dan M.; 20.06.2019
comment
^ Согласен, вы можете упомянуть что-то о бинарной совместимости VS2015 с VS2019. - person drescherjm; 20.06.2019
comment
@drescherjm Я этого не пробовал. У меня даже не установлена ​​VS2019 на моем ПК, я обычно жду ~ пол года, прежде чем перейти на другие основные версии. Если вы это сделали и уверены, что STL действительно совместим с ними в двоичном виде, не стесняйтесь редактировать мой ответ. - person Soonts; 21.06.2019