adwiza
asked on
Named pipes between Delphi win32 and C#
I need to send large amount of data between a C# program and a Delphi program.
I think that named pipes is the way to go, but I can't get it to work, I tried both .Net 3.5 "named pipes" and some class that uses the winApi directly.
I can make it work between C# and C# both in .Net 3.5 and lower versions, and between Delphi and Delphi, but not between C# and Delphi.
http://www.switchonthecode.com/tutorials/interprocess-communication-using-named-pipes-in-csharp
http://home.roadrunner.com/~rllibby/source.html (pipes.zip)
Kind regards Martin
I think that named pipes is the way to go, but I can't get it to work, I tried both .Net 3.5 "named pipes" and some class that uses the winApi directly.
I can make it work between C# and C# both in .Net 3.5 and lower versions, and between Delphi and Delphi, but not between C# and Delphi.
http://www.switchonthecode.com/tutorials/interprocess-communication-using-named-pipes-in-csharp
http://home.roadrunner.com/~rllibby/source.html (pipes.zip)
Kind regards Martin
ASKER
Hi! I get an error from windows but the fatal one "xxx has stopped working...Check online, close, debug"
I can connect, and the server is invoked, but the length of the message is always zero.
When I press OK on the showDialog("com") the Delphi exe crashes out to windows.
The code is just copy an paste from the above to links, but here is what I test with.
Client.cs:
using System;
using System.Collections.Generic ;
using System.Text;
using System.Runtime.InteropServ ices;
using Microsoft.Win32.SafeHandle s;
using System.Threading;
using System.IO;
namespace PipeClient
{
class Client
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeFileHandle CreateFile(
String pipeName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplate);
public const uint GENERIC_READ = (0x80000000);
public const uint GENERIC_WRITE = (0x40000000);
public const uint OPEN_EXISTING = 3;
public const uint FILE_FLAG_OVERLAPPED = (0x40000000);
public delegate void MessageReceivedHandler(str ing message);
public event MessageReceivedHandler MessageReceived;
public const int BUFFER_SIZE = 4096;
string pipeName;
private FileStream stream;
private SafeFileHandle handle;
Thread readThread;
bool connected;
public bool Connected
{
get { return this.connected; }
}
public string PipeName
{
get { return this.pipeName; }
set { this.pipeName = value; }
}
/// <summary>
/// Connects to the server
/// </summary>
public void Connect()
{
this.handle =
CreateFile(
this.pipeName,
GENERIC_READ | GENERIC_WRITE,
0,
IntPtr.Zero,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
IntPtr.Zero);
//could not create handle - server probably not running
if (this.handle.IsInvalid)
return;
this.connected = true;
//start listening for messages
this.readThread = new Thread(new ThreadStart(Read));
this.readThread.Start();
}
/// <summary>
/// Reads data from the server
/// </summary>
public void Read()
{
this.stream = new FileStream(this.handle, FileAccess.ReadWrite, BUFFER_SIZE, true);
byte[] readBuffer = new byte[BUFFER_SIZE];
ASCIIEncoding encoder = new ASCIIEncoding();
while (true)
{
int bytesRead = 0;
try
{
bytesRead = this.stream.Read(readBuffe r, 0, BUFFER_SIZE);
}
catch
{
//read error occurred
break;
}
//server has disconnected
if (bytesRead == 0)
break;
//fire message received event
if (this.MessageReceived != null)
this.MessageReceived(encod er.GetStri ng(readBuf fer, 0, bytesRead));
}
//clean up resource
this.stream.Close();
this.handle.Close();
}
/// <summary>
/// Sends a message to the server
/// </summary>
/// <param name="message"></param>
public void SendMessage(string message)
{
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] messageBuffer = encoder.GetBytes(message);
this.stream.Write(messageB uffer, 0, messageBuffer.Length);
this.stream.Flush();
}
}
}
Form1.cs:
using System;
using System.Collections.Generic ;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace PipeClient
{
public partial class Form1 : Form
{
private Client pipeClient;
public Form1()
{
InitializeComponent();
this.pipeClient = new Client();
this.pipeClient.MessageRec eived +=
new Client.MessageReceivedHand ler(pipeCl ient_Messa geReceived );
}
void pipeClient_MessageReceived (string message)
{
this.Invoke(new Client.MessageReceivedHand ler(Displa yReceivedM essage),
new object[] { message });
}
void DisplayReceivedMessage(str ing message)
{
this.tbReceived.Text += message + "\r\n";
}
private void btnStart_Click(object sender, EventArgs e)
{
if (!this.pipeClient.Connecte d)
{
this.pipeClient.PipeName = this.tbPipeName.Text;
this.pipeClient.Connect();
this.btnStart.Enabled = false;
}
else
MessageBox.Show("Already connected.");
}
private void btnSend_Click(object sender, EventArgs e)
{
this.pipeClient.SendMessag e(this.tbS end.Text);
}
}
}
formNamedPipes_server
unit formNamedPipe_server;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
const
cShutDownMsg = 'shutdown pipe ';
cPipeFormat = '\\%s\pipe\%s';
type
RPIPEMessage = record
Size: DWORD;
Kind: Byte;
Count: DWORD;
Data: array[0..8095] of Char;
end;
TPipeServer = class(TThread)
private
FHandle: THandle;
FPipeName: String;
protected
public
constructor CreatePipeServer(aServer, aPipe: String; StartServer: Boolean);
destructor Destroy; override;
procedure StartUpServer;
procedure ShutDownServer;
procedure Execute; override;
end;
type
TfrmNamedPipe_server = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmNamedPipe_server: TfrmNamedPipe_server;
implementation
{$R *.dfm}
var
FServer: TPipeServer;
procedure CalcMsgSize(var Msg: RPIPEMessage);
begin
Msg.Size :=
SizeOf(Msg.Size) +
SizeOf(Msg.Kind) +
SizeOf(Msg.Count) +
Msg.Count +
3;
end;
{ TPipeServer }
constructor TPipeServer.CreatePipeServ er(
aServer, aPipe: String; StartServer: Boolean
);
begin
if aServer = '' then
FPipeName := Format(cPipeFormat, ['.', aPipe])
else
FPipeName := Format(cPipeFormat, [aServer, aPipe]);
// clear server handle
FHandle := INVALID_HANDLE_VALUE;
if StartServer then
StartUpServer;
// create the class
Create(not StartServer);
end;
destructor TPipeServer.Destroy;
begin
if FHandle <> INVALID_HANDLE_VALUE then
// must shut down the server first
ShutDownServer;
inherited Destroy;
end;
procedure TPipeServer.Execute;
var
I, Written: Cardinal;
InMsg, OutMsg: RPIPEMessage;
begin
while not Terminated do
begin
if FHandle = INVALID_HANDLE_VALUE then
begin
// suspend thread for 250 milliseconds and try again
Sleep(250);
end else begin
if ConnectNamedPipe(FHandle, nil) then
try
// read data from pipe
InMsg.Size := SizeOf(InMsg);
ReadFile(FHandle, InMsg, InMsg.Size, InMsg.Size, nil);
if
(InMsg.Kind = 0) and
(StrPas(InMsg.Data) = cShutDownMsg + FPipeName)
then
begin
// process shut down
OutMsg.Kind := 0;
OutMsg.Count := 3;
OutMsg.Data := 'OK'#0;
Terminate;
end else begin
// data send to pipe should be processed here
OutMsg := InMsg;
frmNamedPipe_server.Memo1. Lines.Add( 'Command: '+{StrPas(InMsg.Data)+}' length: '+intToStr(InMsg.Count));
// we'll just reverse the data sent, byte-by-byte
for I := 0 to Pred(InMsg.Count) do
OutMsg.Data[Pred(InMsg.Cou nt) - I] := InMsg.Data[I];
end;
showmessage('COM');
CalcMsgSize(OutMsg);
WriteFile(FHandle, OutMsg, OutMsg.Size, Written, nil);
frmNamedPipe_server.Memo1. Lines.Add( 'Result: '+StrPas(OutMsg.Data));
finally
DisconnectNamedPipe(FHandl e);
end;
end;
end;
end;
procedure TPipeServer.ShutDownServer ;
var
BytesRead: Cardinal;
OutMsg, InMsg: RPIPEMessage;
ShutDownMsg: String;
begin
if FHandle <> INVALID_HANDLE_VALUE then
begin
// server still has pipe opened
OutMsg.Size := SizeOf(OutMsg);
// prepare shut down message
with InMsg do
begin
Kind := 0;
ShutDownMsg := cShutDownMsg + FPipeName;
Count := Succ(Length(ShutDownMsg));
StrPCopy(Data, ShutDownMsg);
end;
CalcMsgSize(InMsg);
// send shut down message
CallNamedPipe(
PChar(FPipeName), @InMsg, InMsg.Size, @OutMsg, OutMsg.Size, BytesRead, 100
);
// close pipe on server
CloseHandle(FHandle);
// clear handle
FHandle := INVALID_HANDLE_VALUE;
end;
end;
procedure TPipeServer.StartUpServer;
begin
// check whether pipe does exist
if WaitNamedPipe(PChar(FPipeN ame), 100 {ms}) then
raise Exception.Create('Requeste d PIPE exists already.');
// create the pipe
FHandle := CreateNamedPipe(
PChar(FPipeName), PIPE_ACCESS_DUPLEX,
{PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT} 0,
PIPE_UNLIMITED_INSTANCES, SizeOf(RPIPEMessage), SizeOf(RPIPEMessage),
NMPWAIT_USE_DEFAULT_WAIT, nil
);
// check if pipe was created
if FHandle = INVALID_HANDLE_VALUE then
raise Exception.Create('Could not create PIPE.');
end;
procedure TfrmNamedPipe_server.FormC reate(Send er: TObject);
begin
try
FServer:=TPipeServer.Creat ePipeServe r('', 'testpipe', True);
except
on E: Exception do begin
ShowMessage(E.Message);
end;
end
end;
end.
I can connect, and the server is invoked, but the length of the message is always zero.
When I press OK on the showDialog("com") the Delphi exe crashes out to windows.
The code is just copy an paste from the above to links, but here is what I test with.
Client.cs:
using System;
using System.Collections.Generic
using System.Text;
using System.Runtime.InteropServ
using Microsoft.Win32.SafeHandle
using System.Threading;
using System.IO;
namespace PipeClient
{
class Client
{
[DllImport("kernel32.dll",
public static extern SafeFileHandle CreateFile(
String pipeName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplate);
public const uint GENERIC_READ = (0x80000000);
public const uint GENERIC_WRITE = (0x40000000);
public const uint OPEN_EXISTING = 3;
public const uint FILE_FLAG_OVERLAPPED = (0x40000000);
public delegate void MessageReceivedHandler(str
public event MessageReceivedHandler MessageReceived;
public const int BUFFER_SIZE = 4096;
string pipeName;
private FileStream stream;
private SafeFileHandle handle;
Thread readThread;
bool connected;
public bool Connected
{
get { return this.connected; }
}
public string PipeName
{
get { return this.pipeName; }
set { this.pipeName = value; }
}
/// <summary>
/// Connects to the server
/// </summary>
public void Connect()
{
this.handle =
CreateFile(
this.pipeName,
GENERIC_READ | GENERIC_WRITE,
0,
IntPtr.Zero,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
IntPtr.Zero);
//could not create handle - server probably not running
if (this.handle.IsInvalid)
return;
this.connected = true;
//start listening for messages
this.readThread = new Thread(new ThreadStart(Read));
this.readThread.Start();
}
/// <summary>
/// Reads data from the server
/// </summary>
public void Read()
{
this.stream = new FileStream(this.handle, FileAccess.ReadWrite, BUFFER_SIZE, true);
byte[] readBuffer = new byte[BUFFER_SIZE];
ASCIIEncoding encoder = new ASCIIEncoding();
while (true)
{
int bytesRead = 0;
try
{
bytesRead = this.stream.Read(readBuffe
}
catch
{
//read error occurred
break;
}
//server has disconnected
if (bytesRead == 0)
break;
//fire message received event
if (this.MessageReceived != null)
this.MessageReceived(encod
}
//clean up resource
this.stream.Close();
this.handle.Close();
}
/// <summary>
/// Sends a message to the server
/// </summary>
/// <param name="message"></param>
public void SendMessage(string message)
{
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] messageBuffer = encoder.GetBytes(message);
this.stream.Write(messageB
this.stream.Flush();
}
}
}
Form1.cs:
using System;
using System.Collections.Generic
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace PipeClient
{
public partial class Form1 : Form
{
private Client pipeClient;
public Form1()
{
InitializeComponent();
this.pipeClient = new Client();
this.pipeClient.MessageRec
new Client.MessageReceivedHand
}
void pipeClient_MessageReceived
{
this.Invoke(new Client.MessageReceivedHand
new object[] { message });
}
void DisplayReceivedMessage(str
{
this.tbReceived.Text += message + "\r\n";
}
private void btnStart_Click(object sender, EventArgs e)
{
if (!this.pipeClient.Connecte
{
this.pipeClient.PipeName = this.tbPipeName.Text;
this.pipeClient.Connect();
this.btnStart.Enabled = false;
}
else
MessageBox.Show("Already connected.");
}
private void btnSend_Click(object sender, EventArgs e)
{
this.pipeClient.SendMessag
}
}
}
formNamedPipes_server
unit formNamedPipe_server;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
const
cShutDownMsg = 'shutdown pipe ';
cPipeFormat = '\\%s\pipe\%s';
type
RPIPEMessage = record
Size: DWORD;
Kind: Byte;
Count: DWORD;
Data: array[0..8095] of Char;
end;
TPipeServer = class(TThread)
private
FHandle: THandle;
FPipeName: String;
protected
public
constructor CreatePipeServer(aServer, aPipe: String; StartServer: Boolean);
destructor Destroy; override;
procedure StartUpServer;
procedure ShutDownServer;
procedure Execute; override;
end;
type
TfrmNamedPipe_server = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmNamedPipe_server: TfrmNamedPipe_server;
implementation
{$R *.dfm}
var
FServer: TPipeServer;
procedure CalcMsgSize(var Msg: RPIPEMessage);
begin
Msg.Size :=
SizeOf(Msg.Size) +
SizeOf(Msg.Kind) +
SizeOf(Msg.Count) +
Msg.Count +
3;
end;
{ TPipeServer }
constructor TPipeServer.CreatePipeServ
aServer, aPipe: String; StartServer: Boolean
);
begin
if aServer = '' then
FPipeName := Format(cPipeFormat, ['.', aPipe])
else
FPipeName := Format(cPipeFormat, [aServer, aPipe]);
// clear server handle
FHandle := INVALID_HANDLE_VALUE;
if StartServer then
StartUpServer;
// create the class
Create(not StartServer);
end;
destructor TPipeServer.Destroy;
begin
if FHandle <> INVALID_HANDLE_VALUE then
// must shut down the server first
ShutDownServer;
inherited Destroy;
end;
procedure TPipeServer.Execute;
var
I, Written: Cardinal;
InMsg, OutMsg: RPIPEMessage;
begin
while not Terminated do
begin
if FHandle = INVALID_HANDLE_VALUE then
begin
// suspend thread for 250 milliseconds and try again
Sleep(250);
end else begin
if ConnectNamedPipe(FHandle, nil) then
try
// read data from pipe
InMsg.Size := SizeOf(InMsg);
ReadFile(FHandle, InMsg, InMsg.Size, InMsg.Size, nil);
if
(InMsg.Kind = 0) and
(StrPas(InMsg.Data) = cShutDownMsg + FPipeName)
then
begin
// process shut down
OutMsg.Kind := 0;
OutMsg.Count := 3;
OutMsg.Data := 'OK'#0;
Terminate;
end else begin
// data send to pipe should be processed here
OutMsg := InMsg;
frmNamedPipe_server.Memo1.
// we'll just reverse the data sent, byte-by-byte
for I := 0 to Pred(InMsg.Count) do
OutMsg.Data[Pred(InMsg.Cou
end;
showmessage('COM');
CalcMsgSize(OutMsg);
WriteFile(FHandle, OutMsg, OutMsg.Size, Written, nil);
frmNamedPipe_server.Memo1.
finally
DisconnectNamedPipe(FHandl
end;
end;
end;
end;
procedure TPipeServer.ShutDownServer
var
BytesRead: Cardinal;
OutMsg, InMsg: RPIPEMessage;
ShutDownMsg: String;
begin
if FHandle <> INVALID_HANDLE_VALUE then
begin
// server still has pipe opened
OutMsg.Size := SizeOf(OutMsg);
// prepare shut down message
with InMsg do
begin
Kind := 0;
ShutDownMsg := cShutDownMsg + FPipeName;
Count := Succ(Length(ShutDownMsg));
StrPCopy(Data, ShutDownMsg);
end;
CalcMsgSize(InMsg);
// send shut down message
CallNamedPipe(
PChar(FPipeName), @InMsg, InMsg.Size, @OutMsg, OutMsg.Size, BytesRead, 100
);
// close pipe on server
CloseHandle(FHandle);
// clear handle
FHandle := INVALID_HANDLE_VALUE;
end;
end;
procedure TPipeServer.StartUpServer;
begin
// check whether pipe does exist
if WaitNamedPipe(PChar(FPipeN
raise Exception.Create('Requeste
// create the pipe
FHandle := CreateNamedPipe(
PChar(FPipeName), PIPE_ACCESS_DUPLEX,
{PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT} 0,
PIPE_UNLIMITED_INSTANCES, SizeOf(RPIPEMessage), SizeOf(RPIPEMessage),
NMPWAIT_USE_DEFAULT_WAIT, nil
);
// check if pipe was created
if FHandle = INVALID_HANDLE_VALUE then
raise Exception.Create('Could not create PIPE.');
end;
procedure TfrmNamedPipe_server.FormC
begin
try
FServer:=TPipeServer.Creat
except
on E: Exception do begin
ShowMessage(E.Message);
end;
end
end;
end.
ASKER
I found a new example and I got it to work, I think that FILE_FLAG_OVERLAPPED mainly did the thrick.
Thx for showing interrest MvanderKooij.
BTW: I will (try to) use the right zone next time.
Thx for showing interrest MvanderKooij.
BTW: I will (try to) use the right zone next time.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Great that you got it working, can be real pain...
Please tell more about what works and doesn';t work. Can they 'open' each others pipes? Or is the actually sending of data a problem?