Question

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
Was it helpful?

Solution

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.
  • Try a longer sleep...
  • Is it possible that the ?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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top