Модные кнопки для win32 приложений на CL
Aug. 19th, 2011 08:25 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Почему "модные"?
Ну потому, что look-and-feel в стиле Windows 2000 в 2011 году не моден.
Начну издалека.
Стандартные win32-контролы(так называемые Common Controls)(обертками над которыми являются классы MFC, Windows.Forms, или других подобных GUI-тулкитов), начиная с кнопок и заканчивая всякими там TreeView, в винде реализованы в библиотеке comctl32.dll. И с этой библиотекой связан один очень неприятный момент.
Дело в том, что давным-давно, а именно с появлением Windows XP, Microsoft выпустила 6ю версию этой библиотеки, в которую добавила кучу свистелок и перделок("темы" и т.п.), связанных главным образом с отрисовкой(благодаря этому, новые версии винды выглядят не так уныло, как раньше, т.е. во времена Win95-2000). Но, к несчастью, 6я comctl32 стала немножко обратно-несовместимой с предыдущими версиями, и по этой причине, если мы линкуемся с этой библиотекой, или же подружаем ее динамически(через LoadLibrary etc.), винда по дефолту подгружает 5ю версию, и все стандартные виджеты в нашем приложении выглядят так же, как они должны были выглядеть 11 лет назад.
Заставить систему подключать нам как минимум 6ю comctl32 можно через так называемые "контексты активации". К несчастью для динамичного лиспового видения программ, единственный способ задать стандартный контекст активации, или создать новый довольно статичен по своей сути - это так называемые "манифесты". Манифест представляет собой xml-документ, в котором описываются какие-либо свойства приложения/библиотеки(assembly), считываемые Windows при ее загрузке.
Возвращаясь к теме постинга - одними из самых важных вещей, описываемых в манифестах, являются требования к библиотекам и COM-компонентам, от которых данное конкретное assembly зависит, и именно таким образом мы можем сказать системе, что нам нужна как минимум 6я версия библиотеки Common Controls.
Задать манифест для приложения можно двумя способами - первый, и наиболее предпочтительный - вкомпилировать его в секцию "ресурсов" PE, а второй - записать его в файл с именем "имя-приложения.exe.manifest" и положить рядом с исполняемым файлом программы. Со вторым способом связана одна небольшая хитрость - дело в том, что Windows кэширует манифесты такого рода, и это значит, что если программа раньше была без манифеста, и уже когда-нибудь запускалась, а потом нам вдруг приспичило манифест создать, то система не будет о нем знать до того, как мы изменим(обновим дату изменения) исполняемый файл нашей программы.
Я предпочитаю использовать первый способ и собирать рантаймы лиспов с уже включенным в них манифестом.
Как это делается? Ну для примера возьмем SBCL.
Предположим, манифест имеет такой вид:
Запишем его в файл с именем, ну, пусть "program.manifest".
После этого создаем файл описания ресурсов(пусть будет "program.rc"),
и берем утилиту windres из тулчейна MinGW, с помощью которого мы компилируем этот файл:
Далее мы складываем полученный файл в директорию путь_к_сорцам_sbcl/src/runtime/ и немного изменяем файл GNUMakefile, находящийся там же; в частности, строчку
меняем на
Ну и после этого собираем SBCL как обычно(кстати, вот если кому надо, бинарник(x86-32) из HEAD форка
akovalenko, собранный таким образом, с иконкой от lisperati.com: http://rghost.ru/18554241).
Ну и напоследок, вот пример одной и той же программы(http://paste.lisp.org/display/124128) на Windows 7 со включенными Visual Styles(т.е. comctl32 >= 6.0.0.0)(справа) и без них(слева):

Ну потому, что look-and-feel в стиле Windows 2000 в 2011 году не моден.
Начну издалека.
Стандартные win32-контролы(так называемые Common Controls)(обертками над которыми являются классы MFC, Windows.Forms, или других подобных GUI-тулкитов), начиная с кнопок и заканчивая всякими там TreeView, в винде реализованы в библиотеке comctl32.dll. И с этой библиотекой связан один очень неприятный момент.
Дело в том, что давным-давно, а именно с появлением Windows XP, Microsoft выпустила 6ю версию этой библиотеки, в которую добавила кучу свистелок и перделок("темы" и т.п.), связанных главным образом с отрисовкой(благодаря этому, новые версии винды выглядят не так уныло, как раньше, т.е. во времена Win95-2000). Но, к несчастью, 6я comctl32 стала немножко обратно-несовместимой с предыдущими версиями, и по этой причине, если мы линкуемся с этой библиотекой, или же подружаем ее динамически(через LoadLibrary etc.), винда по дефолту подгружает 5ю версию, и все стандартные виджеты в нашем приложении выглядят так же, как они должны были выглядеть 11 лет назад.
Заставить систему подключать нам как минимум 6ю comctl32 можно через так называемые "контексты активации". К несчастью для динамичного лиспового видения программ, единственный способ задать стандартный контекст активации, или создать новый довольно статичен по своей сути - это так называемые "манифесты". Манифест представляет собой xml-документ, в котором описываются какие-либо свойства приложения/библиотеки(assembly), считываемые Windows при ее загрузке.
Возвращаясь к теме постинга - одними из самых важных вещей, описываемых в манифестах, являются требования к библиотекам и COM-компонентам, от которых данное конкретное assembly зависит, и именно таким образом мы можем сказать системе, что нам нужна как минимум 6я версия библиотеки Common Controls.
Задать манифест для приложения можно двумя способами - первый, и наиболее предпочтительный - вкомпилировать его в секцию "ресурсов" PE, а второй - записать его в файл с именем "имя-приложения.exe.manifest" и положить рядом с исполняемым файлом программы. Со вторым способом связана одна небольшая хитрость - дело в том, что Windows кэширует манифесты такого рода, и это значит, что если программа раньше была без манифеста, и уже когда-нибудь запускалась, а потом нам вдруг приспичило манифест создать, то система не будет о нем знать до того, как мы изменим(обновим дату изменения) исполняемый файл нашей программы.
Я предпочитаю использовать первый способ и собирать рантаймы лиспов с уже включенным в них манифестом.
Как это делается? Ну для примера возьмем SBCL.
Предположим, манифест имеет такой вид:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity type="Win32" name="MyProgram" processorArchitecture="x86" version="1.0.0.0" /> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> <dependency> <dependentAssembly> <assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/> </dependentAssembly> </dependency> </assembly>
Запишем его в файл с именем, ну, пусть "program.manifest".
После этого создаем файл описания ресурсов(пусть будет "program.rc"),
#include <winuser.h> 1 ICON program.ico // application icon CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST program.manifest
и берем утилиту windres из тулчейна MinGW, с помощью которого мы компилируем этот файл:
windres -i program.rc -o resources.res -O coff
Далее мы складываем полученный файл в директорию путь_к_сорцам_sbcl/src/runtime/ и немного изменяем файл GNUMakefile, находящийся там же; в частности, строчку
OBJS = $(C_SRC:.c=.o) $(ASSEM_SRC:.S=.o) ${OS_OBJS}
меняем на
OBJS = $(C_SRC:.c=.o) $(ASSEM_SRC:.S=.o) resources.res ${OS_OBJS}
Ну и после этого собираем SBCL как обычно(кстати, вот если кому надо, бинарник(x86-32) из HEAD форка
![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
Ну и напоследок, вот пример одной и той же программы(http://paste.lisp.org/display/124128) на Windows 7 со включенными Visual Styles(т.е. comctl32 >= 6.0.0.0)(справа) и без них(слева):
