Delphi multiThreading - delphi

I'm new at SO, so forgive me if my question isn't in the right place or been answered before.
The questions is about multi-threading with Delphi 10.4.
I'm getting Access Violation error on my app, here is a very simple example:
type
myThread = class(TThread)
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
mySideTask : myThread;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
with mySideTask.Create do
FreeOnTerminate:=True
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if mySideTask<>nil then
begin
mySideTask.Terminate;
mySideTask.WaitFor;
FreeAndNil(mySideTask);
end;
end;
{ myThread }
procedure myThread.Execute;
begin
Synchronize(
procedure
begin
Form1.Memo1.Lines.Add('running my side task')
end);
end;
No error if I don't create an instance of the thread (which is confusing me):
procedure TForm1.Button1Click(Sender: TObject);
begin
myThread.Create
end;
Can you please let me know what am I missing.

The code in Button1Click() is wrong. You are calling Create() as an instance method on your mySideTask variable, but it is not pointing at a valid object instance. You need to instead call Create() as a constructor on the class type itself.
Try this instead:
procedure TForm1.Button1Click(Sender: TObject);
begin
mySideTask := myThread.Create(False{True});
//mySideTask.FreeOnTerminate := True;
//mySideTask.Start;
end;
Notice I commented out the handling of FreeOnTerminate=True. The reason for that is because that setting is meant for create-and-forget type of threads. The thread will destroy itself after its Execute() method exits. So it is not safe to call WaitFor() or Free() on a thread that could destroy itself at any moment.
If you want to use FreeOnTerminate=True, then the code should look more like this instead:
type
myThread = class(TThread)
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
mySideTask : myThread;
procedure SideTaskTerminated(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
mySideTask := myThread.Create(True);
mySideTask.FreeOnTerminate := True;
mySideTask.OnTerminated := SideTaskTerminated;
mySideTask.Start;
end;
procedure TForm1.SideTaskTerminated(Sender: TObject);
begin
mySideTask := nil;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if mySideTask <> nil then
begin
mySideTask.FreeOnTerminate := False;
mySideTask.Terminate;
mySideTask.WaitFor;
FreeAndNil(mySideTask);
end;
end;
{ myThread }
procedure myThread.Execute;
begin
Synchronize(
procedure
begin
Form1.Memo1.Lines.Add('running my side task')
end);
end;

Related

How access webcam that's running in a separated thread?

I have this class to handle my webcam and want know how can stop the webcam started in a separated thread. Camera.Destroy not is working and the camera keep on.
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Panel1: TPanel;
Image1: TImage; // To load camera Bitmap from stream
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TCameraThread = class(TThread)
protected
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
uses
Webcam;
{$R *.dfm}
procedure TCameraThread.Execute;
begin
Camera := TCamera.Create(Form1.Panel1);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
mCamera: TCameraThread;
begin
mCamera := TCameraThread.Create(False);
mCamera.Resume;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if Assigned(Camera) then
Camera.Destroy;
end;
end.

Outlook connect to task events in Delphi

I found the following code in another post. Is it possible to capture Outlook task events in the same way? There does not appear to be an equivalent of TMailItem for tasks - just Outlook2000._TaskItem (and if someone could kindly explain the difference between Outlook2000.MailItem and Outlook2000._MailItem it might give me a clue). Thanks in advance.
uses
..., outlook2000;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
OutlookApplication: TOutlookApplication;
procedure OnMailSend(Sender: TObject; var Cancel: WordBool);
public
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
OutlookApplication := TOutlookApplication.Create(Self);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MailItem: _MailItem;
Mail: TMailItem;
begin
MailItem := OutlookApplication.CreateItem(olMailItem) as _MailItem;
Mail := TMailItem.Create(nil);
try
Mail.ConnectTo(MailItem);
Mail.OnSend := OnMailSend;
MailItem.Recipients.Add('username#example.com');
MailItem.Display(True);
finally
Mail.Free;
end;
end;
procedure TForm1.OnMailSend(Sender: TObject; var Cancel: WordBool);
begin
ShowMessage((Sender as TMailItem).Body);
end;

How to use RegisterPowerSettingNotification

I want to be notified when my computer power source changes.
So first I 've created a simple Delphi application and listening for
WM_POWERBROADCAST at the main form.
WM_POWERBROADCAST
type
TForm38 = class(TForm)
public
procedure WM_POWERBROADCAST(var Msg: TMessage); message WM_POWERBROADCAST;
end;
implementation
procedure TForm38.WM_POWERBROADCAST(var Msg: TMessage);
begin
Caption := Msg.LParam.ToString;
end;
Then I got my notifications, but Msg.LParam is allways 0 (zero)
Then I've tried to call RegisterPowerSettingNotification and found an example in this old SO Question, but I still have the same problem: Msg.LParam is allways 0 (zero)
RegisterPowerSettingNotification
type
TForm38 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FHPOWERNOTIFY: HPOWERNOTIFY;
public
{ Public declarations }
procedure WM_POWERBROADCAST(var Msg: TMessage); message WM_POWERBROADCAST;
end;
implementation
const
GUID_ACDC_POWER_SOURCE: TGUID = '{5D3E9A59-E9D5-4B00-A6BD-FF34FF516548}';
procedure TForm38.FormCreate(Sender: TObject);
begin
FHPOWERNOTIFY := RegisterPowerSettingNotification(Handle, GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
end;
procedure TForm38.FormDestroy(Sender: TObject);
begin
UnregisterPowerSettingNotification(FHPOWERNOTIFY);
end;
procedure TForm38.WM_POWERBROADCAST(var Msg: TMessage);
begin
Caption := Msg.LParam.ToString;
end;
The application run on Windows 10.
What am I doing wrong?
THE RESULT
Using the code from the answer to this question, I've ended up writing this class:
unit PowerWatcherU;
interface
uses
Winapi.Windows, System.Classes, System.SyncObjs, Winapi.Messages;
{$M+}
type
TPowerSource = (PoAc = 0, PoDc = 1, PoHot = 2);
TPowerSourceChanged = procedure(const PowerSource: TPowerSource) of object;
TPowerWatcher = class(TComponent)
private
FMyHWND: HWND;
FHPOWERNOTIFY: HPOWERNOTIFY;
FOnPowerSourceChanged: TPowerSourceChanged;
procedure DoPowerSourceChanged(const Value: TPowerSource);
procedure WndHandler(var Msg: TMessage);
procedure SetOnPowerSourceChanged(const Value: TPowerSourceChanged);
published
property OnPowerSourceChanged: TPowerSourceChanged read FOnPowerSourceChanged write SetOnPowerSourceChanged;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
const
GUID_ACDC_POWER_SOURCE: TGUID = '{5D3E9A59-E9D5-4B00-A6BD-FF34FF516548}';
implementation
uses
System.SysUtils;
{ TPowerWatcher }
constructor TPowerWatcher.Create;
begin
inherited;
FMyHWND := AllocateHWND(WndHandler);
FHPOWERNOTIFY := RegisterPowerSettingNotification(FMyHWND, GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
end;
destructor TPowerWatcher.Destroy;
begin
DeallocateHWND(FMyHWND);
UnregisterPowerSettingNotification(FHPOWERNOTIFY);
inherited;
end;
procedure TPowerWatcher.DoPowerSourceChanged(const Value: TPowerSource);
begin
if Assigned(FOnPowerSourceChanged) then
FOnPowerSourceChanged(Value);
end;
procedure TPowerWatcher.SetOnPowerSourceChanged(const Value: TPowerSourceChanged);
begin
FOnPowerSourceChanged := Value;
end;
procedure TPowerWatcher.WndHandler(var Msg: TMessage);
begin
if (Msg.Msg = WM_POWERBROADCAST) and (Msg.WParam = PBT_POWERSETTINGCHANGE) then
begin
if PPowerBroadcastSetting(Msg.LParam)^.PowerSetting = GUID_ACDC_POWER_SOURCE then
DoPowerSourceChanged(TPowerSource(PPowerBroadcastSetting(Msg.LParam)^.Data[0]));
end
else
Msg.Result := DefWindowProc(FMyHWND, Msg.Msg, Msg.WParam, Msg.LParam);
end;
end.
It is possible that you are suffering from window re-creation. Your code as posted works fine for me but this may not be the case in Win10. With that aside, the only other oddity is that you are duplicating an identifier by naming a method WM_POWERBROADCAST, although this should not cause the code to break. Working example using a dedicated HWND :
unit Unit1;
interface
uses
Windows, SysUtils, Classes, Forms, StdCtrls, Vcl.Controls, Vcl.ExtCtrls,
Messages;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FMyHWND : HWND;
FHPowerNotify: HPOWERNOTIFY;
public
procedure WndHandler(var Msg: TMessage);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
GUID_ACDC_POWER_SOURCE: TGUID = '{5D3E9A59-E9D5-4B00-A6BD-FF34FF516548}';
procedure TForm1.FormCreate(Sender: TObject);
begin
FMyHWND := AllocateHWND(WndHandler);
FHPowerNotify := RegisterPowerSettingNotification(FMyHWND,
GUID_ACDC_POWER_SOURCE,
DEVICE_NOTIFY_WINDOW_HANDLE);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnregisterPowerSettingNotification(FHPowerNotify);
DeallocateHWND(FMyHWND);
end;
procedure TForm1.WndHandler(var Msg: TMessage);
begin
if (Msg.Msg = WM_POWERBROADCAST) and
(Msg.WParam = PBT_POWERSETTINGCHANGE) then
begin
if PPowerBroadcastSetting(Msg.LParam)^.PowerSetting = GUID_ACDC_POWER_SOURCE then
case cardinal(PPowerBroadcastSetting(Msg.LParam)^.Data[0]) of
0: Caption := 'AC Power';
1: Caption := 'DC Power';
2: Caption := 'HOT - UPS, etc';
end;
end else
msg.Result := DefWindowProc(FMyHWND, Msg.Msg, Msg.WParam, Msg.LParam);
end;
end.

Where the TDBGrid Columns resize event was triggered

I have a TDBGrid component. I need to catch the event triggered when I'm resizing a column of the grid.
the only place to get an events seems to be overriding ColWidthChanged...
type
TDBgrid=Class(DBGrids.TDBGrid)
private
FColResize:TNotifyEvent;
procedure ColWidthsChanged; override;
protected
Property OnColResize:TNotifyEvent read FColResize Write FColResize;
End;
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
DBGrid1: TDBGrid;
ADODataSet1: TADODataSet;
DataSource1: TDataSource;
procedure FormCreate(Sender: TObject);
private
procedure ColResize(Sender: TObject);
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TDBgrid }
procedure TDBgrid.ColWidthsChanged;
begin
inherited;
if Assigned(FColResize) then FColResize(self);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DBgrid1.OnColResize := ColResize;
end;
procedure TForm1.ColResize(Sender:TObject);
begin
Caption := FormatDateTime('nn:zzz',now) ;
end;
you need to create a descendent of TDBGrid and implement the event by yourself. Something like this:
unit MyDBGrid;
interface
type
TMyDBGrid = class(TDBGrid)
private
FOnColResize: TNotifyEvent;
protected
procedure ColWidthsChanged; override;
public
published
property OnColResize: TNotifyEvent read FOnColResize write FOnColResize;
end;
implementation
{ TMyDBGrid }
procedure TMyDBGrid.ColWidthsChanged;
begin
inherited;
if (Datalink.Active or (Columns.State = csCustomized)) and
AcquireLayoutLock and Assigned(FOnColResize) then
FOnColResize(Self);
end;
end.
this should work, I don't have time now to test it.

How to define a breakpoint whenever an object field value changes?

As an example, given the code extract below, I would like to define a breakpoint that triggers whenever the object field value changes and optionally, breaks on a condition (False or True in this case).
type
TForm1 = class(TForm)
EnableButton: TButton;
DisableButton: TButton;
procedure EnableButtonClick(Sender: TObject);
procedure DisableButtonClick(Sender: TObject);
private
FValue: Boolean; // <== Would like to define a breakpoint here whenever FValue changes.
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.DisableButtonClick(Sender: TObject);
begin
FValue := False;
end;
procedure TForm1.EnableButtonClick(Sender: TObject);
begin
FValue := True;
end;
Run the application under the debugger,
select 'Run' from the IDE menu then select 'Add Breakpoint' at the very bottom, then 'Data Breakpoint...'.
enter 'Form1.FValue' as input to the 'Adress:' field. You can also set your condition in the same dialog.
Some additional information thanks to Sertac answer and comment from David.
One can define a breakpoint based on changes in an array item with a condition.
In this case the data breakpoint is defined as follow:
Form1.FBooleans[0] = True
Code extract:
type
TBooleanArray = array of Boolean;
TForm1 = class(TForm)
EnableButton: TButton;
DisableButton: TButton;
procedure EnableButtonClick(Sender: TObject);
procedure DisableButtonClick(Sender: TObject);
private
FBooleans: TBooleanArray; // Breakpoint defined here with the condition
public
constructor Create(AOwner: TComponent); override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TForm1.Create(AOwner: TComponent);
var
AIndex: Integer;
begin
inherited;
SetLength(FBooleans, 3);
for AIndex := 0 to Length(FBooleans) - 1 do
begin
FBooleans[AIndex] := (AIndex mod 2) = 1;
end;
end;
procedure TForm1.DisableButtonClick(Sender: TObject);
begin
FBooleans[0] := False;
end;
procedure TForm1.EnableButtonClick(Sender: TObject);
begin
FBooleans[0] := True; // Beakpoint stops here on condition.
end;

Resources