Win32 и C++ library

А верно ли я понимаю, что если у меня есть функция с таким вот прототипом (примерно)

Someclass::somefunc(std::string *);
То если я ея вынесу в DLL, а у себя в коде напишу
std::string foo("blabla");
Someclass bar;
bar(&foo);
То в какой-то момент это меня обязательно поимеет? По той простой причине, что Someclass.dll живет с одним C++-рантаймом, а мой код - с другим?

P.S. Если че, это не я так пишу. Adobe XMP SDK, мать его за ногу, а в нем

SXMPMeta::SerializeToBuffer(string* buffer, some_enum flags);
Как я посмотрел, все разумные люди ему делают С-шный враппер, но мне надо то два раза по 6 строчек и делать враппер противно.

Comments

Хорошее беспокойство. Мне думается, что обоснованное и дело даже, имхо, не столько в рантаймах, сколько в окружении при компиляции, и от этого в потенциально разных структурах данных в ДЛЛ и в компилируемом коде. Ну, потому, что int, там, или enum - оно обычно и в африке int или enum, а вот string может быть принципиально разным (например, в дебуге могут быть дополнительные поля, бинарно не совместимые с релизом).

Ну за debug/release я, допустим, прослежу.

А вот ситуация, когда рантайм сменился (визуальная студия обновилась), DLL - не перекомпилировался (потому что ничего не менялось), а исполняемый файл - тоже обновился, такая ситуация же просто обязана извращенно поиметь.

Ну то есть, понятно, FILE* в такой ситуации тоже может порадовать, но FILE* и меняется реже и вообще...

Поимеет, если при смене библиотеки изменится состав полей класса. А так там фактически только указатель на структуру передается. И тут уже лотерея.

Дополнительные подводные камни могут быть в случае MFC Extension DLL, когда dll закладывается на то, что MFC инициализировалась модулем, который ее загружает.

Вообще если ты динамически линкуешься со всякими msvcrt и STL то это вполне легально.

В смысле, что msvcrt гарантирует одинаковость std::string во всех версиях (от одного Visual C++)? Это где-то явно обещано?

Потому что вот конкретно с malloc()/free() (даже не C++) - я налетал прямо на debug/release builds. В DLL-ке аллоцируется память (релизным malloc()) - и при освобождении этого указателя в .EXE (debug free()) - все рушится. Мне еще тогда лечили голову, что если ты что-то породил в DLL, то там и убивай: http://blog.lexa.ru/2010/09/04/nu_kto_tak_stroit.html

Нет, если у тебя оба модуля слинкованы в динамике(т.е. статического рантайма нет) и в память была загружена только одна версия msvcrt то очевидно, что она память выделяет и она же грохает, просто потому что никого другого кто это умеет нет.

Std::string не в msvcrt, а в stdчтототам, тоже обе должны быть в динамике, и да у них меняется название либы при бинарной несовместимости, на этом построена вся стабильность майкросовтовского dll-hell :)

Так тут же не линукс.
DLL может использовать одну версию msvcrt, EXE - другую.

И это, типа, "правильно"

Не может если ты только не поебался и специально их из разных папок не загрузил, правило ресолвинга одинаковое.
Вот msvcrt и msvcrtd легко, или msvcrt и статический рунтайм да, но это ты ссзб

Ну вот видится мне такой сценарий
- собираем someclass.dll c некоей текущей версией рантайма
- обновляется студия
- собираем exe.exe (someclass.dll - не обновляется, потому что там ничего не меняется)

Запускаем это чудо. someclass.dll возьмет старый рантайм (из Windows/winsxs/...), exe.exe - новый.
Вот, к примеру, msvcr80.dll - у меня на машине в winsxs их лежит 6 штук разных, трех разных размеров.

Может, поскольку этих CRT много разных:
msvcrt.dll
msvcrt20.dll
msvcrt40.dll
msvcr70.dll
msvcr71.dll
msvcr100.dll

Нет уже давно никакой msvcrt.dll. В 2010 студии сишный рантайм живет в msvcr100.dll, плюсовой в msvcp100.dll, в 2012 номера поменялись на 110.

Меня больше волнует случай, когда оба собраны с 100, но разных версий. И я тоже не вижу в этом ничего такого невозможного.

Тут зависит от наличия/состава манифеста у dll и exe. Если они создавались с манифестом, в котором явно указывалось что-то типа <assemblyIdentity type="win32" name="Microsoft.VC90.MFC" version="9.0.xxxxx.yy" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity> - то каждый модуль так и будет за него держаться.

Но практика такой привязки по умолчанию закончилась с 2008 студией, сейчас в манифестах разве что к Common Controls привязка генерируется. Ну в а dll и exe без явной привязки рантайм должен загружаться один и тот же.

В _теории_ должно работать, т.к. очень часто установка VC runtime переписывает все системные dll новой версией. На практике народ говорит, что проблемы иногда бывают и если совсем параноик то проще кинуть в свою директорию правильные версии dll-ок.

В WinSxS есть механизм подмены, когда одна версия полностью закрывает другую, такое используется при небольших итерациях и делалось на моей памяти при выпуске service pack-ов к Visual Studio. В остальных случаях рантаймы имеют разные версии и вполне нормально, когда сторонняя библиотека имеет другой рантайм и плохо когда это не волнует разработчиков той библиотеки. Надо либо все это оборачивать в классы или что-то типа COM делать, либо давать юзеру устанавливать свои аллокаторы.

Вобщем проблема такая есть. Иногда просто требуют - собираешь например плагин к такой-то программе - изволь взять VS такой-то версии и вперед. Multitargeting VS научилась только недавно делать, а иначе приходится держать зоопарк студий.

msvcrt и msvcr это разные dll, а номера это и есть бинарная совместимость. Замечу если у тебя проект загрузил 2 разные именованные версии это не windows плохая, а ты что так слинковал.

> То в какой-то момент это меня обязательно поимеет?

Всенепременно.

И независимо от длины ночной рубашки!

Примерно так.

Насколько мне помнится, стандарт ничего не обещает про инстанцированные шаблоны, кроме ODR (one definition rule). А ODR работает в границах единицы компоновки.

Думаю будет проблема.
Использую такой вариант - надо писать свой класс в котором аллокация и освобождение будет делаться через пару виртуальных функций. Можно даже шаблон сделать с этими виртуальными функциями. Главное что теперь эти функции будут находиться через таблицу виртуальных функций от конкретного экземпляра этой строки.

Причем модульную безопасность с помощью такого метода могли бы везде проделать в STL, но там есть только механизм аллокаторов, который никак не спасет, потому что аллокатор не крепится к классу, а является параметром шаблона и соответственно будет собираться в каждом модуле по своему.

В 94.674% случаях поимеет, а в високосный год - итого больше.
Проблемы - разный heap, разная реализация, разный layout данных.
Но если код somefunc(std::string *) полностью в заголовке(inline), компилируется на твоей стороне и сам границу пересекает только по lingua franca С API - то вполне легально.

И кстати, это походу как раз твой случай:
XMP-Toolkit-SDK-CS6/public/include/client-glue/TXMPMeta.incl_cpp, и дальше если нырять, то находится:

void
WXMPMeta_SerializeToBuffer_1
(
XMPMetaRef xmpObjRef,
void * pktString,
XMP_OptionBits options,
XMP_StringLen padding,
XMP_StringPtr newline,
XMP_StringPtr indent,
XMP_Index baseIndent,
SetClientStringProc SetClientString,
WXMP_Result * wResult
) /* const */
{
XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_SerializeToBuffer_1" )

XMP_VarString localStr;

if ( newline == 0 ) newline = "";
if ( indent == 0 ) indent = "";

thiz.SerializeToBuffer ( &localStr, options, padding, newline, indent, baseIndent );
if ( pktString != 0 ) (*SetClientString) ( pktString, localStr.c_str(), localStr.size() );

XMP_EXIT
}

Но раз у тебя есть сборка - тебе проще по step-into убедится, что дёргаются методы именно твоего string'а.
В общем, с тебя бутылка :)

Есть тут кто? или уже /dev/null?

Ну я есть.

Я собрал это самое XMPCore статикой а не dll от греха.

По-идеи они обо всём позаботились - то что использует std::string из твоего рантайма, собирается на твоей стороне.
Но на практике могли где-то ошибиться, что-то забыть. Так что да, если есть возможность недорого перестраховаться, то почему бы и нет.

Add new comment