Материал из Tkabber Wiki
Внимание! реализация механизмов, обсуждаемых здесь, интегрирована в транк Ткаббера начиная с ревизии 859 (05 янв 2007). Реализованный механизм изложен ниже.
Также был обновлён раздел #4 "Configuration" документации Ткаббера — читайте tkabber.html на своей системе.
Содержание
- 1 Введение
- 2 Инфа по системам
- 3 Проблемы
- 4 Рабочий вариант
- 5 Добавка про SHGetSpecialFolderPath
- 6 Тестовая реализация SHGetSpecialFolderPath через Ffidl
Введение
Обсуждение новой схемы поддержки каталога конфигурации Ткаббера.
Идея: отказаться от схемы ~/.tkabber в Windows по причине того, что при существующей схеме каталог конфигурации на любой Windows-системе оказывается не там, где предполагается хранить конфигурацию на данной системе.
Конкретно нас интересует т.н. каталог "application data" ("appdata"). Предполагается хранить настройки Ткаббера в подкаталоге "Tkabber" этого каталога.
Получить путь каталога "appdata" можно тремя способами:
Переменная окружения APPDATA:
- Появилась только в Windows 2000; присутствует во всей последующей линейке, включая Vista.
Переменная реестра
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\AppData
:- Имеет место как минимум в Win98 SE;
- Присутутвует во всей последующей линейке, однако в Vista весь ключ "Shell Folders" помечен как "устаревший", однако, он содержит правильные значения.
Процедура Win32 API "SHGetSpecialFolderPath":
- On MSDN: http://msdn2.microsoft.com/en-us/library/ms647816.aspx
- Since {Win98 (SE?), Win2000} + on {WinNT 4+, Win95+ with IE4 installed}.
Инфа по системам
Win98 (SE):
- Каталог:
%windir%\Application Data
; - %APPDATA% — нет;
- Ключ "Shell Folders" — есть;
- Примечание: уже есть %windir%.
- Каталог:
WinNT 4.0 (русская):
- Каталог:
%USERPROFILE%\Данные
; - %APPDATA% — нет;
- Ключ "Shell Folders" — есть;
- Примечание: кривое значение %HOMEPATH% (корень загрузочного диска), в правильное место показывает %USERPROFILE% (%windir%\Profiles\<USERNAME>).
- Каталог:
w2k, w2k3, XP:
- Каталог: %APPDATA%;
- Ключ "Shell Folders" — есть, но, откровенно говоря, он всегда был deprecated;
Vista:
- Каталог: %APPDATA%;
- Ключ "Shell Folders" — есть, но deprecated.
(TODO): 95? ME?
Проблемы
Наиболее разумным представляется вызов SHGetSpecialFolderPath, однако это не самый простой путь:
Данную функциональность в готовом виде обеспечивает пакет TWAPI, однако, он:
- Не поставляется вместе с дистром AS;
- Весит >600к в архиве, что будет препятствовать его попаданию в старпак и вообще отобьёт охоту у пользователей его устанавливать.
Возможно "цеплять" DLL, предоставляющую данный вызов API, например, при помощи Ffidl, однако:
- Этот пакет так же не поставляется с AS;
- Прямой вызов функции из DLL выглядит "не очень".
Путь с реестром и окружением не имеет такой "идеологической чистоты", как вызов SHGetSpecialFolderPath.
- Недостаток: прямое чтение реестра и окружения в условиях огульного изменения своих же "стандартов" соответствующей фирмой является "ненадёжным"; кроме того, в ОС, следующей за Vista, соотв. раздел реестра может быть изъят полностью.
- Достоинства:
- Работа с переменными окружения доступна непосредственно;
- Пакет registry для работы с реестром входит в "ядро" виндовой версии тикля, то есть очень велика вероятность его доступности даже в старпаке;
- Обсуждаемый ключ реестра доступен на всех исследованных системах.
Рабочий вариант
При старте:
- Ткаббер проверяет наличие переменной окружения TKABBER_HOME, если она есть, Ткаббер использует её как путь до каталога своих настроек.
Если её нет, то:
- В Unix каталог настроек устанавливается в ~/.tkabber.
- В Windows применяются описанные выше эвристики:
- Проверяется переменная окружения APPDATA;
- Проверяется ключ реестра "Shell Folders", если доступен пакет registry;
- К полученному каталогу добавляется элемент пути "Tkabber", и этот каталог используется как каталог настроек.
Вопросы:
- Возможно, стоит сначала смотреть в реестр, а потом — в окружение;
- Нужно предусмотреть разумный Fallback для различных версий Windows; идея ~/.tkabber для этого случая представляется не очень разумной.
Juriks, посовещавшись с Kostix, предлагает: Запускать несколько Ткабберов с разными настройками с помощью таких вот скриптов:
@echo off
set TKABBER_HOME=c:\home\vasya_pupkin
start tkabber.exe
То есть искать папку с конфигами в таком порядке:
- переменная окружения TKABBER_HOME
- реестр
- прочие переменные оружения
Kostix комментирует: реализованный в альфе Ткаббера вариант сначала ищет переменную окружения APPDATA, и только в случае неудачи смотрит реестр. Причины простые:
- Пакет registry, хоть и входит в "ядро" виндового тикля, технически может отсутствовать (в ткаббер-паке или старпаке, к примеру), а вот помешать тиклю работать с окружением не может никакая конфигурация его рантайма;
- Загрузка пакета — это доп. время для старта + доп. расход памяти. А пакет registry пока что больше нигде в Ткаббере не используется.
- Реестр ничем не лучше переменной APPDATA в плане надёжности: точно так же, как в системе может быть кривое значение APPDATA, может быть крив ключ Shell Folders реестра. Только вызов соотв. функции Win32 API из shell32.dll даёт гарантии. Но он требует "неядерного" (и отсутствующего в дистре от AS) пакета Ffidl. Поэтому включение соответствующего кода в Ткаббер — вопрос сложный...
Добавка про SHGetSpecialFolderPath
SHGetSpecialFolderPath «замещена» процедурой SHGetFolderPath начиная с w2k, хотя и доступна на всех системах. Для нас это ничего не значит, так как нам для совместимости со старыми OS необходимо использовать старую версию.
На современных системах SHGetSpecialFolderPath реализована в shell32.dll. Также эта функция содержится в redistributable library SHFolder.dll, которая может включаться в поставку продукта и худо-бедно работать на любой OS. Starpack это, пожалуй, не спасёт, а вот Pack и выше может спокойно её использовать.
Тестовая реализация SHGetSpecialFolderPath через Ffidl
Скрипт
Скрипт, печатающий путевое имя каталога "application data" в Windows.
Создаваемая команда Tcl "SHGetSpecialFolderPath" способна возвращать имя любого стандартного каталога Windows (заданного соотв. ключом "CSIDL_...").
В случае неуспеха команда возвращает пустую строку.
#! /usr/bin/tclsh
package require Ffidl
ffidl::callout dll_SHGetSpecialFolderPath \
{int pointer-utf16 int int} int \
[ffidl::symbol shell32.dll SHGetSpecialFolderPathW]
proc SHGetSpecialFolderPath {what create} {
array set CSIDL {
CSIDL_DESKTOP 0
CSIDL_INTERNET 1
CSIDL_PROGRAMS 2
CSIDL_CONTROLS 3
CSIDL_PRINTERS 4
CSIDL_PERSONAL 5
CSIDL_FAVORITES 6
CSIDL_STARTUP 7
CSIDL_RECENT 8
CSIDL_SENDTO 9
CSIDL_BITBUCKET 10
CSIDL_STARTMENU 11
CSIDL_DESKTOPDIRECTORY 16
CSIDL_DRIVES 17
CSIDL_NETWORK 18
CSIDL_NETHOOD 19
CSIDL_FONTS 20
CSIDL_TEMPLATES 21
CSIDL_COMMON_STARTMENU 22
CSIDL_COMMON_PROGRAMS 23
CSIDL_COMMON_STARTUP 24
CSIDL_COMMON_DESKTOPDIRECTORY 25
CSIDL_APPDATA 26
CSIDL_PRINTHOOD 27
CSIDL_LOCAL_APPDATA 28
CSIDL_ALTSTARTUP 29
CSIDL_COMMON_ALTSTARTUP 30
CSIDL_COMMON_FAVORITES 31
CSIDL_INTERNET_CACHE 32
CSIDL_COOKIES 33
СSIDL_HISTORY 34
CSIDL_COMMON_APPDATA 35
CSIDL_WINDOWS 36
CSIDL_SYSTEM 37
CSIDL_PROGRAM_FILES 38
СSIDL_MYPICTURES 39
CSIDL_PROFILE 40
СSIDL_SYSTEMX86 41
CSIDL_PROGRAM_FILESX86 42
CSIDL_PROGRAM_FILES_COMMON 43
СSIDL_PROGRAM_FILES_COMMONX86 44
CSIDL_COMMON_TEMPLATES 45
CSIDL_COMMON_DOCUMENTS 46
CSIDL_COMMON_ADMINTOOLS 47
CSIDL_ADMINTOOLS 48
CSIDL_CONNECTIONS 49
CSIDL_COMMON_MUSIC 53
CSIDL_COMMON_PICTURES 54
CSIDL_COMMON_VIDEO 55
CSIDL_RESOURCES 56
CSIDL_RESOURCES_LOCALIZED 57
CSIDL_COMMON_OEM_LINKS 58
CSIDL_CDBURN_AREA 59
CSIDL_COMPUTERSNEARME 61
CSIDL_FLAG_DONT_VERIFY 0x4000
CSIDL_FLAG_CREATE 0x8000
CSIDL_FLAG_MASK 0xFF00
}
set bCreat [expr {$create ? 1 : 0}]
set path [string repeat \u0000 300] ;# MAX_PATH is actually 260
set ok [dll_SHGetSpecialFolderPath 0 $path $CSIDL($what) $bCreat]
if {$ok} {
set ix [string first \u0000 $path]
if {$ix > 0} {
return [string range $path 0 [expr {$ix - 1}]]
}
} else {
return {}
}
}
puts "appdata: [SHGetSpecialFolderPath CSIDL_APPDATA false]"
Запускать следует через tclsh, например, так:
C:\> tclsh sh.tcl
wish не имеет открытого канала stdout и puts там ничего интересного не делает.
Можете так же поменять последнюю строку (с puts) на
tk_messageBox -message [SHGetSpecialFolderPath CSIDL_APPDATA false]
и запускать скрипт "просто", например, двойным щелчком мышью на его файле в Explorer.
Замечания
Вписал статью в тиклевое вики.
ANSI (8-bit) vs Unicode (UTF-16)
- Не совсем ясно, доступна ли юникодная версия (SHGetSpecialFolderPathW) в старых Win9x. Похоже, что доступна.
- Не совсем ясно поведение Ffild по отношению к строкам в UTF-16: приёмный буфер должен быть MAX_PATH символов? Тогда что имеется в виду: тиклевые символы UTF-8 или виндовые символы UTF-16?
В любом случае, ANSI версия используется так:
ffidl::callout dll_SHGetSpecialFolderPath \
{int pointer-utf8 int int} int \
[ffidl::symbol shell32.dll SHGetSpecialFolderPathA]
Реализация в Ткаббере
Потребует более серьёзного подхода:
- Пространство имён config;
- "Пустая" реализация SHGetSpecialFolderPath для платформ, отличных от Windows;
- Отслеживание успешного завершения создания callout'а для SHGetSpecialFolderPath с созданием пустой реализации (см. выше) в случае ошибки.