Why is threadTerminate not called inside a dll - delphi

I have a problem that code inside my dll is acting different compared to the same code within a normal application. After some debugging I found that the thread's OnTerminate is never called within the dll.
type
TTest = class
private
public
procedure threadStart();
procedure threadEnd(Sender: TObject);
procedure lines(value: String);
end;
procedure TTest.threadStart();
var aThread : TThread;
begin
aThread :=
TThread.CreateAnonymousThread(
procedure
begin
lines('start')
end
);
aThread.FreeOnTerminate := True;
aThread.OnTerminate := self.threadEnd;
aThread.Start;
end;
procedure TTest.threadEnd;
begin
lines('end')
end;
procedure TTest.lines(value: String);
var MyText: TStringlist;
begin
MyText:= TStringlist.create;
MyText.Add(value);
MyText.SaveToFile('.\filename.txt');
MyText.Free
end;
If I run this code from a normal VLC Delphi Application, I get end in the text file.
If I run the same code from a dll (loading it either static or dynamic into a VLC Application), I get start in the text file.
My question: Why? Or better asked, how can I let my dll act the same way as my VLC. Current version I'm using is XE7.

The TThread.OnTerminate event is triggered in the context of the main UI thread via a call to TThread.Synchronize(), which stores requests in a queue that the main UI thread checks periodically, executing pending requests when available.
If the DLL and EXE are compiled with Runtime Packages enabled, they share a single copy of the RTL (and thus require you to deploy rtl.bpl with your app). When the EXE checks the RTL's Synchronize() queue, it will see pending requests from both EXE and DLL.
However, if they are not sharing a single RTL, then they will be compiled with separate copies of the RTL that are not linked to each other. By default, there is nothing in the EXE that checks and processes pending requests from the DLL's Synchronize() queue, only from the EXE's Synchronize() queue. To address that, you have to export a function from the DLL that calls the CheckSynchronize() function of the DLL's RTL, and then make the EXE call that exported DLL function periodically, such as in a timer.
Otherwise, the other way to get around this problem is to bypass the Synchronize() call that triggers the OnTerminate event, by overriding the thread's virtual DoTerminate() method (which you cannot do with TThread.CreateAnonymousThread()). You can have DoTerminate() call OnTerminate directly, or just do what you need inside of DoTerminate() itself. But either way, you have to make sure this code is thread safe, as DoTerminate() runs in the context of the worker thread.

Related

Forcing TService OnStop event to wait until some job completes

working with a Windows Service application in Delphi, I stumbled on the issue as in the subject.
I do start a default Delphi Windows Service project on the IDE, follow the wizard and in the end I have a project and a
TService unit. I add to this project another unit, a Data Module (named DM) in which the service code logic is contained.
The DM has a TTimer (design-time) that runs a relatively long job.
Case 1:
DM is created by default in design-time. I have the following code in my TService Start/Stop
procedure TOmegaCAOraNT.ServiceStart(Sender: TService;
var Started: Boolean);
begin
DM.Timer1.Enabled := True;
Started := true;
end;
procedure TOmegaCAOraNT.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
DM.Agent_Stop;
Stopped := true;
end;
When I try to Stop the Service via Windows SCM - in appearance it confirms the Stop, field Status becomes empty - but it does not.
I can see the service .exe still running for a while, and what's more it does terminate the Timer's long job in the middle,
doing part of it only!
This is an undesired behavior!
I fixed this in the second case
Case 2:
DM is created in run-time. The Timer is enabled on DM.OnCreate
I have the following code in my TService Start/Stop
procedure TOmegaCAOraNT.ServiceStart(Sender: TService; var Started: Boolean);
begin
FDataModule := TDM.Create(nil);
Started := true;
end;
procedure TOmegaCAOraNT.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
FreeAndNil(FDataModule);
Stopped := true;
end;
When I try to Stop the Service via Windows SCM - it throws the following Warning:
"Windows could not stop the SERVICE> service on the Local Computer.
The service did not return an error. his could be an internal Windows error or an internal service error.
if the error persists, contact your system Administrator"
field Status remains Started. Timer' long job finishes until the end, and then the service really stops (refresh in SCM to see, Status empty)
This is desired behavior !
My problem is that I would like the DM to be created in design-time, and not in run-time
My question is:
can I have the right behavior of run-time DM (Case 2), with a design-time DM (Case 1) ?
thanks and best regards,
Altin
TService runs in is own worker thread at run-time.
If you configure the DM to be auto-created, it (and its TTimer) will be created in the main thread at run-time, not in the service thread. And thus, the TTimer will run in the main thread, and can be activated only by the main thread, not in the TService.OnStart event handler (an EOutOfResources exception will be raised if you try).
If you manually create the DM in the TService.OnStart event handler, it (and its TTimer) will be created in the service thread, not in the main thread. The TTimer will run in the service thread, and can be (de)activated in the TService events.
Either way, make sure your TTimer.OnTimer event handler uses thread-safe code.
Also, the TService.OnStop event handler must call the TService.ReportStatus() method periodically (before the TService.WaitHint interval elapses) while waiting for other threads to stop whatever they are doing. Which means you shouldn't use thread-blocking code in the TService.OnStop event handler.
You are not handling this correctly, which is why the SCM is having problems.

Why does the doc say not to call Synchronize from the main thread? [duplicate]

I am using CreateAnonymousThread for a worker task, and when I started with it I used Synchronize within the entire declaration as per documented examples, e.g:
procedure Txxx.RunWorker;
begin
FExecutionThread := TThread.CreateAnonymousThread(procedure ()
begin
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
// Here before worker stuff
NotifyBeforeWorkerStuff;
end);
// Do worker stuff
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
// Here after worker stuff
NotifyAfterWorkerStuff;
end);
end);
FExecutionThread.Start;
end;
end;
As you see, from within this thread I launch event notifications to various parts of my app including VCL forms (NotifyBeforeWorkerStuff etc).
Later, I saw that I could move Synchronize() more locally to each VCL form close to the point that actually required it for updating (non-safe) VCL controls:
procedure TSomeVCLForm.ReceiveNotification;
begin
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
Label1.Caption := GetSomeStringFunction;
end);
end;
The worker thread then becomes simpler as long as I live with notifications being from either main or worker threads:
procedure Txxx.RunWorker;
begin
FExecutionThread := TThread.CreateAnonymousThread(procedure ()
begin
NotifyBeforeWorkerStuff;
// Do worker stuff
NotifyAfterWorkerStuff;
end);
FExecutionThread.Start;
end;
I have several questions about whether this is correct:
My notifications may be from the worker thread but also from the main thread (e.g derived from a button press). So, when 'ReceiveNotification' on a VCL form is called from the main thread, is it allowed to call TThread.Synchronize as above? The the XE8 docs imply not, but checking System.Classes this looks ok and it works fine.
Within 'ReceiveNotification' when Label1.Caption fetches the string from GetSomeStringFunction, is it correct that there is absolutely no need for locking within that function even when the call is from a worker thread?
Thanks for any advice.
The documentation says:
Warning: Do not call Synchronize from within the main thread. This can cause an infinite loop.
I think that documentation is simply wrong. The implementation in XE8 checks whether or not the current thread is the main thread. If it is then the method is executed directly.
No locking is required in ReceiveNotification because the call to GetSomeStringFunction is always performed on the main thread.

How to check if the Application.MainForm is valid?

How can I be sure that in some point of my VCL application lifetime the Application.MainForm is valid so I could post a message to it (from a MadExcept ExceptionHandler).
This could be at any point (in the context of any thread) in my application (also initialization, finalization etc...)
I was thinking:
if Assigned(Application)
and (not Application.Terminated)
and Assigned(Application.MainForm)
and Application.MainForm.HandleAllocated then
begin
PostMessage(Application.MainForm.Handle, MyMessage, 0, 0);
end;
Is this correct?
How can I be sure that in some point of my VCL application lifetime the Application.MainForm is valid so I could post a message to it.
OK.
This could be at any point (in the context of any thread) in my application (also initialization, finalization etc...)
Uh oh.
....
Is this correct?
No it certainly is not. Your code can never be made threadsafe because it is not permitted to access VCL objects from outside the main thread.
In your particular case consider the following sequence of events:
You perform your tests in the if culminating in your evaluating Application.MainForm.HandleAllocated as True. Ignore for a moment the fact that you are doing this outside the main thread.
You then set about preparing the call to PostMessage. But at this very instance, the main form is destroyed.
By the time your thread gets round to accessing Application.MainForm, it has gone.
You are going to need to work a little harder here. You'll need to do something like this:
// interface section of some unit
procedure RegisterMainFormHandle(Wnd: HWND);
procedure UnregisterMainFormHandle;
procedure PostMessageToMainForm(...);
// implementation section
var
MainFormHandleLock: TCriticalSection;
MainFormHandle: HWND;
procedure RegisterMainFormHandle(Wnd: HWND);
begin
MainFormHandleLock.Acquire;
try
MainFormHandle := Wnd;
finally
MainFormHandleLock.Release;
end;
end;
procedure UnregisterMainFormHandle;
begin
MainFormHandleLock.Acquire;
try
MainFormHandle := 0;
finally
MainFormHandleLock.Release;
end;
end;
procedure PostMessageToMainForm(...);
begin
MainFormHandleLock.Acquire;
try
if MainFormHandle <> 0 then
PostMessage(MainFormHandle, ...)
finally
MainFormHandleLock.Release;
end;
end;
You also need to create and destroy the critical section, but I assume that you know how to do that.
In your main form you override CreateWnd and DestroyWnd and arrange that they call RegisterMainFormHandle and UnregisterMainFormHandle.
Then you can call PostMessageToMainForm from any thread at any time.
Of course, if the main form's window is recreated then you'll lose some messages. Which sounds like it could be a problem. Using AllocateHwnd to have a window whose lifetime you control is usually a better option than using the main form's window like this.
Make some global variable flag = false at the beginning.
Make your mainform turn it to true.
Check that flag to see if main form was already initialised or not yet.
You can make it from such places as mainform's OnActivate event or overridden TMainForm.Loaded method
Similarly when your application would be terminating and the mainform would get hidden (and later even destroyed) - you would reset the flag back to false

How to execute not TThread based thread in main thread?

For example from thread provided by callback from CreateTimerQueueTimer in executable or dll? It is significant to have same thread id as main thread.
procedure TMyMainClass.ExecuteMe(someparam: paramtype);
begin
{something}
end;
and
procedure TimerCallback(pvContext: pointer; fTimerOrWaitFired: boolean); stdcall;
begin
{do what ?}
end;
Final update:
All this stuff (TThread.Synchronize, TThread.Queue, PostThreadMessage etc) works through messages. So be sure host application of your dll processing messages while waiting for callback.
To execute code in the main thread, without access to a TThread instance, call the class methods TThread.Synchronize or TThread.Queue.
If you happen to be using an old Delphi compiler that does not have those methods, then SendMessage or PostMessage with a user defined message are the simplest solution.

How can i tell if i'm being called during DLL_PROCESS_DETACH after ExitProcess has been called?

i have a unit in Delphi that (has the option) to provide a single global object:
var
InternalThemeParkServices: TThemeParkServices;
function ThemeParkServices: TThemeParkServices;
begin
if InternalThemeParkServices= nil then
InternalThemeParkServices := TThemeParkServices.Create();
Result := InternalThemeParkServices ;
end;
...
initialization
finalization
FreeAndNil(InternalThemeServices);
end.
And i destroy myself during process shutdown.
Note: Another code variant is:
var
InternalThemeParkServices: IThemeParkServices;
function ThemeParkServices: TThemeParkServices;
begin
if InternalThemeParkServices= nil then
InternalThemeParkServices := TThemeParkServices.Create();
Result := InternalThemeParkServices ;
end;
Where the interface variable is implicitly destroyed when it's
reference count goes to zero during program shutdown
When my object is no longer used (i.e. during its destructor), i call call various WinAPI functions.
The problem is that if someone uses my class from a DLL (something which i cannot control), then anything being called during:
finalization
is the Delphi moral equivalent of DLL_PROCESS_DETACH. There are all kinds of things i should not be doing during DLL_PROCESS_DETACH when the process is terminating (e.g. CoCreateInstance).
i know Embarcadero uses:
initialization
if not IsLibrary then
begin
...
Which i perhaps i could adapt, changing my code from:
var
InternalThemeParkServices: IThemeParkServices;
(using implicit cleanup), to:
var
InternalThemeParkServices: IThemeParkServices;
...
finalization
if IsLibrary then
Pointer(InternalThemeParkServices) := nil; //defeat reference counting and let the object leak
end;
and let it leak.
But is this the best resolution? i assume it means that if the dll running my code is unloaded (but not during process shutdown) that i'll leak memory. If the dll is attached and detached, i'll leak each time.
What i really want is Delphi to run its finalization blocks before ExitProcess/DllMain(DLL_PROCESS_DETACH). Is this possible?
Bonus Chatter
#pa deciphered the Delphi application shutdown scheme:
The hierarchy of shutdown is as follows
Application.Terminate()
performs some unidentified housekeeping of application
calls Halt()
Halt()
calls ExitProc if set
alerts the user in case of runtime error
get rid of PackageLoad call contexts that might be pending
finalize all units
clear all exception handlers
call ExitprocessProc if set
and finally, call ExitProcess() from 'kernel32.dll'
ExitProcess()
unloads all DLLs
uses TerminateProcess() to kill the process
With DLLs being unloaded after a call to ExitProcess - because Windows is the one who does it.
To tell if you're being called during DLL_PROCESS_DETACH after ExitProcess has been called, you can write initialization code for your library so that your code is executed when FreeLibrary is called from the main program. The 'lpReserved' parameter will be '1' if ExitProcess have already been called, '0' otherwise:
..
var
SaveDllProcEx: TDllProcEx;
procedure DllMainEx(Reason: Integer; Reserved: Integer);
begin
if (Reason = DLL_PROCESS_DETACH) and (Reserved = 0) then
// Main app is still running, cleanup.
if Assigned(SaveDllProcEx) then
SaveDllProcEx(Reason, Reserved);
end;
initialization
if IsLibrary then begin
SaveDllProcEx := DllProcEx;
DllProcEx := #DllMainEx;
end;
From DllMain entry point:
The lpReserved parameter indicates whether the DLL is being unloaded
as a result of a FreeLibrary call, a failure to load, or process
termination.
What I really want is Delphi to run its finalization blocks before DllMain(DLL_PROCESS_DETACH). Is this possible?
No it is not possible.
If you need to perform shutdown actions that cannot be done during DllMain(DLL_PROCESS_DETACH) then you will need to add an exported function to your DLL that peforms the finalization. You should then require your DLL's clients to call this function before unloading the DLL. This is the same pattern as CoInitialize/CoUninitialize.

Resources