Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Один код работет в Builder, но не работает в Qt


Автор: nvrrus 4.4.2011, 11:42
Здравствуйте, уважаемые форумчане! Помогите, пожалуйста разобраться с одной проблемой

Проблема использования билдеровской библиотеки в Qt

В обоих случаях работаю с функциями библиотеки собранной в Builder.
Получаю интерфейс I_archSource.
Вызываю последовательно три метода, GetListOfQueries, size, Get. Каждый при отработке выводит сообщение с названием метода.

Builder:

Код

            I_archSource *m_Src;

            const GUID xxx = {0xa167cb35, 0x2572, 0x453d, 0x876a, 0x1b4a24f7f40e};

            //CreateInstance - функция библиотеки собранной в Builder (получаем объект с интерфейсом I_archSource для работы с библиотекой)
            CreateInstance(xxx, (void**)&m_Src);
            
            I_QuerriesList* Queries;

            m_Src->GetListOfQueries((void*)getPstk(),  (void*)&getConnect(), sizeof(CONNECTKLIENT), &Queries);

            unsigned size = Queries->size();

            I_archQuery* Q = Queries->Get(0);




Все четко отрабатывает. В итоге три сообщения - GetListOfQueries, size, Get
Qt:
Код

    I_archSource *m_Src;

    GUID xxx = {0xa167cb35, 0x2572, 0x453d, 0x876a, 0x1b4a24f7f40e};

    //Acreate_instance - функция той же библиотеки выгруженная из нее динамически (получаем объект с интерфейсом I_archSource //для работы с библиотекой)
    Acreate_instance(xxx, (void**)&m_Src);

    I_QuerriesList* Queries;

    m_Src->GetListOfQueries(NULL, NULL, 0, &Queries);

    unsigned size = Queries->size();

    I_archQuery* Q = Queries->Get(0);



Валится на последней строчке  I_archQuery* Q = Queries->Get(0);

При этом если пройти size() и встать на последнюю строчку получаю два сообщения: GetListOfQueries, Get (вместо GetListOfQueries, size)

Заранее спасибо! Буду очень признателен, если что-то подскажете.

Автор: borisbn 4.4.2011, 12:46
покажи интерфейс I_archSource. Лучше ВЕСЬ h-ник.

Автор: nvrrus 4.4.2011, 12:55
Нашли ошибку. Кажется она в Билдере.

Это интерфейс нашей библиотеки собранной в билдере:

Код:

Код

class I_QuerriesList : public C_UnknownStub
{
public:
    virtual void clear() = 0;
    virtual void push_back(I_archQuery* Q) = 0;
    virtual unsigned size() = 0;
    virtual I_archQuery* Get(unsigned i) = 0;
};

class C_UnknownStub : public IUnknown
{
private:
    volatile long m_cRef;
public:
    C_UnknownStub();
    virtual~C_UnknownStub();

    virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject);
    virtual ULONG __stdcall AddRef();
    virtual ULONG __stdcall Release();
};





Билдер не заносит виртуальный деструктор в таблицу виртуальных функций. А Qt заносит (что правильно).
Поэтому происходит сдвиг при вызове. Вместо  clear() вызывается push_back, вместо push_back, size(). И т.д.

Убрали виртуальный деструктор, все заработало...

Но так же нельзя, без виртуального деструктора... Подскажите, пожалуйста, может кто сталкивался с этой проблемой.

Автор: borisbn 4.4.2011, 13:06
Цитата(nvrrus @  4.4.2011,  12:55 Найти цитируемый пост)
Билдер не заносит виртуальный деструктор в таблицу виртуальных функций. А Qt заносит (что правильно).

Qt - это набор классов. Какой у тебя компилятор ?
(будем считать, что MSVC).

Тогда, возможно, проблема не в этом. Builder и студия по-разному выравнивают указатель на виртуальную таблицу, если в опциях проекта указано выравнивание на 8 (на 4 - одинакого).

Здесь ещё пару моментов, от которых можно ожидать подвоха:
1. Интерфейс нужно в явном виде (не полагаясь на настройки проекта) выровнять на 4
2. Нужно явно задать тип вызова функций
3. В явном виде добавить виртуальный деструктор

Код

#pragma pack( push, 4 )

class I_QuerriesList : public C_UnknownStub
{
public:
    virtual ~I_QuerriesList() {}
    virtual void __stdcall clear() = 0;
    virtual void __stdcall push_back(I_archQuery* Q) = 0;
    virtual unsigned __stdcall size() = 0;
    virtual I_archQuery* __stdcall Get(unsigned i) = 0;
};

#pragma pack( pop )


у меня в таком вот виде работают десятки dll-ек, написанные как в bcb, так и в студии, и используемые и там и там.

Автор: xvr 4.4.2011, 13:19
Виртуальные деструкторы COM объектов не попадают в их Интерфейсы. Ваш I_QuerriesList не является валидным COM интерфейсом (хотя и является валидным С++ классом)
Интерфейсы должны выглядеть так:
Код

class IQuerriesList : public IUnknownStub
{
public:
    virtual void clear() = 0;
    virtual void push_back(I_archQuery* Q) = 0;
    virtual unsigned size() = 0;
    virtual I_archQuery* Get(unsigned i) = 0;
};
class IUnknownStub : public IUnknown
{
public:
    virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject);
    virtual ULONG __stdcall AddRef();
    virtual ULONG __stdcall Release();
};
Или скорее так:
Код

class IQuerriesList : public IUnknown
{
public:
    virtual void clear() = 0;
    virtual void push_back(I_archQuery* Q) = 0;
    virtual unsigned size() = 0;
    virtual I_archQuery* Get(unsigned i) = 0;
};

Унаследуйте ваш I_QuerriesList от IQuerriesList (и не забудьте преобразовать тип к IQuerriesList* когда будете его возвращать)

Автор: nvrrus 4.4.2011, 14:21
Цитата(borisbn @  4.4.2011,  13:06 Найти цитируемый пост)
1. Интерфейс нужно в явном виде (не полагаясь на настройки проекта) выровнять на 4
2. Нужно явно задать тип вызова функций
3. В явном виде добавить виртуальный деструктор



Что значит явно задать тип вызова функций?

Автор: borisbn 4.4.2011, 14:26
__stdcall или __cdecl (в коде, который я привёл - __stdcall)
Зачем это нужно ?
Представь, что в настройках проекта в билдере установлен тип вызова __stdcall, а в студии - __cdecl.
Если в коде явно не указан тип вызова, то студия будет вызывать ф-цию, например, clear() по сишному, а билдер будет отрабатывать вызов по паскалевски.

Автор: nvrrus 4.4.2011, 17:49
Сделал все как посоветовали.  И теперь вообще ничего не понимаю.

Для тестирования создал интерфейс (без родителей) с одним методом Get5().
Реализовал его и еще 4 метода. Каждый пишет на экран соответствующую цифру - 1, 2, 3, 4, 5.


Builder библиотека:

Код

//Интерфейс
#pragma pack(push,4)
class I_archSource2
{
public:
    virtual __stdcall ~I_archSource2() {}
    virtual long __stdcall Get5() = 0;
};
#pragma pack(pop)

//Реализация
#pragma pack(push,4)
class C_Source: public I_archSource2
{
public:
    virtual __stdcall ~ C_Source() {}
    virtual long __stdcall Get1(){ MessageBox(NULL, "1", "sdf",MB_OK); return 1; }
    virtual long __stdcall Get2(){ MessageBox(NULL, "2", "sdf",MB_OK); return 2; }
    virtual long __stdcall Get3(){ MessageBox(NULL, "3", "sdf",MB_OK); return 3; }
    virtual long __stdcall Get4(){ MessageBox(NULL, "4", "sdf",MB_OK); return 4; }
    virtual long __stdcall Get5(){ MessageBox(NULL, "5", "sdf",MB_OK); return 5; }
};
#pragma pack(pop)




Qt использование:

Код

    //получаю интерфейс
    Acreate_instance(xxx, (void**)&m_Src);
    //вызываю метод, который должен вывести на экран "5"
    res = m_Src->Get5();

    //на экран выводится "1", т.е. вызов Get1()




Если оставить в реализации только одну функцию Get5(), валится с "Segmentation fault"

В чем проблема? Подскажите, пожалуйста.

Автор: borisbn 4.4.2011, 19:16
Хммм. Странно. Должно работать. 

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

Ты так и не сказал, какой у тебя компилятор. Qt - не компилятор, а набор h-ников, lib-ин и dll

Автор: nvrrus 5.4.2011, 10:46
Компилятор mingw.

Вот функция CreateInstance, библиотеки Builder:
Код

extern "C" __declspec(dllexport) int WINAPI CreateInstance(const GUID& archSourceI, void** pOuter)
{             
    if (archSourceI == IID_DAServerSource)
    {
        C_Source *xxx = new C_Source();
        *pOuter = static_cast<I_archSource2*>(xxx);
    }
}


Так я ее выгружаю из библиотеки в Qt:

Код


     typedef int __stdcall (*Tcreate_instance)(const GUID& archSourceI, void** pOuter);

     if (hArx.isLibrary("C:\\Up_Archive\\arch_db_lib\\trunk\\dll\\ArxDll.dll"))
    {
        hArx.setFileName("C:\\Up_Archive\\arch_db_lib\\trunk\\dll\\ArxDll.dll");
        if (!hArx.load())
        {
            throw "Couldn't load ARX.DLL library";
        }
    }

    Acreate_instance = (Tcreate_instance) hArx.resolve("CreateInstance");
    if (!Acreate_instance)
    {
        throw "Couldn't find CreateInstance function";
    }





Автор: xvr 5.4.2011, 10:55
Кстати, деструкторы между mingw/Builder могут не работать. У разных компиляторов могут быть разные взгляды на способы реализации конструкторов и деструкторов  smile 

Автор: borisbn 5.4.2011, 11:11
действительно, странно.
сделал всё как у тебя, за исключением того, что у меня нет minGW, поэтому сделал в студии. У меня "выплёвывается" 5. см. аттач 

Автор: nvrrus 5.4.2011, 12:38
Спасибо, огромное, за участие. 

Я тоже сделал пустой проект в Qt. Беру интерфейс из вашей библиотеки.

Get5() пишет "1".



Цитата

 Кстати, деструкторы между mingw/Builder могут не работать. У разных компиляторов могут быть разные взгляды на способы реализации конструкторов и деструкторов   


Возможно в этом проблема. Можно ли что-то докрутить, чтобы деструкторы Builder нормально виделись mingw?

Автор: borisbn 5.4.2011, 12:42
Цитата(nvrrus @  5.4.2011,  12:38 Найти цитируемый пост)
Можно ли что-то докрутить, чтобы деструкторы Builder нормально виделись mingw?

можно. Выкинуть деструктор. Добавить виртуальную ф-цию destroy, в реализации которой вызывать
Код

void __sdtcall C_Source::destroy() {
    delete this;
}

Автор: nvrrus 5.4.2011, 13:31
 Да, спасибо, это действительно было бы выходом.

Но при отсутствии деструктора, все равно возникает та же проблема.

Автор: borisbn 5.4.2011, 13:47
nvrrus, у меня две мишени: minGW (что-то они наглючили с вирт.таблицами) и ты smile (что-то ты намутил не с тем h-ником, не с теми dll-ками и т.п.). Склоняюсь ко второму, т.к.

1. 
Цитата(nvrrus @  4.4.2011,  12:55 Найти цитируемый пост)
Убрали виртуальный деструктор, все заработало...

Цитата(nvrrus @  5.4.2011,  13:31 Найти цитируемый пост)
Но при отсутствии деструктора, все равно возникает та же проблема.


2. Если между билдером и студией всё работает, а между minGW и билдером нет, то получается, что между студией и minGW не должно работать, а это, скорее всего, не так, т.к. minGW-шные библиотеки сплошь и рядом пользуют системные dll-ки, сделанные на студии

Давай разбираться: у тебя в билдере и minGW точно подключаются один и тот же h-ник с описанием интерфейса ? Подключи его с полным путём и там и там. У тебя точно используется одна и та же dll-ка в билдере и в minGW ? Удали все dll-ки с этим именем, скомпиляй ей билдером, а потом подключай в minGW.

Автор: nvrrus 5.4.2011, 14:48
Спасибо!

Я бы рад ошибиться, да не где.

Собираю проект dll, из этой же директории беру dll,  когда выгружаю динамически функцию CreateInstance в Qt.


Автор: borisbn 5.4.2011, 15:52
давай ещё одну проверочку попробуем: убери virtual у всех ф-ций GetX, кроме той, которая реализует интерфейс...

Автор: nvrrus 6.4.2011, 16:04
Код

class I_archSource2
{
public:
    virtual __stdcall ~I_archSource2() {}
    virtual long __stdcall Get5() = 0;
};
#pragma pack(pop)

#pragma pack(push,4)
class C_Source: public I_archSource2
{
public:
    virtual __stdcall ~C_Source() {}
    long __stdcall Get1(){ MessageBoxA(NULL, "1", "sdf",MB_OK); return 1; }
    long __stdcall Get2(){ MessageBoxA(NULL, "2", "sdf",MB_OK); return 2; }
    long __stdcall Get3(){ MessageBoxA(NULL, "3", "sdf",MB_OK); return 3; }
    long __stdcall Get4(){ MessageBoxA(NULL, "4", "sdf",MB_OK); return 4; }
    virtual long __stdcall Get5(){ MessageBoxA(NULL, "5", "sdf",MB_OK); return 5; }
};


Валится с SegmentationFault

Автор: borisbn 6.4.2011, 16:46
Был ещё один грешок за билдером...
Попробуй "подчистить сопли", как мы это называем, билдеру: удалить (руками, не из IDE) все .obj, .tds, .#00 - .#0N и .csm файлы.
Плюс убери деструктор - он всё равно не нужен, т.к. вызывать delete m_Src; нельзя, т.к. память, выделенная менеджером памяти билдера нельзя освобождать менеджером памяти minGW.

Добавлено через 6 минут и 40 секунд
minGW на работе нет. Вечером (дома) проверю.

Автор: borisbn 6.4.2011, 21:06
nvrrus, проверил на minGW.......
работает точно, как ты говорил.
походу ты открыл очередной глюк .
вот только не пойму в билдере или минГВ.

Цитата(borisbn @  5.4.2011,  13:47 Найти цитируемый пост)
 и ты  (что-то ты намутил не с тем h-ником, не с теми dll-ками и т.п.). Склоняюсь ко второму, т.к.

беру слова назад. с меня пиво smile

Автор: nvrrus 11.4.2011, 13:08
Спасибо!

Пересобрали библиотку в qt)

Автор: borisbn 11.4.2011, 13:17
Цитата(nvrrus @  11.4.2011,  13:08 Найти цитируемый пост)
Пересобрали библиотку в qt)

не понял. если библиотека не была завязана на VCL, то зачем были эти извращения муки ?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)