Показать полную графическую версию : .: NSIS - все вопросы :. часть 2.
AlekseyPopovv,
Изначальный xml-файл (до внесения изменений) есть?
Вот это вот похоже на некорректную запись (атрибуты записаны на простой текст):<Common>CreateBackupCopy="0" ShowSplashScreen="0"</Common>
должно быть
<Common CreateBackupCopy="0" ShowSplashScreen="0" />
Если файла изначально нет, то можно его создать просто через FileWrite
Если нужно именно модифицировать существующий, то это делается через плагин (nsisXML by Wizou (https://nsis.sourceforge.io/NsisXML_plug-in_(by_Wizou)))
AlekseyPopovv
11-07-2023, 19:03
iglezz,
Именно вот так выглядит оригинал.
<?xml version="1.0" encoding="UTF-8"?>
<RDLXSettings>
<LangIDList/>
<Common CreateBackupCopy="0" ShowSplashScreen="0">
<AutoSave Enable="0" Interval="1"/>
</Common>
<ProjectHistory LoadlastProject="0" NoProject="1"/>
<Spelling Type="1">
<HunSpellDictionary Enabled="1">$EXEDIR\${APPDIR}\ru-RU.dic</HunSpellDictionary>
</Spelling>
</RDLXSettings>
Я могу конечно его создать и положить в инсталлятор.
Но как потом удалить и снова добавить строку именно такую:
<HunSpellDictionary Enabled="1">$EXEDIR\${APPDIR}\ru-RU.dic</HunSpellDictionary>
AlekseyPopovv, Что-то в итоге понятнее не стало.
Лучше сначала и по порядку:
1. какое содержимое файла до запуска инсталлера
2. что нужно изменить
3. какое должно быть содержимое файла в итоге
AlekseyPopovv
11-07-2023, 19:55
iglezz,
1. <?xml version="1.0" encoding="UTF-8"?>
<RDLXSettings>
<LangIDList/>
<Common CreateBackupCopy="0" ShowSplashScreen="0">
<AutoSave Enable="0" Interval="1"/>
</Common>
<ProjectHistory LoadlastProject="0" NoProject="1"/>
<Spelling Type="1">
<HunSpellDictionary Enabled="1"></HunSpellDictionary>
</Spelling>
</RDLXSettings>
2. Строку:
<HunSpellDictionary Enabled="1"></HunSpellDictionary>
заменить на:
<HunSpellDictionary Enabled="1">$EXEDIR\${APPDIR}\ru-RU.dic</HunSpellDictionary>
либо в неё записать значение
3. <?xml version="1.0" encoding="UTF-8"?>
<RDLXSettings>
<LangIDList/>
<Common CreateBackupCopy="0" ShowSplashScreen="0">
<AutoSave Enable="0" Interval="1"/>
</Common>
<ProjectHistory LoadlastProject="0" NoProject="1"/>
<Spelling Type="1">
<HunSpellDictionary Enabled="1">C:\my program\ru-RU.dic</HunSpellDictionary>
</Spelling>
</RDLXSettings>
AlekseyPopovv, это простейшее изменение
nsisXML::create
nsisXML::load "x:\path\to\xml\file"
nsisXML::select '/RDLXSettings/Spelling/HunSpellDictionary'
nsisXML::setText 'x:\path\to\dict'
nsisXML::save "x:\path\to\xml\file"
Это без обработки ошибок, при отсутствии файла при чтении или выбираемого через ::select узла установщик просто упадёт.
AlekseyPopovv
19-07-2023, 12:30
Как записать в ini файл такую строку:
LanguagesPath=@ByteArray(P:/Folder/Program Portable/Settings/OCR)
Пишу так:
WriteINIStr "$EXEDIR\${DEFDIR}\${SETINI}" "OCR" "LanguagesPath" "@ByteArray($EXEDIR/${DEFDIR}/OCR)"
И получается так:
LanguagesPath=@ByteArray(P:\Folder\Program Portable/Settings/OCR)
Для начала ту часть строки, в которой нужно преобразовать слеши (в данном случае $EXEDIR) нужно обработать
Например, ${StrRep} $0 $EXEDIR / \
И потом только результат можно использовать в WriteIniStr:
WriteINIStr "$EXEDIR\${DEFDIR}\${SETINI}" "OCR" "LanguagesPath" "@ByteArray($0/${DEFDIR}/OCR)"
Полагаю, что у программы могут возникнуть проблемы, если в записываемом пути будут символы вне ANSI-кодировки.
AlekseyPopovv
21-07-2023, 08:57
Например, ${StrRep} $0 $EXEDIR / \ »
Сработало:
!include "StrFunc.nsh"
${StrRep}
${StrRep} $0 "$EXEDIR" "\" "/"
WriteINIStr "$EXEDIR\${DEFDIR}\${SETINI}" "OCR" "LanguagesPath" "@ByteArray($0/${DEFDIR}/OCR)"
Всем хорошего дня.
Есть такой вопрос. Существует ли в NSIS задержка определенной команды на несколько секунд. Чтобы была не задержка всего процесса, а только задержка одной команды. Нужно добавить в реестр запись через 10 секунд после определенной команды, но чтобы весь процесс не останавливался на 10 секунд, как это делает команда "Sleep 10000".
inco1, https://nsis.sourceforge.io/ThreadTimer_plug-in
AlekseyPopovv
10-08-2023, 11:35
Нужно что то подобное, без MUI. Есть варианты?
!include "MUI.nsh"
!insertmacro MUI_LANGUAGE "Russian"
!insertmacro MUI_LANGUAGE "English"
${If} $Language == 1049
MessageBox MB_USERICON|MB_OK 'Программа "${APP}" уже запущена!'
Abort
${Else}
MessageBox MB_USERICON|MB_OK 'The "${APP}" is already running!'
Abort
${EndIf}
AlekseyPopovv,
Разница между с MUI и без MUI в том, что
с MUI пишется !insertmacro MUI_LANGUAGE "Russian"
без MUI пишется LoadLanguageFile "${NSISDIR}\Contrib\Language files\Russian.nlf"
Дальше всё одинаково.
Кроме того локалицацию проще и правильнее прописывать так:
; Подключение языковый файлов
; English должен быть первый, т.к. если в списке подключённых языков отсутствует язык системы, то грузится первый по списку
LoadLanguageFile "${NSISDIR}\Contrib\Language files\English.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Russian.nlf"
; Объявление многоязычной строки:
LangString AlreadyRunningMessage ${LANG_ENGLISH} "The "${APP}" is already running!"
LangString AlreadyRunningMessage ${LANG_RUSSIAN} "Программа "${APP}" уже запущена!"
...
; Использование многоязычной строки:
MessageBox MB_USERICON|MB_OK $(AlreadyRunningMessage)
Abort
Если программа уже запущена, то вместо сообщения с прерыванием запуска можно активировать уже запущенный установщик
!include SingleInstance.nsh
RequestExecutionLevel user
Page instfiles
UninstPage uninstConfirm
Section
DetailPrint A
Sleep 1000
DetailPrint B
Sleep 1000
DetailPrint C
Sleep 1000
DetailPrint D
Sleep 1000
SectionEnd
Section un.
SectionEnd
Function .onInit
${IfThen} ${AlreadyRunning} ${|} ${ActivateAlreadyRunningAndAbort} ${|}
WriteUninstaller $EXEDIR\un1.exe
FunctionEnd
Function un.onInit
${IfThen} ${AlreadyRunning} ${|} ${ActivateAlreadyRunningAndAbort} ${|}
FunctionEnd
/*
Based on `Allow only one installer instance` example (https://nsis.sourceforge.io/Allow_only_one_installer_instance)
Usage example:
; Define unique mutex name (optional)
!define INSTALLERMUTEXNAME "myVeryUniqueInstallerMutexName"
!define UNINSTALLERMUTEXNAME "myVeryUniqueUninstallerMutexName"
${IfThen} ${AlreadyRunning} ${|} ${ActivateAlreadyRunningAndAbort} ${|}
${If} ${AlreadyRunning}
.. instructions before activate ..
${ActivateAlreadyRunning}
.. instructions after activate ..
Abort
${EndIf}
*/
!ifndef SingleInstance.nsh
!define SingleInstance.nsh
!include LogicLib.nsh
!include WinMessages.nsh
!include Win\WinError.nsh
!include Win\WinUser.nsh
!define AlreadyRunning `"" AlreadyRunning ""`
!macro _AlreadyRunning _a _b _t _f
!insertmacro _LOGICLIB_TEMP
!ifdef __UNINSTALL__
!define /ifndef UNINSTALLERMUTEXNAME '$(^Name)$(^UninstallCaption) Mutex'
!define /redef CHECKALREADYEXISTINGMUTEXNAME '${UNINSTALLERMUTEXNAME}'
!else
!define /ifndef INSTALLERMUTEXNAME '$(^Name)$(^SetupCaption) Mutex'
!define /redef CHECKALREADYEXISTINGMUTEXNAME '${INSTALLERMUTEXNAME}'
!endif
System::Call 'kernel32::CreateMutex(p 0, i 1, t "${CHECKALREADYEXISTINGMUTEXNAME}") ?e'
Pop $_LOGICLIB_TEMP
IntCmpU $_LOGICLIB_TEMP ${ERROR_ALREADY_EXISTS} `${_t}` `${_f}` `${_f}`
!macroend
!define ActivateAlreadyRunning `!insertmacro ActivateAlreadyRunning`
!macro ActivateAlreadyRunning
Push $0 ; Window handle
Push $1 ; Caption we are looking for
Push $2 ; Caption length
Push $3 ; GetWindowText output
StrCpy $0 '' ; Start FindWindow with NULL
!ifdef __UNINSTALL__
StrCpy $1 '$(^UninstallCaption)'
!else
StrCpy $1 '$(^SetupCaption)'
!endif
StrLen $2 $1
IntOp $2 $2 + 1 ; GetWindowText count includes \0
; loop:
FindWindow $0 '#32770' '' '' $0
StrCmp $0 0 +5 ; windownotfound: next
System::Call 'user32::GetWindowText(p r0, t.r3, i r2)'
StrCmp $3 $1 0 -3 ; windowfound: loop:
; windowfound:
SendMessage $0 ${WM_SYSCOMMAND} ${SC_RESTORE} 0 /TIMEOUT=2000 ; Restore the window if it is minimized
System::Call 'user32::SetForegroundWindow(p r0)'
; windownotfound:
Pop $3
Pop $2
Pop $1
Pop $0
!macroend
!define ActivateAlreadyRunningAndAbort `!insertmacro ActivateAlreadyRunningAndAbort`
!macro ActivateAlreadyRunningAndAbort
${ActivateAlreadyRunning}
Abort
!macroend
!endif
Ещё вопрос:
Можно ли "правильней" или "красивей" или по другому написать следующий код:
SetRegView 64
ClearErrors
EnumRegKey $0 HKLM "SOFTWARE\test" 0
${If} ${Errors}
Goto none
${Else}
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Есть нужная запись в реестре"
Quit
${EndIf}
none:
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Нету нужной записи в реестре"
SetRegView 32
inco1,
1. От метки с Goto надо избавляться, раз присутствует конструкция If/Else:
SetRegView 64
ClearErrors
EnumRegKey $0 HKLM "SOFTWARE\test" 0
${If} ${Errors}
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Ключ отсутствует ИЛИ недостаточно привилегий для доступа к ключу ИЛИ какая-то ошибка"
${Else}
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Есть нужная запись в реестре"
Quit
${EndIf}
SetRegView 32
2. Можно применить новый LogicLib-тест - RegKeyExists:SetRegView 64
${If} HKLM RegKeyExists "SOFTWARE\test"
; Существует
${Else}
; Ключ отсутствует ИЛИ недостаточно привилегий для доступа к ключу ИЛИ какая-то ошибка
${EndIf}
SetRegView 32
Код RegKeyExists:
!ifndef RegKeyExists
!macro _RegKeyExists _a _b _t _f
!insertmacro _LOGICLIB_TEMP
ClearErrors
EnumRegKey $_LOGICLIB_TEMP ${_a} `${_b}` 0
IfErrors `${_f}` `${_t}`
!macroend
!define RegKeyExists `RegKeyExists`
!endif
!!! Важное примечание:
Отсутствие ошибки EnumRegKey или положительный тест RegKeyExists значит, что ключ существует.
Ошибка после EnumRegKey или отрицательный тест RegKeyExists значит, что ключ отсутствует ИЛИ недостаточно привилегий для доступа к ключу ИЛИ какая-то ошибка
iglezz,
Для теста создал простой экзешник:
OutFile "Test.exe"
SilentInstall silent
!include "LogicLib.nsh"
Section
SetRegView 64
${If} HKLM RegKeyIsEmpty "SOFTWARE\test"
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Есть нужная запись в реестре"
Quit
${Else}
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Нету нужной записи в реестре"
${EndIf}
SetRegView default
SectionEnd
Пишет, что "Есть нужная запись в реестре", но такой записи в реале нету.
А мой вариант пишет правильно, что "Нету нужной записи в реестре".
OutFile "Test1.exe"
SilentInstall silent
!include "LogicLib.nsh"
Section
SetRegView 64
ClearErrors
EnumRegKey $0 HKLM "SOFTWARE\test" 0
${If} ${Errors}
Goto none
${Else}
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Есть нужная запись в реестре"
Quit
${EndIf}
none:
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Нету нужной записи в реестре"
SetRegView 32
SectionEnd
Что-то не так с вашим кодом.
iglezz,
OutFile "Test.exe"
SilentInstall silent
!include "LogicLib.nsh"
Section
SetRegView 64
${If} HKLM RegKeyIsEmpty "SOFTWARE\test"
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Нету нужной записи в реестре"
Quit
${Else}
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Есть нужная запись в реестре"
${EndIf}
SetRegView default
SectionEnd
В том то и дело, что теперь всегда показывает "Нету нужной записи в реестре", даже если запись есть.
iglezz,
В том то и дело,что я всегда проверяю с реальным реестром в двух реальных вариантах - на наличие записи, которую сам добавляю и на отсутствие,которую удаляю.
Реально работает только мой вот этот вариант, который я просил усовершенствовать:
OutFile "Test1.exe"
SilentInstall silent
!include "LogicLib.nsh"
Section
SetRegView 64
ClearErrors
EnumRegKey $0 HKLM "SOFTWARE\test" 0
${If} ${Errors}
Goto none
${Else}
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Есть нужная запись в реестре"
Quit
${EndIf}
none:
MessageBox MB_OK|MB_TOPMOST|MB_ICONSTOP "Нету нужной записи в реестре"
SetRegView 32
SectionEnd
Все предложенные вами варианты не работают. А последний пример даже не компилится.
inco1,
Обновил своё первое сообщение и поудалял некорректные/ненужные
Пример компилируется и работает корректно.
Если вдруг будут ошибки компилятора, то нужно читать, что именно не так.
iglezz,
Огромнейшее спасибо за помощь.
В процессе проб и тестов присмотрелся к своему первоначальному коду и упростил его. Получился простой код и работает, как и раньше корректно. Вот, что получилось:
ClearErrors
EnumRegKey $0 HKLM "SOFTWARE\test" 0
${IfNot} ${Errors}
; Существует
${Else}
; Не существует
${EndIf}
С помощью функций Win API возможна более корректная проверка ключей на не_существует/нет_доступа/другие_ошибки
ShowInstDetails show
InstallColors /windows
SetFont "Consolas" 10
AutoCloseWindow false
SetOverwrite on
RequestExecutionLevel user
!include LogicLib.nsh
!include RegistryFunc.nsh
!include SystemFunc.nsh
Section
${GetRegKeyStatus} $R0 HKLM 'Software\Microsoft' ; exist
${GetSystemErrorMessage} $R1 $R0
DetailPrint "Soft\MS: $R0: $R1"
${GetRegKeyStatus} $R0 HKLM 'qwertyuiop' ; not exist
${GetSystemErrorMessage} $R1 $R0
DetailPrint "qwertyu: $R0: $R1"
${GetRegKeyStatus} $R0 HKLM 'SAM\SAM' ; access denied
${GetSystemErrorMessage} $R1 $R0
DetailPrint "SAM\SAM: $R0: $R1"
SectionEnd
Section
${If} HKLM RegKeyExists 'Software\Microsoft'
DetailPrint "Soft\MS: exists"
${EndIf}
${If} HKLM RegKeyExists 'qwertyuiop'
DetailPrint "qwertyu: exists"
${EndIf}
${If} HKLM RegKeyExists 'SAM\SAM'
DetailPrint "SAM\SAM: exists"
${EndIf}
${If} HKLM RegKeyNotExists 'Software\Microsoft'
DetailPrint "Soft\MS: not exists"
${EndIf}
${If} HKLM RegKeyNotExists 'qwertyuiop'
DetailPrint "qwertyu: not exists"
${EndIf}
${If} HKLM RegKeyNotExists 'SAM\SAM'
DetailPrint "SAM\SAM: not exists"
${EndIf}
${If} HKLM RegKeyAccessDenied 'Software\Microsoft'
DetailPrint "Soft\MS: access denied"
${EndIf}
${If} HKLM RegKeyAccessDenied 'qwertyuiop'
DetailPrint "qwertyu: access denied"
${EndIf}
${If} HKLM RegKeyAccessDenied 'SAM\SAM'
DetailPrint "SAM\SAM: access denied"
${EndIf}
SectionEnd
!ifndef RegistryFunc.nsh
!define RegistryFunc.nsh
!include LogicLib.nsh
!include WinCore.nsh
!define KEY_QUERY_VALUE 0x0001
!define KEY_ENUMERATE_SUB_KEYS 0x0008
!macro Push_root_key root_key
!if ${root_key} == HKEY_CLASSES_ROOT
Push ${HKEY_CLASSES_ROOT}
!else if ${root_key} == HKCR
Push ${HKEY_CLASSES_ROOT}
!else if ${root_key} == HKEY_CURRENT_USER
Push ${HKEY_CURRENT_USER}
!else if ${root_key} == HKCU
Push ${HKEY_CURRENT_USER}
!else if ${root_key} == HKEY_LOCAL_MACHINE
Push ${HKEY_LOCAL_MACHINE}
!else if ${root_key} == HKLM
Push ${HKEY_LOCAL_MACHINE}
!else if ${root_key} == HKEY_USERS
Push ${HKEY_USERS}
!else if ${root_key} == HKU
Push ${HKEY_USERS}
!else if ${root_key} == HKEY_PERFORMANCE_DATA
Push ${HKEY_PERFORMANCE_DATA}
!else if ${root_key} == HKEY_CURRENT_CONFIG
Push ${HKEY_CURRENT_CONFIG}
!else if ${root_key} == HKCC
Push ${HKEY_CURRENT_CONFIG}
!else if ${root_key} == HKEY_DYN_DATA
Push ${HKEY_DYN_DATA}
!else
Push '${root_key}'
!endif
!macroend
!define GetRegKeyStatus `!insertmacro GetRegKeyStatus `
!macro GetRegKeyStatus result root_key sub_key
Push $0
Push '${sub_key}'
!insertmacro Push_root_key ${root_key}
System::Call "Advapi32::RegOpenKeyEx(is, ts, i0, i${KEY_QUERY_VALUE}|${KEY_ENUMERATE_SUB_KEYS}, *p.r0) i.s"
StrCmp $0 0 +2
System::Call "Advapi32::RegCloseKey(i r0) i"
Exch
Pop $0
Pop ${result}
!macroend
!define RegKeyExists `RegKeyExists`
!macro _RegKeyExists _a _b _t _f
!insertmacro _LOGICLIB_TEMP
ClearErrors
EnumRegKey $_LOGICLIB_TEMP ${_a} `${_b}` 1
IfErrors `${_f}` `${_t}`
!macroend
!define RegKeyNotExists `RegKeyNotExists`
!macro _RegKeyNotExists _a _b _t _f
!insertmacro _LOGICLIB_TEMP
${GetRegKeyStatus} $_LOGICLIB_TEMP ${_a} '${_b}'
StrCmp $_LOGICLIB_TEMP 2 `${_t}` `${_f}`
!macroend
!define RegKeyAccessDenied `RegKeyAccessDenied`
!macro _RegKeyAccessDenied _a _b _t _f
!insertmacro _LOGICLIB_TEMP
${GetRegKeyStatus} $_LOGICLIB_TEMP ${_a} '${_b}'
StrCmp $_LOGICLIB_TEMP 5 `${_t}` `${_f}`
!macroend
!endif
!ifndef SystemFunc.nsh
!define SystemFunc.nsh
!define GetSystemErrorMessage `!insertmacro GetSystemErrorMessage `
!macro GetSystemErrorMessage result error_code
System::Call 'kernel32::GetSystemDefaultUILanguage()i.s'
Push ${error_code}
System::Call "kernel32::FormatMessage(i0x1300, in, is, is, *t.s, i${NSIS_MAX_STRLEN}, in)i"
Pop ${result}
!macroend
!endif
© OSzone.net 2001-2012
vBulletin v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.