1

For some specific needs i need to create procedure that waits for socket request (or answer) in dll:

TForm1 = class(TForm)
  ServerSocket1: TServerSocket;

......

procedure MyWaitProc; stdcall;
begin
  Go := false;
  while not Go do
  begin
    // Wating...
    // Application.ProcessMessages;     // Works with this line
  end;
end;


procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  MessageBoxA(0, PAnsiChar('Received: '+Socket.ReceiveText), '', MB_OK);
  Go := true;
end;

exports
  MyWaitProc;

When I call Application.ProcessMessages everything works fine: application waits for request and then continues. But in my case calling Application.ProcessMessages causes to unlocking main form on host application (not dll's one). When I don't call Application.ProcessMessages application just hangs couse it cannot handle message...

So, how to create such a procedure that's wating for socket answer ? Maybe there a way to wait for socket answer without using Application.ProcessMessages ?

EDIT

I also tried to use TIdTCPServer, for some reasons, the result is the same.

TForm1 = class(TForm)
  IdTCPServer1: TIdTCPServer;
.....

procedure MyWaitProc; stdcall;
begin
  Go := false;
  while not Go do
  begin
    // Waiting ...
    // Application.ProcessMessages;
  end;
end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  s: string;
begin
  s := AContext.Connection.Socket.ReadString(1);
  AllText := AllText + s;
  Go := True;
end;
Astronavigator
  • 2,021
  • 2
  • 24
  • 45
  • I recommend to use a blocking library like Indy or Synapse for this task, as TServerSocket uses Windows event loop which makes it hard to use in a DLL application. – mjn Jul 02 '12 at 14:02
  • @mjn, i tried to use IdTCPServer, but result is the same. It also uses windows messages to call OnExecute event... – Astronavigator Jul 02 '12 at 14:15
  • IdTCPServer does not use Windows messages, it is based on the Windows Winsock API. OnExecute is called in the main loop for a client connection, not in the Windows message loop. – mjn Jul 02 '12 at 14:20
  • @mjn, edited my post. I still cant make work even with TIdTCPServer. – Astronavigator Jul 02 '12 at 14:40
  • Where is the form instance of TForm1 created (and the TCP server running), in the DLL or in the main application? – mjn Jul 02 '12 at 14:45
  • @mjn, in application. Dll has no forms at all. – Astronavigator Jul 02 '12 at 14:55
  • MyWaitProc is in the DLL then? Have you tried it without the DLL first to see if the TCP server works as expected? – mjn Jul 02 '12 at 15:03
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13313/discussion-between-astronavigator-and-mjn) – Astronavigator Jul 02 '12 at 15:07

2 Answers2

4

TServerSocket runs in non-blocking mode by default, which depends on processing window messages. To remove that dependancy, you have to switch it to blocking mode instead.

TIdTCPServer runs in blocking mode exclusively, so no window messages. If you are having a problem with it, then you are misusing it. For example, in your TServerSocket code, you do not set Go = True until after a response has been received, but in your TServerSocket code you are setting Go = True before reading a response instead.

As an alternative, have a look at Indy's TIdSimpleServer component. TIdSimpleServer is synchronous and only accepts 1 connection at a time, whereas TIdTCPServer is asynchronous and accepts many connections at a time. For example:

TForm1 = class(TForm) 
  ServerSocket: TIdSimpleServer; 

procedure MyWaitProc; stdcall; 
var
  s: String;
begin 
  ServerSocket.Listen;
  s := ServerSocket.IOHandler.ReadLn;
  ServerSocket.Disconnect;
  MessageBox(0, PChar('Received: '+s), '', MB_OK); 
end; 

exports 
  MyWaitProc; 
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanx, Remy. But in my case i need both asynchrony and ability to wait for response. I think synchronous component cannot resolve my problem, couse of some other functions in dll, which works asynchronously... – Astronavigator Jul 03 '12 at 06:28
  • And also i need more than one connection. – Astronavigator Jul 03 '12 at 07:22
  • 1
    Then you need a way of telling `MyWaitProc()` **which** connection it has to wait for. And then you need to fix that bug I pointed out earlier where `Go` is set to True too soon. – Remy Lebeau Jul 03 '12 at 19:53
  • For now on i stopped on using TIdSimpleServer (in separate thread), so i accept your answer. – Astronavigator Jul 04 '12 at 10:03
1

Rather than creating a loop that occasionally calls Application.ProcessMessages you can create a descendant of TThread and move the socket request to the TThread.Execute method. Use TThread.OnTerminate to notify your form(or any other class) when the thread has completed its work.

There is sample code which gives more details about how to use TThread.

There are several other 3rd party threading libraries that either provide more flexibility or are easier to use than TThread and I would highly recommend any of them over TThread if you are new to multi-threading.

Note: There are some serious side-effects to using Application.ProcessMessages. You are seeing one of them in your code with the dll unlocking the application's mainform. It breaks the single-threaded UI model the VCL is build upon. ProcessMessages has its place but using threads is more appropriate for the situation you're describing.

var Slowpoke: TMyLongRunningProcessThread;

procedure MyWaitProc(Completed:TNotifyEvent)
begin
  Slowpoke := TMyLongRunningProcessThread.Create(True);
  Slowpoke.FreeOnTerminate := True;
  Slowpoke.OnTerminate := Completed;
  Slowpoke.Resume;
end;

MyWaitProc returns immediately after starting the thread so the GUI is free to respond to user actions. When the thread terminates it calls the event handler pointed to by Completed.

Obviously if you need to retrieve data from the thread you'll want to either have the thread write to an accessible memory location before it Frees itself or remove the FreeOnTerminate so the data can be retreived from the thread through a property.

Community
  • 1
  • 1
Kenneth Cochran
  • 11,954
  • 3
  • 52
  • 117
  • I am not sure i understand you, Kenneth, which call in my dll i am to move to a thread? And how can i notify a form, in case my dll has no form at all? – Astronavigator Jul 02 '12 at 14:21
  • Kenneth, i know how to use TThread (however thanx for your examples), but i still dont understand how it can resolve my problem... – Astronavigator Jul 02 '12 at 19:48
  • Ken, thanx for answer. But i NEED my procedure not to end until it receives request from socket. It seems strange, but that is what i need... (i really need my GUI to be locked when waiting) – Astronavigator Jul 03 '12 at 06:19
  • As soon as I stopped on using TIdSimpleThread in separate Thread, I vote up your answer for threading idea. Thanx :) – Astronavigator Jul 04 '12 at 10:07