CSkin - CBaseSkin - CXPThemeSkin
CSkin представляет из себя интерфейс, CBaseSkin рисует без XP-тем (то есть вполне должен выполняться на Windows 2000), CXPThemeSkin соответственно рисует с помощью XP-тем (если не удается - рисует с помощью своего базового класса - CBaseSkin).
CXPThemeSkin использует всякие OpenThemeData(), CloseThemeData() из uxtheme.dll, CBaseSkin ничего этого не использует.
При старте программа определяет версию Windows и при WinXP создает объект CXPThemeSkin, в противном случае - CBaseSkin.
И я наивно полагал, что это будет работать на Windows 2000. А вчера поставил Win2k на виртуальную машину и... разочаровался - при старте приложение вылетало с ошибкой "Unable to find uxtheme.dll". Самое интересное, что дело не доходило даже до WinMain() и создания объекта CxxxSkin. То есть XP-темы еще не использовались, а приложение уже вылетало.
При разборе полетов выяснил, что любое упоминание в программе функций из uxtheme.dll приводит к ее загрузке uxtheme.dll, даже такой безобидный код:
void foo()
{
HTHEME Theme = OpenThemeData(NULL, NULL);
}
void WindMain(...)
{
return 0; // do not call foo()
}
Я уж было предполагал, что придется отказаться от простых вызовов OpenThemeData() и подобных, и переписать весь код с использованием LoadLibrary("uxtheme.dll"), вручную получением указателей на функции типа OpenThemeData().
Оказалось, все гораздо проще: достаточно указать в настройках проекта
Linker/Input/Delay Loaded DLLs: uxtheme.dll
5 комментариев:
> При разборе полетов выяснил,
> что любое упоминание в программе
> функций из uxtheme.dll приводит
> к ее загрузке uxtheme.dll
Это, как говориться, by design. Этим как раз статическая линковка от динамической и отличается.
> Этим как раз статическая линковка от динамической и отличается.
Но ведь не обязательно же грузить все библиотеки при старте программы. Можно и потом сделать LoadLibrary()
> Но ведь не обязательно же
> грузить все библиотеки при
> старте программы.
Теоретически это так, но на практике всё несколько сложнее. Просматривая список статически прилинкованных библиотек NT Loader просто обязан загрузить каждую из них. Этому есть две причины: во-первых у него не будет никаких шансов загрузить эту dll по требованию, поскольку вызов функции из dll мало чем отличается от простого вызова функции. Loader об этом вызове никак не уведомляется. Во-вторых код в DllMain может иметь скрытые зависимости, о которых Loader так же не подозревает.
С другой стороны отложенная загрузка AKA delayed DLL loading с точки зрения Loader'а ничем не отличается от использования LoadLibrary()/GetProcAddress(). При этом компилятор генерирует дополнительный код, который загружает DLL по первому обращению.
Я веду к тому, что программист должен сам сказать OS как загружать библиотеки, иначе все они будут загружаться согласно выбору по-умолчанию, т.е. при старте.
То есть в случае отложенной загрузки генерируется некий оверхед, и поэтому по умолчанию отложенная загрузка отключениа для уменьшения оверхеда.
В принципе, логично. Логично что отключена.
> То есть в случае отложенной
> загрузки генерируется некий
> оверхед
Почти так. Оверхед есть, но скорее всего он одноразовый и им можно пренебречь. Основная причина - совместимость. Любое изменение логики NT Loader влечет за собой жуткий хвост проблем с совместимостью. А так получается, что delayed loading это просто надстройка над стандартным механизмом. Кроме того, только программист действительно может определить когда правильно загружать DLL. Компилятор и линкер тоже пасуют.
Отправить комментарий