Search code examples
c#.netfortranpipeipc

Pipe between Fortran and C#


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?


Solution

  • @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