I have never made a *.DLL before
I need my *.DLL to use a timer that when the DLL is accessed by an application it sets the timer off and the time gives a delay stops any new Thread from the application and saves a file and when saved any application thread is released.
TTimer is under Vcl.ExtCtrls file and its a TComponent descendant so do I have any windows resources issues or complications with TTimer in the DLL?
Is their better ways to do this like start a 2nd thread and place a pause on that thread before using it that its effectively a timer for my use or does that use resources up?
By default, just creating a TTimer in a DLL won’t work as TTimer depends on the processing of a message loop. You may be better off with WaitForSingleObject or other synchronization objects.
But first, the details of the tasks to perform isn’t quite clear to me, could you add some detail around the expected interplay of DLL / application?
I need my *.DLL to use a timer of some type that when the DLL is accessed by an application thread, it sets the timer off and the timer gives a delay before activating, it then stops any new Thread from the application using the *.DLL and saves a file and when finished saving any application thread is released to use the DLL.
There is CreateWaitableTimer or possibly even just Sleep and you could use a TSemaphore to limit only one thread to access particular code.
In any case you likely don’t want the main UI thread to hang as this will make the UI unresponsive, which would likely happen if you do as you have described.
Here is an example of using a queue and a threadpool, which may assist
Your description is still a bit too unclear, so it is hard to be specific.
Eivind Bakkestuen
says to stay away from a timer in a *.DLL because of many complex issues
I think a TTimer needs a form to get a handle if I remember and so the overhead is heavy?
So I’ve been thinking
1/ Start a thread object in my DLL
2/ as the application thread finishes it records its action in the DLL and starts the DLL thread as it leaves
3/ The DLL thread is delayed with a pause (that you can do in threads)
4/ Should the application thread return to the DLL to do another task, it first stops the DLL thread, the application thread when finishes it records its action making any number of actions in the DLL and restarts the DLL thread that the pause starts all over again.
5/ When the DLL thread gets pass the pause it stops any new application thread using the DLL and does the job it needs and goes through the list of tasks and when finished releases any application thread should there be one in the DLL and closes down.
No
A thread runs from the application to ask for a task
and on the return to the application The DLL I’m making needs a new internal thread, and the new thread has a pause as the application may use my DLL many times within a short time frame. Every time My DLL gets used this thread in the DLL needs to restart with a new pause if the thread is in the pause state.
Then when the thread pause over it needs to block any new application thread and do it job. When the job is finished it releases any application thread and finishes its self.
I know I have a pause
But can the DLL support its own thread ?
Do I need a application to make a thread for the DLL and be messier
Reading between the lines what I think you need is for your DLL to queue up the requests from the application that are made in a period of time before processing them, and by “block any new application thread” I think you mean lock the request queue during processing.
I would go about it by creating a worker thread when the DLL is loaded by the application and free the thread when the DLL is unloaded by the application. An exported function in the DLL can add each request to a queue and tell the worker thread that a request has been added. The application will call that function to make requests. The thread can use TEvent to handle both the wait time before processing requests and for notifications to the thread. Note that a notification needs to be sent to the thread when your DLL is being unloaded to tell the thread to terminate (break the “pause”).
This is a bit tricky if you haven’t worked with DLLs, threads, or TEvent before. This has the guts of a basic solution:
library MyDLL;
uses
System.SysUtils,
System.Classes,
System.SyncObjs,
Windows;
type
TWorkerThread = class(TThread)
private
FEvent: TEvent;
FLock: TCriticalSection;
protected
procedure Execute;
public
constructor Create;
destructor Destroy;
procedure AddRequest(<something>);
procedure Notify;
end;
var
GWorkerThread: TWorkerThread;
constructor TWorkerThread.Create;
begin
inherited Create;
FEvent := TEvent.Create;
FLock := TCriticalSection.Create;
end;
destructor TWorkerThread.Destroy;
begin
FLock.Free;
FEvent.Free;
inherited;
end;
procedure TWorkerThread.Execute;
begin
while not Terminated do
begin
if <HaveRequestsToProcess> then
begin
// Wait a while for other requests. If more are added or we are signalled to stop
// then the result is wrSignalled and we will reset the time or quit. If the time
// expires then we can process the requests
if FEvent.WaitFor(<PauseTime>) = wrTimeout then
ProcessRequests;
end
else
FEvent.WaitFor(INFINITE);
FEvent.ResetEvent;
end;
end;
procedure TWorkerThread.Notify;
begin
FEvent.SetEvent;
end;
procedure TWorkerThread.AddRequest(<something>);
begin
FLock.Acquire;
try
// Add the quest to a queue here (e.g. private TList<TRequest>)
Notify;
finally
FLock.Release;
end;
end;
procedure TWorkerThread.ProcessRequests;
begin
FLock.Acquire;
try
// Process the requests here
finally
FLock.Release;
end;
end;
procedure DllMain(Reason: Integer);
begin
if (Reason = DLL_PROCESS_ATTACH) and not Assigned(GWorkerThread) then
GWorkerThread := TWorkerThread.Create
else if (Reason = DLL_PROCESS_DETACH) and Assigned(GWorkerThread) then
begin
GWorkerThread.Terminate; // Signal to stop
GWorkerThread.Notify; // Instantly break the pause
GWorkerThread.WaitFor; // Wait for thread to actually stop
FreeAndNil(GWorkerThread); // Clean up the thread object
end;
end;
procedure AddRequest(<something>);
begin
if Assigned(GWorkerThread) then
GWorkerThread.AddRequest(<something>);
end;
exports
AddRequest;
begin
DllProc := @DllMain;
DllMain(DLL_PROCESS_ATTACH);
end.
The lock is for thread safety but will have the effect of pausing new requests from the application until the existing requests are processed. If HaveRequestsToProcess involves inspecting the requests (e.g. not RequestList.IsEmpty) then you should acquire the lock while doing that.
Its an application
most times applications have one thread - but I cannot control that
The application is likely to call my *.DLL many times in quick succession and then likely to pause in use.
How applications call my *.DLL I have no control over at all
I can introduce one thread at a time running through the *.DLL - do you advice that?
Their are many functions/procedures available to enter my *.DLL but they all being using a function inside my *.DLL to simplify things and to add a leaving function inside my *.DLL is not hard.
You are right on track with what I need - I have a TStringList of things to up date from Application thread access
this is stable to use in a *.DLL
procedure TWorkerThread.AddRequest(<something>);
Is to start the *.DLL thread that I can do as the application thread leaves my *.DLL
correct?
I have not used threads much but have had a play and not to hard to use.
I write object events all the time easy.
This is my first *.DLL so: -
exports
AddRequest;
Very very new stuff to me and I have learned I have to convert to ‘C’ for other applications to use my *.DLL
I just do not know what I can do and what I cannot do in Delphi with a *.DLL
Like example - when you ask for the windows file dialog from on screen - is it a *.DLL or is it a software interrupt or something else that the application asks for?
I can understand hidden object are ok in *.DLL’s, but TControl descendants and canvas handles I have no idea about in *.DLL’s
example is a sloping line on a canvas that does not have a hacksaw edge, or an arc with a smooth edge we have no Delphi support for that I know of? can this be put in a *.dll?
I asked for some timer/thread because that is my topic of concern at the moment and yes this can work I think?
There’s plenty in the old, thick books about DLLs … I’m sure you can find any number of versions of Marco Cantu’s books … they have chapters on “Dynamic Link Libraries”.
You do realise that each application “instance” that loads your dll will get it’s own copy of the dll in memory right?. So each application would have it’s own thread processing things.
Is to start the *.DLL thread that I can do as the application thread leaves my *.DLL
correct?
The worker thread is automatically created (and started) when the application loads the DLL. What happens is that when the application loads or unloads the DLL it calls our DllMain method (this was set up in the begin/end block at the end of the code). DllMain creates the thread when the DLL is loaded (attached to the process) and frees the thread when the DLL is unloaded (detached from the process).
The exported AddRequest method just calls the worker thread AddRequest method (it is good practice to keep all request queue and locking code within the worker thread class) which locks the queue, adds the request to the queue (insert your code), notifies the thread that it needs to do something, and unlocks the queue. The worker thread will break the pause immediately when notified but then block when trying to acquire the lock to check if there are requests to process or to actually process the requests. Once the application has finished executing AddRequest its lock is released and the worker thread will acquire the lock and do the work.
Note that it is the application’s thread that is executing the AddRequest methods. The fact that the second AddRequest method is defined within the worker thread class has nothing to do with which thread is executing it. The worker thread is just continually running the Execute method to look for things to do, simultaneous to the application thread executing AddRequest or doing its own thing. The lock co-ordinates access to the request queue between the two threads and the event lets the application thread notify the worker thread of something to do and conveniently lets us implement the pause to queue up requests.
One thing I left out in the example code is that you should add stdcall; to the end of the exported AddRequest method. That makes it possible to call it from non-Delphi applications which seems to be the case here. That changes the way that Delphi methods are called to make them work like most other languages. There should be no need to “convert to ‘C’” - you can do everything in Delphi but strings used in params are best done using PChar or PAnsiChar depending on the application because Delphi strings won’t be directly supported by the language used to create the app.
procedure AddRequest(<something>); stdcall;
begin
...
I can understand hidden object are ok in *.DLL’s, but TControl descendants and canvas handles I have no idea about in *.DLL’s
You can do pretty much everything in a Delphi DLL. How you go about it depends on the specific requirements. Yes it is possible to use a canvas and draw anti-aliased lines.
vincentVincent ParrettForum Wrangler
I can pause the thread in the DLL as I need that fine
this stdcall; on the end of functions that have ShortStrings and Integers… is some thing that I will play with in a hash project to get right first my self I think
This project has much for me to learn and has been good flexing of the brain for me
I think this is the best way for me as when I have done this part - its all about removing the bugs to finish it up to a good standard
Paul_McGeeCommittee
ok you can put forms in *.dll’s big time so I could use TTimer but dose that require a form that is not visible? a lot of overhead for a thread I guess. I mite look to a book as you say that I did not think of.
The support video’s like this only make the one procedure link
Did not cover stdcall; and said little about the
a) DLL interface to the application that was the heart of the subject in my mind,
b) only covered his example and not about what you can and cannot do in a DLL,
But its good that he talked through with a voice of what he was doing
I believe a well designed video series by this man can be an asset to Delphi -
he needs help to deliver the right continent to be very good.