3

I'm wanting to set up a pipe between a Fortran program and a C# program. The Fortran program will be doing the heavy lifting and the C# program will provide a 'view' onto what is being done (using the data sent to it from the Fortran program). To check that this will work, I've written two small programs. (The Fortran code was poached from examples of piping data between C++ and C#.)

Fortran code:

  PROGRAM Test

  USE kernel32
  IMPLICIT NONE

  ! Data pipe
  INTEGER*4 EqnData /100/
  ! Paths for pipes
  CHARACTER*128 dataname /'\\.\pipe\EqnData'/

  INTEGER(HANDLE) :: pipe1

  pipe1 = CreateNamedPipe('\\\\.\\pipe\\EqnData'C, PIPE_ACCESS_DUPLEX, &
    PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, &
    1024, 1024, 120 * 1000, NULL)

  PRINT*, pipe1

  ! Open data pipe
  OPEN(UNIT=EqnData, FILE=dataname, ACCESS='STREAM', STATUS='OLD')

  READ* 

  ! Close pipe
  CLOSE(EqnData)

  WRITE (*,*) 'end'

  END

C# code:

using System;
using System.IO;
using System.IO.Pipes;

class PipeClient
{
  static void Main(string[] args)
  {

    using (NamedPipeClientStream pipeClient
      = new NamedPipeClientStream(".",
                             "EqnData",
                             PipeDirection.InOut))
    {
      // Connect to the pipe or wait until the pipe is available.
      Console.Write("Attempting to connect to pipe...");
      pipeClient.Connect();

      Console.WriteLine("Connected to pipe.");
      Console.WriteLine("There are currently {0} pipe server instances open.",
         pipeClient.NumberOfServerInstances);
      using (StreamReader sr = new StreamReader(pipeClient))
      {
        // Display the read text to the console
        string temp;
        while ((temp = sr.ReadLine()) != null)
        {
          Console.WriteLine("Received from server: {0}", temp);
        }
      }
    }
    Console.Write("Press Enter to continue...");
    Console.ReadLine();
  }
}

If I start up both programs, I see that a pipe is created by the Fortran code and it gets down to the READ statement but the C# code only gets to pipeClient.Connect() and then just sits there.

Have I set up the C# side of things correctly? Or maybe I don't have things quite right on the Fortran side of things so that the C# client 'sees' the pipe?

Alex M
  • 101
  • 7
  • Not sure about the string ending with C. That doesn't work on the Fortran compiler I am using. Try halving the number of backslashes in the CreateNamedPipe. You will also need a ConnectNamedPipe before reading/writing. At least one of the programs should be writing. If both are reading, and nobody is writing then nothing will happen. – cup Jul 29 '21 at 13:17
  • The following may be helpful: https://www.csharpcodi.com/vs2/241/AutoHotkey.Interop/src/AutoHotkey.Interop/Pipes/NamedPipeServer.cs/ – Tu deschizi eu inchid Jul 29 '21 at 14:19
  • Which Fortran compiler are you using? I can't get pipes to work with open/read/write. They do work with WriteFile and ReadFile, which are native windows calls. – cup Jul 29 '21 at 16:32
  • I'm using Intel Fortran. @cup's suggestions did the trick. Posted the working code below for others. – Alex M Aug 01 '21 at 02:45

1 Answers1

0

@Cup was right. Adding a connection to the pipe and switching to WriteFile worked. Fortran code:

  program Test

  use kernel32
  implicit none

  ! Pipes
  integer(HANDLE) :: pipe
  character*128 dataname /'\\.\pipe\EqnData'/
  ! Flags
  integer :: connectNamedPipeErrFlag, errorCode, writeFileErrFlag
  ! Data to send
  character*128 :: message

  pipe = CreateNamedPipe( &                       ! CreateNamesPipe: Creates an instance of a named pipe and returns a handle for subsequent pipe operations
    !                                               https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
    '\\.\pipe\EqnData', &                         ! lpName: Unique pipe nam in th following form: \\.\pipe\pipename
    PIPE_ACCESS_DUPLEX, &                         ! dwOpenMode: Open mode (eg. PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, PIPE_ACCESS_OUTBOUND, etc.)
    PIPE_WAIT, &                                  ! dwPipeMode: Pipe mode (eg. PIPE_WAIT, etc.)
    PIPE_UNLIMITED_INSTANCES, &                   ! nMaxInstances: Maximum number of instances that can be created for this pipe
    1024, &                                       ! nOutBufferSize: Number of bytes to reserve for the output buffer
    1024, &                                       ! nInBufferSize: Number of bytes to reserve for the input buffer
    120 * 1000, &                                 ! nDefaultTimeOut: Default time-out value, in milliseconds
    NULL)                                         ! lpSecurityAttributes: A pointer to a SECURITY_ATTRIBUTES structure
  if(pipe .NE. INVALID_HANDLE_VALUE) then
    write (*,*) 'Pipe created'
  else
    ! TODO:
  endif

  connectNamedPipeErrFlag = ConnectNamedPipe( &   ! ConnectNamedPipe: Returned nonzero value if the connection is successful
    !                                               https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe
    pipe, &                                       ! hNamedPipe: Handle to the server end of a named pipe instance (returned by the CreateNamedPipe function)
    NULL)                                         ! lpOverlapped: A pointer to an OVERLAPPED structure
  if(connectNamedPipeErrFlag .NE. 0) then
    write(*,*) 'Pipe connected'
  else
    ! TODO:
  endif

  message = 'Some data'

  writeFileErrFlag = WriteFile( &                 ! Writes data to the specified file or IO device
    !                                               https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
    pipe, &                                       ! hfile: A handle to the file or IO device
    loc(message), &                               ! lpBuffer: A pointer to the buffer containing the data to be written to the file or device.
    len(trim(message)), &                         ! nNumberOfBytesToWrite: Number of bytes to write
    0, &                                          ! lpNumberOfBytesWritten: number of bytes that were written
    NULL)                                         ! lpOverlapped:
  if (writeFileErrFlag .NE. 0) then
    write (*,*) 'Write successful'
  else
    errorCode = GetLastError()
    if(errorCode .EQ. ERROR_IO_PENDING) then
      ! TODO:
    elseif (errorCode .EQ. ERROR_INVALID_USER_BUFFER ) then
      ! TODO:
    elseif(errorCode .EQ. ERROR_NOT_ENOUGH_MEMORY ) then
      ! TODO:
    elseif(errorCode .EQ. ERROR_OPERATION_ABORTED) then
      ! TODO:
    elseif(errorCode .EQ. ERROR_NOT_ENOUGH_QUOTA) then
      ! TODO:
    else
      ! TODO:
    endif
  endif

  close(pipe)

  write(*,*) 'Pipe closed'

  end program
Alex M
  • 101
  • 7
  • An alternative is https://stackoverflow.com/questions/16619888/use-pipe-between-fortran-and-c – cup Aug 01 '21 at 17:56