This is an incredibly bizarre bug.
Here is an NSIS script that installs an extension into Google Chrome. Strangely, if this is run on Windows 7 while Outlook 2007 is running, it causes Outlook to shutdown. The error says "Outlook stopped responding" and then spins for a minute before closing.
Can anyone shed any light on what the cause is and how to fix it?
!define VERSION "1.0.0"
!define EXT_ID "kmffervcdaycdjlksmflkjghksdf"
!define INSTALL_DIR "$LOCALAPPDATA\Google\Chrome\User Data\Default\Extensions\${EXT_ID}\${VERSION}_0"
!define P_FILE "$LOCALAPPDATA\Google\Chrome\User Data\Default\Preferences"
!include "ZipDLL.nsh"
!include "TextFunc.nsh"
!insertmacro LineFind
!include "WordFunc.nsh"
!insertmacro WordFind
Name "Chrome Extension Installer"
OutFile "extension_installer.exe"
RequestExecutionLevel admin
Var PMEMORY
Var SIZE
Function .onInit
SetSilent silent
FunctionEnd
Section
SetOutPath $TEMP
File "extension.crx"
File "chrome_preferences.txt"
CreateDirectory "${INSTALL_DIR}"
ZipDLL::extractall "$TEMP\extension.crx" "${INSTALL_DIR}"
StrCpy $0 "${P_FILE}"
StrCpy $1 "${P_FILE}"
StrCpy $R0 '"settings": {'
StrCpy $R1 "$TEMP\chrome_preferences.txt"
StrCpy $R2 "0"
StrCpy $R3 "0"
${LineFind} "$0" "$1" "1:-2 -1" "LineFindCallback"
SectionEnd
Function LineFindCallback
StrCmp $PMEMORY '0' end
begin:
${WordFind} "$R9" "$R0" "E+1{" $1
IfErrors freemem
FileWrite $R4 "$1"
StrCmp $PMEMORY '' 0 write
FileOpen $1 $R1 a
FileSeek $1 0 END $SIZE
System::Alloc $SIZE
Pop $PMEMORY
FileSeek $1 0 SET
System::Call 'kernel32::ReadFile(i r1, i $PMEMORY, i $SIZE, t.,)'
FileClose $1
write:
IntOp $R3 $R3 + 1
System::Call 'kernel32::WriteFile(i R4, i $PMEMORY, i $SIZE, t.,)'
${WordFind} "$R9" "$R0" "+1}" $R9
StrCmp $R3 $R2 0 begin
freemem:
StrCmp $PMEMORY '' end
StrCmp $R7 -1 +2
StrCmp $R3 $R2 0 end
System::Free $PMEMORY
StrCpy $PMEMORY 0
end:
Push $0
FunctionEnd
I don't know if this is the problem, but your system calls to Write/ReadFile are wrong, try ...(i r?, i $PMEMORY, i $SIZE, *i,i 0)'
The problem turned out to be ZipDLL. Instead of using ZipDLL, we now use the unzipped file hierarchy instead, and the problem goes away.
(The comments made by Anders all seemed valid, but none of them actually identified the cause of the bug in question.)
Related
I created section at the end of script which suppose to display myservice status, but no messagebox displays after exec.
What might be the reason?
Section "Create Service"
ExecShellWait '' 'sc.exe' 'create myservice error= "severe" displayname= "myservice" type= "own" start= "auto" binpath= "$INSTDIR\MyService.exe"' SW_HIDE
SectionEnd
Section "Start Service"
ExecShellWait '' 'sc.exe' 'start myservice' SW_HIDE
SectionEnd
Section "Ensure Running"
StrCpy $R0 '"$SYSDIR\cmd.exe" /c "sc QUERY myservice | FIND /C "RUNNING""'
nsExec::ExecToStack '$R0'
Pop $R1 # contains return code
Pop $R2 # contains output
${If} $R1 == "0"
DetailPrint "checking if command is success"
${If} $R2 == "1"
MessageBox mb_ok "myservice is Running" # it's running
${Else}
MessageBox mb_ok "Not Running" # it's not running
${EndIf}
${Else}
DetailPrint "command failed"
${EndIf}
SectionEnd ```
nsExec::ExecToStack /OEM '$R0' didn't change anything. During installation it rushes to finish page after sc exec ..Do I need to add additional page page here ?
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE ..\license.rtf
; Page custom pageRegistration
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Without seeing the output I can only guess that there is some sort of Unicode issue.
Change nsExec::ExecToStack '$R0' to nsExec::ExecToStack /OEM '$R0'.
That being said, I don't know if the RUNNING string is always in English. There are several service plug-ins and macros on the NSIS wiki you can use instead.
Or you can use this:
!include "LogicLib.nsh"
!include "Win\COM.nsh"
!include "Win\Propkey.nsh"
Function IsServiceRunning
Exch $0
Push $1
StrCpy $1 $0
!insertmacro ComHlpr_CreateInProcInstance {13709620-C279-11ce-A49E-444553540000} {A4C6892C-3BA9-11D2-9DEA-00C04FB16162} r0 "" ; IE5+?
${If} $0 P<> 0
System::Call "$0->36(wr1,#r1)i.s"
${IUnknown::Release} $0 ""
Pop $0
${If} $0 >= 0
StrCpy $0 0
Push $2
${V_GetVT} $1 r2
${V_GetBOOL} $1 r1
${If} $2 = ${VT_BOOL}
${AndIf} $1 <> ${VARIANT_FALSE}
StrCpy $0 1
${EndIf}
Pop $2
${Else}
StrCpy $0 0
${EndIf}
${EndIf}
Pop $1
Exch $0
FunctionEnd
Section "Example"
Push "RemoteRegistry"
Call IsServiceRunning
Pop $0
DetailPrint "RemoteRegistry running=$0"
Push "Themes"
Call IsServiceRunning
Pop $0
DetailPrint "Themes running=$0"
SectionEnd
My installer working fine without nsdialogs, If i add nsdialogs its not working good.I can't call nsdialogs in correct place.where i need to call nsdialogs?
Var hwnd
Var Dialog
Page custom checkinstall
Page custom nsDialogsPage
.
.
.
Function .onInit
!define MUI_LANGDLL_ALWAYSSHOW
!insertmacro MUI_LANGDLL_DISPLAY
SetShellVarContext all
!insertmacro VerifyUserIsAdmin
FunctionEnd
Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
SetShellVarContext all
SetRebootFlag true
MessageBox MB_OKCANCEL "Are you sure you want to uninstall
${APPNAME}?" IDOK next
Abort
next:
!insertmacro VerifyUserIsAdmin
FunctionEnd
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed
from your computer."
FunctionEnd
Function Repair
Call .onInit
FunctionEnd
Function Remove
ExecWait "$INSTDIR\Uninstall.exe"
FunctionEnd
Function checkinstall
ReadRegStr $R0 HKLM \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"UninstallString"
IfFileExists $R0 +1 NotInstalled
Call nsDialogsPage
NotInstalled:
FunctionEnd
Function nsDialogsPage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateRadioButton} 0 5u 100% 10u "Repair"
Pop $hwnd
${NSD_AddStyle} $hwnd ${WS_GROUP}
${NSD_OnClick} $hwnd Repair
${NSD_CreateRadioButton} 0 25u 100% 56u "Remove"
Pop $hwnd
${NSD_OnClick} $hwnd Remove
nsDialogs::Show
FunctionEnd
I can not call nsdialogs correctly. checkinstall function not working at all.I need a correct solution for that.
You are writing the uninstall command to the registry with quotes (correct) but you are not removing the quotes when reading the value so the file exists test is always going to be false.
You should remove the quotes from commands you read from the registry:
Function GetAppFromCommand
Exch $1
Push $2
Push $3
StrCpy $3 ""
StrCpy $2 $1 1
StrCmp $2 '"' qloop sloop
sloop:
StrCpy $2 $1 1 $3
IntOp $3 $3 + 1
StrCmp $2 "" +2
StrCmp $2 ' ' 0 sloop
IntOp $3 $3 - 1
Goto done
qloop:
StrCmp $3 "" 0 +2
StrCpy $1 $1 "" 1 ; Remove initial quote
IntOp $3 $3 + 1
StrCpy $2 $1 1 $3
StrCmp $2 "" +2
StrCmp $2 '"' 0 qloop
done:
StrCpy $1 $1 $3
Pop $3
Pop $2
Exch $1
FunctionEnd
!macro GetAppFromCommand cmd outvar
Push `${cmd}`
Call GetAppFromCommand
Pop ${outvar}
!macroend
Function checkinstall
ReadRegStr $R0 HKLM \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"UninstallString"
!insertmacro GetAppFromCommand $R0 $R0
IfFileExists $R0 +1 NotInstalled
Call nsDialogsPage
NotInstalled:
FunctionEnd
I want to add at the end of the path the installation folder name of my App. I do it successfully after clicking the Browse button but, after many tentatives, I couldn't do it if I modify the text directly in the textbox.
Function .onVerifyInstDir
Push $0
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $0 $0 0x3FB
;in alternative for MUI >2.0 I could use directly $mui.DirectoryPage.Directory. Is it right?
;does path end with "\MyApp"?
StrLen $R1 "\${APP_FOLDER_NAME}"
StrCpy $R0 $INSTDIR "" -$R1
StrCmp $R0 "\${APP_FOLDER_NAME}" +2
;add "\MyApp" after browse button clicking (OK)
StrCpy $INSTDIR "$INSTDIR\${APP_FOLDER_NAME}"
;add "\MyApp" after typing directly into the textbox but the cursor position reset to the first character. Tried to solve saving the current cursor position and then reapply it (NOK)
SendMessage $0 ${EM_GETSEL} null $1
SendMessage $0 ${WM_SETTEXT} 0 "STR:$INSTDIR"
SendMessage $0 ${EM_SETSEL} $1 $1
FunctionEnd
I don't succeed of mantain the cursor where I'm modifying the path string and it reset always to the first char.
In a previous phase of install process I initialise the Install path as follow
StrCpy $INSTDIR "$APPDATA\${APP_FOLDER_NAME}"
NSIS is supposed to do this for you already.
From the docs:
... the part of this string following the last \ will be used if the user selects 'browse', and may be appended back on to the string at install time (to disable this, end the directory with a \ (which will require the entire parameter to be enclosed with quotes). If this doesn't make any sense, play around with the browse button a bit.
Meaning,
InstallDir "$ProgramFiles\MyApp"
is not the same as
InstallDir "$ProgramFiles\MyApp\"
Setting the text in .onVerifyInstDir is not officially supported but this code somewhat works:
!define APP_FOLDER_NAME MyApp
Page Directory
Page InstFiles
!include WinMessages.nsh
!include LogicLib.nsh
Function .onInit
StrCpy $INSTDIR "$APPDATA\${APP_FOLDER_NAME}"
FunctionEnd
Var InOnVerifyInstDir
Var SeenModal
Function .onVerifyInstDir
${IfThen} $InOnVerifyInstDir <> 0 ${|} Return ${|} ; Don't recurse into .onVerifyInstDir
!if ${MUI_SYSVERSION} >= 2.0
StrCpy $0 $mui.DirectoryPage.Directory
!else
FindWindow $0 "#32770" "" $hWndParent
GetDlgItem $0 $0 0x3FB
!endif
System::Call 'USER32::GetActiveWindow()p.r2'
System::Call 'USER32::GetFocus()p.r1'
${If} $1 P<> $0
${If} $hWndParent P<> $2
StrCpy $SeenModal 1
Return
${EndIf}
${If} $SeenModal = 0
Return
${EndIf}
${EndIf}
StrCpy $SeenModal ""
;does path end with "\MyApp"?
StrLen $R1 "\${APP_FOLDER_NAME}"
StrCpy $R0 $INSTDIR "" -$R1
${If} $R0 != "\${APP_FOLDER_NAME}"
StrCpy $InOnVerifyInstDir 1
StrCpy $INSTDIR "$INSTDIR\${APP_FOLDER_NAME}"
SendMessage $0 ${EM_GETSEL} "" "" $1
IntOp $1 $1 >> 16 ; shift hiword
IntOp $1 $1 & 0xffff ; mask possible sign bit
SendMessage $0 ${WM_SETTEXT} 0 "STR:$INSTDIR"
SendMessage $0 ${EM_SETSEL} $1 $1
StrCpy $InOnVerifyInstDir 0
${EndIf}
FunctionEnd
but I would still recommend just using InstallDir.
During my setup i install redistributables. After installing my program i want to execute the program during the setup. For that the redistributables are needed. But the environment is not refreshed during setup, so i can't start the program. Can i refresh the environtment during the setup, or reread the environment? Or read the system environment which has changed after the redistributable installation?
I need the environment changes made during the installation of the redistributables during the setup.
Thanks for your help.
I had your same issue, that I solved with a workaround, but I came across this solution and I would like to share with you.
This should reload environment variables during setup:
!include LogicLib.nsh
!include WinCore.nsh
!ifndef NSIS_CHAR_SIZE
!define NSIS_CHAR_SIZE 1
!define SYSTYP_PTR i
!else
!define SYSTYP_PTR p
!endif
!ifndef ERROR_MORE_DATA
!define ERROR_MORE_DATA 234
!endif
/*!ifndef KEY_READ
!define KEY_READ 0x20019
!endif*/
Function RegReadExpandStringAlloc
System::Store S
Pop $R2 ; reg value
Pop $R3 ; reg path
Pop $R4 ; reg hkey
System::Alloc 1 ; mem
StrCpy $3 0 ; size
loop:
System::Call 'SHLWAPI::SHGetValue(${SYSTYP_PTR}R4,tR3,tR2,i0,${SYSTYP_PTR}sr2,*ir3r3)i.r0' ; NOTE: Requires SHLWAPI 4.70 (IE 3.01+ / Win95OSR2+)
${If} $0 = 0
Push $2
Push $0
${Else}
System::Free $2
${If} $0 = ${ERROR_MORE_DATA}
IntOp $3 $3 + ${NSIS_CHAR_SIZE} ; Make sure there is room for SHGetValue to \0 terminate
System::Alloc $3
Goto loop
${Else}
Push $0
${EndIf}
${EndIf}
System::Store L
FunctionEnd
Function RefreshProcessEnvironmentPath
System::Store S
Push ${HKEY_CURRENT_USER}
Push "Environment"
Push "Path"
Call RegReadExpandStringAlloc
Pop $0
${IfThen} $0 <> 0 ${|} System::Call *(i0)${SYSTYP_PTR}.s ${|}
Pop $1
Push ${HKEY_LOCAL_MACHINE}
Push "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Push "Path"
Call RegReadExpandStringAlloc
Pop $0
${IfThen} $0 <> 0 ${|} System::Call *(i0)${SYSTYP_PTR}.s ${|}
Pop $2
System::Call 'KERNEL32::lstrlen(t)(${SYSTYP_PTR}r1)i.R1'
System::Call 'KERNEL32::lstrlen(t)(${SYSTYP_PTR}r2)i.R2'
System::Call '*(&t$R2 "",&t$R1 "",i)${SYSTYP_PTR}.r0' ; The i is 4 bytes, enough for a ';' separator and a '\0' terminator (Unicode)
StrCpy $3 ""
${If} $R1 <> 0
${AndIf} $R2 <> 0
StrCpy $3 ";"
${EndIf}
System::Call 'USER32::wsprintf(${SYSTYP_PTR}r0,t"%s%s%s",${SYSTYP_PTR}r2,tr3,${SYSTYP_PTR}r1)?c'
System::Free $1
System::Free $2
System::Call 'KERNEL32::SetEnvironmentVariable(t"PATH",${SYSTYP_PTR}r0)'
System::Free $0
System::Store L
FunctionEnd
Section
Call RefreshProcessEnvironmentPath
SectionEnd
Source: http://forums.winamp.com/showpost.php?p=3028153&postcount=12
How can I determine if is there available internet connection on my NSIS function?
I saw Intec plugin but I didn't find how to do that
Thanks.
You should use Dailer plugin for that.
Try this:
Dialer::GetConnectedState
Pop $1
if there is internet connection, $1 will be online either, it will be offline
Read more on:
http://nsis.sourceforge.net/Docs/Dialer/Dialer.txt
According to NSIS documentation on Windows Vista and later it is recommended to use INetworkListManager::GetConnectivity() to check if Internet is connected.
Here is a function that uses recommended approach:
!include "LogicLib.nsh"
Function IsInternetConnected
!define CLSID_NetworkListManager "{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"
!define IID_INetworkListManager "{DCB00000-570F-4A9B-8D69-199FDBA5723B}"
!define CLSCTX_ALL 23
Push $R0
Push $R1
Push $R2
Push $R3
Push $R9
; Result - is internet connected.
StrCpy $R0 false
SetPluginUnload alwaysoff
System::Call "ole32::CoCreateInstance(g '${CLSID_NetworkListManager}', i 0, i ${CLSCTX_ALL}, g '${IID_INetworkListManager}', *i .R1) i .R9"
IntCmp $R9 0 0 end
; INetworkListManager->GetConnectivity(NLM_CONNECTIVITY*)
System::Call "$R1->13(*i .R2) i .R9"
IntCmp $R9 0 0 end
; IUnknown->Release()
System::Call "$R1->2() i"
; Internet IPv4: NLM_CONNECTIVITY_IPV4_INTERNET = 64.
IntOp $R3 $R2 & 64
${If} $R3 <> 0
StrCpy $R0 true
Goto end
${EndIf}
; Internet IPv6: NLM_CONNECTIVITY_IPV6_INTERNET = 1024.
IntOp $R3 $R2 & 1024
${If} $R3 <> 0
StrCpy $R0 true
Goto end
${EndIf}
end:
SetPluginUnload manual
Pop $R9
Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd
This is how to use this function:
!include "LogicLib.nsh"
Section
Push $0
Call IsInternetConnected
Pop $0
${If} $0 == false
MessageBox MB_OK "Internet is not connected"
${Else}
MessageBox MB_OK "Internet is connected"
${EndIf}
Pop $0
SectionEnd
May be it will be useful for somebody.
TESTAGIN:
ClearErrors
SetDetailsPrint both
DetailPrint "$(TEST_TEST)"
SetDetailsPrint none
inetc::head /silent "http://www.msn.com" "$SYSDIR\test.txt"
Pop $0 # return value = exit code, "OK" if OK
${If} $0 != OK
GOTO Done
${Else}
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(TEST_INTERNET_CONNECTION)" IDRETRY TESTAGIN
#quit
${EndIf}
Done:
SetDetailsPrint both
DetailPrint "$(TEST_OK)"
SetDetailsPrint none
ClearErrors