Lazarus
Programming => Operating Systems => Windows => Topic started by: Aidex on February 12, 2019, 08:08:12 am
-
Hello!
I want to use Named Pipes to communicate between two programs.
With the Windows function I manage to create a named pipe, but unfortunately I fail to write into the pipe.
What am I doing wrong?
uses Windows;
function Server_createPipe(): THandle;
var
SD: SECURITY_DESCRIPTOR;
SA: SECURITY_ATTRIBUTES;
const
PIPE_UNLIMITED_INSTANCES = 255;
begin
InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@SD, true, nil{ACL}, false);
SA.lpSecurityDescriptor := @SD;
SA.nLength := SizeOf(SA);
SA.bInheritHandle := true;
Result := CreateNamedPipe(PChar('\\.\pipe\Test'),
PIPE_ACCESS_DUPLEX or FILE_FLAG_WRITE_THROUGH,
PIPE_TYPE_BYTE or PIPE_NOWAIT, // others: PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE
PIPE_UNLIMITED_INSTANCES,
1024,
1024,
50,
@SA);
end;
function writeToPipe(Pipe: THandle; const s: String): Boolean;
var written: DWord;
begin
if s='' then Exit(true);
Result := WriteFile(Pipe, s[1], Length(s), {out}written, nil);
end;
The handle seems to be correct (value 252), but WriteFile always returns false.
-
By the way, is there a cross-operating system solution for Named Pipes?
Unfortunately, the Lazarus unit Pipes only provides functions for Anonymous Pipes.
-
SA.bInheritHandle := true;
Why are you creating the server pipe as inheritable?
Result := CreateNamedPipe(PChar('\\.\pipe\Test'),
PIPE_ACCESS_DUPLEX or FILE_FLAG_WRITE_THROUGH,
PIPE_TYPE_BYTE or PIPE_NOWAIT, // others: PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE
PIPE_UNLIMITED_INSTANCES,
1024,
1024,
50,
@SA);
Did you check to make sure that CreateNamedPipe() is not returning INVALID_HANDLE_VALUE on failure?
Result := WriteFile(Pipe, s[1], Length(s), {out}written, nil);
Note that this code assumes that String is AnsiString. I would suggest encoding the String to a byte array first, using whatever charset you want to use (like UTF-8) and then send the bytes.
The handle seems to be correct (value 252), but WriteFile always returns false.
Did you check with GetLastError() to find out WHY WriteFile() is returning false?
Note that you are creating the pipe with the PIPE_NOWAIT flag:
PIPE_NOWAIT
Nonblocking mode is enabled. In this mode, ReadFile, WriteFile, and ConnectNamedPipe always return immediately.
When WriteFile() is used asynchronously, it always returns false, so you MUST check with GetLastError() afterwards to see if it returns ERROR_IO_PENDING indicating the write operation has actually been started in the background and did not really fail.
Note, however:
Note that nonblocking mode is supported for compatibility with Microsoft LAN Manager version 2.0 and should not be used to achieve asynchronous I/O with named pipes. if you want asynchronous I/O - use FILE_FLAG_OVERLAPPED
You should replace the PIPE_NOWAIT flag with FILE_FLAG_OVERLAPPED when calling CreateNamedPipe(), and then pass OVERLAPPED structs to ConnectNamedPipe(), ReadFile(), and WriteFile() so you can track the status of I/O operations that are running in the background.
See the following articles on MSDN:
- Named Pipe Type, Read, and Wait Modes (https://docs.microsoft.com/en-us/windows/desktop/ipc/named-pipe-type-read-and-wait-modes)
- Synchronous and Overlapped Pipe I/O (https://docs.microsoft.com/en-us/windows/desktop/ipc/synchronous-and-overlapped-input-and-output)
- Named Pipe Server Using Overlapped I/O (https://docs.microsoft.com/en-us/windows/desktop/ipc/named-pipe-server-using-overlapped-i-o)
-
Thank you very much for your detailed answer.
The idea with GetLastError() really could have come to me.
After WriteFile() GetLastError() returns the value 536 (ERROR_PIPE_LISTENING) = "Waiting for a process to open the other end of the pipe."
After the other process has also opened the pipe, WriteFile() works now! :)
Thank you very much!
-
The idea with GetLastError() really could have come to me.
Next time, please read the documentation more carefully.
After WriteFile() GetLastError() returns the value 536 (ERROR_PIPE_LISTENING) = "Waiting for a process to open the other end of the pipe."
That means you are calling WriteFile() too soon, you are not waiting for a client to connect to the pipe first. Call ConnectNamedPipe() and wait for it to report a successfully connected client before trying to read/write to it:
Enables a named pipe server process to wait for a client process to connect to an instance of a named pipe. A client process connects by calling either the CreateFile or CallNamedPipe function.
And don't forget to disconnect the pipe when done.