Инициализация члена класса POD, совместимая с бинарными файлами С++, вызывает сбой

Я пытаюсь создать класс, совместимый с компилятором, в dll, созданный с помощью mingw, который можно использовать в приложении Windows VS. Моя проблема в том, что мой класс падает в тот момент, когда он пытается инициализировать переменную-член, когда функция вызывается из программы VS. Использование STL или создание локальных переменных в одних и тех же функциях работает нормально.

Необработанное исключение по адресу 0x6BEC19FE (test.dll) в ConsoleApplication2.exe: 0xC0000005: место записи нарушения прав доступа 0x154EB01E.

Простая демонстрация:

DLL-код

#include <iostream>

class Tester 
{
public:

    virtual void test() = 0;
};

class TesterImp : public Tester
{
public:
    TesterImp(){}
    ~TesterImp(){}

    virtual void test() { 
        int test = 5; // fine
        m_test = 5; // access violation
    }

private:

    int m_test;

};

    extern "C"
    {
        __declspec (dllexport) Tester* Create()
        {
            return new TesterImp();
        }
    }

Основная программа, загружающая dll и вызывающая тестовую функцию:

#include <iostream>
#include <Windows.h> 

class Tester 
{
public:

    virtual void test() = 0;
};

typedef Tester* (*createPtr)();

int main(int argc, const char **argv, char * const *envp) {

  HINSTANCE hGetProcIDDLL = LoadLibraryA("test.dll");

  if (hGetProcIDDLL == NULL) {
    std::cout << "could not load the dynamic library" << std::endl;
    return EXIT_FAILURE;
  }

  createPtr ctr = (createPtr)GetProcAddress(hGetProcIDDLL, "Create");
  if (!ctr) {
    std::cout << "could not locate the function" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "dll loading success!" << std::endl;

  Tester* testf = ctr();

  std::cout << "Got test class" << std::endl;

  testf->test(); // crash if the member variable is referenced, fine otherwise with local variable initialization or STL use inside the function

  std::cout << "Running dll functions success!" << std::endl;

  return 0;
}

Основная программа скомпилирована в режиме отладки с помощью VS2012, а dll построена с использованием g++ из mingw -

g++ -shared -o test.dll test.cpp

Может ли кто-нибудь объяснить мне это поведение, пожалуйста?

Спасибо.


person user2380227    schedule 14.05.2013    source источник
comment
Я бы начал с того, что убедился, что ваши соглашения о вызовах совпадают, а затем вошел бы в ctr() с отладчиком, который почти сразу скажет вам, в чем проблема. Мне также не совсем понятно, где ваше утверждение о доступе к динамически выделенному производному классу с виртуальными методами через абстрактный базовый интерфейс квалифицируется как POD-что угодно в C++.   -  person WhozCraig    schedule 14.05.2013
comment
Tester не является POD.   -  person Michael Burr    schedule 14.05.2013
comment
Я имел в виду int m_test как член класса POD.   -  person user2380227    schedule 14.05.2013
comment
К сожалению, я не могу отладить эту функцию через VS, так как mingw не генерирует файл символов, который VS использует для отладки.   -  person user2380227    schedule 14.05.2013
comment
Что ж, я безуспешно пытался возиться с соглашениями о вызовах, похоже, мне нужно было просто написать весь интерфейс как экспортированные функции стиля C.   -  person user2380227    schedule 14.05.2013
comment
@ user2380227: вы можете не получить символы, но VS выполнит функцию MinGW DLL в окне дизассемблирования. Функция TesterImp::test() состоит всего из 10 строк ассемблера, так что, вероятно, это не должно быть слишком болезненным.   -  person Michael Burr    schedule 14.05.2013
comment
@user2380227 user2380227: какие версии MSVC и MinGW вы используете - мне кажется, это работает с MSVC 2010 и MinGW 4.7.2   -  person Michael Burr    schedule 14.05.2013
comment
VS 2012 и последний mingw, я попробую с VS 2010 и посмотрю, что произойдет. Вы скомпилировали этот код без каких-либо изменений?   -  person user2380227    schedule 14.05.2013
comment
Да - скомпилировал без изменений. Вы уверены, что использовали последнюю версию MinGW? Я собирался опубликовать ответ, в котором предполагалось, что вы используете версию MinGW с GCC 4.6.x или более ранней. До версии 4.7.0 MinGW передавал указатель this в стеке — MSVC ожидает его в ecx (что делает MinGW 4.7.0 и более поздние версии).   -  person Michael Burr    schedule 14.05.2013


Ответы (1)


Вероятно, вы используете более старую версию MinGW. Вплоть до 4.7.0 (я думаю) MinGW передавал указатель this в стеке, что отличается от соглашения MSVC thiscall о передаче указателя this в ecx.

Начиная с версии 4.7.0, MinGW использует то же соглашение о вызовах thiscall, что и MSVC.

Я также могу добиться успешного вызова примера функции TesterImp::test() из MSVC, пометив Tester::test() и TesterImp::test() в test.cpp атрибутом __attribute__((thiscall)). Это работало с MinGW 4.6.2, что приводило к сбою без использования атрибута.

person Michael Burr    schedule 14.05.2013
comment
Вы абсолютно правы, я только что проверил версию gcc, и она была 4.62 (не знаю, как это произошло, я получил последнюю версию установщика пакетов с сайта mingw). Большое спасибо за ваше время, вы действительно помогли мне! - person user2380227; 14.05.2013