Делаем правильную установку под Вистой 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?
    Quit

UAC_Err:
    MessageBox mb_iconstop “Unable to get admin access, error $0”
    Abort

UAC_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:
    Abort

UAC_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!
FunctionEnd

Function .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?
    Quit

UAC_Err:
    MessageBox mb_iconstop “Unable to get admin access, error $0”
    Abort

UAC_ElevationAborted:
    # elevation was aborted, run as normal?
    MessageBox mb_iconstop “This installer requires admin access, try again”
    Abort

UAC_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
  Abort

FunctionEnd

Function un.onUninstFailed
    UAC::Unload ;Must call unload!
FunctionEnd

Function 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 $0

SectionEnd

Function un.RunFromUserFeedback
   SetShellVarContext current
    ; надо для того что бы брать из реестра юзера
     ReadRegStr $APPRUNS HKCU “SOFTWARE\softvoile\flashpaste” “runs”
     ExecShell “” “http://flashpaste.com/uninstall.php?runs=$APPRUNS&ver=${PRODUCT_VERSION}”
FunctionEnd

Function un.RunFromUser
   SetShellVarContext current
  Delete “$DESKTOP\Flashpaste.lnk”
FunctionEnd

 

Более менее корректно работает под вистой\хп\(и помоему) 2000, под админом и обычным юзером.

Еще два момента:

  1. Задача (2) решается инсталлером только для текущего пользователя, но не решается для всех остальных. Ее приходится решать программно, то есть при первом запуске проверять наличие бд пользователя, и в случае отсутствия копировать дефолтную из program files.
  2. Для установки нужны админские права. Теоретически можно разрешить ставить и без них, в папку пользователя – но там появляются свои проблемы, алгоритм усложняется и написать нормальный инсталлер корректно отрабатывающий во всех ситуациях сложно. Я решил пожертвовать этой возможностью, и чтобы компенсировать потери, собрался с силами и сделал портабельную версию, там в коде видно место где пользователю предлагается ее скачать, если нет прав для установки.

 

В тему:

С рассылки [CodeProject] Newsletter (9 Mar 2009)

WEEKLY POLL RESULTS

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-автору – судя по описанию и рассказам автора – супер полезная система, особенно для тех, кто практикует платную подписку\апдейты, к тому же от зубра отечественного шаровароварения:) Я вот думаю надо мне или нет, а вам?
Related:

13 Responses to “Делаем правильную установку под Вистой c NSIS”

  1. а не пора ли освоить нативный MSI ?

  2. Аргументируй 🙂

  3. wholegroup Says:

    ну как минимум не будет проблемы описанной выше.
    как максимум не будет в принципе подобных проблем.

    да и ваще кашерно, мне кажется, пользовать родные средства 😉

  4. 2Begemot: просто к слову – а в чем БД у FlashPaste Просто аккурат сейчас маюсь тут с выбором движка для БД, и чего только не перепробовал – и хочется, и колется, и мама не велит. Любопытно Ваше мнение…

  5. Sqlite.

    Disclaimer : сравнений не проводил, так исторически сложилось что во всех проектах ее использую. Ничего плохого сказать не могу – у меня запросы маленькие. Хотя разве что говорят что по при записи по сети у склайт траблы…

  6. А если не секрет на чем писалось? Чистый API в стиле C к SQLLite или какой-то сторонний движок-надcтройка над sql-API?

  7. Для mfc использовал CppSQLite3U is a C++ unicode wrapper around the SQLite database 🙂
    Для программ на wxWidgets – DatabaseLayer

  8. 2Begemot:
    В яблочко!
    Заморочка именно касалась поддержки Юникода для новых студий, в ANSI-шном варианте это как класс в 6-ой студии пошел как родной.
    СПАСИБО! 🙂 🙂 🙂

  9. Я новичок в NSIS, поэтому заметка была очень полезной. Спасибо. Но, возник вопрос.
    Если я использую плагин UAC, то инсталятор требует права Админа, запрашивая его пароль, что часто пугает пользователей.
    Но если пользователь имеет права записи в PF (например в XP все имеют), то и необходимости в правах админа нет.

    Можно ли как-то сначала проверять права на запись в PF, а только после этого требовать или не требовать права Админа ??

  10. Незнаю не думал об этом. Но я бы начал с чтения документации на функцию копирования файла\создания папки – возвращает ли она ошибку, если возвращает – пытатся туда что-то записать\сделать папку.
    И не удивлюсь если рядом найдется функция проверки прав на запись…

  11. функция такая есть.. но проблема в том, что инициализация UAC идет в самом начале скрипта, а проверку каталога мы делаем только после его выбора пользователем. вот если бы можно было запросить права админа в процессе выполнения, было бы вообще отлично.

  12. Ilya_Nsk Says:

    Оч.полезно.. А у мну другая проблема, не могу придумать, как из NSIS-ом сделать ярлык запуска от лица админа? Система Win7.

  13. Ilya_Nsk Says:

    я тоже думаю…