I have an NSIS installer that uses the ExecDOS plug-in to call a command line tool that runs SQL scripts. ExecDos is called in async mode and I then loop around updating a progress bar and calling the IsDone function until the command line tool finishes.
The problem I am having is that on some computers the IsDone check is wrongly reporting the process as finished when it is in fact still running and so it continues on in the background after the installer has finished. I cannot find any commonality between the computers it doesn't work on so I am hoping someone has some ideas or can spot an error in my code.
Function UpdateDatabase
!insertmacro SetBannerText "Updating database" 0 0
!insertmacro Log "About to update the database"
StrCpy $UpdateDatabase_PreviousScriptNumber 0
SetShellVarContext all
Push "ExecDos::End" # Add a marker for the loop to test for.
ExecDos::exec /NOUNLOAD /ASYNC '"$ComSpec" /S /C ""$INSTDIR\Database\Tool\DatabaseResetTool.Console.exe" -sp="$INSTDIR\Database\Scripts" -cs="Data Source=$DatabaseServer;Initial Catalog=$Database;User ID=sa;Password=*********;Persist Security Info=true;MultipleActiveResultSets=True;Language=English" -eoe"' '' '$APPDATA\Company\$ApplicationPrefix\Database Update.log'
Pop $DatabaseUpdate_ProcessHandle
Loop:
ExecDos::isdone /NOUNLOAD $DatabaseUpdate_ProcessHandle
Pop $DatabaseUpdate_Done
StrCpy $0 "$APPDATA\Company\$ApplicationPrefix\Database Update.log"
StrCpy $1 "$TEMP\Database Update.log"
StrCpy $2 0
System::Call 'kernel32::CopyFile(t r0, t r1, b r2) ?e'
${LineRead} "$TEMP\Database Update.log" "-2" $DatabaseUpdate_LogFileLine
${StrTok} $DatabaseUpdate_LogFileLineEndPart $DatabaseUpdate_LogFileLine "(" "L" "1"
${StrTok} $DatabaseUpdate_ScriptNumber $DatabaseUpdate_LogFileLineEndPart "/" "0" "1"
${StrTok} $DatabaseUpdate_ScriptCountUnprocessed $DatabaseUpdate_LogFileLineEndPart "/" "1" "1"
${StrTok} $DatabaseUpdate_ScriptCount $DatabaseUpdate_ScriptCountUnprocessed ")" "0" "1"
${If} $DatabaseUpdate_ScriptNumber != ""
${AndIf} $DatabaseUpdate_ScriptCount != ""
FloatOp::D $DatabaseUpdate_ScriptNumber $DatabaseUpdate_ScriptCount
FloatOp::M $0 100
${If} $UpdateDatabase_PreviousScriptNumber != $DatabaseUpdate_ScriptNumber
!insertmacro SetBannerText "Updating database$\r$\n$\r$\nRunning script $DatabaseUpdate_ScriptNumber of $DatabaseUpdate_ScriptCount" 0 $0
StrCpy $UpdateDatabase_PreviousScriptNumber $DatabaseUpdate_ScriptNumber
${EndIf}
${EndIf}
Sleep 200
StrCmp $DatabaseUpdate_Done "0" Loop Done
Done:
Push "$TEMP\Database Update.log"
Push "exception"
Call FileSearch
Pop $0
Pop $1
${If} $0 != "0"
StrCpy $InstallError 1
!insertmacro LogError "Database update failed. Please review the 'Database Update.log' to see the detail error message" ""
${EndIf}
FunctionEnd
I don't know what the exact problem is but you have some other issues with your script you should probably fix:
System::Call 'kernel32::CopyFile(t r0, t r1, b r2) ?e'
b
is not a valid system plugin type, use i
. ?e
pushes to the stack, you never pop this value and you don't check if the copy succeeded. Replace it with System::Call 'kernel32::CopyFile(t r0, t r1, i 0)i.r0'
and check with ${If} $0 = 0 ...deal with error
You are using $ComSpec and ""
which I assume is to deal with cmd.exe quote issues, ExecDos uses pipes so redirection should work without bringing cmd.exe into the mix. Try executing just ExecDos::exec /NOUNLOAD /ASYNC '"$INSTDIR\Da...
You don't really control all files in $Temp, use $Pluginsdir as your scratch space
You never pop and test for Push "ExecDos::End"
Some other things you might want to investigate:
SetShellVarContext all
converts $APPDATA to the common appdata/programdata so make sure you are admin.?e
value you never pop ends up filling the stack so that the isdone parameter is never pushed to the stack so the function ends up with a invalid parameter?To debug this you should be able to replace the call to isdone with System::Call 'kernel32::WaitForSingleObject(i $DatabaseUpdate_ProcessHandle
,i 0)i.r0'
and compare the value in $0 to the return values listed on MSDN.