TidCustomTCPServer.Active := False, never returns - delphi

I downloaded some interesting code from Indy 10 TIdTCPServer and compiled and ran it. It's a bit messy and it probably needs work but it's a good foundation for something interesting. At least I don't have to post all the code here. Anyway, I decided that the ExampleServer needs to be de-activated first, not just freed in OnDestroy. So I added:
procedure TServerPushExampleForm.FormClose(Sender: TObject);
begin
ExampleServer.Active := False; // bug here - never returns
end;
To try to debug this, I added IdCustomTCPServer.pas to the project and stepped into it.
An exception is raised in TIdCustomTCPServer.StopListening; at the line LListener.WaitFor; This exception is trapped in the exception handler in TIdListenerThread.Run; but the E.message isn't retrieved there, so I had to modify the code there to get the message:
"Operation Aborted"
I traced it further after that, but the code execution eventually comes back to the same exception handler.
Is this a bug or is it just better to never set the Active property to False? In my tests, if I close the app and let the RTL manage all the free'ing. The infinite loop doesn't occur (the app does actually close)

The exception is normal behavior.
Indy uses blocking sockets.
TIdListenerThread runs a loop waiting for clients to connect. Each wait is a blocking operation.
When the server is being deactivated, it closes its listening socket(s), causing pending socket operations to abort, and then the listening thread(s) are terminated. The abort exception is handled internally.
The server's destructor also sets Active=False, so whether you deactivate explicitly or not, the server will be deactivated.
This, in if itself, is not causing your hang. Typically, the only way setting Active=False can ever hang is if a server event handler tries to sync with the main UI thread synchronously while the main thread is blocked waiting for the server to deactivate. Classic deadlock scenario.
That does not appear to be the case in the demo you linked to, though (the only sync being used is asynchronous instead). So, something else is likely going on that your debugging hasn't reveiled yet. There should be no hang.

Related

How to immediately completely stop an application and show a message?

When my application needs to make an emergency stop, I could call Halt or ExitProcess to terminate it immediately. This shouldn't happen quietly, though; the user needs to be shown a message. Therefore I looked to FatalAppExit but was surprised that it keeps everything (timers, threads) running until the user has closed the dialog.
I understand that showing a UI owned by the process requires the process to keep running. The UI does not need to be owned by my process, however. The dialog presented by FatalAppExit seems to be owned by Windows, even though it keeps the process running as well.
The main problem with FatalAppExit is that it's blocking—I want the dialog but not wait for confirmation—so I still can't terminate right after. To circumvent this, I'd have to start a thread that calls FatalAppExit, wait for a bit and then terminate the process. Not liking the race condition there.
I could also launch a process of my own to show the message, but I'd rather not if Windows can handle it.
Does Windows provide means to both terminate and show a friendly message at the same time? Any other suggestions to handle this as cleanly as possible?
As far as I know the only option here is to start an external process that displays the message and terminate immediately. You could write your own program for that purpose (which might be the easiest option) or you could start a batch file like this:
msg "%USERNAME%" %*
This sends a message to the given user (%USERNAME% is the currently logged on user) and uses all parameters passed to it as the actual message.
Personally I would write my own program, so I could write a log entry and do other stuff if it becomes necessary later.
You can have a global variable, such as
var ShutdownMessage: string;
When you need to terminate with an error, do:
ShutdownMessage := 'Error occured';
Application.Terminate;
and in the dpr file after Application.Run:
if ShutdownMessage <> '' then
MessageBox(0, PChar(ShutdownMessage), 'Error', MB_OK or MB_ICONERROR);
Then the message will be shown after all forms closed and main loop exited.
Or, if that fails, you can try to raise an exception after Application.Run - it should be propagated to OS

Delphi TIdHttp with proxy sometimes no timeout and a GET does not return

I use Delphi 10.2 Tokyo with Indy (the integrated version).
Scenario:
I do http GET requests (TIdHttp) in threads, and use proxies.
Sometimes, a proxy seems to cause Indy not to timeout, and not to return from a GET.
I also assign the onWork event handler to react to an Abort button and call within this handler the idHttp.Disconnect function. When the GET seems to be frozen, the abort also does not work, possible that in this case the onWork event is not triggered, I have no idea.
The main thread is idle and only create lets say 50 threads, each does a GET via its instance of TIdHttp. Ans sometiumes, as I mentioend, a proxy cause of GET not to return which result then in a "hanging" thread.
My question is: How can I force Indy to abort, from an external thread? Is there anything what can be done via code when the GET refuse to return?
I solved my issues by using a background thread to disconnect sockets and implement a timeout, which seems to work even socket are "frozen" and the onWork is not triggered.
I do this by adding the TIdHttp instances I create to an array, together with the time the instance was created. If the GET return normal, the array entry will be removed. In a background thread, I check if the user clicked Abort, and then loop through the array and call disconnect on each instance. I also check in the same thread if a timeout period was reached, and also call disconnect.
Might not be the perfect solution, but it works for me.

How I can create a global exception handler in a DLL?

I am working in a DLL which sometimes raises unhandled exceptions. I am using madExcept to detect and debug the buggy code, but when I finally deploy my DLL I want include my own global exception handler inside the DLL to log the exceptions.
So the question is how I can set a global exception handler in my Delphi DLL?
The concept of "a global exception handler" doesn't really exist in DLLs the way it exists in the VCL. To understand why, remember that exceptions propagate by unwinding the stack until they can find a handler. The VCL can install a global exception handler because in a VCL app, everything that happens (excluding startup and shutdown) will have TApplication.Run somewhere in the call stack, and that's where it puts the exception handler. Since your DLL doesn't have a single central point like that, you can't do it that way.
What you can do is set up a "central exception handler routine" in your DLL somewhere. It should take an Exception object as a parameter. Then do something like this for all your exported routines:
procedure MyExportedRoutine(param: integer);
begin
try
//do normal stuff
except
on E: Exception do
CentralExeptionHandler(E);
end;
end;
That's really the best you can do, unless you're using COM. If you're writing a COM DLL, mark your interface methods with the safecall calling convention and the compiler will silently generate code for you that takes care of exception propagation.
What exactly do you mean by "global exception handler"?
Windows Structured Exception Handling (SEH), in 32-bit, finds a handler by walking the exception handler chain of the thread in which the exception occurred. The exception handler chain is a linked list of records, the head of which is found in FS:[0]; the records are normally allocated on the stack, pushed at every try and popped when the protected block is exited. There's a callback routine referred to by each exception record; Windows calls this routine with details of the exception during its search phase to determine whether or not this "level" of the chain is going to "handle" the exception. Windows then unwinds the call stack to that point by going through the exception chain again, calling each callback with a different value letting it know that unwinding is going on, until it reaches the handler that elected to handle the exception. If no handler is found, the process is terminated, hard, without notification. Normally this doesn't happen; the OS installs a last-chance handler of its own at the bottom of the stack (last element in the chain), and this normally pops up the familiar Windows "this program has encountered a problem" dialog. But if things have gotten very corrupt, or the exception handler chain has been messed around with to remove it, then the process goes down hard.
So from this brief overview of Windows exception handling, it should be clear that there is no single "global" handler, only a list of handlers, one list per thread (FS register is part of the thread context); and the "last chance" handler is the one that is installed earliest in the stack. The easiest way of catching exception that occur inside your DLL is to immediately install an exception handler at every entry point. See Mason's answer for details of how to do that (it's with try/except); but be aware that if your DLL calls back into somewhere else (e.g. via a callback routine), then you may be catching exceptions that weren't "meant" for you and not caused by your code. (It's poor style to expect exceptions to propagate like that through third-party code at the DLL level, but it can happen.)

Is it possible to terminate a TCPClient immediately?

My app sends data to a Server using a TidTCPClient. The Server uses a TidTCPServer. All works fine and I am now trying to handle unexpected situations.
When I disconnect the network cable between server and client, and then try to close the client, it waits for a long time, until it finally closes:
TCPClient.IOHandler.InputBuffer.Clear;
TCPClient.Disconnect;
TCPClient.Free;
TCPClient.Free is the place where it waits. This is sometimes 30 seconds, sometimes even longer.
Is there a way to terminate a TCPClient immediately, no matter what it is doing at that moment?
You should be clearing the InputBuffer after calling Disconnect(), not before.
In any case, there is no reason for a socket to cause such a hang when disconnecting/freeing the TIdTCPClient, unless you have messed around with the socket's LINGER options. So I have to suspect your own code is at fault first. Which version of Indy are you using? Do you have any event handlers assigned to the TIdTCPClient? Have you tried stepping through TIdTCPClient's destructor in the debugger to see what is really happening?
It probably waits for a timeout. Look at the properties of the component to lower the timeout value

try/except doesn't seem to capture exceptions - Delphi Service Application

I have a service written in Delphi 2007 in which I'm trying capture any unknown exceptions. Assigning a method to the on exception doesn't seem to work ('Forms.Application.OnException:=UnknownApplicationException'). The 'UnknownApplicationException' doesn't appear to get called - I attribute this to the fact that there is no form in the application so the method never actually gets assigned. Aside from this, I've also tried creating an exception on a timer (after commenting out 'Forms.Application.OnException:=UnknownApplicationException' so that it does not interfere). The timer triggers 60 seconds after the service has started up:
procedure TProcessScheduler.Timer1Timer(Sender: TObject);
begin
try
Raise Exception.Create('THIS GIG SUCKS');
except
LogEvent(Name,rsUNKNOWN_EXCEPTION,EVENTLOG_AUDIT_FAILURE,0);
ExitCode:=-1;
Halt;
end;
end;
The exception never seems to get captured - the service starts up and after 60 seconds when this timer triggers, I hear a windows error sound but don't see any error dialog - perhaps this could be due to the fact that the application is a service? The 'Halt' never gets called and the application keeps running (i assume its waiting for someone to click ok on the invisible error dialog that it created). Any ideas why the code under the 'except' doesn't get called? Thanks in advance! KP
Reassigning Forms.Application.OnException is a bad idea, as TServiceApplication.Run() does this itself. You either do it before, then your assignment won't have an effect, or you do it afterwards, in which case you remove the exception handling mechanism that has been put into place.
If you leave the handling in place, then all exceptions will be logged to the Windows Event Logger, which seems a reasonable thing to do from a service.
A couple of notes:
As you are raising an exception within a try-except block, it should not trigger any Application.OnException handler, simply because the exception isn't unhandled.
How have you determined that the Halt doesn't get called? Does the exception get logged through your LogEvent?
In a Service application ExitCode and Halt don't function the way you would expect them to in a normal windows application. A service isn't stopped by calling halt, it should be stopped by going through the Windows' Service Control Manager.
If the except part of your try-except block is indeed not reached, it means that Windows has cut in because something has happened that it isn't happy with. That could be something in the LogEvent method you are calling. If that shows a dialog or if that raises an exception as well, the ExitCode and Halt won't be reached.
A service doesn't normally have a desktop associated with it, so showing dialogs isn't going to work.
If you need the service to show dialogs (bad idea by the way, services are intended to run without user interaction), you need to make it interactive and have it run under another user account than the normal "system" account that services run under. You do this through the services manager.
Why are you setting Forms.Application? AFAIK a service uses the Application variable declared in SvcMgr, which is declared as:
var
Application: TServiceApplication = nil;
Moreover a service should not display any dialog, it may not have access to the user desktop, and your dialog will hang the service. There are ways to display a dialog anyway, but services could also run when no human user is watching the screen.
Log events to the event log (or if you don't like it to a file, but the event log has several useful features, including remote access).
I create my own version of the SvcMgr.pas file to eliminate the in-place hook to the Application global exception handler so that I can instantiate my own. I do this because 1) I could find no other simple way of doing this, and 2) since this unit is a stand-alone unit that is only included with Windows Services then the effect on other units is minimal. You can download the code from my web-site to see how this works.

Resources