Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29274 Discussions

CreateProcess to Fortran EXE - Redirect Errors

msrado
Beginner
1,867 Views
Oh Great and Knowledgable Ones!
I know a lot has been written about this subject on the message boards, but none of it is working for me ...
I am using Visual Basic 6 to run a CVF 6.1 EXE with the CreateProcess API function. If the user's input data is screwy, the Fortran EXE with display a run-time error on the DOS screen. The WaitForSingleObject function doesn't return an error code, though, and I can't warn my user. (I realize that this is because the WaitForSingleObject is just waiting for the DOS window to close.) Because the Fortran crash may occur after some of the output has printed, I can't use the presence of an output file to detect the errors.
What I'd really like to do is send the run-time error to an error file. With any other DOS program, I can do that by typing "[exename].EXE > [errfile]". That doesn't seem to work with CVF EXEs. I've tried using "[exename].EXE 1>[errfile] 2>&1", but that doesn't create my error file. I've tried using CreateFileto open the error file and assigning its handle to theSTARTUPINFO.hStdError field.That creates the error file, buttherun-time errors don't get there.
Please, please, please throw me a line! Do I have to set a switchin CVF when the EXE is compiled toredirect the run-time messages? Do I have to use one of the utility programs? I'm using Windows 2000 and I didn't think "2>&1" would have a problem by itself. Do I have topray to the god of CVF Console Applications?
A snippet of my VB code to run the Fortran EXE follows.
In humble supplication,
Robin Olson
VB CODE - I've declared all of the APIs and constants elsewhere
' Initialize the STARTUPINFO structure:
udtStart.cb = Len(udtStart)
udtStart.dwFlags = STARTF_USESHOWWINDOW
udtStart.wShowWindow = SW_HIDE
' Clear and re-Initialize the security attributes
RtlZeroMemory udtSecurity, Len(udtSecurity)
udtSecurity.nLength = Len(udtSecurity)
' Start the shelled application:
lRet = CreateProcess(vbNullString, sEXEPath & sProgName & _
".EXE >" & sTempPath & sErrFile, _
udtSecurity, udtSecurity, 1, NORMAL_PRIORITY_CLASS, 0, vbNullString, _
udtStart, udtProcInfo)
' Wait for the shelled application to finish:
lRet = WaitForSingleObject(udtProcInfo.hProcess, 6000)
'lShell = Shell(sEXEPath & sProgName & ".EXE", vbHide)
'lHndl = OpenProcess(PROCESS_QUERY_INFORMATION, 0, lShell)
''lRet = WaitForSingleObject(lHndl, INFINITE)
'lRet = WaitForSingleObject(lHndl, 6000)
If lRet = WAIT_TIMEOUT Then
lRet = CloseHandle(lHndl)
lHndl = OpenProcess(PROCESS_TERMINATE, 0, lShell)
If lHndl = 0 Then Exit Sub
lRet = TerminateProcess(lHndl, 0)
ElseIf lRet = WAIT_FAILED Then
MsgBox "Error Calling Program EXE - #" & Err.LastDllError & _
". Please contact CAE.", iMsgBox
GoTo RunProgErr
End If
lRet = CloseHandle(lHndl)
lRet = CloseHandle(lFileHndl)
0 Kudos
11 Replies
Jugoslav_Dujic
Valued Contributor II
1,867 Views
I've tried using CreateFileto open the error file and assigning its handle to theSTARTUPINFO.hStdError field.That creates the error file, buttherun-time errors don't get there.
That's the way to go. You should probably fill in hStdOutput as well with the same handle. Note that you have to add STARTF_USESHOWWINDOW to STARTUPINFO.dwFlags to make it work and specify fInheritHandles = TRUE.
However, in addition, take a look at GetExitCodeProcess. Process' exitcode is notvery used feature under Windows, but I think they differ for normal and abnormal exit -- see for yourself.
Jugoslav
0 Kudos
msrado
Beginner
1,867 Views

Hi Jugoslav,

I've modified my CreateProcess call according to your response, but still no luck on getting the screen output to appear in an error file. The code now looks like:

' open a file to catch errors
udtSecurity.nLength = Len(udtSecurity)
lFileHndl = CreateFile(sTempPath & sErrFile, FILE_WRITE_DATA, 0, _
udtSecurity, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, ByVal 0&)
' Initialize the STARTUPINFO structure:
udtStart.cb = Len(udtStart)
udtStart.dwFlags = STARTF_USESHOWWINDOW
udtStart.wShowWindow = SW_NORMAL
udtStart.hStdOutput = lFileHndl
udtStart.hStdError = lFileHndl
' Clear and re-Initialize the security attributes
RtlZeroMemory udtSecurity, Len(udtSecurity)
udtSecurity.nLength = Len(udtSecurity)
' Start the shelled application:
lRet = CreateProcess(vbNullString, sEXEPath & sProgName & ".EXE", _
udtSecurity, udtSecurity, True, NORMAL_PRIORITY_CLASS, 0, vbNullString, _
udtStart, udtProcInfo)
' Wait for the shelled application to finish:
lRet = WaitForSingleObject(udtProcInfo.hProcess, 6000)
If lRet = WAIT_TIMEOUT Then
lRet = CloseHandle(lHndl)
lHndl = OpenProcess(PROCESS_TERMINATE, 0, lShell)
If lHndl = 0 Then Exit Sub
lRet = TerminateProcess(lHndl, 0)
ElseIf lRet = WAIT_FAILED Then
MsgBox "Error Calling Program EXE - #" & Err.LastDllError & _
". Please contact CAE.", iMsgBox
GoTo RunProgErr
Else
'check normal/abnormal termination
lRet = GetExitCodeProcess(udtProcInfo.hProcess, lExit)
If lExit <> 0 Then _
MsgBox "An error occurred while attempting to run " & sProgName & _
" . Please examine your input data for errors.", iMsgBox
End If
lRet = CloseHandle(lHndl)
lRet = CloseHandle(lFileHndl)

Your suggestion to try the GetExitCodeProcess API call was gold, though! I can at least detect when the termination was abnormal and warn my user. I don't suppose youknowwhere I can get a list of exit codes?
I really appreciate the help!
Robin Olson
0 Kudos
Steven_L_Intel1
Employee
1,867 Views
Are you building the EXE as a console application? Does it have console output normally? You may want to consider redoing it as a "Fortran Windows Application".
See the online help under "Environment variables" for values you can set to affect how the error output is produced or displayed. You could call SETENV from the Fortran program to change these settings.
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,867 Views
As I said :-), you need
udtStart.dwFlags = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES
(Also, here's a ratherFAQish MSDN article about redirecting child process handles)
Regarding exit codes, there's no "standard" list -- they're application-specific. A common convention is to return zero if successful, and something else otherwise. Help page "exit codes, Fortran" suggests that it's actually 0 if no error, and Visual Fortran error code otherwise.
Jugoslav
0 Kudos
msrado
Beginner
1,867 Views

Hi Jugoslav,

I was justlogging on to tell you that I found STARTF_USESTDHANDLES and got the code to work when I saw your post. Yay!!! Thanks for sending me in the right direction.

Robin

For anyone else who may be tackling a similar issue, this is what my final result looks like:

' open a file to catch errors
udtSecurity.nLength = Len(udtSecurity)
udtSecurity.bInheritHandle = True
lFileHndl = CreateFile(sTempPath & sErrFile, FILE_WRITE_DATA, 0, _
udtSecurity, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, ByVal 0&)
' Initialize the STARTUPINFO structure:
udtStart.cb = Len(udtStart)
udtStart.dwFlags = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES
udtStart.wShowWindow = SW_HIDE
udtStart.hStdOutput = lFileHndl
udtStart.hStdError = lFileHndl
' Clear and re-Initialize the security attributes
RtlZeroMemory udtSecurity, Len(udtSecurity)
udtSecurity.nLength = Len(udtSecurity)
' Start the shelled application:
lRet = CreateProcess(vbNullString, sEXEPath & sProgName & ".EXE", _
udtSecurity, udtSecurity, True, NORMAL_PRIORITY_CLASS, 0, vbNullString, _
udtStart, udtProcInfo)
' Wait for the shelled application to finish:
lRet = WaitForSingleObject(udtProcInfo.hProcess, 6000)
If lRet = WAIT_TIMEOUT Then
lRet = CloseHandle(lHndl)
lHndl = OpenProcess(PROCESS_TERMINATE, 0, lShell)
If lHndl = 0 Then Exit Sub
lRet = TerminateProcess(lHndl, 0)
ElseIf lRet = WAIT_FAILED Then
MsgBox "Error Calling Program EXE - #" & Err.LastDllError & _
". Please contact CAE.", iMsgBox
GoTo RunProgErr
Else
'check normal/abnormal termination
lRet = GetExitCodeProcess(udtProcInfo.hProcess, lExit)
If lExit <> 0 Then _
MsgBox "An error occurred while attempting to run " & sProgName & _
" . Please examine your input data for errors.", iMsgBox
End If
lRet = CloseHandle(lHndl)
lRet = CloseHandle(lFileHndl)

0 Kudos
msrado
Beginner
1,867 Views

Hi Steve,

Thanks for the reply. We haven't really played with the Fortran Windows applications because we're pretty well versed in VB and it is so easy to use. (We haven't moved to .NET yet - that may change our opinion!) We love CVF for the DLL and console application creation, because we have a LARGE library of legacy code in Fortran on anancient mainframe.

In this case, we're creating console applications to rapidly port the mainframe programs to the PC. Since the mainframe versions of these programs read from JCL and write to the output queue, its easy to convert them to console apps that read a text file and write a text file.

Thanks for the suggestion - we'll have to check out those Windows apps!

Robin

0 Kudos
Steven_L_Intel1
Employee
1,867 Views
I suggested a Windows app because if the program has no user interface, a Windows app works well (you can skip all the stuff about dialogs and menus - just enter the "main program" at WinMain. It's usually a matter of about five minutes converting. If the program writes to the console, then a console app is what you need.
0 Kudos
g_f_thomas
Beginner
1,867 Views
It's good practice to

CloseHandle(udtProcInfo.hProcess)
CloseHandle(udtProcInfo.hThread)

Just a comment on design :

Right now you detect the presence of errors but not the specific error(s). That's not very useful to the user. Why don't you use VB to validate the input so that whatever error(s) the app generates won't be of the user's doing? When you've bullet proofed the input you won't need to generate a user error file and then you can focus on figuring out how to handle those errors that are of the apps sole creation.

Ciao,
Gerry T.
0 Kudos
msrado
Beginner
1,867 Views

Hi Gerry,

That's a great idea if its possible to use VB to validate the user's input. In this case, the programs we are working with do engineering analysis and the input data can be valid within a certain range, but the combination of input data may cause a divide by zero or square root of -1. In most cases, it's not blatantly obvious which combination of input data will cause a crash, or where the crash will occur. Ideally, there would be an error trap at every SQRT or / operation in the Fortran. That's not always practical with our large programs.Sinceour goal is to rapidly migrate a lot of programs, it's enough at this point to detect the fatal exception and alert the user without crashing the user interface.

We do perform input validation to make sure the user's input is inside of the valid ranges.

Thanks for the advice on closing the thread. I'll have to put that into the code.
Robin
0 Kudos
g_f_thomas
Beginner
1,867 Views
Using VB for input validation is somewhat of a no brainer. Likewise for VBA with an Excel or Access interface.
Despite this, the unforeseen happens and the Fortran app has to respond. The CVF docs has an excellent discussion on the use of Windows(C/C++) SEH to overcome Fortran's lack of support for exception handling, including those of the floating point variety. For math lib exceptions you have to supply a MATHERRQQ subroutine. However, if your Fortran is a DLL then to use MATHERRQQ you must link against the _static_ C rtl.

Ciao,
Gerry T.
0 Kudos
msrado
Beginner
1,867 Views

Hi Gerry,

I wasn't aware that I could handle the Fortran run-time errors inside of the Fortran code. How exciting! I'll have to look at the SEH and start using it in our code. Thanks for the tip!

Have a great weekend!

Robin

0 Kudos
Reply