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

Redirecting input when using WinApi

Alexander_I_1
Beginner
376 Views

Hello,

this is somewhat of a continuation of an issue that I've had here, which was very helpfully solved.

now I've ran into an issue where I need to call an executable and redirect the input.

Previsouly it was done via:

CALL SYSTEM('C:\DATA\Executable.exe < Instruction.txt')

Calling an executable is not an issue, I've found some examples out there for CreateProcess winapi and it works fine. Below is a sample code that I've adopted.

  SUBROUTINE ExecuteProgram(CommanLine)

  USE KERNEL32

  IMPLICIT NONE

  CHARACTER(LEN=*), INTENT(IN)   :: CommanLine
  INTEGER                        :: rval

  TYPE(T_STARTUPINFO)            :: si
  TYPE(T_PROCESS_INFORMATION)    :: pi

  CALL ZeroMemory (LOC(pi), SIZEOF(pi))
  CALL ZeroMemory (LOC(si), SIZEOF(si))
  si%cb          = SIZEOF(si)
  si%dwFlags     = STARTF_USESHOWWINDOW
  si%wShowWindow = SW_SHOWNORMAL

  IF (CreateProcess (NULL,     &! process name
  CommanLine,                    & ! command line
  NULL_SECURITY_ATTRIBUTES,    & ! security attributes
  NULL_SECURITY_ATTRIBUTES,    & ! thread attributes
  FALSE,                       & ! handle inheritance
  0,                           & ! creation flags
  NULL,                        & ! environment block
  NULL,                        & ! initial working path
  si,                          & ! startup info
  pi) ) THEN                     ! process info
    rval = WaitForSingleObject (pi%hProcess, INFINITE)
    rval = CloseHandle (pi%hProcess)
    rval = CloseHandle (pi%hThread)
  END IF

  END SUBROUTINE ExecuteProgram

Now how on earth can I redirect input?

Thanks in advance!

0 Kudos
4 Replies
mecej4
Honored Contributor III
376 Views

What you are asking is really a Windows API question rather than a Fortran question; since the code that you showed is simply Fortran "glue" code for Windows Kernel32 calls. The natural answer is that you should locate a similar Windows API call for redirecting input, or call CMD.EXE and use its redirection facilities.

I do not do Windows programming, so I cannot give you a specific answer.

0 Kudos
Steve_Lionel
Honored Contributor III
376 Views

The STARTUPINFO structure has a component for a standard input handle. https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/ns-processthreadsapi-_startupinfoa

0 Kudos
Alexander_I_1
Beginner
376 Views

Thanks Steve for the pointers.

I've redirected the StdIn of the child process to a pipe and also have sent the intruction. I now have a different issues.

The child process essentially promts the user for the name of the input file and then for name of the output file.

Using Call System(Program.exe < Input.txt) this executes the program just fine.

The contents of the pipe are identical to the information in Input.txt, however the child process just sits there not advancing past the initial promt. What am I missing?

Below is my current code including a bit where I just check the read end of the pipe to see if the information arrives.

  SUBROUTINE ExecuteProgram(CommandLine)

  USE KERNEL32

  IMPLICIT NONE

  CHARACTER(LEN=*), INTENT(IN)   :: CommandLine
  INTEGER                        :: rval
  
  INTEGER(HANDLE)                :: hR = NULL, hW = NULL, HT
  LOGICAL(BOOL)                  :: WriteDone = .FALSE.
  INTEGER(DWORD)                 :: dwRead = 0, dwWritten = 0
  CHARACTER(LEN=20)               :: StrIn

  TYPE(T_STARTUPINFO)            :: si
  TYPE(T_PROCESS_INFORMATION)    :: pi
  TYPE(T_SECURITY_ATTRIBUTES)    :: sa
  
  sa%nLength = sizeof(sa)
  sa%lpSecurityDescriptor = NULL
  sa%bInheritHandle = TRUE
  
  IF(.NOT.CreatePipe(hR,hW,sa,0)) STOP
  IF(.NOT.SetHandleInformation(hW, HANDLE_FLAG_INHERIT, 0)) STOP

  CALL ZeroMemory (LOC(pi), SIZEOF(pi))
  CALL ZeroMemory (LOC(si), SIZEOF(si))
  si%cb          = SIZEOF(si)
  si%dwFlags     = STARTF_USESHOWWINDOW
  si%wShowWindow = SW_SHOWNORMAL
  si%hStdInput = hR
  si%hStdOutput = NULL
  si%hStdError = NULL
  !si%dwFlags = .NOT.STARTF_USESTDHANDLES

  IF (CreateProcess (NULL,     & ! process name
  CommandLine,                 & ! command line
  NULL,                        & ! security attributes
  NULL,                        & ! thread attributes
  TRUE,                        & ! handle inheritance
  0,                           & ! creation flags
  NULL,                        & ! environment block
  NULL,                        & ! initial working path
  si,                          & ! startup info
  pi) ) THEN                     ! process info  
    
    rval = CloseHandle (pi%hProcess)
    rval = CloseHandle (pi%hThread)
  ELSE
    Stop
  END IF

  StrIn = 'gas.inp'//CHAR(13)//CHAR(10)//'gas.out'//CHAR(13)//CHAR(10)
  WriteDone = WriteFile(hW, Loc(StrIn), SizeOf(StrIn), loc(dwWritten), NULL)
  IF(.NOT.CloseHandle(hW)) STOP  
  
  !hT = CreateFile('C:\test\INPUT.TXT',GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL)
  !WriteDone = ReadFile(hT, Loc(StrIn), SizeOf(StrIn), loc(dwRead), NULL)
  !WriteDone = WriteFile(hW, Loc(StrIn), dwRead, loc(dwWritten), NULL)
  !IF(.NOT.CloseHandle(hT)) STOP
  !IF(.NOT.CloseHandle(hW)) STOP
  
  !Quick and dirty, check the read end of the pipe
  hW = CreateFile('C:\test\OUTPUT.TXT',GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL)
  WriteDone = ReadFile(hR, Loc(StrIn), SizeOf(StrIn), loc(dwRead), NULL)
  WriteDone = WriteFile(hW, Loc(StrIn), dwRead, loc(dwWritten), NULL)
  IF(.NOT.CloseHandle(hW)) STOP
  
  END SUBROUTINE ExecuteProgram

 

0 Kudos
Steve_Lionel
Honored Contributor III
376 Views

I've never worked with pipes, but if I were facing this situation I would first create a test case using separate programs and run them each in the debugger to make sure the pipe I/O was working properly. Note that you can also attach the debugger to different processes.

0 Kudos
Reply