Service application in Delphi - delphi

I am struggling with service application in Delphi but no major success so far. I tried to recreate this project, but it doesn't seem to work properly. File is created, but date and time aren't added to file every 10 seconds. I also don't see a message popping up from my ShowMessage. I successfully install and start service application.
Here is my code:
unit TMS;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs,
Vcl.ExtCtrls;
type
TWorkflow = class(TService)
Timer1: TTimer;
procedure ServiceExecute(Sender: TService);
procedure Timer1Timer(Sender: TObject);
procedure ServiceBeforeInstall(Sender: TService);
private
{ Private declarations }
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
Workflow: TWorkflow;
implementation
{$R *.dfm}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Workflow.Controller(CtrlCode);
end;
function TWorkflow.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TWorkflow.ServiceBeforeInstall(Sender: TService);
begin
Interactive := True;
end;
procedure TWorkflow.ServiceExecute(Sender: TService);
begin
while not Terminated do
begin
ServiceThread.ProcessRequests(True);
end;
end;
procedure TWorkflow.Timer1Timer(Sender: TObject);
const
FileName = 'D:\Projekti\EMBRACADERO\TMSWorkflow\Win32\Debug\Log.txt';
var
F : TextFile;
begin
AssignFile(F, FileName);
if FileExists(FileName) then
Append(F)
else
Rewrite(F);
Writeln(F, DateTimeToStr(Now), ' ', DiskFree(0));
ShowMessage(DateTimeToStr(Now));
CloseFile(F);
end;
end.
Could somebody give me an example of a service application with threads maybe or service with visual components included?
UPDATE1:
It is working with following code for inserting some data in database every 3 seconds.
private
thread : TThread;
procedure TWorkflow.InsertInDatabase;
begin
FDTransaction1.StartTransaction;
try
FDQuery1.Execute;
FDTransaction1.Commit;
except
FDTransaction1.Rollback;
end;
end;
procedure TWorkflow.ServiceExecute(Sender: TService);
begin
while not Terminated do
begin
ServiceThread.ProcessRequests(False);
InsertInDatabase();
thread.sleep(3000);
end;
end;
procedure TWorkflow.ServiceStart(Sender: TService; var Started: Boolean);
begin
thread := TThread.Create;
end;
procedure TWorkflow.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
FreeAndNil(thread);
end;

The TTimer code you showed is fine (though your OnExecute event is redundant can should be completely removed), except for the call to ShowMessage(), which you cannot use in a service at all (the TService.Interactive property has no effect on Windows Vista+). If you must display a popup message box from a service (which you should strive not to), you must use the Win32 API MessageBox() with the MB_SERVICE_NOTIFICATION flag specified, or use WTSSendMessage() instead. Otherwise, you have to delegate any UI to a separate non-service process that the service spawns and/or communicates with as needed.
Your TThread code, on the other hand, is completely wrong. It should be more like this instead:
unit TMS;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.SvcMgr;
type
TWorkflowThread = class(TThread)
protected
procedure Execute; override;
end;
TWorkflow = class(TService)
FDTransaction1: TFDTransaction;
FDQuery1: TFDQuery;
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
procedure ServiceShutdown(Sender: TService);
private
{ Private declarations }
thread: TWorkflowThread;
procedure InsertInFile;
procedure InsertInDatabase;
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
Workflow: TWorkflow;
implementation
{$R *.dfm}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Workflow.Controller(CtrlCode);
end;
function TWorkflow.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TWorkflow.InsertInFile;
const
FileName = 'D:\Projekti\EMBRACADERO\TMSWorkflow\Win32\Debug\Log.txt';
var
F : TextFile;
begin
try
AssignFile(F, FileName);
try
if FileExists(FileName) then
Append(F)
else
Rewrite(F);
Writeln(F, DateTimeToStr(Now), ' ', DiskFree(0));
//ShowMessage(DateTimeToStr(Now));
finallly
CloseFile(F);
end;
except
end;
end;
procedure TWorkflow.InsertInDatabase;
begin
try
FDTransaction1.StartTransaction;
try
FDQuery1.Execute;
FDTransaction1.Commit;
except
FDTransaction1.Rollback;
end;
except
end;
end;
procedure TWorkflow.ServiceStart(Sender: TService; var Started: Boolean);
begin
thread := TWorkflowThread.Create(False);
Started := True;
end;
procedure TWorkflow.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
ServiceShutdown(Sender);
Stopped := True;
end;
procedure TWorkflow.ServiceShutdown(Sender: TService);
begin
if Assigned(thread) then
begin
thread.Terminate;
while WaitForSingleObject(thread.Handle, WaitHint-100) = WAIT_TIMEOUT do
ReportStatus;
FreeAndNil(thread);
end;
end;
procedure TWorkflowThread.Execute;
begin
while not Terminated do
begin
Workflow.InsertInFile;
Workflow.InsertInDatabase;
TThread.Sleep(3000);
end;
end;
end.

Your timer code will not execute because timers rely on a window handle and message pump that TService does not provide. Furthermore, TTimer is not thread-safe because in uses the VCL's AllocateHwnd() function which is not thread-safe and should not be used outside the context of the main thread. Generally, when writing service applications you would spawn a worker thread to perform the main logic.
If you need a thread-safe timer, I would suggest you use a different timer mechanism, such as WaitForSingleObject()
Assitionally, services should not contain any visual controls as they should not interact with the desktop at all.

Could somebody give me an example of service application with threads.
If your code is doing all its work in a thread, you are almost done.
Just start your thread in the service start event. For debugging, run the thread in a small (console) program.
Instead of a timer, let your main thread sleep for a while.

Related

Why does the service OnCreate get called multiple times?

The code below, via...
procedure TTimetellServiceServerMonitor.ServiceDebugLog(const AMsg: String);
const cDebugLogFile = 'd:\temp\service.log';
... outputs this debug info showing that we go through the OnCreate several times (I added the - - descriptions):
- testsvcserverMonitor /install -
S 1802 servicecreate
S 1802 AfterInstall
- start from services app -
S 1741 servicecreate
S 1741 servicestart
S 1741 MonitorThread.Start
- stop from services app -
S 1741 servicestop
- testsvcserverMonitor /uninstall -
S 1336 servicecreate
S 1336 beforeuninstall
I assign a random tag value to the service in its OnCreate and you can see that these are different.
Why does this happen, is there a bug, should I prevent it and how?
(Windows 32 bit, Delphi 10.4.2. Sydney)
.pas code:
unit USvcServerMonitor;
interface
uses
WinApi.Windows, WinApi.Messages, System.SysUtils, System.Classes, Vcl.SvcMgr, WinApi.WinSvc;
type
TMonitorServiceThread = class(TThread) // Worker thread
private
FCheckLiveEvery,
FLastLiveCheck : TDateTime;
public
procedure Execute; override;
end;
type
TApplicationMonitor = class(TService)
procedure ServiceBeforeUninstall(Sender: TService);
procedure ServiceCreate(Sender: TObject);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
// procedure ServiceExecute(Sender: TService); Not necessary, WorkerThread does the work
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceAfterInstall(Sender: TService);
private
procedure ServiceDebugLog(const AMsg: String);
public
function GetServiceController: TServiceController; override;
end;
var
MonitorThread : TMonitorServiceThread;
ApplicationMonitor: TApplicationMonitor;
implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
ApplicationMonitor.Controller(CtrlCode);
end;
function TApplicationMonitor.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TApplicationMonitor.ServiceAfterInstall(Sender: TService);
begin
ServiceDebugLog('AfterInstall');
// StartType is stAuto, but start manually after install
end;
procedure TApplicationMonitor.ServiceBeforeUninstall(Sender: TService);
begin
ServiceDebugLog('beforeuninstall');
end;
procedure TApplicationMonitor.ServiceCreate(Sender: TObject);
begin
Self.Tag := 1000 + Random(1000); // For debugging
ServiceDebugLog('servicecreate');
end;
procedure TApplicationMonitor.ServiceStart(Sender: TService; var Started: Boolean);
begin
ServiceDebugLog('servicestart');
MonitorThread := TMonitorServiceThread.Create(true); // Suspended
ServiceDebugLog('MonitorThread.Start');
MonitorThread.Start;
Started := true;
end;
procedure TApplicationMonitor.ServiceDebugLog(const AMsg: String);
// Quick-n-dirty debugging routine
const cDebugLogFile = 'd:\temp\service.log';
var t: textfile;
begin
if not fileexists(cDebugLogFile) then
begin
assignfile(t,cDebugLogFile);
Rewrite(t);
end
else
begin
assignfile(t,cDebugLogFile);
Append(T);
end;
writeln(T,'S ' + Inttostr(self.Tag) + ' ' + AMsg);
closefile(t);
end;
procedure TApplicationMonitor.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
ServiceDebugLog('servicestop');
MonitorThread.Terminate;
Sleep(100);
MonitorThread.Free;
Sleep(100);
Stopped := True;
end;
{ TMonitorServiceThread }
procedure TMonitorServiceThread.Execute;
begin
inherited;
FLastLiveCheck := Now;
FCheckLiveEvery := 1;
while not Terminated do
begin
try
if (FCheckLiveEvery > 0) and (Now-FLastLiveCheck > FCheckLiveEvery/1440) then
begin
// Do some checks
FLastLiveCheck := Now;
end;
Sleep(500);
finally
end;
end;
end;
end.
.dfm file:
object ApplicationMonitor: TApplicationMonitor
Tag = 123
OldCreateOrder = False
OnCreate = ServiceCreate
AllowPause = False
DisplayName = 'Test Application Monitor Service'
AfterInstall = ServiceAfterInstall
BeforeUninstall = ServiceBeforeUninstall
OnStart = ServiceStart
OnStop = ServiceStop
Height = 250
Width = 400
end
TService is derived from TDataModule, so OnCreate will be called when the TService instance is created. That obviously happens when the service is going to be started, but also when it is installed and uninstalled.
So, no, it is not a bug and you also should not prevent it.
Perhaps it is just that your expectations are wrong?

Terminate a 'sleeping' thread

I have a task I want to run in the background and not interrupt the GUI thread to check for new program versions.
When the application starts, it immediately queues a thread to wait 15 seconds then execute the remainder of the code. If the check is manually triggered before the 15 seconds are up, the existing automatic thread should be terminated. If the application is closed at any point, any remaining threads should be terminated as soon as possible.
I use 2 minutes in the example below for easier debugging.
My issue right now is that no combination that I've tried using WaitFor, Terminate, FreeOnTerminate, and OnTerminated will get the desired result. Either Destroy isn't called and I get memory leaks, the application hangs when terminating a thread, or I get Cannot terminate externally created thread exceptions.
Thread code
unit unCheckThread;
interface
uses SysUtils, Classes, SyncObjs, Dialogs;
type
TCheckThread = class(TThread)
private
FDelayEvent: TEvent;
FDelay: Integer;
public
constructor Create(const ADelay: Integer);
destructor Destroy; override;
procedure Execute; override;
procedure TerminatedSet; override;
end;
implementation
{ TCheckThread }
constructor TCheckThread.Create(const ADelay: Integer);
begin
inherited Create(True);
FreeOnTerminate := False;
FDelay := ADelay;
FDelayEvent := TEvent.Create(nil, True, False, '');
end;
destructor TCheckThread.Destroy;
begin
FDelayEvent.Free;
inherited;
end;
procedure TCheckThread.Execute;
begin
FDelayEvent.WaitFor(MSecsPerSec * FDelay);
{ if another thread has checked while waiting for the delay, cancel this check }
if Terminated then Exit;
{ some long running code }
Sleep(10000);
if Terminated then Exit;
Synchronize(
procedure()
begin
MessageDlg('Thread completed', mtConfirmation, [mbOK], 0);
end);
end;
procedure TCheckThread.TerminatedSet;
begin
FDelayEvent.SetEvent;
end;
end.
UI
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, unCheckThread, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
AThread: TCheckThread;
procedure onterminate(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(AThread) then begin
AThread.Terminate;
// AThread.WaitFor; // ?
end;
AThread := TCheckThread.Create(0); // start immediately
AThread.OnTerminate := onterminate;
AThread.Start;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Assigned(AThread) then begin
AThread.Terminate;
// AThread.WaitFor; // ??
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
AThread := TCheckThread.Create(SecsPerMin * 2); // wait for 2 mintues before starting
end;
procedure TForm1.onterminate(Sender: TObject);
begin
FreeAndNil(AThread);
end;
end.
You can't interrupt a Sleep(), so that is not a good choice for debugging, especially for long intervals. You should use FDelayEvent.WaitFor() instead. That way, the thread can "wake up" quickly when it is being terminated.
Also, instead of calling Synchronize() at the end of Execute(), you should use the OnTerminate event instead for any operations you need when the thread is finished. OnTerminate is already synchronized with the main UI thread.
For instance, you can use OnTerminate to set your AThread variable to nil when that thread is terminated, so that your Assigned(AThread) check doesn't fail. You are already trying to do this, however you can't safely Free a thread from inside its OnTerminate event handler, so you might consider using FreeOnTerminate=True instead, or at least delay the Free until after the handler exits.
For that matter, I would not suggest creating a delayed thread at app startup to begin with. Use a TTimer instead. When its OnTimer event is fired, THEN create a non-delayed thread. If the user triggers a manual check, simply disable the timer. This way, you don't waste resources creating a thread that just sits idle, and you don't have to worry about syncing multiple threads to each other.
With that said, try something more like this:
unit unCheckThread;
interface
uses
Classes;
type
TCheckThread = class(TThread)
protected
procedure Execute; override;
public
constructor Create(AOnTerminate: TNotifyEvent);
end;
implementation
uses
SysUtils;
{ TCheckThread }
constructor TCheckThread.Create(AOnTerminate: TNotifyEvent);
begin
inherited Create(False);
FreeOnTerminate := False;
OnTerminate := AOnTerminate;
end;
procedure TCheckThread.Execute;
var
I: Integer;
begin
{ some long running code }
for I := 1 to 20 do
begin
if Terminated then Exit;
Sleep(500);
end;
end;
end.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, unCheckThread;
const
WM_FREE_THREAD = WM_APP + 1;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Timer1: TTimer;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
AThread: TCheckThread;
procedure ThreadFinished(Sender: TObject);
procedure StartThread;
procedure StopThread;
procedure WMFreeThread(var Message: TMessage); message WM_FREE_THREAD;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
StartThread;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Interval := 120000; // wait for 2 minutes before starting
Timer1.Enabled = True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
StopThread;
end;
procedure TForm1.StartThread;
begin
Timer1.Enabled := False;
if not Assigned(AThread) then
begin
AThread := TCheckThread.Create(ThreadFinished);
Button1.Enabled := False;
end;
end;
procedure TForm1.StopThread;
begin
if Assigned(AThread) then
begin
AThread.OnTerminate := nil;
AThread.Terminate;
AThread.WaitFor;
FreeAndNil(AThread);
end;
end;
procedure TForm1.ThreadFinished(Sender: TObject);
begin
AThread := nil;
// in 10.2 Tokyo and later, you can use TThread.ForceQueue() instead...
// TThread.ForceQueue(nil, Sender.Free);
PostMessage(Handle, WM_FREE_THREAD, 0, LPARAM(Sender));
MessageDlg('Thread completed', mtConfirmation, [mbOK], 0);
Button1.Enabled := True;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
StartThread;
end;
procedure TForm1.WMFreeThread(var Message: TMessage);
begin
TObject(Message.LParam).Free;
end;
end.
If:
for whatever reason, you want to keep the structure of your code ...
when you said "If the application is closed at any point, any remaining threads should be terminated as soon as possible." -> it means that we do not really care about the result of any process being run/worked on by the thread ...
Then:
this won't be a "clean", academic, software engineering kind of way
this is just an alternative to simply get things done (targeting 2 objectives: "killing"/terminating a thread and avoiding memory leaks)
Keep your line of code below.
FreeOnTerminate := False;
"Kill"/terminate the thread (regardless of its state).
TerminateThread(AThread.Handle, 0);
TForm1 has/owns AThread, and because your code may re-create AThread (in FormCreate then in Button1Click), free the thread every time right after it has been terminated (currently, you do this in Button1Click and FormClose). Just in case you do if Assigned(AThread) anywhere else, use FreeAndNil (it frees AThread and sets it to nil, this way Assigned(AThread) can correctly return False when the thread has been terminated and freed using your code structure).
FreeAndNil(AThread);
The TerminateThread method (above) needs Winapi.Windows (which you have had in the uses list of your Unit1). It replaces AThread.Terminate.
It would look like below:
if Assigned(AThread) then begin
TerminateThread(AThread.Handle, 0);
FreeAndNil(AThread);
end;

Delphi - How do I ensure proper and reliable termination of thread without memory loss

Situation: I have a program that initiates a thread to run continuously in the background. When I terminate this thread, I intermittently get a Runtime Error 204 (Invalid Pointer) and memory leaks.
Code to Create the Thread (it is created in an object TJEList that has a method OnLJ2DOSyncThreadNotification to be called on notification that performs some actions using some of the TStringList objects in the thread):
FLJ2DOSyncThread:=TLJ2DOSyncThread.Create (True);
FLJ2DOSyncThread.NotifyEvent:=OnLJ2DOSyncThreadNotification;
FLJ2DOSyncThread.Start;
The Thread's Execute code:
FreeOnTerminate:=True;
//I create 5 StringList objects here - they are declared as private variables in the Thread
try
while Not Terminated do
begin
//Perform operation (which internally also checks for Terminated)
if Terminated then
Break;
//Perform different operation (which internally also checks for Terminated)
if Terminated then
Break;
//Perform different operation (which internally also checks for Terminated)
if Terminated then
Break;
//etc...
Sleep (1500);
end;
finally
//I FreeAndNil(...) all the TStringlist objects
end;
For good measure (although likely redundant), I also have a destructor in the thread that does:
if Assigned (TStringList object) then FreeAndNil (TStringList object);
for all of the TStringList objects that were created (and then calls inherited;).
My code to stop the thread is:
if Assigned(FLJ2DOSyncThread) then
if FLJ2DOSyncThread.Started then
FLJ2DOSyncThread.Terminate;
I call the code to stop the thread from the destructor of the TJEList object (which gets called on the closure of the main form of the application).
The Issue:
Sometimes, the program terminates cleanly (no memory leaks, or error messages). Other times, I get the following error message and memory leak (for what it's worth, the memory leak message appears before the Runtime error message):
My Question: How do I ensure that the thread always reliably terminates (and therefore gets freed)? Any help and/or guidance would be most appreciated!
UPDATE 20170310 5.08pm HKT: Including the code for MCVE as requested
Program Code:
program ThreadIssueMCVE;
uses
System.StartUpCopy,
FMX.Forms,
frmMain in 'frmMain.pas' {fMain},
MyList in 'MyList.pas',
MyThread in 'MyThread.pas';
{$R *.res}
begin
{$IFDEF DEBUG}
System.ReportMemoryLeaksOnShutdown:=true;
{$ENDIF}
Application.Initialize;
Application.CreateForm(TfMain, fMain);
Application.Run;
end.
Main Form Code:
unit frmMain;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, MyList,
FMX.Controls.Presentation, FMX.StdCtrls;
type
TfMain = class(TForm)
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FMyList: TMyList;
public
{ Public declarations }
end;
var
fMain: TfMain;
implementation
{$R *.fmx}
procedure TfMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FreeAndNil(FMyList);
end;
procedure TfMain.FormCreate(Sender: TObject);
begin
FMyList:=TMyList.Create;
end;
end.
MyList Code:
unit MyList;
interface
uses
System.Classes, System.SysUtils, MyThread;
type
TMyList = Class (TObject)
private
FSomeList: TStringList;
FMyThread: TMyThread;
protected
procedure OnMyThreadNotification (Sender: TObject);
procedure OnMyThreadTerminate (Sender: TObject);
procedure ActOnThreadResults (AList: TStringList);
public
procedure InitMyThread;
procedure StopMyThread;
constructor Create;
destructor Destroy; override;
property
SomeList: TStringList read FSomeList;
end;
implementation
{ TMyList }
constructor TMyList.Create;
begin
inherited Create;
FSomeList:=TStringList.Create;
InitMyThread;
end;
destructor TMyList.Destroy;
begin
StopMyThread;
FreeAndNil(FSomeList);
inherited Destroy;
end;
procedure TMyList.ActOnThreadResults (AList: TStringList);
var
i: Integer;
begin
for i:= 0 to AList.Count-1 do
begin
if FMyThread.CheckTerminated then
exit;
FSomeList.Add(AList.Strings[i]);
end;
end;
procedure TMyList.InitMyThread;
begin
FMyThread:=TMyThread.Create (True);
FMyThread.NotifyEvent:=OnMyThreadNotification;
FMyThread.OnTerminate:=OnMyThreadTerminate;
FMyThread.Start;
end;
procedure TMyList.OnMyThreadNotification(Sender: TObject);
var
fullList: TStringList;
begin
if (FMyThread.FList4.Count>0) or (FMyThread.FList5.Count>0) then
begin
fullList:=TStringList.Create;
try
fullList.Text:=FMyThread.FList4.Text + FMyThread.FList5.Text;
ActOnThreadResults(fullList);
finally
FreeAndNil (fullList);
end;
end;
end;
procedure TMyList.OnMyThreadTerminate(Sender: TObject);
begin
FreeAndNil(FMyThread);
end;
procedure TMyList.StopMyThread;
begin
FMyThread.Terminate;
end;
end.
MyThread Code:
unit MyThread;
interface
uses
System.Classes, System.SysUtils;
type
TMyThread = Class (TThread)
private
FLastRun: TDateTime;
FList1: TStringList;
FList2: TStringList;
procedure SomeProcess;
procedure SomeOtherProcess;
protected
procedure Execute; override;
public
NotifyEvent: TNotifyEvent;
FList3: TStringList;
FList4: TStringList;
FList5: TStringList;
destructor Destroy; override;
End;
implementation
destructor TMyThread.Destroy;
begin
FreeAndNil(FList1);
FreeAndNil(FList2);
FreeAndNil(FList3);
FreeAndNil(FList4);
FreeAndNil(FList5);
inherited;
end;
procedure TMyThread.SomeOtherProcess;
var i: integer;
begin
for i := 1 to 1000000 do
begin
if Terminated then
break;
//do some stuff here
FList5.Add(i.ToString);
end;
end;
procedure TMyThread.SomeProcess;
var i: integer;
begin
for i := 1 to 1000000 do
begin
if Terminated then
break;
//do some stuff here
FList4.Add(i.ToString);
end;
end;
procedure TMyThread.Execute;
var
boolCheck: Boolean;
begin
NameThreadForDebugging('Thread with issues');
FreeOnTerminate:=False;
FList1:=TStringList.Create;
FList2:=TStringList.Create;
FList3:=TStringList.Create;
FList4:=TStringList.Create;
FList5:=TStringList.Create;
FLastRun:=Now; //i get this from an ini file normally
try
while Not Terminated do
begin
if Terminated then
Break;
FList1.Clear;
FList2.Clear;
FList3.Clear;
FList4.Clear;
FList5.Clear;
if Terminated then
Break;
SomeProcess;
if Terminated then
Break;
SomeOtherProcess;
if Terminated then
Break;
SomeProcess;
if Terminated then
Break;
SomeOtherProcess;
if Terminated then
Break;
SomeProcess;
if Terminated then
Break;
if (FList4.Count>0) OR (FList5.Count>0) then
boolCheck:=True;
if Terminated then
Break;
if boolCheck then
NotifyEvent(Self);
if Terminated then
Break;
Sleep (2000);
if Terminated then
Break;
FLastRun:=Now; //i save to ini file as well
end;
finally
//i save to ini file the last run
FreeAndNil(FList1);
FreeAndNil(FList2);
FreeAndNil(FList3);
FreeAndNil(FList4);
FreeAndNil(FList5);
end;
end;
end.
You have a race hazard. If you destroy your TMyList object before the thread terminates naturally, it can still call the notify event, but your object no longer exists (even though the thread still does). The simplest way I found to deal with this and stop memory leaks is to wait for the thread to terminate in the StopMyThread routine, and put the destruction of FMyThread into the destructor (as it stands OnTerminate is not called if you exit the application).
destructor TMyList.Destroy;
begin
StopMyThread;
FreeAndNil(FSomeList);
FreeAndNil(FMyThread);
inherited Destroy;
end;
and
procedure TMyList.StopMyThread;
begin
FMyThread.Terminate;
FMyThread.WaitFor;
end;

Timer process affect the user interface in delphi

i have developed application for read information from card reader. Here i have used timer for get the information each five second, so every five second the user interface getting slow
because it's get the information from reader. how to run the timer in background with out affecting user interface
unit frmVistorreg;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms;
type
thread1=class(TThread)
private
FEvent: THandle;
protected
procedure Execute; override;
public
procedure MyTerminate;
end;
TForm3 = class(TForm)
txt_name: TEdit;
txt_cardno.Text TEdit;
private
public
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure thread1.Execute;
var
idcard_info :array[0..1024*5] of byte;
flag :Integer;
portflag :Integer;
st :TStrings;
str :string;
begin
FEvent:= CreateEvent(nil, False, false, nil);
try
while not Terminated do begin
if MainForm.PortFlag=0 then
begin
Form3.Label11.Caption:='port has been successfully opened';
Form3.Label11.Font.Color :=32768;
flag := GetIdCardInfo(#idcard_info[0],1024*5,5);
str := byteArray2Str(#idcard_info[0],1024*5);
if(flag=0) then
begin
st := TStringList.Create;
try
SplitStr('^_^',str,st);
Form3.txt_name.Text := st.Strings[0];
Form3.txt_cardno.Text := st.Strings[5];
finally
st.Free;
end;
end;
end
else
begin
Form3.Label11.Caption:='Please open the port';
Form3.Label11.Font.Color:=clRed;
end;
if WaitForSingleObject(FEvent, 500) <> WAIT_TIMEOUT // 5 seconds timeout
then Terminate;
end;
finally
CloseHandle(FEvent);
end;
end;
procedure thread1.MyTerminate;
begin
SetEvent(FEvent);
end;
procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
var
Objthread1:thread1;
begin
Objthread1.MyTerminate;
Action := caFree;
end;
procedure TForm3.FormCreate(Sender: TObject);
var
Objthread1:thread1;
begin
Objthread1:=thread1.Create(false);
end;
end.
when i close the form have error like
Project MDIAPP.exe raised exception class EAccessViolation with message 'Access violation at address 0051B9F1 in module 'MDIAPP.exe'. Read of address 00000198'.
how can i solve this.
You need not a timer component for that, you need a background thread. A simplest solution is to use Sleep function in the thread:
unit Unit2;
interface
uses
Classes;
type
TMyThread = class(TThread)
protected
procedure Execute; override;
end;
implementation
procedure TMyThread.Execute;
begin
while not Terminated do begin
// do your processing here
Sleep(5000); // wait 5 seconds
end;
end;
end.
A better approach is to use WaitForSingleObject and an event instead of Sleep to be able to terminate your background thread immediately without 5 seconds delay:
unit Unit2;
interface
uses
Windows, Classes;
type
TMyThread = class(TThread)
private
FEvent: THandle;
protected
procedure Execute; override;
public
procedure MyTerminate;
end;
implementation
procedure TMyThread.Execute;
begin
FEvent:= CreateEvent(nil, False, False, nil);
try
while not Terminated do begin
// do your processing here
// ..
if WaitForSingleObject(FEvent, 5000) <> WAIT_TIMEOUT // 5 seconds timeout
then Terminate;
end;
finally
CloseHandle(FEvent);
end;
end;
procedure TMyThread.MyTerminate;
begin
SetEvent(FEvent);
end;
end.
To terminate TMyThread instance on closing a form call MyTerminate method from OnClose event handler of a form.
And yes, it is interesting to know what error message you receive, not just 'showing error'.

delphi 7 on 64 bit server 2008, problem

Has anyone ever tried to attach delphi to his own windows service(32 bit app.) process under Windows Server 2008 64 bit?
When I try to do this I get the error:
Unable to create the process. The parameter is incorrect.
if anyone of you know how to do this, that help would be really appreciated.
Thanks!
Whilst you can debug a Delphi service there are a number of hoops that you need to jump through to make it work. I never bother and simply ensure that my services can run either as a service or as a standard app. When I want to debug I run as a standard app and so sidestep all the headaches.
I've hacked out all the code into a single file for the purpose of this answer, but you'd want to structure it a bit differently.
program MyService;
uses
SysUtils, Classes, Windows, Forms, SvcMgr;
type
TMyService = class(TService)
private
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
procedure ServicePause(Sender: TService; var Paused: Boolean);
procedure ServiceExecute(Sender: TService);
procedure ServiceContinue(Sender: TService; var Continued: Boolean);
protected
FDescription: string;
FEventLogSourceName: string;
procedure Initialise; virtual; abstract;
class function CreateRunner: TObject; virtual; abstract;
public
constructor Create(AOwner: TComponent); override;
function GetServiceController: TServiceController; override;
end;
TMyServiceClass = class of TMyService;
{ TMyService }
constructor TMyService.Create(AOwner: TComponent);
begin
inherited;
Initialise;
OnStart := ServiceStart;
OnStop := ServiceStop;
OnPause := ServicePause;
OnExecute := ServiceExecute;
OnContinue := ServiceContinue;
end;
procedure TMyService.ServiceStart(Sender: TService; var Started: Boolean);
begin
Started := True;
end;
procedure TMyService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
Stopped := True;
end;
procedure TMyService.ServiceContinue(Sender: TService; var Continued: Boolean);
begin
ServiceStart(Sender, Continued);
end;
procedure TMyService.ServicePause(Sender: TService; var Paused: Boolean);
begin
ServiceStop(Sender, Paused);
end;
procedure TMyService.ServiceExecute(Sender: TService);
var
Runner: TObject;
begin
Runner := CreateRunner;
Try
while not Terminated do begin
ServiceThread.ProcessRequests(True);
end;
Finally
FreeAndNil(Runner);
End;
end;
var
Service: TMyService;
procedure ServiceController(CtrlCode: DWORD); stdcall;
begin
Service.Controller(CtrlCode);
end;
function TMyService.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure RunAsService(ServiceClass: TMyServiceClass; var Service);
var
Application: TServiceApplication;
begin
Application := SvcMgr.Application;
Application.Initialize;
Application.CreateForm(ServiceClass, Service);
Application.Run;
end;
procedure RunAsStandardExecutable(ServiceClass: TMyServiceClass);
var
Application: TApplication;
Runner: TObject;
begin
Application := Forms.Application;
Application.Initialize;
Runner := ServiceClass.CreateRunner;
Try
while True do begin
Try
Application.HandleMessage;
Except
Application.HandleException(Application);
End;
end;
Finally
FreeAndNil(Runner);
End;
end;
procedure ServiceMain(ServiceClass: TMyServiceClass);
begin
if FindCmdLineSwitch('RunAsApp', ['-', '/'], True) then begin
RunAsStandardExecutable(ServiceClass);
end else begin
RunAsService(ServiceClass, Service);
end;
end;
begin
ServiceMain(TMyService);
end.
To use this you need to create a new class, inherited from TMyService, and implement Initialise and CreateRunner. CreateRunner is the key. In my services this creates an object which in turn opens a listening socket ready for clients to communicate over.
The standard app code is pretty basic. It doesn't even have a mechanism to terminate—it runs inside a while True loop. That doesn't matter for my debugging needs.
Did you try running the IDE as Administrator?
I've already done such process attach under Win64, but I had to run the IDE with Administrator rights, as far as I remember.

Resources