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();
};
CBuilder игнорирует виртуальный конструктор...
Есть класс
Код:
Который реализует некоторую "заглушку" для интерфейса IUnknown.
От него наследуется:
Код:
class I_StringsList : public C_UnknownStub
{
public:
virtual void clear() = 0;
virtual void push_back(const char*) = 0;
virtual unsigned size() = 0;
virtual const char* Get(unsigned i) = 0;
};
class C_StringList : public I_StringsList
{
private:
C_VectorW<C_StringW> m_SV;
public:
C_StringList(){ }
C_StringList(C_VectorW<C_StringW>& newSV)
{
m_SV = newSV;
}
virtual ~C_StringList() {}
virtual void clear() { m_SV.clear(); }
virtual void push_back(char* string)
{
C_StringW Temp(string);
m_SV.push_back(Temp);
}
virtual unsigned size() { return m_SV.size(); }
virtual const char* Get(unsigned i) { return m_SV.c_str(); }
};
{
public:
virtual void clear() = 0;
virtual void push_back(const char*) = 0;
virtual unsigned size() = 0;
virtual const char* Get(unsigned i) = 0;
};
class C_StringList : public I_StringsList
{
private:
C_VectorW<C_StringW> m_SV;
public:
C_StringList(){ }
C_StringList(C_VectorW<C_StringW>& newSV)
{
m_SV = newSV;
}
virtual ~C_StringList() {}
virtual void clear() { m_SV.clear(); }
virtual void push_back(char* string)
{
C_StringW Temp(string);
m_SV.push_back(Temp);
}
virtual unsigned size() { return m_SV.size(); }
virtual const char* Get(unsigned i) { return m_SV.c_str(); }
};
Формируется DLL, собранная Builder, которая имеет метод
Код:
I_StringsList* GetStrings();
Внутри она создает экземпляр класса C_StringList и возвращает указатель на него пользователю.
Если клиент к этой DLL написан на CBuilder, то все работает корректно.
Если же клиент написан на Qt, то указатель на I_StringsList* имеет сдвиг:
при вызове метода size() реально вызывается метод Get(), при вызове метода Push_back(...), вызывается метод size(). Т.е. сдвиг на один метод. Как будто один метод потерялся в таблице Builder.
Таблица виртуальных функций, построенная CBuilder:
0) virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject);
1) virtual ULONG __stdcall AddRef();
2) virtual ULONG __stdcall Release();
3) virtual void clear() = 0;
4) virtual void push_back(const char*) = 0;
5) virtual unsigned size() = 0;
6) virtual const char* Get(unsigned i) = 0;
Таблица виртуальных функций, построенная Qt:
0) virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject);
1) virtual ULONG __stdcall AddRef();
2) virtual ULONG __stdcall Release();
3) virtual ~C_UnknownStub();
4) virtual void clear() = 0;
5) virtual void push_back(const char*) = 0;
6) virtual unsigned size() = 0;
7) virtual const char* Get(unsigned i) = 0;
В таком случае правда, если в QT мы вызываем size(), он идет под номером 6, и вызывает метод Get() объекта, созданного в CBuilder.
Сборка без виртуального деструктора у C_UnknownStub показала, что все работает корректно.
Вопрос 1: почему CBuilder теряет виртуальный деструктор в своей таблице виртуальных функций?
Вопрос 2: как корректно решить эту проблему? :)
Заранее спасибо за участие, проблема, имхо, очень специфична.
Код:
class I_StringsList : public C_UnknownStub
{
public:
virtual ~I_StringsList() {}
virtual void clear() = 0;
virtual void push_back(const char*) = 0;
virtual unsigned size() = 0;
virtual const char* Get(unsigned i) = 0;
};
{
public:
virtual ~I_StringsList() {}
virtual void clear() = 0;
virtual void push_back(const char*) = 0;
virtual unsigned size() = 0;
virtual const char* Get(unsigned i) = 0;
};
Ну и как-то подозрительно, что сигнатуры виртуального метода I_StringsList:: push_back и метода C_StringList:: push_back немного не совпадают. В одном случае аргумент const char*, в другом - char*
Цитата: Нездешний
Попробуйте определить виртуальный деструктор для I_StringsList.
Ну и как-то подозрительно, что сигнатуры виртуального метода I_StringsList:: push_back и метода C_StringList:: push_back немного не совпадают. В одном случае аргумент const char*, в другом - char*
Код:
class I_StringsList : public C_UnknownStub
{
public:
virtual ~I_StringsList() {}
virtual void clear() = 0;
virtual void push_back(const char*) = 0;
virtual unsigned size() = 0;
virtual const char* Get(unsigned i) = 0;
};
{
public:
virtual ~I_StringsList() {}
virtual void clear() = 0;
virtual void push_back(const char*) = 0;
virtual unsigned size() = 0;
virtual const char* Get(unsigned i) = 0;
};
Ну и как-то подозрительно, что сигнатуры виртуального метода I_StringsList:: push_back и метода C_StringList:: push_back немного не совпадают. В одном случае аргумент const char*, в другом - char*
Да, там в обоих случаях const char*. И попробовал, сделал так всюду, проблема осталась.
А тело у virtual ~C_UnknownStub() есть какое-нибудь? Хотя бы {}?
Цитата: Нездешний
А тело у virtual ~C_UnknownStub() есть какое-нибудь? Хотя бы {}?
Да. Мы вообще, борясь с проблемой, пришли к такому:
Код:
#pragma pack(push,4)
class IF
{
public:
virtual __stdcall IF() {}
virtual __stdcall Func() = 0;
};
#pragma pack(pop)
#pragma pack(push,4)
class RIF : public IF
{
public:
virtual __stdcall ~RIF() {}
virtual __stdcall Func() { MessageBox(NULL, "sdf", "wer", MB_OK); }
};
#pragma pack(pop)
class IF
{
public:
virtual __stdcall IF() {}
virtual __stdcall Func() = 0;
};
#pragma pack(pop)
#pragma pack(push,4)
class RIF : public IF
{
public:
virtual __stdcall ~RIF() {}
virtual __stdcall Func() { MessageBox(NULL, "sdf", "wer", MB_OK); }
};
#pragma pack(pop)
Так вот, dll (сделанная на Билдере) имеет метод CreateInstance, который возвращает по интерфейсу IF объект RIF.
Если возвращает Билдеру, то все отлично. Если возвращает в Qt, то при вызове метода Func получаем segmentation fault.
Если реализующий класс выглядит так
Код:
#pragma pack(push, 4)
class RIF : public IF
{
public:
virtual __stdcall ~RIF() {}
virtual __stdcall Func1() { MessageBox(NULL, "sdf1", "wer", MB_OK); }
virtual __stdcall Func2() { MessageBox(NULL, "sdf2", "wer", MB_OK); }
virtual __stdcall Func3() { MessageBox(NULL, "sdf3", "wer", MB_OK); }
virtual __stdcall Func4() { MessageBox(NULL, "sdf4", "wer", MB_OK); }
virtual __stdcall Func() { MessageBox(NULL, "sdf", "wer", MB_OK); }
};
#pragma pack(pop)
class RIF : public IF
{
public:
virtual __stdcall ~RIF() {}
virtual __stdcall Func1() { MessageBox(NULL, "sdf1", "wer", MB_OK); }
virtual __stdcall Func2() { MessageBox(NULL, "sdf2", "wer", MB_OK); }
virtual __stdcall Func3() { MessageBox(NULL, "sdf3", "wer", MB_OK); }
virtual __stdcall Func4() { MessageBox(NULL, "sdf4", "wer", MB_OK); }
virtual __stdcall Func() { MessageBox(NULL, "sdf", "wer", MB_OK); }
};
#pragma pack(pop)
То при вызове Func, получаем MessageBox "sdf1".
Т.е. сдвиг на два указателя вниз идет.
(в таблице-то виртуальных функций они идут в таком порядке: Func, ~~RIF, Func1, Func2...)
На другом форуме один человек собрал свою dll под билдером, проверил ее с Qt, использующим компилятор VS, все нормально.
Его же dll, при попытке вызова в Qt с компилятором minGW, уже у нас, дает ту же проблему.
Я развожу руками, понятия не имею, что еще можно сделать, чтобы указатели в таблицу виртуальных функций совпадали =(
Цитата: Arkady
Да. Мы вообще, борясь с проблемой, пришли к такому:
Т.е. сдвиг на два указателя вниз идет.
(в таблице-то виртуальных функций они идут в таком порядке: Func, ~~RIF, Func1, Func2...)
Т.е. сдвиг на два указателя вниз идет.
(в таблице-то виртуальных функций они идут в таком порядке: Func, ~~RIF, Func1, Func2...)
Было проведено исследование:
если убрать оба деструктора, то при вызове метода Func, вызывается метод Func2
если оставить деструктор интерфейса, деструктор реализации или оба деструктора, то при вызове метода Func, вызывается метод Func1.