08. januar 2010 - 10:23
#2
Hej! Jeg får ingen fejl, min server crasher og windows giver dialogne "xxx has stopped working...Check online, close, debug"
Jeg kan connecte, og serveren reagerer, men længden på message er altid zero, og når jeg trykker OK i showmessage('com') så crasher Delphi exe'en.
Client.cs:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
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(string 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(readBuffer, 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(encoder.GetString(readBuffer, 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(messageBuffer, 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.MessageReceived +=
new Client.MessageReceivedHandler(pipeClient_MessageReceived);
}
void pipeClient_MessageReceived(string message)
{
this.Invoke(new Client.MessageReceivedHandler(DisplayReceivedMessage),
new object[] { message });
}
void DisplayReceivedMessage(string message)
{
this.tbReceived.Text += message + "\r\n";
}
private void btnStart_Click(object sender, EventArgs e)
{
if (!this.pipeClient.Connected)
{
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.SendMessage(this.tbSend.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.CreatePipeServer(
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.Count) - 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(FHandle);
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(FPipeName), 100 {ms}) then
raise Exception.Create('Requested 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.FormCreate(Sender: TObject);
begin
try
FServer:=TPipeServer.CreatePipeServer('', 'testpipe', True);
except
on E: Exception do begin
ShowMessage(E.Message);
end;
end
end;
end.