Link to home
Start Free TrialLog in
Avatar of rafay092498
rafay092498

asked on

External Application: Return Codes and Execution

The question is similar to one already posted.  I need to know how I can run an exe file "synchronously" from VB (not return to VB till the new application that I am launching terminates).  In addition, I need to trap the code returned by the application I launched.  I can use Shell to launch the application, but all that gives me is an AppId.  I do not want to do a "busy wait" till the new application exits, and even if I do decide to wait in a loop till the application exits, how do I trap the return code?

Help!!!
Avatar of Tommy Hui
Tommy Hui

The ideal way is to not use the Shell statement. Instead, use the CreateProcess() Windows API. This returns back a HANDLE. You can use this HANDLE for the WaitForSingleObject() API to know when the application has terminated.
Avatar of rafay092498

ASKER

Agreed.  But how does this get around the problem of trapping the exact code returned by the error (e.g. did the application exit with code 0, 1, 2, other???)?  From what I know (and it is quite little!), the API only indicates what event caused the process to terminate (a timeout, normal termination etc) but the exact return code returned by the application cannot be captured by the calling function.
  Dim ecode As Long, nret As Long, nret2 As Long
   nret = WaitForSingleObject(hProcess, CLng(TimeOut))
   nret2 = GetExitCodeProcess(hProcess, ecode) ' ecode will contain exitcode
   Call CloseHandle(hProcess)

Or, get exitcode only when 'Normal completion'
   If nret = 0 Then
      nret2 = GetExitCodeProcess(hProcess, ecode) ' ecode will contain exitcode
    End If
For some reason, the CreateProcess is not returning a handle.  Maybe I am making a basic mistake here, but when I execute the following code, lResult contains a zero after CreateProcess call.  Somehow, it seems that there is something wrong here! Any suggestions?
--
Dim uSecurityAttrib As SECURITY_ATTRIBUTES   'passed by ref
Dim uSecurityAttrib2 As SECURITY_ATTRIBUTES
Dim uProcessInfo As PROCESS_INFORMATION
Dim uStartupInfo As STARTUPINFO
Dim lResult As Long, nRet As Long, nret2 As Long, ecode As Long, Timeout As Long
Timeout = 3000000    'set to see if this was causing problems
If Index = 0 Then       'OK
  lResult = CreateProcess("c:\dummyapp.exe", "12", uSecurityAttrib, uSecurityAttrib2, False, 0, "", "", uStartupInfo, uProcessInfo)
   nRet = WaitForSingleObject(lResult, CLng(Timeout))
   nret2 = GetExitCodeProcess(lResult, ecode)
   Call CloseHandle(lResult)
Else                    'Cancel
    Unload Me
End If

what are the DECLAREations of WaitForSingleObject and GetExitCodeProcess?
what are the DECLAREations of WaitForSingleObject and GetExitCodeProcess?
Oops... error...  The calls should have been (I think)...
---
lResult = CreateProcess("c:\dummyapp.exe", "12", uSecurityAttrib, uSecurityAttrib2, False, 0, "", "", uStartupInfo, uProcessInfo) 'retruns a BOOl and NOT a handle!
nRet = WaitForSingleObject(uProcessInfo.hProcess, CLng(Timeout))
nret2 = GetExitCodeProcess(uProcessInfo.hProcess, ecode)
Call CloseHandle(uProcessInfo.hProcess)
---
and the declares are
---
Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As SECURITY_ATTRIBUTES, lpThreadAttributes As SECURITY_ATTRIBUTES, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Declare Sub GetStartupInfo Lib "kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
---

thui?
so... can u see the problem???  Note that the CreateProcess API never creates the process... what do i do??
First, create EXE which uses ExitProcess API
and put this in Form_Click
   ExitProcess 5
--
I don't have a problem using it. But, since you didn't reject thui's answer, it means you are expecting solution from him.
Well, actually the reason I didnt reject thui's answer was that I was expecting a response from him.  I guess I have waited long enough.  As for your latest suggestion, the problem is not that the new process is NOT returning an exit code.  The problem is that it is never started with the CreateProcess API.  Is there anything wrong with the code that I am using?  Do I need to set anything else? I am a bit new to VB, but in C++, API calls as shown in code above work fine.  Also, what else can I check to make sure that the CreateProcess does actually start the application.  I know for sure that it does not start it because I dont see the new application ask for an input which it should do! Finally, the application that I am going to be calling is not written by me, but i know that it exits with some codes and those codes are what I am trying to capture.  A quick reply (yet again :-)) would be GREATLY appreciated :-)
ASKER CERTIFIED SOLUTION
Avatar of ameba
ameba
Flag of Croatia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Finally! Thanks :-)

P.S. Any idea why the API calls were not working?

Thanks again!
I think your call to CreateProces might have incorrect arguments (lpCommandLine = "12"), but I didn't try.
CORRECTION:
In my answer, in function ShellAndWait, this line:
   Call CloseHandle(hProcess)
should be moved, to be the last line in function, after the End Select statement.
----
This code was tested with a simple VB project, that uses:
   ExitProcess 5
in Sub Form_Click()
It looks like this doesn't work for all the possible cases, but rafay found a fix.

One last comment.  In order for the GetExitCodeProcess API to be able to get the return code, the fdwAccess parameter in OpenProcess has to be set to PROCESS_ALL_ACCESS and not to SYNCHRONIZE, and this is equal to &H80000 + &HF0000 + &HFFF.  With this the API correctly returns the return code of the process.