Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Call a Exe , Wait the end of the job and get the return value 1

Status
Not open for further replies.

olivvv

Programmer
Dec 6, 2022
8
FR
Hi everyone,

I try to call a executable.
Wait the job to finish
et Get the executable return value to see if the job was good

I try :
Code:
nRetVal = ShellExecute(0, "open", test, "", "", 1)
it doesn't seen to wait for the end of the job .

I try this :
Code:
oProcess = CREATEOBJ('API_AppRun',test,"C:\",'NOR')
oProcess.LaunchAppAndWait()

he waits the end of the job.
I have got a object in return.

but it doesn't seem to show me the return value :
2023-04-21_14_39_14-D%C3%A9bogueur_Visual_FoxPro_arr%C3%AAt_-_menu_start.scx_menugeneral.command2.click_z7flsf.png



any ideas ?

Thanks
Olivier L
 
Olivier,

First, ShellExecute() is not appropriate here, as it is completely modeless. In other words, once you have called it, your program continues to run independently, and has no communication with the called process.

API_AppRun should do what you want, but (as I understand it), it does not return an object. It returns an exit code, but that's a code generated by API_AppRun itself, not the application that you are calling. What you are seeing in the debugger is the API_AppRun object itself, not anything that is returned by the called process.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I think you would need to run a batch script and use that to create a semaphore file of some kind based on the
error code from your .exe

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
I think it is called an errorlevel in a batch file, and you could use it like this

Code:
@echo off
myApp
echo %Errorlevel% > c:\temp\semaphore.txt

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Hi Olivier,

You can use the Windows Scripting Host:

Code:
LOCAL commandVal
LOCAL WshShell
LOCAL execObj
LOCAL exitCode

commandVal = "Notepad.exe"

WshShell = CREATEOBJECT( "Wscript.Shell" )

execObj = WshShell.Exec( commandVal )

DO WHILE execObj.Status = 0
	DOEVENTS
ENDDO

exitCode = execObj.ExitCode

Regards, Stefan
 
It seems your API_AppRun class is designed for this job. Especially as you show us it has a method called LaunchAppAndWait(). from the name it doesn't tell whether it not only waits but also returns the result value of the launched app, but the most straightforward way to find out would be to do

Code:
? oProcess.LaunchAppAndWait()

I would almost say "Can you please get this idea yourself?", but even more so: Why don't you look into the code of that method and see what it does and whether it has a RETURN in it?

Besides that, as Stefan said there are well known solutions to this problem, another one is using the Windows API functions CreateProcess and WaitForSingleObject. But if you have the class you have, it would be wasting time to redo what it does. Is there no documentation about this class you could refer to? Are you not even a little bit curious, to look yourself? Or are you afraid you could break code just by looking into it?

Chriss
 
But if you have the class you have, it would be wasting time to redo what it does. Is there no documentation about this class you could refer to?

And the source code is easily available to read and understand, including here:

I note that it says:

You can either 'fire and forget', or wait on the launched executable to terminate, and can query the termination code.

That's where I picked up the point about the termination code. But if you look at the procedure CheckProcessxitCode(), it looks like the termination code indicates the status of API_AppRun itself, rather than any value returned by the called program (I might be wrong about that).

Mike



__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Thanks for that reference, Mike.

There's an example at the and at the oProcess object has another method used there, called CheckProcessExitCode(). I haven't looked deeply into the source code, but this could also work after LaunchAppAndWait() ended to get the exitcode.

By the way, if you're also at the other end of this problem and want to write an EXE that does return an exitcode, it can't be done in VFP by RETURN 'something' you have to use the API function ExitProcess.
Or as shown in another Ed Rauh post there at levelextreme:
I don't just add this as an additional side note, it shows the mechanism of an EXEcutable to return something is limited to such an exitcode, an integer. If you'd need something more than that returned, I think you'll need to look into things like stdout, which is a named pipe a process can make use of, that will feed output, not only at the time of exiting, but can be output a process creates during its whole lifetime and as the name "standardoutput" suggests this can be accessed by the process that created this second process by standard.

And on a much more general level, you could look into interprocess communication. There are man articles and conference sessions out there about this topic.

Chriss
 
Mike Lewis said:
if you look at the procedure CheckProcessxitCode(), it looks like the termination code indicates the status of API_AppRun itself...
No, it calls CheckExitCode(nProcessToCheck, @nExitCode) with nProcessToCheck by default being this.inProcessHandle, and that is what the class sets when you launch the other process.

So it's about getting the exit code of the launched process.

I see, it's not that straight forward tounderstand from Ed Raughs documentation:
Ed Rauh said:
* examine the Process handle object's termination code member
* Provide the user with the option to examine another process
* termination code by passing an explicit handle, otherwise
* use the object's process instance

Should I translate this to English? The description of the used Windows API function GetExitCodeProcess is simpler to understand:
"Retrieves the termination status of the specified process."

Ed Raugh wants to say this method of his class gets the exit code of the process you launched with this class or - if you pass in another process handle - of that handle.
The article about GetExitCodeProcess also explains why 259 is a special result of this. When you use LaunchAppAndWait you wait for the launched process to exit, so you can expect to not get this special exit code, but simply the real final exit code, as the other process has ended.

And if you ask why could you even call that after the launched process has finished. Well, that's because a) the launched process returns his exit code to the Windows OS and b) the GetExitCodeProcess function asks the OS about what this process you know by its handle has returned. And that's also why this is a standardized OS process with just one type of a result: An Integer. And then also, because c) the process is not finally complete with you as the creator of that process still having the opened process handle. You have to close it. That's done in the Destroy of Ed Rauhs class by using CloseHandle. After that the OS will forget about this process completely and the handle that's just a number, an ID, becomes invalid.

There are easier ways to get a return value from another process you write yourself in VFP, create a COM server (an olepublic class) and you cannot only start it by CREATEOBJECT() instead of needing a whole new class about Windows API calls, you can then also offer methods to tell this COM server what to do and get any type of return value back. You also don't need to write any code to wait for it to be done, it becomes just like any other method call of any other class, doing what it does and returning what it returns. And afterwards, it stays in memory and you can do the next thing with it. So that's also a good alternative in terms of not needing to launch the other EXE everytime you want it to do one thing and return its exitcode only.

Ed Rauhs class only is good for the case you only launch an EXE and want its exitcode by the standard os exitcode mechanism. It saves you writing all that API stuff, but it doesn't really save you from understanding the mechanisms behind it. At least read the example given to see the usage of its CheckProcessExitCode() method.

Chriss
 
Chris, thanks for clarifying those points. I admit I don't completely understand the source code, but what you said seems to confirm what I thought, that the launched application must behave like a traditional command-line program that returns an integer "error level" for the OS to interpret.

What would be really helpful now is if Olivia could come back to the thread and let us know more about her requirements and especially about the nature of the program that she is calling. In particular, is it a VFP program, and does she have any control over what it does?

Depending on the answer to those question, this whole discussion about API_AppRun might be irrelevant.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Indeed, as I also pointed out.

If you don't write test.exe yourself and you just need the exitcode, as Mikes describes it as the old DOS errorlevel, indeed Ed Rauhs class is delivering that, you just need to call the CheckProcessExitCode() method. And as your intent is...
olivvv said:
..to see if the job was good
...that's of course doable with your own EXEs, too, you just need to define your exit codes, like 1 for success and any other value than meaning failure. Just check out what's returned if you exit before setting the exit code yourself and don't use that for signalling success, as it also comes back before you actively use this return mechanism.

Indeed any EXE, even those not calling ExitProcess or TerminateProcess with an integer as their errorlevel will return an integer. So this works with any EXE, no matter if console or not, no matter if it has a command option interface or not. As GetExitCodeProcess describes it, this value is one of the following:
GetExitCodeProcess said:
1. The exit value specified in the ExitProcess or TerminateProcess function.
2. The return value from the main or WinMain function of the process.
3. The exception value for an unhandled exception that caused the process to terminate.

Point 2 is also the reason every C/C++ programs main function has an int as return value, i.e. there is int main(int argc, char* argv[]). But VFPs main project item isn't its C++ main or WinMain, that's always in the stub the compiler adds to your code when building an EXE, the starting code that runs before your first line of code and for example opens or creates a foxuser.dbf and loads runtime DLLs or errors, when they are not found.

I can't tell if that's the root of the OS convention, I think it's more valid to put it that way around, as Windows is a C++ application it was programmed with an integer result of any process in mind, as that's a default of C++ programs. Windows just introduced the mechanism that a process can post its exit code to it and it will forward it to those having the necessary permissions and a process handle to prove it. It's not a direct return mechanism this way, that you get returned a value from the EXE you call directly to you. If you want a direct return mechanism that also gives you the freedom to return other things the OLE class mechanism does that differently and not as exit code, but as a return value of an OLE class method and that's then even working across different programming languages as long as they support OLE.

Chriss
 
I guess Olivier has already thrown away Ed Rauhs code and went for Stefan Foerners use of Wscript.Shell, solved his problem and didn't follow the further discussion.

I wouldn't mind that.

You could also use that to get output written to stdout, by
Code:
? execObj.StdOut.ReadAll()

Chriss
 
Below is a code for executing and waiting for the process to end and returns the exit code. I do not remember where I got it from...

Code:
*========================================================================================
* Executes another application, waits for it to complete and returns the error level
*========================================================================================
LParameter tcCmdLine

	*--------------------------------------------------------------------------------------
	* API declarations
	*--------------------------------------------------------------------------------------
	DECLARE INTEGER CreateProcess IN kernel32.DLL ;
		String lpApplicationName, ;
		STRING lpCommandLine, ;
		INTEGER lpProcessAttributes, ;
		INTEGER lpThreadAttributes, ;
		INTEGER bInheritHandles, ;
		INTEGER dwCreationFlags, ;
		String lpEnvironment, ;
		String lpCurrentDirectory, ;
		STRING @lpStartupInfo, ;
		STRING @lpProcessInformation
	Declare long GetLastError in Win32API

	*--------------------------------------------------------------------------------------
	* Launch the application
	*--------------------------------------------------------------------------------------
	Local lcStartupInfo, lcProcessInfo, lnOK
	lcStartupInfo = ;
		BINTOC(68,"RS") + ;
		Replicate(Chr(0),40) + ;
		BinToC(1,"RS") + ;
		BinToC(0,"2RS") + ; && SW_HIDE
		BinToC(0,"2RS") + ;
		Replicate(Chr(0),16)
	lcProcessInfo = Replicate(Chr(0),16)
	lnOK = CreateProcess( ;
		NULL, ;
		m.tcCmdLine, ;
		0, ;
		0, ;
		1, ;
		0x20, ;
		NULL, ;
		NULL, ;
		@lcStartupInfo, ;
		@lcProcessInfo ;
	)

	*--------------------------------------------------------------------------------------
	* Extract the handles from the PROCESSINFO structure
	*--------------------------------------------------------------------------------------
	Local lnProcessHandle, lnThreadHandle
	If m.lnOK == 0
		Return -1
	Else
		lnProcessHandle = CTOBIN(Substr(m.lcProcessInfo,1,4),"RS")
		lnThreadHandle = CTOBIN(Substr(m.lcProcessInfo,5,4),"RS")
	EndIf 

	*--------------------------------------------------------------------------------------
	* Wait for the process to terminate
	*--------------------------------------------------------------------------------------
	Declare Long WaitForSingleObject in Win32API Long, Long
	WaitForSingleObject( m.lnProcessHandle, -1 )
	
	*--------------------------------------------------------------------------------------
	* Get the error code
	*--------------------------------------------------------------------------------------
	Local lnExitCode
	lnExitCode = -1
	Declare Long GetExitCodeProcess in Win32API ;
	  Long hProcess, ;
  	Long @lpExitCode
	GetExitCodeProcess( m.lnProcessHandle, @lnExitCode )

	*--------------------------------------------------------------------------------------
	* Close handles
	*--------------------------------------------------------------------------------------
	Declare CloseHandle in Win32API Long
	CloseHandle( m.lnProcessHandle )
	CloseHandle( m.lnThreadHandle )

Return m.lnExitCode

Greg
 
Greg,

thanks!

I was about to take Ed Rauhs code and minimize it for the use case of Olivier. That spares me to do so.


Chriss
 
Just keep in mind that, with all these suggestions, the called program has to know that it must return an error level. The majority of Windows applications don't do that. As we don't know anything about the program that Olivier want to execute, we cannot know if these suggestions are appropriate. For example, if the program in question is a VFP program, there will be simpler ways of communicating with its caller.

Oliver, once again, it would be helpful if you could respond to this thread. Let us have your feedback on our suggestions to avoid us wasting time with ideas that might not be relevant.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hello Eveyone,


I didn't expect to get so many responses.

I try @stefan code, it works. I have got the return value :)

@Chris , yes it is a colleague c# software. I know the differents return values.

I will take time to read all your solution to learn different solution for this problem et take the best one for me :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top