Send/Receive text with buffer using sockets in Delphi - sockets

So I have searched for a way to make this work but found no working solution. I am trying to send text that has been inserted in to a memo from the Client to the Server using sockets. Unfortunately I cannot get the coding working. It might be something stupid I'm doing wrong; but I don't know where the mistake is. I created a stable connection between the server and client, and I am able to send text using Server.Socket.Connections[Form1.ListView1.Selected.Index].SendText('Texthere'); but I just cant get it to send and receive text or anything using buffers.
Code on Client:
procedure TIBAT.Sends1Click(Sender: TObject); //Button to start the sending of the text on the memo component
var
ms: TMemoryStream;
size: Integer;
begin
if (Form1.ListView1.Selected <> nil) then //if a server is selected
begin
Form1.Server.Socket.Connections[Form1.ListView1.Selected.Index].SendText('IBAT');
ms:= TMemoryStream.Create;
try
IBAT.Memo1.Lines.SaveToStream(ms);
ms.Position:= 0;
Size:= MS.Size;
Form1.Server.Socket.Connections[Form1.ListView1.Selected.Index].SendBuf(Size,SizeOf(Size));
Form1.Server.Socket.Connections[Form1.ListView1.Selected.Index].SendStream(ms);
except
ms.Free;
ShowMessage('FAILED');
end;
end;
end;
Code on the Server:
private
Stream: TMemoryStream;
FSize: Integer;
writing: Boolean;
...
procedure TSock.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
Command :String;
BytesReceived: Longint;
CopyBuffer: Pointer; //buffer for copying
ChunkSize: Integer;
TempSize: Integer;
const
MaxChunkSize: Longint = 8192;
begin
Command := Socket.ReceiveText;
if split(Command, '|', 0) = 'IBAT' then
begin
If FSize=0 then
begin
//ShowMessage(IntToStr(Socket.ReceiveLength)); added messageboxes everywhere to figure out where the problem is
If Socket.ReceiveLength>SizeOf(TempSize) then
begin
Socket.ReceiveBuf(TempSize,SizeOf(TempSize));
Stream:= TMemoryStream.Create;
Stream.SetSize(TempSize);
//ShowMessage(IntToStr(TempSize));
FSize:= TempSize;
writing:= True;
End;
End;
If (FSize>0) and (writing) then
begin
GetMem(CopyBuffer, MaxChunkSize); //allocate the buffer
While Socket.ReceiveLength>0 do
Begin
ChunkSize:= Socket.ReceiveLength;
If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
BytesReceived:= Socket.ReceiveBuf(CopyBuffer^,ChunkSize);
Stream.Write(CopyBuffer^, BytesReceived); //write chunk
Dec(FSize,BytesReceived);
End;
FreeMem(CopyBuffer, MaxChunkSize); //free allocated buffer
If FSize = 0 then
begin
Stream.SaveToFile(TempDir+IntToStr(GetTickCount)+'.cmd');
Socket.SendText('File received!');
Stream.SetSize(0);
FSize:= 0;
End;
FreeMem(CopyBuffer, MaxChunkSize);
Writing:= False;
End;
end;
I know using Indy is more effective and better than sockets, but I've already started coding using it and switching over to Indy is a big pain.
If anyone can help me get this working I would really appreciate it.

I've just make a very-very simple example of TCP client and server on Indy. It is very simple, even no exceptions handling there.
Take a look at a repository at github and feel free to ask any questions.
Anyway, using of native socket components is not a good idea. Try to study Indy, then Synapse or mORMot may be. Indy will be a good start.
UPD I'll try to get some code here in answer:
Start a TCP server listening on specified port:
var
IdTcpServer1: TIdTcpServer
[...]
IdTcpServer1.DefaultPort := SERVER_PORT;
IdTcpServer1.Active := True;
Server will start listening on 0.0.0.0:1234.
Connect a TCP client to TCP server:
var
IdTcpClient1: TIdTcpClient;
[...]
IdTcpClient1.Host := SERVER_HOST; // IP or DNS name
IdTcpClient1.Port := SERVER_PORT;
IdTcpClient1.Connect;
Send a text line to socket (after it's connected):
IdTcpClient1.IOHandler.Writeln('Hello world!');
Server must implement OnExecute event handler:
// manual init in code or you can assign it in design-time in IDE
IdTcpServer1.OnExecute := IdTcpServer1ExecuteHandler;
[...]
// very simple example
procedure IdTcpServer1ExecuteHandler(AContext: TIdContext);
var
s: string;
begin
s := AContext.Connection.IOHandler.ReadLn();
if s <> EmptyStr then
begin
// do something with received string s
end;
end;

Related

Delphi REST not updating VCL db components when created runtime

I have a REST client which sends a request and updates some VCL db components with the response using an adapter, that works fine when the REST VCL components are put on a TDatamodule, however if I try to create the same REST components runtime, the VCL db components are not showing the data. I can see that the data are in fact returned (I can read them in the FDMemtable and store the data as a file) so the data is there, but the GUI components doesnt display data. The DBGrid does however show the vertical scroll bare after the update.
I'm using Delphi XE10.2.
I got stuck with this, I got to be missing something.
function TRESTClientModule.UpdateHistory: Boolean;
var
ARESTClient : TRESTClient;
ARESTRequest: TRESTRequest;
ARESTResponse: TRESTResponse;
ARESTResponseDataSetAdapter: TRESTResponseDataSetAdapter;
i : Integer;
s : string;
begin
Result := False;
try
ARESTClient := TRESTClient.Create('http://localhost:8080');
ARESTRequest := TRESTRequest.Create(nil);
ARESTResponse := TRESTResponse.Create(nil);
ARESTResponseDataSetAdapter := TRESTResponseDataSetAdapter.Create(nil);
try
// bind to RESTClient
ARESTRequest.Client := ARESTClient;
//- Prepare to get data from 'alarms' end-point.
ARESTRequest.Resource := 'alarms';
ARESTRequest.Method := TRESTRequestMethod.rmGET;
ARESTRequest.Response := ARESTResponse;
// Link Response to dataset (FDMemtable + TDataSource)
ARESTResponseDataSetAdapter.Dataset := AlarmHistory;
ARESTResponseDataSetAdapter.ResponseJSON := ARESTResponse;
ARESTResponseDataSetAdapter.RootElement := 'alarms';
//- Add the parameter to the request.
ARESTREquest.Params.Clear;
with ARESTRequest.Params.AddItem do
begin
name := 'IncludeOOS';
value := '0';
end;
ARESTRequest.Execute;
s := ARESTResponse.Content; // returned content is present
i := AlarmHistory.RecordCount; // the expected Recordcount is present
Result := True;
finally
ARESTResponse.Free;
ARESTResponseDataSetAdapter.Free;
ARESTRequest.Free;
end;
except on E: Exception do
end;
end;

Delphi Unique Dynamic Form Creation

I am using multiple forms on my project. (Client Server Application) I used chat but i have some problems.
1- I have a user list on my listview. And i am open new chat form here.
procedure CreateNewChat(User: String);
var
ChatForm: TChatForm;
begin
ChatForm:= TChatForm.Create(nil);
if assigned (ChatForm) then
ChatForm.User:= User;
Chat.Socket:= MySocket; // TClientSocket new Instance
ChatForm.Show;
end;
///Chatform
procedure ParseData(Cmd:string);
begin
if Cmd <> '' then
begin
/// parsing...
end;
New connections are adding to listview like that
var
Item: TListItem;
NewTempForm: TTempForm;
begin
NewTempForm := tempForm.Create;
Item := Listview.Items.Add;
if User = '' then
Exit;
Item.Caption := User;
Item.SubItems.Add('OK');
Item.GroupId := GroupId;
Item.SubItems.Objects[0] := NewTempForm;
end;
My problems are started on here.
1- How can i detect form according to user? So, if two or higher form is open then how can i show received message on them? Because i was create them dynamically. I need a object for detect.
I tryed like that
var
tempForm: TTempForm; // this is an empty object class
sTempStr: String;
begin
if ListView.Selected <> nil then
begin
tempForm := TTempForm(ListView.Selected.SubItems.Objects[0]); // this is for unique form creation
if tempForm.frmTasks = nil then // if there is not, create!
begin
tempForm.frmTasks := TfrmTasks.Create(nil);
end;
But my received message is appear on all forms. Actually should appear just one form. (Which user sent it)
Thanks.

Wrong Form is focused after closing form in firemonkey

When showing and closing Forms in firemonkey, the application cannot remember wich form was the last activated form, and activates the wrong form.
How can I activate the last active form instead of an arbitrary form chosen by the application?
To replicate : Create 3 forms and open each one in succession from previous form.
I a mainform and 2 ChildForms, the second form is parent to the third form.
I open the first childForm from my MainForm.
var
tmpForm2:TForm2;
begin
tmpForm2:=TForm2.Create(self);
tmpForm2.Show;
end;
In this Form there is a button that shows second childform
var
form3:Tform3;
begin
form3:=TForm3.Create(nil);
form3.Show;
end;
When I open the second ChildForm and close it, the Mainform is activated. Instead of the first ChildForm
Now I repeat the process but when closing the second ChildForm, the first one is actived, as one would expect.
Next time the Mainform is again activated, so the order keeps chainging, instead of the real last active form.
Looks like it was bug in Delphi XE7/XE7 Update 1 in function
function TScreen.NextActiveForm(const OldActiveForm:
TCommonCustomForm): TCommonCustomForm;
On Delphi XE8 this function works correctly and you return to previous window.
In XE8 they rewrite function function TScreen.NextActiveForm(const OldActiveForm: TCommonCustomForm): TCommonCustomForm;
Dog-nail for XE7. I copy function from XE8 and using it before close form.
I tested it only under Windows platform.
unit ufmForm3;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls;
type
TfmForm3 = class(TForm)
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
function NextActiveForm(const OldActiveForm: TCommonCustomForm): TCommonCustomForm;
end;
var
fmForm3: TfmForm3;
implementation
{$R *.fmx}
procedure TfmForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
NextActiveForm(Self);
end;
function TfmForm3.NextActiveForm(const OldActiveForm: TCommonCustomForm): TCommonCustomForm;
var
I, CurrIndex: integer;
begin
Result := nil;
CurrIndex := Screen.IndexFormOfObject(OldActiveForm);
if CurrIndex >= 0 then
begin
I := CurrIndex - 1;
while (I >= 0) and (Screen.Forms[I].Released or not Screen.Forms[I].Visible) do
Dec(I);
if I < 0 then
begin
I := Screen.FormCount - 1;
while (I >= 0) and (I <> CurrIndex) and (Screen.Forms[I].Released or not Screen.Forms[I].Visible) do
Dec(I);
end;
if (I >= 0) and (I <> CurrIndex) then
begin
Result := Screen.Forms[I];
Screen.ActiveForm := Result;
end;
end;
end;
end.
I had a related problem in Delphi FMX Berlin. My SDI application has a hidden "real" main form and one or more instances of the working form. When one of the working forms called a modal dialog, I was finding that on closure of the dialog, the focus was going to a form different to the calling form. The solution turned out to be simple.
(1) Create the dialog with owner Self:
MyDlg := TMyDlg.Create(Self);
MyDlg.ShowModal;
(2) Use the following in the modal dialog OnClose:
procedure TMyDlg.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := TCloseAction.caFree;
Screen.ActiveForm:=TMySDIAppForm(Self.Owner);
end;

How To logout and close any active forms in Delphi

Sorry for my bad English, first this is the logout syntax.
When I click logout, all active forms keep showing up and not closing.
procedure Tf_utama.KELUAR1Click(Sender: TObject);
begin
if MessageDlg('Logout ??',mtConfirmation,mbOKCancel,0)=mrOK
then
DATAINPUTAN1.Visible:=False;
INFODATA1.Enabled:=False;
TRANSAKSI.Enabled:=False;
LAPORAN1.Enabled:=False;
PENGATURAN1.Enabled:=False;
f_databuku:=nil;
f_rakbuku:=nil;
f_permintaan_pembeli:=nil;
f_rakbuku:=nil;
f_pengguna:=nil;
f_transaksi_penjualan:=nil;
f_transaksi_pembelian:=nil;
f_supplier:=nil;
StatusBar1.Panels[0].Text:='Nama Pengguna :';
StatusBar1.Panels[1].Text:='Hak Akses :';
end;
On each form to close I am using:
procedure Tf_caribuku.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree;
f_caribuku:=nil;
end;
That's not how you close and free the form. You are just assigning nil to form pointers, which doesn't close them. What you have to call is TForm.Close(). Also, you have to open your if MessageDlg(... statement with begin..end; blocks if you want to execute multiple commands if statement is satisfied. So your code would look something like this:
procedure Tf_utama.KELUAR1Click(Sender: TObject);
begin
if MessageDlg('Logout ??',mtConfirmation,mbOKCancel,0) = mrOK then
begin
DATAINPUTAN1.Visible:=False;
INFODATA1.Enabled:=False;
TRANSAKSI.Enabled:=False;
LAPORAN1.Enabled:=False;
PENGATURAN1.Enabled:=False;
f_databuku.Close;
f_rakbuku.Close;
f_permintaan_pembeli.Close;
f_rakbuku.Close;
f_pengguna.Close;
f_transaksi_penjualan.Close;
f_transaksi_pembelian.Close;
f_supplier.Close;
StatusBar1.Panels[0].Text:='Nama Pengguna :';
StatusBar1.Panels[1].Text:='Hak Akses :';
end;
end;
Also, if any of forms you're trying to close is not created, I believe it will fail with AV exception. Better way to do this is to make another class which will serve as form container, e.g. TFormContainer, where you can Add and Remove forms on need. That way, upon logout, opened forms will be in TFormContainer class, and you can close them.

Using Word OLE in Lazarus FreePascal

Im trying to get FreePascal to open up a word document, append some text and data to it and then close it. I've managed to get connected and can write a single line to the document but anything over that is defeating me. Currently I am attempting methods details in this Visual Basic reference, which is pretty similar to how I would expect FreePascal to handle things.
Basically I think I have misunderstood how the relationship between Lazarus and the Word OLE actually works, can anyone offer me any examples on how to construct a simple document that I can build on?
The following code, opens the document but then completely replaces its contents
program officAuto;
{$IFDEF FPC}
{$MODE Delphi}
{$ELSE}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
SysUtils, Variants, ComObj;
const
ServerName = 'Word.Application';
var
Server, Doc : Variant;
oPara : Variant;
w:widestring;
begin
if Assigned(InitProc) then
TProcedure(InitProc);
try
Server := CreateOleObject(ServerName);
except
WriteLn('Unable to start Word.');
Exit;
end;
w:= UTF8Decode('c:\mydoc.docx');
Server.Visible := True; {Make Word visible}
Doc := Server.Documents.Open(w);
Doc.Range.Text := 'This is a Heading';
Doc.Range.Font.Bold := True;
Doc.Format.SpaceAfter := 24;
end.
Whereas this, based on the code above, in attempting to print a string at a bookmark, opens the document, retains the contents, moves to the bookmark and then does nothing.
w:= UTF8Decode('c:\mydoc.docx');
Server.Visible := True;
Doc := Server.Documents.Open(w);
oPara := Doc.Content.Paragraphs.Add(Doc.Bookmarks.Item('\Bookmark1').Range);
oPara := Doc.Range.Text('Where will this appear if at all!');
Ah I worked it out. The following code works as expected:
program officAuto;
{$IFDEF FPC}
{$MODE Delphi}
{$ELSE}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
SysUtils, Variants, ComObj;
var
Server, Connect : Variant;
oWord, oPara1, oPara2 : Variant;
w:widestring;
begin
if Assigned(InitProc) then
TProcedure(InitProc);
try
Server := CreateOleObject('Word.Application');
except
WriteLn('Unable to start Word.');
Exit;
end;
// oWord := Server.Documents.Add;
w:= UTF8Decode('c:\mydoc.docx');
Server.Visible := True;
Server.Documents.Open(w);
oPara1 := Server.ActiveDocument.Content.Paragraphs.Add;
oPara1.Range.Text := 'This is a Heading';
oPara1.Range.Font.Bold := True;
oPara1.Format.SpaceAfter := 24;
oPara1.Range.InsertParagraphAfter();
oPara2 := Server.ActiveDocument.Content.Paragraphs.Add;
oPara2.Range.Text := 'Where will this appear if at all!';
oPara2.Range.Font.Bold := False;
oPara2.Format.SpaceAfter := 24;
oPara2.Range.InsertParagraphAfter();
end.