Разбирающимся в dll и osg2.2
dll и прога собирались с одной и той же версией stl-я? pack-настроек и т.д.?
Потому что объекты и stl-контейнеры нельзя передавать через границы dll. Если ты это все же делаешь, то ты должен убедиться в совпадении версий компилятора и STL. В Debug режиме версия STL, на сколько мне известно, отличается от Release'a. Да, еще не забывай, что память выделенную в dll'ке должна эта же dll'ка и освобождать.
Да, все собиралось на моей машине в MSVC8.
Меня в таком случае удивляет, почему обо всем этом не знают разработчики OpenSceneGraph? Вплоть до версии 2.2 все у них замечательно работало, теперь вдруг косяки пошли.
Еще один пример, иллюстрирующий, насколько я понимаю, разницу между реализациями STL в MSVC и Linux. Там есть примерно такой кусок кода:
read(&front size*sizeof(value_type str);
Ну так вот, в винде на этом куске валится, т.к. size == 0. В линуксе не валится, наверное, front-таки что-то возвращает, и потом все равно читается 0 байт, ошибки не происходит.
Кстати, в отличие от предыдущих версий OSG, собиравшихся в msvc7.1, эта собрана в 8.
Блин, с учетом
Потому что объекты и stl-контейнеры нельзя передавать через границы dllя могу сделать вывод, что версия OSG 2.2 не может использоваться в принципе, а до этого она работала только потому, что MSVC7 отличается от MSVC8.
Это верно и для статически линкуемых dll тоже?
А в linux компиляторов си используемых ярыми линуксоидами 1 штука. И отладочной версии STL там вроде нет.
Я вот чего не понимаю: нельзя передавать контейнеры потому, что в них используются указатели, которые в контексте dll-ки становятся невалидными? Тогда как объяснить то, что в функции log4cpp, которые я использую из dll, свободно передаются как указатели const char*, так и ссылки const std::string& ?
PS Из "шаманских" действий можно попробовать вырубить флаг "Whole program optimization" как у DLL, так и основной программы.
Странное определение, да и вообще странно звучит, Статическая линкуемая dynamic-link library ). Тем более если учеть, что для загруски библиотек при старте используется LoadModule, а дальше просто прописываются адреса функции в нужное место. И разницы что ты сам вызовишь LoadModule, что нет, просто не придется получать указатели не функции.
Имею ввиду, что вручную ничего загружать не придется.
Юзай ollydbgСпасибо, попробую. Правда, в ассемблере я ни бум-бум.
Я вот чего не понимаю: нельзя передавать контейнеры потому, что в них используются указатели, которые в контексте dll-ки становятся невалидными?у основной программы и у каждой dll - свой C-ишный менеджер памяти.
соответственно, выделенную память в одной из dll/основной программе - можно только там и удалить.
соответственно получается, что надо жестко контролировать чтобы ответственность за удаление - не передавалась между dll-ями.
при передаче stl-контейнеров такое правило не соблюдается, и происходит как раз передача ответственности за удаление с одного модуля на другой, что и приводит к ошибкам.
ps
именно поэтому C++ и не любят в больших приложениях (когда отдельные модули собираются в разное время в разных местах т.к. получается что либо надо писать на C, либо городить свое подобие COM-а
именно поэтому open source развит именно на C++, т.к. без исходников вообще фиг соберешь большое приложение
в той же Delphi развивался именно рынок готовых бинарных компонентов (в том числе и free-шных а не исходников.
статически линкуемая dll, или статически линкуемая либа?
Объяснил же уже выше: dll, которая подключается к проекту посредством соответствующей .lib, в которой прописаны адреса функций в .dll, чтобы загрузка происходила автоматически при старте программы. Файл .lib может заменяться файлом .def, насколько я знаю.
соответственно, выделенную память в одной из dll/основной программе - можно только там и удалить.соответственно получается, что надо жестко контролировать чтобы ответственность за удаление - не передавалась между dll-ями.при передаче stl-контейнеров такое правило не соблюдается, и происходит как раз передача ответственности за удаление с одного модуля на другой, что и приводит к ошибкам.Т.е. если я передаю ссылку на константный контейнер в dll, то он должен нормально читаться? И если я передаю контейнер по значению, то он просто скопируется в стеке dll и тоже все будет в порядке?
А если я передаю ссылку на контейнер в dll, и там пытаются добавить в него элементы, то возможны вызовы new и delete в dll, что приведет к косякам?
у основной программы и у каждой dll - свой C-ишный менеджер памяти.Кстати, в паскале можно сделать общий менеджер памяти (модуль sharemem). Может и в си есть нечто подобное?
Объяснил же уже выше: dll, которая подключается к проекту посредством соответствующей .lib, в которой прописаны адреса функций в .dllАФАИК, это не есть статически слинкованная dll
да, если dll-ки собираются одним и тем же компилятором с одними и теми же настройками
ты кстати так и не ответил, #pragma pack в обоих случаях одиннаковая или разная - скорее всего именно она как раз и переопределяется в Release для ускорения
ps
кстати ты уверен, что у тебя нет банального проезда по памяти? просто в debug-е память выделяется с запасом, а в release - память выделяется в притык, поэтому все проезды по памяти в release намного заметнее.
И если я передаю контейнер по значению, то он просто скопируется в стеке dll и тоже все будет в порядке?нет, конечно, т.к. копирование происходит до передачи.
А если я передаю ссылку на контейнер в dll, и там пытаются добавить в него элементы, то возможны вызовы new и delete в dll, что приведет к косякам?да, это приведет к косякам.
в паскале(в дельфи) вроде и так общий менеджер памяти внутри одного процесса.
ты уверен, что не путаешь это с шарингом памяти между процессами?
АФАИК, это не есть статически слинкованная dllНу, как ты это называешь? Давай будем называть, как тебе привычнее, но от тебя какого-либо термина на этот счет я еще не увидел
ты кстати так и не ответил, #pragma pack в обоих случаях одиннаковая или разная - скорее всего именно она как раз и переопределяется в Release для ускоренияНу, если я сам ее не переопределяю нигде, то должна быть одинаковая, я так думаю? Тем более, что во всех STL файлах сначала идет #pragma pack (push, _CRT_PACKING а в конце - #pragma pack(pop).
кстати ты уверен, что у тебя нет банального проезда по памяти? просто в debug-е память выделяется с запасом, а в release - память выделяется в притык, поэтому все проезды по памяти в release намного заметнее.Там вроде нечему проезжаться.
Сверху я приводил кусок кода - создается пустой контейнер map, далее сразу по ссылке передается в функцию из dll - и в ней дебаг показывает размер контейнера много-много элементов. Где тут можно было бы напортачить?
Ну, если я сам ее не переопределяю нигде, то должна быть одинаковая, я так думаю?ты может и не переопределяешь, но какой-нибудь h-ник переопределяет
В смысле, ни в одном из файлов моего проекта и проектов dll не встречается комбинация символов "#pragma pack", т.ч. должна быть одинаковая.
Собрал Release/Release, только оба с Debug Info. Решил вывести размер вектора до операции и после, написал std::cout << windows.size << std::endl; Прога не собралась, ошибка error LNK2001: unresolved external symbol "__declspec(dllimport) public: class std::basic_ostream<char,struct std::char_traits<char> > & __thiscall std::basic_ostream<char,struct std::char_traits<char> >::operator<<(unsigned int)" (__imp_?6?$basDU?$c@@@Z) (стандартная библиотека линкуется статически (/MT. Почему ошибка - совершенно непонятно, проект со статическими библиотеками не линкуется, во всем проекте стоит /MT, должно собираться.
Сменил с /MT на /MD, и все собралось и даже заработало, как ни странно. Решил посмотреть на этот вектор windows, дебаггер показывает в нем много миллиардов элементов, но прога отрабатывает нормально. А вот cout'ы выводят правильно - сначала 0, а после вызова функции - 1.
Наверное, я сошел с ума.
Ты не одинок. У меня были похожие случаи c GDB. Прога работает, но дебаггер при печати переменных выдает полную чушь. IMHO это свойство дебаггеров.
Проблема в том, что ты линкуешь библиотеки динамически, то есть загружаешь соответствующие dll во время выполнения, сам или во время загрузки. Как следствие тебе и надо установить правильное значение в списке Runtime library == Multithreaded DLL. Если бы ты линковал статически, то есть полностью линковал при линковке lib файл Runtime library, то надо было бы выбирать Multithreaded. Просто проблема в том, что ты с самого начала путал понятия статической и динамической линковки и при этом придумал совершенно свое понятие статически линкуемая dll .
Проблема в том, что ты линкуешь библиотеки динамически, то есть загружаешь соответствующие dll во время выполнения, сам или во время загрузки. Как следствие тебе и надо установить правильное значение в списке Runtime library == Multithreaded DLL. Если бы ты линковал статически, то есть полностью линковал при линковке lib файл Runtime library, то надо было бы выбирать Multithreaded. Просто проблема в том, что ты с самого начала путал понятия статической и динамической линковки и при этом придумал совершенно свое понятие статически линкуемая dllНе хотелось бы тебя огорчать, но то, что ты говоришь, не совсем верно.
Значение Runtime Library касается только стандартных библиотек C++. При линковке проекта с какими-либо статическими библиотеками у них у всех должно быть одинаковое значение Runtime Library, это бесспорно.
Но вот какое значение Runtime Library у .DLL, которую я подключаю к своему проекту, чтобы она автоматически подгружалась при старте, при помощи ее .lib файла, не имеет никакого значения, потому что в моей проге разрешения имен при этом не происходит, оно происходит только на этапе выполнения, и там уже не важно, как именно эта DLL использует стандартную библиотеку - связана ли она с ней статически или динамически, это ее дело.
Т.ч. проблема тут может быть в том, что когда я подключаю файлы OSG к проекту, то не все функции там экспортируются в DLL, может быть там есть какие-то inline-функции, использующие стандартную библиотеку, и вот тогда уже мне важно, чтобы Runtime Library совпадали.
А насчет инлайн неосилил, так как это вообще просто бред сивой кобы.
А насчет инлайн неосилил, так как это вообще просто бред сивой кобы.Много думал, пожалуй, да. А насчет выделения памяти вопрос, скажем так, непростой.
Например: для линковки проекта со статическими библиотеками нужно совпадение их рантаймов. Для подгрузки же DLL неважно, каким рантаймом она собиралась, т.е. у меня нормально собираются и работают проекты+DLL в любой комбинации их рантаймов.
В свете вышесказанного про то, что память, выделенная в программе, должна уничтожаться в программе, а выделенная в DLL, должна уничтожаться в DLL, и того, что собранный с одинаковыми рантаймами основного модуля и DLL проект работает, можно сделать вывод, что это правило в моем проекте не нарушается. Значит, и при любых других комбинациях типов рантаймов DLL не должна уничтожать память, выделенную в программе, и наоборот, значит, должно работать.
Верно?
osgViewer::getWindows(windows); - аллокирует память внутри dll.
windows.clear - уничтожает уже внутри exe.
Аналогично и со стрингом было.
osgViewer::getWindows(windows); - аллокирует память внутри dll.windows.clear - уничтожает уже внутри exe.Блин, точно. Тогда получается, что это проект с OSG в принципе не должен работать. Что же теперь, reserve пользоваться перед вызовами функций dll ?
память-то под виндовсы наверное, вектор указателей у тебя в exe оталлоцирован, резерв не спасет. (топег не читал)
топег не читалПочитай, и пиши по-русски, я ни хрена не понял.
OSG (я не знаю что это такое наверно, собирается с /MD тебе нужно поставить тоже в своем проекте, тогда у вас будет одна копия рантайма на двоих и будет счастье.
соответственно, выделенную память в одной из dll/основной программе - можно только там и удалить.По ходу, это не так.
Пару дней мучался с log4cpp, он тоже, зараза, в дебаге нормально работал, а в релизе вылетал. Обнаружил, что указатель на выделяемую в программе память передается затем в функцию из dll и там сохраняется в мапе, а деструктор статического объекта dll затем уничтожает этот указатель. Долго думал, что это косяк log4cpp. Однако заметил, что перед включением хедеров не определил макрос LOG4CPP_HAS_DLL, которые ответственен за добавление __declspec(dllimport) перед объявлениями функций. После его определения проблемы кончились...
Если функция объявлена как __declspec(dllimport а мы пытаемся слинковать ее со статической библиотекой, будет ошибка линкера. К сожалению, если такого объявления перед функцией нет, и она предполагает статическую линковку, то попытавшись слинковать проект с .lib-файлом DLL'ки, MSVC не выдаст никаких предупреждений, и, возможно, программа даже будет работать, но никто не гарантирует.
Я не знаю что такое "статически линкуемая dll".Я нашел. Это называется библиотекой импорта!
Оставить комментарий
erotic
Имеем в коде OSG:И мой код:
Так вот - первая строчка моего кода создает пустой вектор, когда же он по ссылке передается в функцию, то, хотя адрес его остается тот же, объект получается испорченным с каким-то нереальным количеством элементов в нем, и первая же операция с нем - windows.clear - заканчивается вываливанием проги.
Такое наблюдается во всех комбиациях Debug и Release между поим проектом и OSG, за исключением Debug/Debug - тогда эта строчка проходит нормально.
Почему такая хрень? И такое же случается с другими функциями, подгружаемым из DLL (DLL слинкована статически которым передаются ссылки на объекты, которые производные от контейнеров STL.
P.S. Помню еще, что давно у меня были косяки с возвратом std::string из dll-функции, была какая-то хрень с порчей памяти, поэтому пришлось в dll завести static std::string str, и возвращать из функции константную ссылку на нее, тогда работало.