Search code examples
sqldelphidelphi-7indytadoquery

Indy Server and AdoQuery are conflicting


I have two programs one is a client the other is a server for the client. The client sends some data to the server like this and then reads the response:

 idtcpclient1.WriteLn(command); //Command contains data that the server needs eg. name and surname
 progressbar1.StepIt;
 sresult := idtcpclient1.ReadLn();

The server then reads the line, manipulates it and creates a SQL Query.

adoquery1.Close;
adoquery1.SQL.Text := 'select * from '+sGrade;
adoquery1.Open;

But as soon as it opens the connection to the database the client gives an error "Connection closed gracefully"

I have tested the server code without the client by simulating the input and it works fine.

I think Indy and AdoQuery are conflicting If so why and how can I fix it

If not then what is the problem and how should I fix it?


Solution

  • ADO uses apartment-threaded COM objects that have an affinity to the thread that creates them. They cannot be used across thread boundaries unless they are marshalled.

    Indy's TCP server is multi-threaded. Each client runs in its own thread.

    A thread must call CoInitialize/Ex() to establish its relationship with COM before it can then access any COM objects, and call CoUninitialize() when it is done using COM.

    Your server fails because it is raising an uncaught exception that disconnects the client. Most likely because you did not initialize COM.

    You need to create ADO objects on a per-client basis, do not use them from the main thread. In the server's OnConnect event, call CoInitialize/Ex(). In the OnDisconnect event, call CoUninitialize(). In the OnExecute event, dynamically create and use new ADO objects as needed.

    This does mean that each client will need its own database connection. If you do not want that, then move your ADO logic to a dedicated thread that clients can post requests to when needed. Stay away from doing the database work in the main thread, it does not belong there.