love5an: (Default)
[personal profile] love5an
Почему "модные"?
Ну потому, что 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] akovalenko, собранный таким образом, с иконкой от lisperati.com: http://rghost.ru/18554241).

Ну и напоследок, вот пример одной и той же программы(http://paste.lisp.org/display/124128) на Windows 7 со включенными Visual Styles(т.е. comctl32 >= 6.0.0.0)(справа) и без них(слева):

Date: 2011-08-22 02:57 am (UTC)
From: [identity profile] akovalenko.livejournal.com
Возможно, стоит обратить внимание на BeginUpdateResource / UpdateResource / EndUpdateResource: это "у себя дома" уместно использовать windres, а поставлять что-то с зависимостью от mingw toolchain я бы не стал.

(Примечание для SBCL: если натравить UpdateResource на исполняемый дамп, он откусит built-in core. Так что работать надо с чистым sbcl.exe, а когда туда засунут манифест, иконки, versioninfo и что ещё захотелось -- тогда уже дампить (примечание 2: распилить исполняемый дамп на runtime+core довольно просто, в том числе из лиспа)).

Date: 2011-08-29 02:20 pm (UTC)
From: [identity profile] love5an.livejournal.com
>а поставлять что-то с зависимостью от mingw toolchain я бы не стал.
Ну, да, это так. Но я как раз и пишу про "у себя дома", в принципе, ну то есть, как настроить это всё под себя, когда оно надо.

>Примечание для SBCL: если натравить UpdateResource на исполняемый дамп, он откусит built-in core.
Угу, я уже сталкивался с этим.

Date: 2011-08-29 02:21 pm (UTC)
From: [identity profile] love5an.livejournal.com
Кстати, а как насчет того, чтобы в виндовом форке SBCL завести все-таки под дамп какую-либо секцию? Ну, чтобы вот тот же UpateResource не откусывал его.

Date: 2011-08-30 05:21 pm (UTC)
From: [identity profile] akovalenko.livejournal.com
Отдельную секцию (или ресурс) я не сделал потому, что лелеял более амбициозный план: хотелось всобачивать дамп таким образом, чтобы dynamic space на старте прямо сразу отображался куда надо. Оказалось, что такое сделать нетривиально, хотя и возможно в принципе (например, я не ожидал, что загружаемые секции обязаны следовать непрерывно, и нигде про это не читал -- узнал, только когда винды отказались грузить "прерывистый" образ). В общем, буду откатываться на "запасной план" пока.

В смысле генерации имиджей у нас любопытная хрень только что появилась: возможность указывать комбинированный executable в качестве аргумента --core (оно есть в моих исходниках и прошло в апстрим, но бинарники с этой фичей я пока не собирал). Т.е., например, если у тебя вначале есть большой-развесистый dumped executable, он может скопировать свой исполняемый префикс (искать его длину проще всего через предпоследнее слово embedded core, а не через pe header), потом отредактировать ресурсы в копии, а потом запустить копию, указав в --core текущий *runtime-pathname*, и скомандовать копии, чтобы она сдампилась.

Есть ещё такая идея, кстати: завести ресурс, из которого будет читаться относительный путь к non-embedded core (более приоритетный, чем любая дедукция из SBCL_HOME и из "хвоста" GetModuleFileName). Таким образом можно будет деплоить пару exe+core, которая будет независима от окружения -- и я подозреваю, что в ситуации, когда одним exe дело уже не ограничивается, это не хуже embededded core по удобству (к примеру, если всё равно нужно воткнуть sqlite3.dll рядом -- нет особой разницы, в двух частях едет runtime+core или в одной).

Date: 2011-08-30 06:04 pm (UTC)
From: [identity profile] akovalenko.livejournal.com
UPD. Бинарники только что обновил, так что новый способ обработки --core в них есть.

Кстати, я тут на днях документировал полезные команды SB-ACLREPL, которые добавляю в "standalone exe" билды (не в msi). Типа, говоришь :qload hunchentoot, и (при необходимости) автоматом качается quicklisp, ставится, притаскивает hunchentoot (жалуясь в процессе на libssl, правда), компилирует его и грузит. Может, сгодится для чего (а если есть идеи по инновациям-модернизациям, они сгодятся мне :).

Profile

love5an: (Default)
Dmitry Ignatiev

June 2020

S M T W T F S
 123456
78910 111213
14151617181920
21222324252627
282930    

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Aug. 1st, 2025 08:44 am
Powered by Dreamwidth Studios