Starting an external process asynchronously

Starting an external process asynchronously


in my Fortran program, I want to start an external process and directly return to my program, not waiting for its completion. 
Thus, as I understand it, the compiler routines RUNQQ, SYSTEM or SYSTEMQQ are not suitable for this problem.

I have read about the possibility to use the Windows-Routine CreateProcess instead and implemented the following function:

subroutine start_help()
c.... Purpose: start an external process
use Kernel32
implicit none
CHARACTER(len=500) :: fcis, fcis1
 bIH = FALSE ! inherit handles [bool] dCF = NULL ! creation flags [dword]
lpE = NULL ! environment [lpStr]

fcis = "C:\\Program Files (x86)\\Adobe\\Reader 10.0\\Reader\\
fcis1 = "/s /o C:\\path\\to\\my.pdf"C
res = CreateProcess(fcis,fcis1,tPA,tTA,bIH,dCF,lPE,NULL,tSI,tPI) if (res .eq. 0) then
write(16,*) "Error! Could not create process.",GetLastError()
end if

This routine works fine when calling it in a blank test program, which does nothing but call this routine.

However, as soon as I implement it to my larger program, it somehow refuses to work at all.
I tried adding a break point to the subroutine, but somehow the debugger just skips the whole routine, even when setting the break point to its call-statement and proceding with single steps.

The routine is called though, as test and error output is written just fine.

CreateProcess seems to fail too, as there is no process opened, even with other executables. The GetLastError error code is 0.

I am using Visual Studio 2010 with Intel(R) Visual Fortran Compiler XE [Intel(R) 64]. I have used the same compiler for both programs.
No external libraries are used, OpenMP is activated but the routine and its calls are outside of parallel regions. 

Thanks for your help!

4 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Some of the arguments (the derived type ones) passed to CreateProcess don't look like they are being initialised. The values of those arguments will be random garbage in a larger program which may cause odd things to happen (in a smaller program their values may be somewhat consistent, to the extent that things seem to work, but there be dragons near). Most of those arguments you don't need to supply anything other than a NULL pointer for, unless you are doing fancy stuff.

This snippet doesn't do what you want - it explicitly waits for the process to terminate - but should give you an idea of how to use CreateProcess. If you obliterate the wait_result, bool_result and success_flag assignment statements towards the end of the procedure (leave the cleanup bit!) then things should carry on asynchronously. It is an internal procedure in a main program - while I don't see any host associated stuff in this section of code there may be the odd declaration missing.



  !> Execute a child program, determine whether it succeeded or failed based

  !! on the process exit code.


  !! @param[in]     command_name      The name of the executable file for the

  !! program to execute.


  !! @param[in]     command_line      The command line to execute.  Typically

  !! the first argument would be the same as @a command_name.


  !! @param[out]    success_flag      Flag to indicate whether successful

  !! termination of the command was detected.  This is based on the process

  !! exit code being zero.
  SUBROUTINE check_return_code(command_name, command_line, success_flag)

    ! Arguments
    CHARACTER(*), INTENT(IN) :: command_name

    CHARACTER(*), INTENT(IN) :: command_line

    LOGICAL, INTENT(OUT) :: success_flag

    ! Local variables
    INTEGER(BOOL) :: bool_result

    TYPE(T_STARTUPINFO) :: startup_info


    INTEGER(DWORD) :: wait_result

    INTEGER(DWORD) :: process_code
    CALL ZeroMemory(LOC(startup_info), SIZEOF(startup_info))

    startup_info%cb = SIZEOF(startup_info)
    bool_result = CreateProcess(  &

        command_name // ACHAR(0),  &   ! LPCTSTR lpApplicationName

        command_line // ACHAR(0),  &   ! LPTSTR lpCommandLine

        0,  &                 ! LPSECURITY_ATTRIBUTES lpProcessAttributes

        0,  &                 ! LPSECURITY_ATTRIBUTES lpThreadAttributes

        1_BOOL,  &            ! BOOL bInheritHandles

        0_DWORD,  &           ! DWORD dwCreationFlags

        0,  &                 ! LPVOID lpEnvironment

        0,  &                 ! LPCTSTR lpCurrentDirectory

        startup_info,  &      ! LPSTARTUPINFO lpStartupInfo,

        proc_info )           ! LPPROCESS_INFORMATION lpProcessInformation
    wait_result = WaitForSingleObject(proc_info%hProcess, INFINITE)

    bool_result = GetExitCodeProcess(proc_info%hProcess, LOC(process_code))

    success_flag = process_code == 0
    ! Cleanup.

    bool_result = CloseHandle(proc_info%hProcess)

    bool_result = CloseHandle(proc_info%hThread)
  END SUBROUTINE check_return_code

Note that I don't go in for this 'string'C business - I prefer to stick with the standard fortran approach to creating a null terminated string.

(Edit to further note that I don't have some sort of bizarre vertical spacing convention - that's just the forum software botching things.)

Thanks, that was exactly the problem. :) Works just fine now.

Until Execute_Command_Line appears in ifort, could you use one of those ifort system functions to start a background process? The start command of cmd shell, or the usual background "&" of bash and similar shells, might perform your task.

Leave a Comment

Please sign in to add a comment. Not a member? Join today