Archive: ExecDos::Exec /ASYNC and ExecDos::wait - called exe doesn't end upon sucess


ExecDos::Exec /ASYNC and ExecDos::wait - called exe doesn't end upon sucess
I'm building an NSIS installer to install a data warehouse solution based on SQL Server 2005. The installer also launches a stored proc as a data job when the installer completes. Here are the basic steps:

1. Check for MSI 3.1, install if not exists.
2. Check for .NET 2.0, install if not exists.
3. Check for SQL Server 2005, install if not exists.
4. Run SQL scripts to create the data warehouse.
5. If user chooses, obtain job run configuration from them and invoke the procedure as a "post-install" step (i.e. like Run on Complete).

All works fabulous until the last step. My idea was to invoke the job after the main install via calling sqlcmd.exe. Since this job can take upwards of 20 minutes to complete, I attempted to use ExecDos::Exec /NOUNLOAD /ASYNC and kick it off that way. Then I check on the status in .onGUIEnd, and wait if it's not done, effectively allowing the GUI to tear down, yet the installer exe stay in the processes. So I do a ExecDos::wait inside .onGUIEnd to allow it to finish.

THE PROBLEM: sqlcmd suceeds but does not seem to return to the installer - it hangs in processes, but idle. So when I manually terminate it, then ExecDos returns back to the installer, I get a return code of '1' from ExecDos::wait, and then it finishes.

THE CODE:

Function LaunchJob
ExecDos::Exec /NOUNLOAD /ASYNC 'sqlcmd -v DataSource="$JobConfigDataSource" \ User="$JobConfigUserName" \ Password="$JobConfigPassword" \ Catalog="$JobConfigCatalog" \ Schema="$JobConfigSchema" \ Provider="$JobConfigProvider" \
-Q "$JobProcScript" \
-d "$DatabaseName" \
-b \
-S "$ServerName"'

Pop $R7

; NOTE: I tried it here too and commented out in .onGUIEnd and had the same effect.
;MessageBox MB_ICONEXCLAMATION|MB_OK 'onGUIEnd called...'
;
;ExecDos::isdone /NOUNLOAD $R7
;Pop $R1
;
;${If} $R1 == 0
; ; check process exit code
; ExecDos::wait $R7
; Pop $R2
; MessageBox MB_ICONEXCLAMATION|MB_OK 'Returned $R2"'
;${ElseIf} $R1 == -1
; MessageBox MB_ICONEXCLAMATION|MB_OK 'Error "$R1"'
;${EndIf}

;other stuff ...

FunctionEnd

Function .onGUIEnd
MessageBox MB_ICONEXCLAMATION|MB_OK 'onGUIEnd called...'

ExecDos::isdone /NOUNLOAD $R7
Pop $R1

${If} $R1 == 0
; check process exit code
ExecDos::wait $R7
Pop $R2
MessageBox MB_ICONEXCLAMATION|MB_OK 'Returned $R2"'
${ElseIf} $R1 == -1
MessageBox MB_ICONEXCLAMATION|MB_OK 'Error "$R1"'
${EndIf}
FunctionEnd


FACTS:

* Running sqlcmd manually at the command prompt works and closes at it should, because I use the '-Q' switch - which according to sqlcmd documentation will do a execute and exit; this works.

* When I do the same call with nsExec::ExecToStack, it works successfully.

* Even if I run it with ExecDos::Exec /NOUNLOAD /ASYNC "inline" - immediately grabbing the handle and calling wait on it - it still hangs out there and wont return back (see code comments).


I've isolated it down to this, and still cannot find what the problem is. Note though, of course, that sqlcmd does send data to stdout, so I thus get a file back from DosExec with that data - but ONLY if I manually blow away the sqlcmd process after it's completed. Can I have a few sharp eyes assist?

Also, is there source code for this plug-in that I can peek at?

Thanks much in advance,

Timex


You can create a second installer that'd just execute sqlcmd.exe with ExecDos and execute that installer with Exec. This way you won't have to use /ASYNC.

The source code is part of the ZIP available on the Wiki page.

http://nsis.sourceforge.net/ExecDos_plug-in


But wouldn't this installer then be synchronous to the main installer?

I'd rather keep it within a single installer solution for several reasons:

* Less maintenance and more important dependencies.
* I don't want or need any GUI for this last step ... it just needs to quietly run, without hanging up anything, then gracefully exit.

So, what I'm really get at...shouldn't ExecDos return from it once it's completed?! That is, return back via ExecDos::wait, so that I can Pop the return. It doesn't do this. It's like it hangs indefinitely on the ExecDos::wait, never returning me a return code. Why?

Thanks for your suggestion though! I did think of maybe moving my code to the .onGUIEnd, and running the job there SYNCHRONOUSLY via plain old nsExec::ExecToStack. That way, my GUI tears down first, then runs it silently i nthe background, and closes when finished. But, I'd hate to have to refactor my script in this fashion - less readable and not as elegant - yuck! :(

I want ExecDos::Exec /ASYNC to WORK! It is the solution I am after.

- Timex


No, it wouldn't be synchronous as you'd use Exec and not ExecWait.


Hmmmm, good point. Tried it and that works. Sometimes it's the simple things, heh. Well, the reason I was so hung up on doing the ExecDos and/or /ASYNC method was because originally I was doing this step during the install step and thus logging the stdout. This way works, but with the sacrifice of rich logging. Also, the command window is open, of course. But, I'll handle all of that! I saw some thread discussing this and thought I remember reading a thing or two about how to handle the command window (e.g. SW_HIDE). Besides, my job does logging in the database.

Thanks kichik! Sometimes four eyes are better than 2 (or one, lol).

- Timex


SQL commands execution has a long history on this forum and many people had problems with it :(. The same situation I see here because simple script using consApp.exe (included to ExecDos pack) works as expected message appears 5 sec after installer close:

!define APP_NAME onguiend
!define DOS_APP consApp.exe

Name "${APP_NAME} Test"
OutFile "${APP_NAME}.exe"

AutoCloseWindow true

!include "MUI.nsh"
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"


Section "Dummy Section" SecDummy
ExecDos::exec /NOUNLOAD /ASYNC /TIMEOUT=5000 "$EXEDIR\consApp.exe" \
"test_login$\ntest_pwd$\n" "$EXEDIR\stdout.txt"
Pop $0 ; thread handle for 'wait'
SectionEnd

Function .onGUIEnd
ExecDos::wait $0
Pop $0
MessageBox MB_OK "Exit code $0"
FunctionEnd

The only thing I can offer is to add 2 parameters to ExecDos command line, "" for stdin and "$EXEDIR\stdout.txt" for stdout (temp file would be better for release script because of possible RO location).