Delphi: FireDac Connection blocking the application - delphi

I'm working to an application with a login form at the start-up.
Until user is writing the login data, I would like to connect discreetly to the SQL server.
The problem is that, when I have a slow connection or a wrong path to the server, the application is looking for the server or trying to connect and in this time the application is not responding.
For connection I use this procedure:
//-- SQL connect --//
procedure TSql.Connect;
var
DriverId: String;
i: Byte;
begin
try
Screen.Cursor:=crAppStart;
//connection DriverName
DriverId:=Server[FServerType].DriverId;
FConnection.DriverName:=DriverId;
//connection Params
FConnection.Params.Clear;
FConnection.Params.Add('DriverID='+DriverId); //mandatory
if FConnString.Count>0 then
for i := 0 to FConnString.Count-1 do FConnection.Params.Add(FConnString.Strings[i]);
try
FConnection.Open;
FQuery.Connection:=FConnection;
except
on E : Exception do ShowError(_('Connection could not be established!'),E);
end;
finally
Screen.Cursor:=crdefault;
end;
end;
Please help me with some suggestion about how this can be done. I've read about threads and Application.ProcessMessages but I did not succeed to make it work smoothly.

Create a new thread, and do everything you need on it, that will not hang the main form and the user will not see anything you can see simillar functionality here

Related

FireDAC "unable to complete network request to host"

The full error text is Remote error: [FireDAC][Phys][FB]Unable to complete network request to host "dataserver16". Error writing data to the connection. Now it seems that others have had this problem then once they sorted it, it went away, but I have the problem sporadically.
My Datasnap ISAPI.dll which contains the FireDAC Firebird connection, is running on an IIS server on a different machine to the one where the database is hosted (dataserver16) but on the same subnet. I know everything is configured correctly, because the application works to expectations about 70% of the time! The other 30% of the time, my Datasnap client receives this error (as passed back from the dll).
IMHO it looks like there is a Network issue. If the Connection is Etablished and you can read and write Data to this connection it seams to be correct.
Have you tried to do a Ping from your Source System to the Target and log that Ping so you can See if the hole Connection to the Server disapears?
Open Commandwindow as Admin and Type:
Ping {TARGET} -t >> c:\ping.log
Than wait until the Error apears and check the Logfile if your Target was available the hole Time.
For more Help we need more Background Information, like Firebird Version or If you are able to reproduce the Error + Source Code how you set up your Connection.
For completeness, I am posting my solution here. Perhaps others will gain benefit from this answer. The solution is to perform retries of the Firebird connection. The way I did it, is every TSQLQuery's BeforeOpen event handler is wired to the same method. This has improved reliability considerably (even if it slowed it down a little). The code for FireDAC is similar. Both DBX and FireDac work equally well here.
const
retrycount = 3;
procedure TServerMethodsDBX.QueryBeforeOpen(DataSet: TDataSet);
begin
TryConnect(TSQLQuery(DataSet).SQLConnection);
// ...
end;
procedure TServerMethodsDBX.TryConnect(SQLConn: TSQLConnection);
var
i: Integer;
Error: String;
begin
i := 0;
SQLConn.Close;
while (not SQLConn.Connected) and (i < retrycount) do
begin
try
SQLConn.Connected := True
except
on e: exception do
begin
Error := Error + ' ' + e.Message;
Sleep(500);
Inc(i);
end;
end;
end;
if i = retrycount then
LogMessage('Tryconnect error: ' + Error);
end;

Delphi Datasnap client code not getting unauthorized exception

I'm using Delphi 10.1 Berlin Update 2 Enterprise and the DataSnap client/server REST framework.
If I run the app without debugging and invoke a method the user isn't authorized to invoke, the code runs without any exception and the method returns a null response.
When interactively debugging a call on the client to a DataSnap server method, I get two popup exceptions regarding "unauthorized".
The first bubbles up and is replaced by the second.
The second exception gets "eaten" and the session/connection simply closed and then the method returns a blank result (e.g. a zero if the return type is integer, and an empty string for a string return type).
This is happening in the following section of code near the end of the ExecuteRequest method in the Datasnap.DSClientRest unit:
except
on E: TDSRestProtocolException do
LSessionExpired;
end;
Why are these exceptions (e.g. TDSRestProtocolException) not reaching my code?
I kind of think this is new to Update 2, and I remember seeing those exceptions bubble up to my code prior to Update 2.
Attached is a skeleton example (standard example generated by Delphi wizards) that demonstrates the issue - click the button and you get "" instead of "4321" because the user isn't authorized - but no runtime exception.
I'm new to DataSnap, so bear with me :-)
Thanks in advance for helpful responses =)
This is happening due to DSAuthenticationManager1 component added to webmodule of the server and client side is failing to authenticate.
Please go through this to check how to work with authentication
Adding Authentication and Authorization
Well..I'm not sure but try providing username and password to DSRestConnection1 component before the instance of server methods gets created
procedure TClientModule1.TestCon(aUsername, aPassword: string);
var
lServerMethodsClient : TServerMethodsClient;
begin
DSRestConnection1.UserName := aUsername;
DSRestConnection1.Password := aPassword;
lServerMethodsClient:=TServerMethodsClient.Create(DSRestConnection1);
end;
and try to call this functn from ur clientform
procedure TF_ClientForm.Button1Click(Sender: TObject);
begin
ClientModule1.TestCon(EdtUsername.Text, EdtPassword.Text);
end;
Maybe a little late but this morning I've had a deep dive into this because, after upgrading from Delphi XE6 to Tokyo 10.2, applications where I used the TDSRestConnection component got broken. Although I supplied the correct username and password, they did not appear in the TDSAuthenticationManager.OnUserAuthenticate event.
The 'problem' has to do with the new System.Net.HttpClient implementation.
To make a long story short (or a little bit less long):
The client component does not send the credentials until the receiving server demands one by sending a 401 response. After receiving this (properly formatted) response the client looks at de TDSConnection credentials en tries again. At the client side a complete list of urls with credential requirements is maintaned so repetitive calls to the same url go 'smoother'.
I added this code to the server's WebModule (where the TDSRESTWebDispatcher resides) which solved my problems:
procedure TwbmMain.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
LAuthorization: string;
begin
inherited;
if Request.PathInfo.StartsWith('/datasnap/') then
begin
LAuthorization := TNetEncoding.Base64.Decode(Request.Authorization.Replace('Basic ', ''));
if LAuthorization.IsEmpty then
begin
Response.StatusCode := 401;
Response.WWWAuthenticate := 'Basic';
Handled := True;
end;
end;
end;
Because my applications provides some downloadable items like a logo etc., I limited the check to just those URLs that have anything to do with datasnap.
Hope this is useful to others!

Delphi 10 Seattle Datasnap error: "Operation failed. Connection was closed."

I created a stand-alone Datasnap TCP/IP server using the Wizard. I selected sample methods (echostring and reversestring). I saved the server and ran it. Then I created a client application, and using the file-new-other, added a ClientModule to that client project, along with the ClientClasses unit. On the main form. I added a button. On the button's onclick event handler, I added the following code:
procedure TForm1.Button1Click(Sender: TObject);
begin
if ClientModule1.SQLConnection1.Connected then
begin
Button1.Text := 'Open';
ClientModule1.SQLConnection1.Close;
end
else
begin
Button1.Text := 'Close';
// ClientModule1.SQLConnection1.Open;
ClientModule1.ServerMethods1Client.ReverseString('myteststring');
end;
end;
The purpose here is to simulate a situation where the client is logging into and logging out of the server regularly rather than keeping a connection. This is especially important on apps deployed to mobile.
You can see I commented out the Connection.Open, because the first call to the ServerMethods1client opens the connection. The generated code is shown here:
function TClientModule1.GetServerMethods1Client: TServerMethods1Client;
begin
if FServerMethods1Client = nil then
begin
SQLConnection1.Open;
FServerMethods1Client := TServerMethods1Client.Create(SQLConnection1.DBXConnection, FInstanceOwner);
end;
Result := FServerMethods1Client;
end;
Now the problem arises. On first click to the button, the connection is opened, and the method is called. On the second click to the button, the connection is closed.
On the 3rd click, an exception is raised "Operation Failed. Connection was Closed" is raised from with the TDBXCommand code.
As a workaround, I tried this:
procedure TForm1.Button1Click(Sender: TObject);
begin
if ClientModule1.SQLConnection1.Connected then
begin
Button1.Text := 'Open';
ClientModule1.SQLConnection1.Close;
ClientModule1.ServerMethods1Client := nil;
end
else
begin
Button1.Text := 'Close';
// ClientModule1.SQLConnection1.Open;
ClientModule1.ServerMethods1Client.ReverseString('myteststring');
end;
end;
This does sort-of solve the problem, since the ClientModule1's FServerMethods1Client instance is reset so the create code runs again like it did on the first run.
The only other problem now, is (I am using Eurekalog) it creates a memory leak.
What am I doing wrong? What's the right way to connected/disconnect from a Datasnap server repeatedly without restarting the app?
The reason for the first error is that the code that binds the client side proxy (which allows server methods to be called) is tied to the local SQL connection. Note the call to create the proxy class:
FServerMethods1Client := TServerMethods1Client.Create(SQLConnection1.DBXConnection, ...)
The underlying DBExpress connection is passed by reference, and the proxy class uses that connection to call the server. You closed and re-opened the connection, but the underlying DBExpress connection that ServerMethodsClient1 was using has been destroyed. Thus, you receive the "Connection was closed" exception. The connection that ServerMethodsClient1 was using has been closed. You have to recreate ServerMethodsClient1 as you did in your second example.
I can't answer your second question, as I believe it is ARC specific. For a VCL DataSnap app, I would call ServerMethodsClient1.Free rather than setting it to nil. Based on my very, very limited understanding of Delphi's ARC implementation (which is all from the newsgroups), I believe you should call ServerMethodsClient1.DisposeOf, since the class descends from TComponent
But I'm not sure about that. I'm sure someone will jump on here that understands ARC and the proper solution to destroy the object rather than having a memory leak.
In my Android FMX implementation, I only call servermethods to get stuff done. (ie I don't use Datasnap data components). There's too much uncontrolled data transmission overhead to the Datasnap architecture to contemplate anything else realistically on a mobile device... To get around it (and not have memory leaks), I now create local instances of the TServermethods1Client as and when I need them and free them in context:
function TClientModule1.PostTheLog: Boolean;
var
Server: TServerMethods1Client;
begin
Server := TServerMethods1Client.Create(ClientModule1.SQLConnection1.DBXConnection);
try
UserID := Server.GetUserID;
...
finally
Server.Free;
end;
end;
Now the ClientModule1.SQLConnection1 can be connected and disconnected at will (preferably connected just before any call to a servermethod, and disconnected thereafter) and no further issues arise.
Which then begs the question: In which ideal world would the publicly accessible ServerMethods1Client actually be useful?

Why won't TIdIRC connect to channel? Is there a better component?

I've been struggling with the crap documentation of Google and can't get the program to join the channel even though it connects to the server fine. (It says Connected to server)
//On Form Make
procedure TForm2.FormCreate(Sender: TObject);
begin
IdIRC1.Connect();
end;
//on connected
procedure TForm2.IdIRC1Connected(Sender: TObject);
begin
ShowMessage('Connected to server');
IdIRC1.Join('#TheChannel', 'password');
end;
Once i close the form an error comes up saying:
Project raised exception class EIdException with message 'Not Connected'
Also once connected what functions do i use to talk on the channel/check input?
And what other IRC connection options (components) are there for Delphi applications?
ANY HELP WOULD BE APPRECIATED, GOOGLE HAS NOTHING ON THIS REALLY.
All i want is to be able to connect/check channel chat messages & talk on the chat; Simple String IO through IRC...
Guess you are not filling all the server requirements. Just IdIrc.Connect isn't enought, this works for me:
FIRC.Host:= 'irc.freenode.org';
FIRC.Port := 6667;
FIRC.Username:= 'SapoIndy';
FIRC.Nickname:= 'SapoIndy';
FIRC.RealName:= 'Fabio Gomes';
FIRC.Connect;
FIRC.Join('#TheChannel');
To figure out what is going on, you need to get the output of some events, I had implemented these:
FIRC.OnStatus:= #Status;
FIRC.OnNotice:= #Notice;
FIRC.OnMOTD:= #MOTD;
Get some events up and you should figure out what the server is telling you, don't go with trial and error.
And about sending and receiving messages, I've implemented some of this some time ago, here is the project, it was made using Lazarus.
https://bitbucket.org/fabioxgn/irc/src/b510d73e553d/main.pas
Do not call Join() in the OnConnected event. That event simply means the underlying socket connection is established. Connect() is still running, and has not actually logged into the IRC server yet when the OnConnected event is triggered. Wait until Connect() exits before issuing any commands:
procedure TForm2.FormCreate(Sender: TObject);
begin
IdIRC1.Connect;
ShowMessage('Connected to server');
IdIRC1.Join('#TheChannel', 'password');
end;

access events when running intraweb application as a service

I'm running my Standalone Intraweb App as a service. Now i need to implement a function that writes
a "heartbeat" timestamp to a database table. I've done this in other service app that uses
the TService Classm where i can use Events like OnAfterInstall, OnExecute etc.
Is there a way i can use that events in a standalone intraweb app running as service ?
Thanks for all info
Wolfgang
I started doing exactly this in my own Intraweb application, though because the IWServiceWizard hides the service details including the main Execute loop, I did it all server-side, I was using Application Mode.
I defined a heartbeat method on my session class (RunSQL is a method on my own Data Access Layer object DBConnection, this could be a simple wrapper around TADOConnection).
function TIWUserSession.UpdateHeartbeat: boolean;
var
sSQL : string;
begin
sSQL := 'UPDATE Heartbeats SET LastComms = getdate()'+
' WHERE SessionID = '+ IntToStr(FSessionID);
Result := DBConnection.RunSQL(sSQL);
end;
Once I'd done this it was trivial to call this method (for example) whenever a user opened a new web page.
procedure TIWMyPage.IWAppFormCreate(Sender: TObject);
begin
inherited;
Session.UpdateHeartbeat;
end;
This can also be used whenever the user does something that communicates with the server, even if it's an asynchronous event (AJAX).
procedure TIWMyPage.btnRefreshAsyncClick(Sender: TObject;
EventParams: TStringList);
begin
Session.UpdateHeartbeat;
end;
Intraweb supports TIWTimer, so sending a timestamp to the database should be pretty straightforward. Specifics of coding depend on detailed specifications.

Resources