Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > [C++] Доступ к объекту, созданному в dll


Автор: scroollocker 23.8.2013, 19:43
Всем привет.

Случилась такая ситуация у меня, непонятная для меня... Вообщем прошу помощи. 

Есть некоторый класс - интерфейс:
Код

class IDb {
    public:
        virtual ~IDb() {}
        virtual int getData() = 0;    
        virtual void clearData() = 0;
};


В dll создаю его наследника, 

Код

class clDb : public IDb {
    private:
        int *_data;
        
    public:
                clDb() {
                      _data = new int(50);
                }
        virtual ~clDb() {
                          clearData();
                 }
        virtual int getData(){
                          return *_data
                }    
        virtual void clearData() {
                      if (_data) delete _data;
                      _data = NULL;
                }            
};


Затем в той же dll создаю 2е функции, одна будет возвращать созданный в dll объект. Другая уничтожать его.

Код

IDb* makeObject() {
    clDb *myUpDb = new clDb();
    return myUpDb;         
}

void freeObject(IDb *pDb) {
     if (pDb) {
        try {
            delete pDb;         
        }
        catch(...) {}
     }
}


А уже в программе, которая загружает dll, используя интерфейс IDb, хочу пользоваться методами. Но, по какой-то причине, объект возвращается указывающий на какой то мусор... 

Вот код главной программы:
Код

typedef IDb* (*pGetObjectFunc)(void);
typedef void (*pFreeObjectFunc)(IDb *);

int main(int argc, char *argv[])
{
     char *cDllPath = new char[50];
     sprintf(cDllPath,"dll_test.dll");
          
     HMODULE hPluginDBDll = LoadLibraryA(cDllPath);
     if (!hPluginDBDll) {
        return 0;           
     }    
     
     pGetObjectFunc getFunc;
     pFreeObjectFunc freeFunc;
     
     getFunc   = (pGetObjectFunc)GetProcAddress(hPluginDBDll,"makeObject");
     freeFunc  = (pFreeObjectFunc)GetProcAddress(hPluginDBDll,"freeObject");
     
     IDb *myObject;
     myObject = getFunc();
     cout << "Data = " << myObject->getData() << endl;
     
     myObject->clearData();
     
     freeFunc(myObject);
     
     FreeLibrary(hPluginDBDll);
     
     return 0;
}


Что я делаю не так? чего я не знаю? подскажите пожалуйста

*UPD
- очепятался

Автор: feodorv 23.8.2013, 20:16
Цитата(scroollocker @  23.8.2013,  20:43 Найти цитируемый пост)
указывающий на какой то мусор

Гм. Что-то странное Вы делаете с указателями:
Цитата(scroollocker @  23.8.2013,  20:43 Найти цитируемый пост)
        int *_data;

это понятно;
Цитата(scroollocker @  23.8.2013,  20:43 Найти цитируемый пост)
                      *_data = new int(50);

Вот это не понятно. По адресу _data размещается другой адрес, указывающий на int со значением 50... Должно быть, имелось в виду:
Код

_data = new int(50);


Автор: scroollocker 23.8.2013, 22:53
feodorv, 

Да все верно, это я опечатка здесь, т.к. в программе конструкция немного помассивней. Здесь хотел показать то, что в dll в классе выделяется  память под объекты. А в основной программе, данные просматриваются. 

Автор: borisbn 24.8.2013, 06:11
Dll и exe сделаны одним компилятором? С одними и теми же настройками типа вызова и упаковкой структур? GetProcAddress возвращает не NULL?

Автор: scroollocker 24.8.2013, 13:39
borisbn, 
да, оба собраны в minGW, настройки одни и те же. 

Цитата

GetProcAddress возвращает не NULL? 


Да, не NULL.  

Здесь пример уж слишком прост, в программе, над которой работаю, там есть методы, которые динамически работают со списками. Добавляют удаляют, получают данные. 

Автор: feodorv 24.8.2013, 16:57
Цитата(scroollocker @  24.8.2013,  14:39 Найти цитируемый пост)
Здесь пример уж слишком прост

Но он работает?
Может, дело как раз в том, что
Цитата(scroollocker @  23.8.2013,  23:53 Найти цитируемый пост)
в программе конструкция немного помассивней


Автор: scroollocker 24.8.2013, 19:26
feodorv, 

пример этот тоже проверял, тоже не работает. Даже не происходит вызов конструктора класса. 

Никак не могу понять причину. Может кто нибудь посоветует другой способ, чтобы был один интерфейс для взаимодействия с объектами, созданными в dll. Если я изменю методы, добавлю поля, чтобы не было необходимости изменять\перекомпилировать код основной программы.

Автор: Alexeis 25.8.2013, 12:31
 Одинаковая версия компилятора для Dll и для Exe совсем не обязательно. Достаточно использовать COM совместимые интерфейсы. У меня студия прекрасно работает с объектами созданными в билдере и наоборот. При передаче объектов, просто нужно соблюдать несколько простых правил. 
1) В каком модуле выделил, в том и освободил. 
2) Передавать в методы объекта можно либо POD типы (с указанным выравниванием) или интерфейсы.
3) Не рекомендуется передавать тип enum, так как его размер зависит от настроек компилятора.
4) Исключения не должны покидать модуль. Если функция может сгенерить исключение, то его лучше поймать и вернуть кодом ошибки.
5) Внимательно работать со строками. Объект std::string или аналогичный не должен покидать границу модуля. Тут же со строками. Если обмениваться строками типа whar_t*/char* можно использовать функцию SysAllocString, она использует внешний менеджер памяти. Тоже самое послабление можно сделать для POD типов если объединить менеджеры памяти (общий менеджер памяти в отдельной Dll).
6) Не создавать для интерфейсов конструкторы и деструкторы.  


Автор: feodorv 25.8.2013, 14:23
Цитата(scroollocker @  24.8.2013,  20:26 Найти цитируемый пост)
пример этот тоже проверял, тоже не работает

Проверил на MS Visual C++ 6.0, работает)))

Автор: scroollocker 25.8.2013, 19:52
Код заработал. Обновил компилятор на новую версию. НО. 
Программа все равно вылетает на этом месте:
Код

freeFunc(myObject); 


Оказалось, если объявленное в dll 
Код

void freeObject(IDb *pDb) {
     if (pDb) {
        try {
            delete pDb;         
        }
        catch(...) {}
     }
}

изменить на 
Код

void freeObject(clDb *pDb) {
     if (pDb) {
        try {
            delete pDb;         
        }
        catch(...) {}
     }
}

программа работает нормально. 

feodorv, 
да спасибо, обновил компилятор, тоже уже стало работать. Попробуйте вызвать freeFunc(myObject); На этом месте у меня вылетает теперь =(


Alexeis, 

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

Автор: borisbn 25.8.2013, 22:39
Alexeis, 
7) Нельзя в интерфейсе создавать перегруженные функции

2 ТС:
Я для удаления объекта добавляю в него метод destroy, который в наследнике делает delete this; IMHO удобнее

Автор: scroollocker 29.8.2013, 18:45
Вроде все шло хорошо, на одинаковых компиляторах было все супер... Решил попробовать на C++ Builder, и началось.... Не хочет понимать объект через интерфейс.. В связи с этим, хочу уточнить пару моментов:

Цитата

5) Внимательно работать со строками. Объект std::string или аналогичный не должен покидать границу модуля. 


Получается нельзя в созданные в dll объект ни отдавать не принимать std::string. Можно об этом пункте поподробней. Т.к. у меня существует класс - Хранилище. 
В него можно добавлять, удалять, изменять хранящиеся там объекты. А вот эти объекты как раз и хранят в себе std::string. Вопрос, как мне подогнать классы, чтобы можно было хранить\получать\изменять эту структуру и чтобы интерфейс был кросскомпиляторным?


Интерфейс работы с БД:
Код

class IDb {
    public:
        virtual ~IDb() {}
        virtual int getCount() = 0;    
        virtual void init() = 0;
        virtual void clearData() = 0;
        virtual bool load() = 0;
        virtual bool save() = 0;
        virtual bool deleteItem(int) = 0;
        virtual void setPath(std::string ) = 0;
};

class IUpDataDb : public IDb {
    public:
        virtual ~IUpDataDb() {}    
        virtual void add(UP_DATA) = 0;
        virtual UP_DATA get(int) = 0;        
        virtual bool insert(int,UP_DATA) = 0;
        virtual bool replaceItem(int,UP_DATA) = 0;                    
};


сама структура UP_DATA имеет вид:

Код

struct UP_DATA {
    int id;
    std::string host;
    std::string uname;
    std::string pass;
};

Автор: Alexeis 29.8.2013, 23:50
Цитата(scroollocker @  29.8.2013,  19:45 Найти цитируемый пост)
Получается нельзя в созданные в dll объект ни отдавать не принимать std::string. Можно об этом пункте поподробней.

   Нельзя передавать такие объекты в качестве параметров интерфейсов. Тем более структуры содержащие такие объекты. А еще тут сразу видно деструктор интерфейса. Интерфейс не должен иметь конструкторов и деструкторов, тем более виртуальных. Можно использовать char* char[] в таких структурах. Но в любом случае желательно чтобы память выделялась с одной стороны. 

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