Выбор правильного варианта CRT-библиотеки
Некоторая неразбериха вокруг применения CRT-библиотек при разработке приложений Windows связана с тем, что необходимо решать, какую библиотеку следует использовать. Существует шесть версий этой библиотеки, которые можно разделить на две категории: отладочную (DCRT) и выпускную (CRT). В каждую категорию входит однопоточная статическая библиотека, многопоточная статическая библиотека и многопоточная библиотека динамической компоновки (DLL).
Статические версии CRT-библиотек включают библиотечные функции прямо в приложение. Эти версии используются по умолчанию для не-МРС-приложений, которые собираются с помощью мастеров. Преимущество статических версий заключается в том, что DLL таких библиотек не нужно отправлять заказчику вместе с разработанным продуктом. Недостатком же является то, что сильно возрастает размер двоичных файлов, так что рабочая конфигурация приложения становится довольно большой. Однопоточный и многопоточный варианты статической библиотеки CRT не требуют объяснений. Если вы создаете DLL и хотите использовать статическую CRT-библиотеку, то нужно выполнять компоновку только с многопоточной версией этой библиотеки (в противном случае приложение не сможет использовать вашу DLL, потому что однопоточные статические CRT-библиотеки не являются потокобезопасными).
DLL-версии библиотек CRT с именами MSVCRT(D).DLL позволяют импортировать их функции. Большим преимуществом этих DLL является то, что размер двоичных файлов радикально уменьшается, значительно сокращая, таким образом, рабочий набор приложения.
Поскольку другие приложения загружают одну и ту же DLL-версию библиотеки CRT, операционная система может разделять таблицы страниц кодовых секций DLL между процессами, так что вся система будет выполняться быстрее. К недостатком таких DLL-версий следует отнести то, что приложения могут быть ориентированы на другие варианты DLL, поэтому вместе с ними придется распространять и соответствующие варианты DLL.
Для приложений чрезвычайно важен выбор одной версии CRT-библиотеки, используемой всеми двоичными файлами, которые загружаются в адресное пространство их главной программы.
Если приложение работает с несколькими DLL, каждая из которых использует свою статическую CRT-библиотеку, то мало того что увеличивается адресное пространство за счет дублирования кода, но также повышается риск введения одной из наиболее коварных ошибок, которую можно отслеживать месяцами. Если вы распределяете область кучи в одной DLL, а пытаетесь освобождать ее в другой, использующей иную версию библиотеки CRT, то легко можно вызвать аварийное завершение, потому что DLL, освобождающая память не знает, откуда взялась распределяемая память. Было бы ошибкой полагать, что в данном случае мы имеем дело с обычной кучей, т. к. наличие различных версий CRT-библиотеки, выполняющихся одновременно, означает, что имеется много версий кода управления кучей.
Я почти всегда использую DLL-версии библиотек CRT и советую всем придерживаться этого правила. Выгода от уменьшения количества и размера компилированных файлов перекрывает любые другие соображения. Например, при разработке игр, где никогда не будет нужна многопоточность и где производительность является суперкритичным параметром, я допускаю даже использование однопоточных статических версий CRT-библиотек, чтобы избежать накладных расходов, возникающих за счет применения механизмов многопоточной блокировки.
В утилите BUGSLAYERUTIL.DLL используются DLL-версии библиотек CRT. Кроме того, в BUGSLAYERUTIL.DLL включено два расширения DCRT-библиотеки: MemDumperValidator и MemStress, которые рассмотрены ниже в этой главе. Ожидается, что вы пользуетесь DLL-версиями этих расширений. Однако если вы хотите, чтобы приложение работало с модулями ЕХЕ, а не их DLL-версиями, то нужно просто взять исходные файлы MEMDUMPERVALIDATOR.CPP, MEMDUMPERVALIDATOR.H, а также MEMSTRESS.CPP, MEMSTRESSCONSTANTS.H и MEMSTRESS.H, изменить компоновку указанных функций и поместить их в приложение.
Хочу обратить ваше внимание на одну дополнительную деталь применения BUGSLAYERUTIL.DLL. Можно наблюдать некоторое замедление приложения в зависимости от того, как распределяется память.Чтобы разрешить полное прослеживание и проверку корректности памяти, я устанавливаю в расширении MemDumperValidator все подходящие флажки DCRT-библио-теки, включая _CRTDBG_CHECK_ALWAYS_DF. Установка этого флажка приводит к тому, что каждый раз, когда вы распределяете или освобождаете кучу, DCRT-библиотека просматривает каждый ее участок и проверяет корректность данных. Если в приложении происходят тысячи распределений небольших областей памяти, то его выполнение ощутимо замедлится. Если замедление недопустимо, то возможны два решения. Первое — перед выполнением распределений памяти нужно сбросить флажок _CRTDBG_CHECK _ALWAYS_DF (обратившись к функции _GrtsetDbgFiag). Второе — проверить алгоритм и посмотреть, нужны ли вообще распределения небольших участков памяти, потому что даже без проверки отладочной кучи операции выделения памяти чрезвычайно замедляют приложение
.