I have just started working with Threads use the onexecute event with Delphi 2009, indy IdTCPServer1. I wrote a very basic application for testing and am getting an access violation on exit. The application runs fine and does everything I want it to, but I think that I am leaving "Threads running" on exit. I have no experience with threads so any help would be appreciated.
Heres my code
unit FT_Communicator_pas;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp, IdContext, IdTCPServer,
INIFiles, ExtCtrls, ComCtrls, adscnnct,
DB, adsdata, adsfunc, adstable, Wwdatsrc, Grids, Wwdbigrd, Wwdbgrid,
IdBaseComponent, IdComponent, IdCustomTCPServer;
type
TfrmMain = class(TForm)
IdTCPServer1: TIdTCPServer;
PgMain: TPageControl;
TsMain: TTabSheet;
tsConfig: TTabSheet;
Label1: TLabel;
Label2: TLabel;
txtServer: TEdit;
txtPort: TEdit;
Panel1: TPanel;
Panel3: TPanel;
tsLog: TTabSheet;
mnolog: TMemo;
Button1: TButton;
Button3: TButton;
procedure IdTCPServer1Connect(AContext: TIdContext);
procedure IdTCPServer1Execute(AContext: TIdContext);
procedure Button3Click(Sender: TObject);
procedure IdTCPServer1Disconnect(AContext: TIdContext);
procedure Logit(const Logstr: String);
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.Button1Click(Sender: TObject);
begin
IdTCPServer1.Active:=FALSE;
application.Terminate;
end;
procedure TfrmMain.Button3Click(Sender: TObject);
begin
IdTCPServer1.Active:=true;
end;
procedure TfrmMain.FormShow(Sender: TObject);
begin
PgMain.ActivePage:=tsMain;
EnableMenuItem( GetSystemMenu( handle, False ),SC_CLOSE, MF_BYCOMMAND or MF_GRAYED );
end;
procedure TfrmMain.IdTCPServer1Connect(AContext: TIdContext);
begin
mnoLog.lines.Add ('Connected from: ' + AContext.Connection.Socket.Binding.PeerIP);
end;
procedure TfrmMain.IdTCPServer1Disconnect(AContext: TIdContext);
begin
mnoLog.lines.Add ('Disconnected from: ' + AContext.Connection.Socket.Binding.PeerIP);
end;
procedure TfrmMain.IdTCPServer1Execute(AContext: TIdContext);
var
myReadln,mySendln,sqlqry:string;
begin
sleep(10);
myReadln:=AContext.Connection.IOHandler.ReadLn();
mnolog.Lines.Add(AContext.Connection.Socket.Binding.PeerIP + '>' + myReadln );
mySendln:= AContext.Connection.Socket.Binding.PeerIP + ' Sent me ' + myReadln;
AContext.Connection.IOHandler.WriteLn(mySendln);
try
except
on E:Exception do
begin
logit('Error occured During execute function ' + #13#10 + e.message);
end;
end;
end;
procedure TfrmMain.logit(const logstr:String);
var
curdate,Curtime:string;
StrGUID:string;
begin
StrGUID:=FormatDateTime('YYYYMMDDHHnnsszzz', Now())+'_ ';
mnolog.lines.add(StrGUID +logstr );
end;
end.
Your TIdTCPServer event handlers contain unsafe code in them.
TIdTCPServer is a multithreaded component, its events are triggered in the context of worker threads. But you are directly accessing a VCL UI control (mnoLog) without synchronizing with the main UI thread. Bad things happen when you do not synchronize, as the VCL is not thread-safe. You must synchronize properly when accessing the UI from a worker thread.
It is also important to avoid performing a synchronous synchronization when deactivating TIdTCPServer from the main UI thread, as that will cause a deadlock. Use an asynchronous synchronization instead.
Try something more like the following:
procedure TfrmMain.IdTCPServer1Connect(AContext: TIdContext);
begin
Logit('Connected from: ' + AContext.Connection.Socket.Binding.PeerIP);
end;
procedure TfrmMain.IdTCPServer1Disconnect(AContext: TIdContext);
begin
Logit('Disconnected from: ' + AContext.Connection.Socket.Binding.PeerIP);
end;
procedure TfrmMain.IdTCPServer1Execute(AContext: TIdContext);
var
myReadln, mySendln, peerIP: string;
begin
myReadln := AContext.Connection.IOHandler.ReadLn();
peerIP := AContext.Connection.Socket.Binding.PeerIP;
Logit(peerIP + '>' + myReadln);
mySendln := peerIP + ' Sent me ' + myReadln;
AContext.Connection.IOHandler.WriteLn(mySendln);
end;
procedure TfrmMain.IdTCPServer1Exception(AContext: TIdContext; AException: Exception);
begin
if not (AException is EIdConnClosedGracefully) then
Logit('Error occured. ' + AException.Message);
end;
procedure TfrmMain.Logit(const Logstr: String);
var
Str: string;
begin
Str := Trim(Logstr);
TThread.Queue(nil,
procedure
begin
mnolog.Lines.Add(FormatDateTime('YYYYMMDDHHnnsszzz', Now()) + ': ' + Str);
end
);
end;
Related
I have a problem with Delphi 6 and Indy's TIdIcmpClient component.
I get this message when compiling the following code, in the marked line (51):
FPing.OnReply := OnPingReply;
[Error] fire.pas(51): Incompatible types: 'TComponent' and 'TIdIcmpClient'
How should I fix it?
unit fire;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
IdIcmpClient;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMyThread = class(TThread)
private
FIndex: Integer;
FPing: TIdIcmpClient;
FIP: string;
protected
procedure Execute; override;
procedure OnPingReply(ASender: TIdIcmpClient; AReplyStatus: TReplyStatus);
public
constructor Create(AIndex: Integer);
destructor Destroy; override;
end;
constructor TMyThread.Create(AIndex: Integer);
begin
inherited Create(False);
FIndex := AIndex;
FIP := '192.168.1.' + IntToStr(FIndex + 1);
FPing := TIdIcmpClient.Create(nil);
FPing.Host:=FIP;
FPing.ReceiveTimeout:=1500;
FPing.OnReply := OnPingReply;
end;
destructor TMyThread.Destroy;
begin
FPing.Free;
inherited;
end;
//var// icmp:array[0..10] of TIdIcmpClient;
// ip:string;
procedure TMyThread.Execute; // aici e ce face thread-ul
var
i: Integer;
begin
FPing.Ping;
// ICMP.Ping('a',1000);
// Sleep(1300);
// form1.memo1.lines.add(IntToStr(findex)+' '+ICMP.ReplyStatus.fromipaddress);
for i := 1 to 1 do
begin
// 'findex' este indexul thread-ului din matrice
form1.memo1.lines.add(inttostr(findex)+' Thread running...');
application.ProcessMessages;
Sleep(1000);
end;
end;
procedure TMyThread.OnPingReply(ASender: TIdIcmpClient; AReplyStatus: TReplyStatus);
begin
if AReplyStatus.BytesReceived > 0 then
form1.memo1.Lines.add(FIP+ ' is reachable')
else
form1.memo1.Lines.add(FIP+ ' is not reachable: ');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyThreads: array[0..10] of TMyThread;
// icmp:array[0..10] of TIdIcmpClient;
i: Integer;
begin
{ for i := 0 to 10 do //10 fire
begin
icmp[i]:=tidicmpclient.create(nil);
icmp[i].ReceiveTimeout:=1200;
ip:=Format('%s.%d', ['192.168.1', i]);
ICMP[i].Host :=ip;
end; }
for i := 0 to 10 do //10 fire
begin
MyThreads[i] := TMyThread.Create(i);
MyThreads[i].Resume;
application.ProcessMessages;
end;
// Readln;
for i := 0 to 10 do
begin
MyThreads[i].Free;
// icmp[i].Free;
end;
end;
end.
I expected it to be compilable, but I don't see the reason why it is not.
Your event handler is declared wrong. The ASender parameter needs to be TComponent rather than TIdIcmpClient, and the AReplyStatus parameter needs to be const:
procedure OnPingReply(ASender: TComponent; const AReplyStatus: TReplyStatus);
That being said, you don't need to use the OnReply event at all in this situation. TIdIcmpClient operates synchronously, so you can simply use the TIdIcmpClient.ReplyStatus property after the TIdIcmpClient.Ping() method exits:
procedure TMyThread.Execute; // aici e ce face thread-ul
var
...
begin
FPing.Ping;
if FPing.ReplyStatus.BytesReceived > 0 then
...
else
...
...
end;
Also, you must synchronize with the main UI thread when accessing UI controls in a worker thread. You can use TThread.Synchronize() method for that.
And, you do not need to call Application.ProcessMessages() in a worker thread. Doing so will have no effect on the main UI thread.
With all of that said, try something more like this:
unit fire;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
IdIcmpClient;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure AddText(const AText: String);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMyThread = class(TThread)
private
FIndex: Integer;
FPing: TIdIcmpClient;
FIP: string;
FText: String;
procedure AddTextToUI(const AText: String);
procedure DoSyncText;
protected
procedure Execute; override;
public
constructor Create(AIndex: Integer);
destructor Destroy; override;
end;
constructor TMyThread.Create(AIndex: Integer);
begin
inherited Create(False);
FIndex := AIndex;
FIP := '192.168.1.' + IntToStr(FIndex + 1);
FPing := TIdIcmpClient.Create(nil);
FPing.Host := FIP;
FPing.ReceiveTimeout := 1500;
end;
destructor TMyThread.Destroy;
begin
FPing.Free;
inherited;
end;
procedure TMyThread.AddTextToUI(const AText: String);
begin
FText := AText;
Synchronize(DoSyncText);
end;
procedure TMyThread.DoSyncText;
begin
Form1.AddText(FText);
end;
procedure TMyThread.Execute; // aici e ce face thread-ul
begin
AddTextToUI(IntToStr(FIndex) + ' Thread running...');
try
FPing.Ping;
except
AddTextToUI('Error pinging ' + FIP);
Exit;
end;
if FPing.ReplyStatus.BytesReceived > 0 then
AddTextToUI(FIP + ' is reachable')
else
AddTextToUI(FIP + ' is not reachable');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyThreads: array[0..10] of TMyThread;
I: Integer;
begin
for I := Low(MyThreads) to High(MyThreads) do //10 fire
begin
MyThreads[I] := TMyThread.Create(I);
end;
for I := Low(MyThreads) to High(MyThreads) do
begin
MyThreads[i].WaitFor;
MyThreads[i].Free;
end;
end;
procedure TForm1.AddText(const AText: String);
begin
Memo1.Lines.Add(AText);
end;
end.
I want to sync Indy's TIdTCPServer's OnExecute, according to this question's example, but I don't receive the strings. Before I sent the strings directly from the server's execute, the client did receive them, so I'm fairly sure there's not a problem on that side.
Because I need a context to write lines to the buffer, the ServerSync contains an attribute that is to which the context of the execute procedure is assigned.
Server form:
unit ServerForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdCustomTCPServer,
IdTCPServer, IdContext;
type
TForm1 = class(TForm)
Button1: TButton;
Server: TIdTCPServer;
memMessages: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Execute(AContext: TIdContext);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses ServerSync;
{$R *.dfm}
procedure TForm1.Execute(AContext: TIdContext);
var
Sync : TServerSync;
begin
Sync := TServerSync.Create(AContext);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Server := TIdTCPServer.Create;
Server.Bindings.Add.IP:= '0.0.0.0';
Server.Bindings.Add.Port:= 1990;
Server.OnExecute := Execute;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
try
memMessages.Lines.Add('Activated Server.');
Server.Active := True;
except
on E : Exception do
ShowMessage( E.ClassName + ' error raised, with message: ' + E.Message );
end;
end;
end.
Server Sync:
unit ServerSync;
interface
uses
IdContext, IdSync;
type
TServerSync = class(TIdSync)
constructor Create( AContext : TIdContext ); overload;
private
FContext : TIdContext;
protected
procedure DoSynchronize; override;
end;
implementation
constructor TServerSync.Create(AContext: TIdContext);
begin
inherited;
FContext := AContext;
end;
procedure TServerSync.DoSynchronize;
begin
FContext.Connection.IOHandler.WriteLn('Synced Hello World');
end;
end.
Client:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, FMX.Layouts,
FMX.Memo, FMX.StdCtrls, IdGlobal, IdIntercept;
type
TpocForm1 = class(TForm)
ButtonConnect: TButton;
ButtonDisconnect: TButton;
Memo1: TMemo;
procedure ButtonConnectClick(Sender: TObject);
procedure ButtonDisconnectClick(Sender: TObject);
procedure AddLine(text : String);
private
public
{ Public declarations }
end;
TpocTCPClientThread = class(TThread)
TCPClient: TIdTCPClient;
protected
procedure Execute; override;
procedure AddLineToMemo;
procedure Connect;
procedure Disconnect;
end;
var
pocForm1: TpocForm1;
implementation
{$R *.fmx}
Const
PC_IP = '192.168.32.85';
PORT = 1990;
var
thread: TpocTCPClientThread;
procedure TpocForm1.ButtonConnectClick(Sender: TObject);
begin
Memo1.Lines.Add('Client connected with server');
thread:= TpocTCPClientThread.Create(False);
end;
procedure TpocForm1.ButtonDisconnectClick(Sender: TObject);
begin
thread.Terminate;
thread.WaitFor;
FreeAndNil(thread);
Memo1.Lines.Add('Client disconnected from server');
end;
procedure TpocForm1.AddLine(text : String);
begin
Memo1.Lines.Add(text);
end;
procedure TpocTCPClientThread.Execute();
begin
Connect;
while not Terminated do
begin
Synchronize(AddLineToMemo);
end;
Disconnect;
end;
procedure TpocTCPClientThread.AddLineToMemo;
begin
pocForm1.AddLine(TCPClient.IOHandler.ReadLn(IndyTextEncoding_OSDefault()));
end;
procedure TpocTCPClientThread.Connect;
begin
TCPClient := TIdTCPClient.Create;
TCPClient.Host := PC_IP;
TCPClient.Port := PORT;
TCPClient.Connect;
end;
procedure TpocTCPClientThread.Disconnect;
begin
TCPClient.Disconnect;
TCPClient.Free;
end;
end.
You are making MANY mistakes in this code.
The server code is creating 2 Bindings entries when it should only be creating 1 entry.
The server code is never calling TIdSync.Synchronize(), which is what queues your overridden DoSynchronize() method to be called by the main thread.
The server code is leaking many TServerSync objects. OnExecute is a looped event, it is called in a continuous loop for the lifetime of the connection. You are never calling Free() on the TServerSync objects that you create on each loop iteration.
The server code is calling IOHandler.WriteLn() inside your synchronized DoSynchronize() code, and your client code is calling IOHandler.ReadLn() inside your synchronized AddLineToMemo() code. They do not belong there! Socket I/O belongs in your OnExecute handlers, not synchronized. Use synchronizaton to access shared data, update UIs, etc, not to perform socket I/O.
In short, all this code needs to be re-written. Try something more like this instead:
Server:
unit ServerForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdCustomTCPServer,
IdTCPServer, IdContext;
type
TForm1 = class(TForm)
Button1: TButton;
Server: TIdTCPServer;
memMessages: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Execute(AContext: TIdContext);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
ServerSync;
{$R *.dfm}
procedure TForm1.Execute(AContext: TIdContext);
var
Sync : TServerSync;
begin
Sync := TServerSync.Create(AContext);
try
Sync.Synchronize;
AContext.Connection.IOHandler.WriteLn(Sync.Value);
finally
Sync.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Server := TIdTCPServer.Create(Self);
with Server.Bindings.Add do begin
IP := '0.0.0.0';
Port:= 1990;
end;
Server.OnExecute := Execute;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Server.Active then Exit;
try
Server.Active := True;
except
on E : Exception do
begin
ShowMessage( E.ClassName + ' error raised, with message: ' + E.Message );
Exit;
end;
end;
memMessages.Lines.Add('Activated Server.');
end;
end.
unit ServerSync;
interface
uses
IdSync;
type
TServerSync = class(TIdSync)
protected
procedure DoSynchronize; override;
end;
implementation
procedure TServerSync.DoSynchronize;
begin
// this is called in the context of the main UI thread, do something ...
Value := 'Synced Hello World';
end;
end.
Client:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, FMX.Layouts,
FMX.Memo, FMX.StdCtrls, IdGlobal, IdIntercept;
type
TpocForm1 = class(TForm)
ButtonConnect: TButton;
ButtonDisconnect: TButton;
Memo1: TMemo;
procedure ButtonConnectClick(Sender: TObject);
procedure ButtonDisconnectClick(Sender: TObject);
procedure AddLine(text : String);
private
public
{ Public declarations }
end;
var
pocForm1: TpocForm1;
implementation
{$R *.fmx}
const
PC_IP = '192.168.32.85';
PORT = 1990;
type
TpocTCPClientThread = class(TThread)
private
TCPClient: TIdTCPClient;
FLine: string;
procedure AddLineToMemo(text: string);
procedure DoAddLineToMemo;
protected
procedure Execute; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
end;
var
thread: TpocTCPClientThread = nil;
procedure TpocForm1.ButtonConnectClick(Sender: TObject);
begin
if thread = nil then
thread := TpocTCPClientThread.Create(False);
end;
procedure TpocForm1.ButtonDisconnectClick(Sender: TObject);
begin
if thread = nil then Exit;
thread.Terminate;
thread.WaitFor;
FreeAndNil(thread);
end;
procedure TpocForm1.AddLine(text : String);
begin
Memo1.Lines.Add(text);
end;
constructor TpocTCPClientThread.Create;
begin
inherited Create(False);
TCPClient := TIdTCPClient.Create;
TCPClient.Host := PC_IP;
TCPClient.Port := PORT;
end;
destructor TpocTCPClientThread.Destroy;
begin
TCPClient.Free;
inherited;
end;
procedure TpocTCPClientThread.Execute;
begin
try
TCPClient.Connect;
except
on E: Exception do
AddLineToMemo('Unable to connect to server. ' + E.ClassName + ' error raised, with message: ' + E.Message );
Exit;
end;
try
try
AddLineToMemo('Client connected to server');
TCPClient.IOHandler.DefStringEncoding := IndyTextEncoding_OSDefault;
while not Terminated do
begin
AddLineToMemo(TCPClient.IOHandler.ReadLn);
end;
except
on E: Exception do
AddLineToMemo( E.ClassName + ' error raised, with message: ' + E.Message );
end;
finally
TCPClient.Disconnect;
AddLineToMemo('Client disconnected from server');
end;
end;
procedure TpocTCPClientThread.AddLineToMemo(text: string);
begin
FLine := text;
Synchronize(DoAddLineToMemo);
end;
procedure TpocTCPClientThread.DoAddLineToMemo;
begin
pocForm1.AddLine(FLine);
end;
end.
i am very new to delphi and i have two projects written in delphi which must communicate - a client and a server. i have managed to get comms going for the connection and disconnection, but i can't get the client to send messages to the server at the click of a button - it appears that the messages do not make it to the server.
here is the client code:
unit my_client;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdAntiFreezeBase, IdAntiFreeze, IdTCPConnection,
IdTCPClient, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack,
IdBaseComponent, IdComponent;
type
TForm1 = class(TForm)
lstLog: TListBox;
Label1: TLabel;
txtData: TEdit;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
txtServer: TEdit;
txtPort: TEdit;
btnConnect: TButton;
btnSend: TButton;
btnDisconnect: TButton;
IdAntiFreeze1: TIdAntiFreeze;
Client: TIdTCPClient;
procedure btnConnectClick(Sender: TObject);
procedure btnSendClick(Sender: TObject);
procedure btnDisconnectClick(Sender: TObject);
private
ip: string;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnConnectClick(Sender: TObject);
begin
Client.Host := txtServer.Text;
Client.Port := StrToInt(txtPort.Text);
ip := txtServer.Text + ':' + txtPort.Text;
lstLog.Items.Add('attempting to connect to ip [' + ip + ']');
with Client do
begin
try //try connecting
Connect;
lstLog.Items.Add('successfully connected to ip [' + ip + ']');
try //try getting data
lstLog.Items.Add('response: [' + Client.IOHandler.ReadLn() + ']');
BtnConnect.Enabled := False;
BtnSend.Enabled := True;
btnDisconnect.Enabled := True;
except
lstLog.Items.Add('cannot send data to ip [' + ip + ']');
Client.Disconnect();
end; //end try getting data
except
lstLog.Items.Add('cannot connect to ip [' + ip + ']');
end; //end try connecting
end; //end with
end; //end begin
procedure TForm1.btnSendClick(Sender: TObject);
begin
lstLog.Items.Add('sending data: [' + txtData.Text + ']...');
with Client do
begin
try
IOHandler.Write(txtData.Text);
lstLog.Items.Add('sent data: [' + txtData.Text + ']');
except
lstLog.Items.Add('failed to send data [' + ip + ']');
Client.Disconnect();
lstLog.Items.Add('Disconnect with ' + txtServer.Text + ' !');
BtnConnect.Enabled := True;
BtnSend.Enabled := False;
btnDisconnect.Enabled := False;
end;//end try
end;//end with
end;
procedure TForm1.btnDisconnectClick(Sender: TObject);
begin
Client.Disconnect();
lstLog.Items.Add('disconnected from ip [' + ip + ']');
BtnConnect.Enabled := True;
BtnSend.Enabled := False;
btnDisconnect.Enabled := False;
end;
end.
and here is my server code:
unit my_server;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdTCPConnection, IdTCPClient, IdBaseComponent,
IdComponent, IdCustomTCPServer, IdTCPServer, IdContext, IdThread, IdSync;
type
TfrmTCPServer = class(TForm)
edtPort: TEdit;
Label1: TLabel;
Label2: TLabel;
lbLog: TListBox;
btnStart: TButton;
btnStop: TButton;
IdTCPServer: TIdTCPServer;
procedure btnStartClick(Sender: TObject);
procedure btnStopClick(Sender: TObject);
procedure IdTCPServerConnect(AContext: TIdContext);
procedure IdTCPServerExecute(AContext: TIdContext);
procedure IdTCPServerDisconnect(AContext: TIdContext);
procedure IdTCPServerException(AContext: TIdContext;
AException: Exception);
private
{ Private declarations }
public
end;
var
frmTCPServer: TfrmTCPServer;
implementation
uses
StrUtils;
{$R *.dfm}
procedure TfrmTCPServer.btnStartClick(Sender: TObject);
begin
IdTCPServer.DefaultPort := StrToInt(EdtPort.Text);
IdTCPServer.Active := True;
BtnStart.Enabled := False; //don't let it be clicked again
BtnStop.Enabled := True; //let it be clicked again
lbLog.Items.Add('server started');
end;
procedure TfrmTCPServer.btnStopClick(Sender: TObject);
begin
if IdTCPServer.Active = true then
begin
IdTCPServer.Active := False;
BtnStart.Enabled := True; //let it be clicked now
BtnStop.Enabled := False; //don't let it be clicked again
lbLog.Items.Add('server stopped');
end
else
begin
lbLog.Items.Add('server already stopped');
end
end;
procedure TfrmTCPServer.IdTCPServerConnect(AContext: TIdContext);
begin
try
AContext.Connection.IOHandler.WriteLn('100: welcome to tcp test server');
lbLog.Items.Add('server received connection from [' + Acontext.Connection.Socket.Binding.PeerIP + ']');
except
lbLog.Items.Add('got here2');
end;
end;
procedure TfrmTCPServer.IdTCPServerExecute(AContext: TIdContext); //this is looped infinitely?
var
client_data: string; //strCommand
begin
with AContext.Connection do
begin
IOHandler.CheckForDataOnSource(10);
if not IOHandler.InputBufferIsEmpty then
begin
client_data := IOHandler.ReadLn();
IOHandler.WriteLn('received data [' + client_data + ']');
lbLog.Items.Add('received data [' + client_data + '] from ' + Socket.Binding.PeerIP);
end; //end if
end; //end with
end;
procedure TfrmTCPServer.IdTCPServerDisconnect(AContext: TIdContext);
begin
lbLog.Items.Add('client at ip [' + Acontext.Connection.Socket.Binding.PeerIP + '] has disconnected');
end;
procedure TfrmTCPServer.IdTCPServerException(AContext: TIdContext;
AException: Exception);
begin
lbLog.Items.Add('server exception: [' + AException.Message + ']');
end;
end.
the problem seems to lie in the IdTCPServerExecute procedure on the server. maybe i am not using it correctly, or maybe i need to set up some parameters before using this procedure?
Your server code is calling IOHandler.ReadLn(), which expects a (CR)LF after the text. Your client is calling IOHandler.Write(), which does not send a (CR)LF. The client needs to call IOHandler.WriteLn() instead.
hello i am just new to delphi 7 and i have written a app which manages my mdb database. i just want to put a search box wherein if i put in a keyword it will return results with the keyword on a specific row of the database.
example: on the row named first name i want to search the database with the john keyword then when i hit enter or search button the app will return results with all the data containing john on its first name
type
Tcollector = class(TForm)
Image1: TImage;
ADOConnection1: TADOConnection;
ADOTable1: TADOTable;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
procedure DataSource1DataChange(Sender: TObject; Field: TField);
private
{ Private declarations }
public
{ Public declarations }
end;
var
collector: Tcollector;
implementation
{$R *.dfm}
procedure Tcollector.DataSource1DataChange(Sender: TObject; Field: TField);
begin
end;
EDIT:
i have done this:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, DB, ADODB, StdCtrls;
type
TForm1 = class(TForm)
ComboBox1: TComboBox;
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
Button1: TButton;
Button2: TButton;
ADOQuery2: TADOQuery;
ADOQuery3: TADOQuery;
ADOQuery4: TADOQuery;
ADOQuery5: TADOQuery;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
ADOConnection1.GetTableNames(ComboBox1.Items);
end;
procedure TForm1.Button1Click(Sender: TObject);
var tblname : string;
begin
if ComboBox1.ItemIndex < 0 then Exit;
tblname := ComboBox1.Items[ComboBox1.ItemIndex];
with ADOQuery1 do begin
Close;
SQL.Text := 'SELECT * FROM ' + tblname;
Open;
end;
with ADOQuery2 do begin
Close;
SQL.Text := 'SELECT * FROM ' + tblname;
Open;
end;
with ADOQuery3 do begin
Close;
SQL.Text := 'SELECT * FROM ' + tblname;
Open;
end;
with ADOQuery4 do begin
Close;
SQL.Text := 'SELECT * FROM ' + tblname;
Open;
end;
with ADOQuery5 do begin
Close;
SQL.Text := 'SELECT * FROM ' + tblname;
Open;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
form2.show;
end;
end.
so far i can pull all the table data. what i want my program to do is to display data which i have typed on a tedit
btw sorry for my first post im still not familiar with the forum shortcuts and rules on posting. :D
TDataSet.Filter
or
TDataSet.OnFilterRecord
or use SQL directly.
got it just some minor problems but maybe i can figure it out
begin
ADOTable1.First;
if ADOTable1.Locate('Last',edit1.Text ,[]) then begin
Label1.Caption := ADOTable1.FieldByName('Last').AsString;
Label2.Caption := ADOTable1.FieldByName('First').AsString;
Label3.Caption := ADOTable1.FieldByName('address').AsString;
Next;
end else begin
Label1.Caption := '';
Label2.Caption := '';
Label3.Caption := '';
How to detect (from Delphi) when the laptop is running on batteries (or AC)?
To be notified when the status changes on Vista and Windows 7 you can use RegisterPowerSettingNotification.
For Windows 2000 and later, look at GetSystemPowerStatus, or go to MSDN and read about Power Management.
(Someone always posts while I am typing :-( )
function GetBattery : Boolean;
var
SysPowerStatus: TSystemPowerStatus;
begin
Win32Check(GetSystemPowerStatus(SysPowerStatus));
case SysPowerStatus.ACLineStatus of
0: Result := False;
1: begin
Result := True;
// You can return life with
// String := Format('Battery power left: %u percent.', SysPowerStatus.BatteryLifePercent]);
end;
else
raise Exception.Create('Unknown battery status');
end;
end;
There's a WINAPI function that I believe does this, GetSystemPowerStatus, which I believe you can execute from Delphi.
Here part of code that detect when laptop is running on batteries (if not it triggers some event):
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
WTSSessionNotification, StdCtrls, MediaPlayer, Buttons, ShellAPI, Settings,
ExtCtrls;
const
WM_ICONTRAY = WM_USER + 1;
type
TSettingsForm = class(TForm)
OpenDialog: TOpenDialog;
pnl1: TPanel;
InfoLabel: TLabel;
grp1: TGroupBox;
AlarmSoundLabel: TLabel;
lbl1: TLabel;
checkIfLocked: TCheckBox;
Filename: TEdit;
Browse: TBitBtn;
TestSound: TBitBtn;
btn1: TBitBtn;
lbl2: TLabel;
procedure Minimize(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure TestSoundClick(Sender: TObject);
procedure BrowseClick(Sender: TObject);
procedure checkIfLockedClick(Sender: TObject);
procedure OpenHomepage(Sender: TObject);
procedure btn1Click(Sender: TObject);
private
TrayIconData: TNotifyIconData;
procedure CheckForAC;
protected
procedure WndProc(var Message: TMessage); override;
public
{ Public declarations }
Function SecuredLockWorkStation : Boolean ;
end;
var
SettingsForm: TSettingsForm;
implementation
{$R *.DFM}
{$R WindowsXP.RES}
var
MPlayer: TMPlayer;
mySettings: TSettings;
isLocked: boolean = false;
// true if A/C is connected, false if not
function ACConnected: boolean;
var PowerStatus: TSystemPowerStatus;
begin
GetSystemPowerStatus(PowerStatus);
result := (PowerStatus.ACLineStatus = 1);
end;
// handles application.minimize; do not really
// minimize, but hide settings window
procedure TSettingsForm.Minimize(Sender: TObject);
begin
Application.Restore;
self.Hide;
end;
// processes window messages (notification about
// power status changes, locking of workstation and
// tray icon activity)
procedure TSettingsForm.WndProc(var Message: TMessage);
begin
case Message.Msg of
WM_WTSSESSION_CHANGE:
begin
if Message.wParam = WTS_SESSION_LOCK then
isLocked := true;
if Message.wParam = WTS_SESSION_UNLOCK then
begin
isLocked := false;
if MPlayer.isPlaying then
MPlayer.Close;
end;
end;
WM_POWERBROADCAST:
begin
if (isLocked) or (checkIfLocked.checked=false) then
CheckForAC;
end;
WM_ICONTRAY:
begin
case Message.lParam of
WM_LBUTTONDOWN:
begin
if SettingsForm.visible then
SettingsForm.Hide
else
SettingsForm.Show;
end;
WM_RBUTTONUP:
begin
if SettingsForm.visible then
SettingsForm.Hide
else
SettingsForm.Close;
end;
end;
end;
end;
inherited;
end;