Нарушение доступа при доступе к объекту STL через указатель или ссылку в другой DLL или EXE - PullRequest
2 голосов
/ 16 марта 2010

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

http://support.microsoft.com/kb/172396

Так как нет способа экспортировать карту, мой запланированный обходной путь - использовать статическое связывание вместо динамического связывания.

Мне было интересно, сталкивались ли вы все с подобной ситуацией? Какой у вас обходной путь для этого?

Другим обходным решением является создание класса-оболочки вокруг карты stl, чтобы обеспечить создание и доступ к карте stl в пределах одного пространства DLL. Обратите внимание, что fun0, который использует класс-обертку, будет работать нормально. fun1 вылетит.

Вот пример кода:

// main.cpp. Compiled it as exe.
#pragma warning (disable : 4786)
#include <map>
#include <string>

template <class K, class V>
class __declspec(dllimport) map_wrapper {
public:
    map_wrapper();
    ~map_wrapper();    
    map_wrapper(const map_wrapper&);
    map_wrapper& operator=(const map_wrapper&);

    V& operator[](const K&); 
    const V& operator[](const K&) const;

    const V& get(const K&) const;
    void put(const K&, const V&);
    int size() const;

private:
    std::map<K, V> *m;
};

__declspec(dllimport) void fun0(map_wrapper<std::string, int>& m);
__declspec(dllimport) void fun1(std::map<std::string, int>& m);

int main () {
    map_wrapper<std::string, int> m0;
    std::map<std::string, int> m1;

    m0["hello"] = 888;
    m1["hello"] = 888;

    // Safe. The we create std::map and access map both in dll space.
    fun0(m0);
    // Crash! The we create std::map in exe space, and access map in dll space.
    fun1(m1);

    return 0;
}

// dll.cpp. Compiled it as dynamic dll.
#pragma warning (disable : 4786)
#include <map>
#include <string>
#include <iostream>

/* In map_wrapper.h */
template <class K, class V>
class __declspec(dllexport) map_wrapper {
public:
    map_wrapper();
    ~map_wrapper();    
    map_wrapper(const map_wrapper&);
    map_wrapper& operator=(const map_wrapper&);

    V& operator[](const K&); 
    const V& operator[](const K&) const;

    const V& get(const K&) const;
    void put(const K&, const V&);
    int size() const;

private:
    std::map<K, V> *m;
};
/* End */

/* In map_wrapper.cpp */
template <class K, class V>
map_wrapper<K, V>::map_wrapper() : m(new std::map<K, V>()) {
}

template <class K, class V>
map_wrapper<K, V>::~map_wrapper() {
    delete m;
}

template <class K, class V>
map_wrapper<K, V>::map_wrapper(const map_wrapper<K, V>& map) : m(new std::map<K, V>(*(map.m))) {
}

template <class K, class V>
map_wrapper<K, V>& map_wrapper<K, V>::operator=(const map_wrapper<K, V>& map) {
    std::map<K, V>* tmp = this->m;
    this->m = new std::map<K, V>(*(map.m));
    delete tmp;
    return *this;
}

template <class K, class V>
V& map_wrapper<K, V>::operator[](const K& key) {
    return (*this->m)[key];
}

template <class K, class V>
const V& map_wrapper<K, V>::operator[](const K& key) const {
    return (*this->m)[key];
}

template <class K, class V>
const V& map_wrapper<K, V>::get(const K& key) const {
    return (*this->m)[key];
}

template <class K, class V>
void map_wrapper<K, V>::put(const K& key, const V& value) {
    (*this->m)[key] = value;
}

template <class K, class V>
int map_wrapper<K, V>::size() const {
    return this->m->size();
}

// See : http://www.parashift.com/c++-faq-lite/templates.html#faq-35.15
// [35.15] How can I avoid linker errors with my template classes?
template class __declspec(dllexport) map_wrapper<std::string, int>;
/* End */

__declspec(dllexport) void fun0(map_wrapper<std::string, int>& m) {
    std::cout << m["hello"] << std::endl;
}

__declspec(dllexport) void fun1(std::map<std::string, int>& m) {
    std::cout << m["hello"] << std::endl;
}

Ответы [ 3 ]

1 голос
/ 16 марта 2010

Статья в КБ, на которую вы ссылались, старая, она была написана до выхода VS2008. Это не решает проблему, это фундаментальное ограничение C ++. Не существует механизма проверки того, что классы в отдельно создаваемых двоичных файлах имеют совместимые макеты памяти и были распределены с одним и тем же распределителем памяти.

Что вы можете сделать, чтобы избежать проблемы:

  • избегать экспорта объектов класса
  • создайте свои DLL и EXE с / MD, чтобы они распределяли память
  • сборка всех DLL и EXE с одним и тем же компилятором и версией CRT
  • тщательно контролируйте параметры сборки, чтобы убедиться, что они одинаковы для всех проектов, листы свойств проекта - лучший способ обеспечить это.
0 голосов
/ 16 марта 2010

Следует также иметь в виду, что реализация STL в MSVC включена в «безопасную» итеративную проверку Microsoft (среди других добавлений, связанных с безопасностью). Если эти флаги не совпадают между вашим двоичным файлом и тем, с которым вы связываете не будет двоично-совместимым, даже если символы совпадают.

0 голосов
/ 16 марта 2010

Я никогда не сталкивался с этой проблемой, но я не думаю, что статическое связывание решит эту проблему для вас. Нарушение доступа происходит из-за того, что два модуля компиляции скомпилированы с разными реализациями STL и пытаются получить доступ к объекту STL из другого модуля. Неважно, статически или динамически связаны два модуля компиляции. Если вы не можете экспортировать карту, вы должны экспортировать функции доступа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...