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