Делаем правильную установку под Вистой c NSIS
April 4th, 2009 Begemot Posted in Шаровароварение
Итак, о том, как решать проблему с правами пользователя при инсталле, использую NSIS. В кратце напомню суть проблемы: надо поставить программу в Programm Files (1), установить бд по умолчанию в каталог пользователя (2), добавить себя в автозагрузку (3), автоматически запустить программу после установки (4).
Устанавливаем под Висту. Если запускать под аккаунтом пользователя без прав админа – траблы уже с (1) пунктом. Если дать права админа, то не получается корректно сделать все остальное – инсталлер, запущенный из под админа, совершенно логично пишет в application data админа, добаляет ярлыки на desktop админа и запускает программу опять же из под его аккаунта, что приводит к “потере” всех данных сохраненных в первом сеансе работы…
Все что нам надо для наведения порядка, вот этот замечательный UAC plug-in и ловкость рук, а тестирование я уже взял на себя:) За давностью решения (почти два месяца собирался написать!) я уже помню не все, буду дампить сюда куски скрипта с небольшими пояснениями
В самом начале говорим, что нам надо права простого юзера(!)
RequestExecutionLevel user ;// see UAC plugin
Дальше заменяем код, ответственный за запуск программы в конце инсталляции, обратите внимание в первой строчке вторая половина – это старый код, теперь просто комментарий
; Finish page
!define MUI_FINISHPAGE_RUN ;”$INSTDIR\Flashpaste.exe”
!define MUI_FINISHPAGE_RUN_FUNCTION ExecAppFile
Function ExecAppFile
UAC::Exec ” ‘”$INSTDIR\Flashpaste.exe”‘ ” ”
FunctionEnd
Говорим что ярлыки будем ставить для всех юзеров в начале секции и запускаем функцию RunFromUser в конце
Section “MainSection” SEC01
SetShellVarContext all
… весь код главной секции
GetFunctionAddress $0 RunFromUser
UAC::ExecCodeSegment $0
SectionEnd
Ну и сразу же код функции: Говорим, что ставим ярлык для текущего юзера, пишем файл в апп дата для текущего юзера и добавляем в автозагрузку для него же. Код для добавления в автозагрузку дублируется и тут и в той части что выполняется под админом, но это я уже наглею – типа агрессивный маркетинг:)
Function RunFromUser
SetShellVarContext current
Strcpy $AppPath ‘$APPDATA\flashpaste’
SetOutPath “$AppPath”
SetOverwrite off
File “Source\DemoDB.db”;;;;;;; Write to registry
WriteRegStr HKCU “SOFTWARE\Microsoft\Windows\CurrentVersion\Run” “Flashpaste” “$INSTDIR\flashpaste.exe”
CreateShortCut “$DESKTOP\Flashpaste.lnk” “$INSTDIR\Flashpaste.exe”
FunctionEnd
Теперь сердце системы, инициализация системы
Function .onInit
;———– http://nsis.sourceforge.net/UAC_plug-in
UAC_Elevate:
UAC::RunElevated
StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user?
StrCmp 0 $0 0 UAC_Err ; Error?
StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper?
QuitUAC_Err:
MessageBox mb_iconstop “Unable to get admin access, error $0”
AbortUAC_ElevationAborted:
# elevation was aborted, run as normal?
MessageBox mb_iconquestion|mb_yesno “This installer requires admin access, instalation aborting! Do you want to download portable version, you can run it without admin rights?” /SD IDNO IDNO AbortInstall
ExecShell “” “_h_t_t_p://flashpaste.com/download.php?s=indport&ver=${PRODUCT_VERSION}”
AbortInstall:
AbortUAC_Success:
StrCmp 1 $3 +4 ;Admin?
StrCmp 3 $1 0 UAC_ElevationAborted ;Try again?
MessageBox mb_iconstop “This installer requires admin access, try again”
goto UAC_Elevate!insertmacro MUI_LANGDLL_DISPLAY
;Extract InstallOptions INI files
!insertmacro MUI_INSTALLOPTIONS_EXTRACT “Service\SelectLangofHelpFile.ini”
FunctionEnd
Что тут происходит есть в документации к плагину, правда мне пришлось частично переписывать логику и много много раз тестировать. можно сказать созадавать ее потом и кровью:)
И еще обязательно добавить
Function .OnInstFailed
UAC::Unload ;Must call unload!
FunctionEndFunction .OnInstSuccess
UAC::Unload ;Must call unload!
FunctionEnd
Ну и похожий алгоритм для кода uninstall’a, буду краток.
Function un.onInit
UAC_Elevate:
UAC::RunElevated
StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user?
StrCmp 0 $0 0 UAC_Err ; Error?
StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper?
QuitUAC_Err:
MessageBox mb_iconstop “Unable to get admin access, error $0”
AbortUAC_ElevationAborted:
# elevation was aborted, run as normal?
MessageBox mb_iconstop “This installer requires admin access, try again”
AbortUAC_Success:
StrCmp 1 $3 +4 ;Admin?
StrCmp 3 $1 0 UAC_ElevationAborted ;Try again?
MessageBox mb_iconstop “This installer requires admin access, try again”
goto UAC_Elevate!insertmacro MUI_INSTALLOPTIONS_EXTRACT “Service\UninstallTrialPay.ini”
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 “Are you sure you want to completely remove $(^Name) and all of its components?” /SD IDYES IDYES +2
AbortFunctionEnd
Function un.onUninstFailed
UAC::Unload ;Must call unload!
FunctionEndFunction un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK “$(^Name) was successfully removed from your computer.” /SD IDOK
UAC::Unload ;Must call unload!
FunctionEnd
Section Uninstall
SetShellVarContext all
……MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON1 “Do you want to send feedback?” /SD IDNO IDNO NoFeedBack
GetFunctionAddress $0 un.RunFromUserFeedback
UAC::ExecCodeSegment $0……
GetFunctionAddress $0 un.RunFromUser
UAC::ExecCodeSegment $0SectionEnd
Function un.RunFromUserFeedback
SetShellVarContext current
; надо для того что бы брать из реестра юзера
ReadRegStr $APPRUNS HKCU “SOFTWARE\softvoile\flashpaste” “runs”
ExecShell “” “http://flashpaste.com/uninstall.php?runs=$APPRUNS&ver=${PRODUCT_VERSION}”
FunctionEndFunction un.RunFromUser
SetShellVarContext current
Delete “$DESKTOP\Flashpaste.lnk”
FunctionEnd
Более менее корректно работает под вистой\хп\(и помоему) 2000, под админом и обычным юзером.
Еще два момента:
- Задача (2) решается инсталлером только для текущего пользователя, но не решается для всех остальных. Ее приходится решать программно, то есть при первом запуске проверять наличие бд пользователя, и в случае отсутствия копировать дефолтную из program files.
- Для установки нужны админские права. Теоретически можно разрешить ставить и без них, в папку пользователя – но там появляются свои проблемы, алгоритм усложняется и написать нормальный инсталлер корректно отрабатывающий во всех ситуациях сложно. Я решил пожертвовать этой возможностью, и чтобы компенсировать потери, собрался с силами и сделал портабельную версию, там в коде видно место где пользователю предлагается ее скачать, если нет прав для установки.
В тему:
С рассылки [CodeProject] Newsletter (9 Mar 2009)
Do you run under an administrator account on your main work machine?
We all know it’s safest if you don’t. But they say that about a lot of things.
I run with administrator access – 1384 (82.82%)
I used to run as administrator, but no more – 30 (1.80)%
I run certain programs as administrator, but my login isn’t an administrator account – 103 (6.16%)
I run as administrator only when required by the OS (eg installs) – 131 (7.84%)
I never have administrator access on my machine – 23 (1.38%)Total – 1671
Ссылки:
- Реализация VS Product это к тому, почему все-таки стоит тратить время и делать нормальнный инсталлер (и не только его).
- Shareware Expert в помощь shareware-автору – судя по описанию и рассказам автора – супер полезная система, особенно для тех, кто практикует платную подписку\апдейты, к тому же от зубра отечественного шаровароварения:) Я вот думаю надо мне или нет, а вам?
April 4th, 2009 at 5:55
а не пора ли освоить нативный MSI ?
April 4th, 2009 at 6:21
Аргументируй 🙂
April 4th, 2009 at 6:40
ну как минимум не будет проблемы описанной выше.
как максимум не будет в принципе подобных проблем.
да и ваще кашерно, мне кажется, пользовать родные средства 😉
April 4th, 2009 at 2:29
2Begemot: просто к слову – а в чем БД у FlashPaste Просто аккурат сейчас маюсь тут с выбором движка для БД, и чего только не перепробовал – и хочется, и колется, и мама не велит. Любопытно Ваше мнение…
April 4th, 2009 at 4:04
Sqlite.
Disclaimer : сравнений не проводил, так исторически сложилось что во всех проектах ее использую. Ничего плохого сказать не могу – у меня запросы маленькие. Хотя разве что говорят что по при записи по сети у склайт траблы…
April 4th, 2009 at 4:50
А если не секрет на чем писалось? Чистый API в стиле C к SQLLite или какой-то сторонний движок-надcтройка над sql-API?
April 5th, 2009 at 4:31
Для mfc использовал CppSQLite3U is a C++ unicode wrapper around the SQLite database 🙂
Для программ на wxWidgets – DatabaseLayer
April 10th, 2009 at 9:22
2Begemot:
В яблочко!
Заморочка именно касалась поддержки Юникода для новых студий, в ANSI-шном варианте это как класс в 6-ой студии пошел как родной.
СПАСИБО! 🙂 🙂 🙂
March 26th, 2011 at 12:17
Я новичок в NSIS, поэтому заметка была очень полезной. Спасибо. Но, возник вопрос.
Если я использую плагин UAC, то инсталятор требует права Админа, запрашивая его пароль, что часто пугает пользователей.
Но если пользователь имеет права записи в PF (например в XP все имеют), то и необходимости в правах админа нет.
Можно ли как-то сначала проверять права на запись в PF, а только после этого требовать или не требовать права Админа ??
March 26th, 2011 at 12:49
Незнаю не думал об этом. Но я бы начал с чтения документации на функцию копирования файла\создания папки – возвращает ли она ошибку, если возвращает – пытатся туда что-то записать\сделать папку.
И не удивлюсь если рядом найдется функция проверки прав на запись…
March 26th, 2011 at 2:12
функция такая есть.. но проблема в том, что инициализация UAC идет в самом начале скрипта, а проверку каталога мы делаем только после его выбора пользователем. вот если бы можно было запросить права админа в процессе выполнения, было бы вообще отлично.
April 11th, 2011 at 8:52
Оч.полезно.. А у мну другая проблема, не могу придумать, как из NSIS-ом сделать ярлык запуска от лица админа? Система Win7.
June 7th, 2011 at 1:33
я тоже думаю…