In a program which uses the OmniThread library to create a parallel task, when I try to access a parameter in the parallel task, the code following after the parameter access is not executed, so obviously the task is aborted:
uses
System.SysUtils, System.Classes,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
OtlComm, OtlTask, OtlTaskControl, OtlEventMonitor;
type
TForm1 = class(TForm)
btnStartOTLTask: TButton;
OTLMonitor: TOmniEventMonitor;
procedure btnStartOTLTaskClick(Sender: TObject);
procedure OTLMonitorTaskMessage(const task: IOmniTaskControl; const msg:
TOmniMessage);
private
{ Private-Deklarationen }
FTestTask: IOmniTaskControl;
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ Place a TOmniEventMonitor component on the form,
name it OTLMonitor
and implement the OnTaskMessage event-handler: OTLMonitorTaskMessage }
procedure TestParameters(const ATask: IOmniTask);
var
test: Integer;
begin
ATask.Comm.Send(16); // does execute
test := ATask.Param['From']; // ?? <<<===========================
ATask.Comm.Send(17); // does NOT execute!
end;
procedure TForm1.btnStartOTLTaskClick(Sender: TObject);
begin
if not Assigned(FTestTask) then // prevent multiple tasks
FTestTask := CreateTask(TestParameters, 'TestParameters')
.MonitorWith(OTLMonitor)
.SetParameters(['From', 0, 'To', 99])
.Run
else
MessageDlg('Task is already running!', mtInformation, [mbOK], 0);
end;
procedure TForm1.OTLMonitorTaskMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
Self.Caption := IntToStr(msg.MsgID);
end;
So what's wrong with accessing the parameter 'From'?
I've found a workaround:
procedure TestParameters(const ATask: IOmniTask);
begin
// this does work:
ATask.Comm.Send(ATask.Param.ByName('From'));
ATask.Comm.Send(ATask.Param.ByName('To'));
// ALSO this does work:
ATask.Comm.Send(ATask.Param['From']);
ATask.Comm.Send(ATask.Param['To']);
end;
procedure TForm1.btnStartOTLTaskClick(Sender: TObject);
begin
if not Assigned(FTestTask) then // prevent multiple tasks
FTestTask := CreateTask(TestParameters, 'TestParameters')
.MonitorWith(OTLMonitor)
// SetParameters does not work:
//.SetParameters(['From', 1, 'To', 99])
// this does work:
.SetParameter('From', 1)
.SetParameter('To', 99)
.Run
else
MessageDlg('Task is already running!', mtInformation, [mbOK], 0);
end;
procedure TForm1.OTLMonitorTaskMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
Memo1.Lines.Add(IntToStr(msg.MsgID));
end;
procedure TForm1.OTLMonitorTaskTerminated(const task: IOmniTaskControl);
begin
FTestTask := nil;
Memo1.Lines.Add('Task terminated');
end;
Related
I have written a simple loader to install my program and its help file.
unit PSInstaller;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Registry, Vcl.StdCtrls, HTMListB,
HTMLabel, System.Zip;
type
TfmPDSInstaller = class(TForm)
HTMLabel1: THTMLabel;
HTMListBox1: THTMListBox;
btnNext: TButton;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure btnNextClick(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
function InstallFile(ResID: integer; pName: String): Boolean;
public
{ Public declarations }
end;
var
fmPDSInstaller: TfmPDSInstaller;
implementation
{$R 'ProtonStudio32.res' 'ProtonStudio32.rc'}
{$R *.dfm}
Var IDEDirectory: String;
Const APP = 100;
HELP = 200;
procedure TfmPDSInstaller.btnNextClick(Sender: TObject);
begin
HTMListBox1.AddItem('Copying Proton Studio to Proton IDE directory',nil);
if InstallFile(APP, 'Studio Application') then begin
HTMListBox1.AddItem('Copying Proton Studio Help to Proton IDE directory',nil);
If InstallFile(HELP, 'Studio Help') then
HTMListBox1.AddItem('Proton Studio Installed', nil);
end;
end;
function TfmPDSInstaller.InstallFile(ResID: integer; pName: String): Boolean;
Var rs: TResourceStream;
Zip: TZipFile;
s: String;
begin
Result := false;
try
Rs := TResourceStream.CreateFromID(HInstance, ResID, RT_RCDATA);
Zip := TZipFile.Create;
try
Zip.Open(Rs,zmRead);
Zip.ExtractAll(IDEDirectory);
finally
Rs.Free;
Zip.Free;
Result := true;
end;
except
on EFOpenError do
s := 'Unable to Open resource ' + pName;
else
s := 'Unable to Copy file from resource ' + pName;
end;
HTMListBox1.AddItem(s, nil);
end;
procedure TfmPDSInstaller.Button1Click(Sender: TObject);
begin
Close;
end;
procedure TfmPDSInstaller.FormCreate(Sender: TObject);
Var Reg: TRegistry;
begin
btnNext.Enabled := false;
Reg := TRegistry.Create;
HTMListBox1.AddItem('Checking for ProtonIDE',nil);
if Reg.OpenKey('Software\MecaniqueUK\ProtonIDE\Install', false) then begin
IDEDirectory := Reg.ReadString('IDE');
Reg.CloseKey;
end;
Reg.Free;
end;
procedure TfmPDSInstaller.FormShow(Sender: TObject);
begin
btnNext.Enabled := false;
if DirectoryExists(IDEDirectory) then begin
HTMListbox1.AddItem('Click Next to install Proton Studio in ' + IDEDirectory, nil);
btnNext.Enabled := true;
end
else
HTMListBox1.AddItem('Proton IDE must be installed first', nil);
end;
end.
I have created a .rc script to load my program and help
#100 RT_RCDATA "D:\Data\Documents\RAD Studio\Projects\ProtonNewIDE\Win32\Debug\ProtonNewIDE.zip"
#200 RT_RCDATA "D:\Data\Documents\RAD Studio\Projects\ProtonNewIDE\Win32\Debug\Proton Studio.zip"
I'm working in Delphi Berlin 10.1, Build resulted in my resource file being generated and I can open it in my Resource Editor but when I try and open the resource:
Rs := TResourceStream.CreateFromID(Application.Handle, ResID, RT_RCDATA);
I get an Address violation. It breaks in System.Classes at this point:
HResInfo := FindResource(Instance, Name, ResType);
and both the Name and ResType are empty.
I would appreciate a pointer to what am I doing wrong?
You are passing a window handle instead of a module handle. Pass HInstance instead, the handle to the module containing this code.
How can i send messages (TOmniMessage) from Background Task to Main Form?
I want send follow message to Mainform:
Memo1.Lines.Add(Format('BEGIN: %s', [msg.MsgData.CastToStringDef('')]));
main.pas (MainForm)
unit main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
Beep;
type
TfrmDemoParallelAsync = class(TForm)
btnAsync: TButton;
Memo1: TMemo;
procedure btnAsyncClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
public
end;
var
frmDemoParallelAsync: TfrmDemoParallelAsync;
fBeep: TBeep;
implementation
{$R *.dfm}
procedure TfrmDemoParallelAsync.FormCreate(Sender: TObject);
begin
fBeep := TBeep.Create;
fBeep.CanClose := True;
end;
procedure TfrmDemoParallelAsync.FormDestroy(Sender: TObject);
begin
fBeep.Free;
end;
procedure TfrmDemoParallelAsync.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if fBeep.CanClose then
Action := caFree
else
Action := caNone;
end;
procedure TfrmDemoParallelAsync.btnAsyncClick(Sender: TObject);
begin
fBeep.CanClose := false;
//btnAsync.Enabled := false;
fBeep.BackgroundTask;
end;
end.
beep.pas (Background Task)
unit Beep;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils,
OtlComm, OtlTask, OtlTaskControl, OtlParallel;
const
WM_MESSAGE_BEGIN = WM_USER + 1;
WM_MESSAGE_END = WM_USER + 2;
type
TBeep = class
private
procedure BeepEventHandler(const task: IOmniTaskControl; const msg: TOmniMessage);
public
CanClose: Boolean;
procedure BackgroundTask;
end;
implementation
procedure TBeep.BackgroundTask;
begin
CanClose := false;
Parallel.Async(
procedure(const task: IOmniTask)
begin
task.Comm.Send(WM_MESSAGE_BEGIN, 'Background Task ' + GetCurrentThreadID.ToString() + ' started');
// executed in background thread
Sleep(5000);
MessageBeep($FFFFFFFF);
task.Comm.Send(WM_MESSAGE_END, 'Background Task ' + GetCurrentThreadID.ToString() + ' ended');
end,
// executed in main thread
Parallel.TaskConfig.Onmessage(BeepEventHandler));
end;
procedure TBeep.BeepEventHandler(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
//if msg.MsgID = WM_MESSAGE_BEGIN then
// Memo1.Lines.Add(Format('BEGIN: %s', [msg.MsgData.CastToStringDef('')]));
if msg.MsgID = WM_MESSAGE_END then
begin
//Memo1.Lines.Add(Format('END: %s', [msg.MsgData.CastToStringDef('')]));
CanClose := True;
end;
end;
end.
Here is a sample code with all functions in MainForm. Works good.
I want move the BackgrounTask in its own class.
main.pas (MainForm):
unit main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
OtlComm, OtlTask, OtlTaskControl, OtlParallel;
const
WM_MESSAGE_BEGIN = WM_USER + 1;
WM_MESSAGE_END = WM_USER + 2;
type
TfrmDemoParallelAsync = class(TForm)
btnAsync: TButton;
Memo1: TMemo;
procedure btnAsyncClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
CanClose: Boolean;
procedure BeepEventHandler(const task: IOmniTaskControl; const msg: TOmniMessage);
public
end;
var
frmDemoParallelAsync: TfrmDemoParallelAsync;
implementation
{$R *.dfm}
procedure TfrmDemoParallelAsync.FormCreate(Sender: TObject);
begin
CanClose := True;
end;
procedure TfrmDemoParallelAsync.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if CanClose then
Action := caFree
else
Action := caNone;
end;
procedure TfrmDemoParallelAsync.btnAsyncClick(Sender: TObject);
begin
CanClose := false;
btnAsync.Enabled := false;
Parallel.Async(
procedure(const task: IOmniTask)
begin
task.Comm.Send(WM_MESSAGE_BEGIN, 'Background Task ' + GetCurrentThreadID.ToString() + ' started');
// executed in background thread
Sleep(5000);
MessageBeep($FFFFFFFF);
task.Comm.Send(WM_MESSAGE_END, 'Background Task ' + GetCurrentThreadID.ToString() + ' ended');
end,
// executed in main thread
Parallel.TaskConfig.Onmessage(BeepEventHandler));
end;
procedure TfrmDemoParallelAsync.BeepEventHandler(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
if msg.MsgID = WM_MESSAGE_BEGIN then
Memo1.Lines.Add(Format('BEGIN: %s', [msg.MsgData.CastToStringDef('')]));
if msg.MsgID = WM_MESSAGE_END then
begin
Memo1.Lines.Add(Format('END: %s', [msg.MsgData.CastToStringDef('')]));
btnAsync.Enabled := True;
CanClose := True;
end;
end;
end.
This is a sample code for a stopwatch I have implemented as a separate thread with the OmniThread library.
This is my question: Do I have to terminate and nil the task when the form closes or will it be destroyed automatically when the form closes?
uses
System.SysUtils, System.Classes,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
OtlComm, OtlTask, OtlTaskControl, OtlEventMonitor;
type
TForm1 = class(TForm)
OTLMonitor: TOmniEventMonitor;
btnStartClock: TButton;
btnStopClock: TButton;
procedure btnStartClockClick(Sender: TObject);
procedure btnStopClockClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure OTLMonitorTaskMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
procedure OTLMonitorTaskTerminated(const task: IOmniTaskControl);
private
{ Private-Deklarationen }
FClockTask: IOmniTaskControl;
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ Place a TOmniEventMonitor component on the form,
name it OTLMonitor,
implement the OnTaskTerminated event-handler: OTLMonitorTaskTerminated
and implement the OnTaskmessage event-handler: OTLMonitorTaskMessage }
var
StopMessage: string;
procedure ShowElapsedSeconds(const ATask: IOmniTask);
var
ElapsedSeconds: Integer;
begin
ElapsedSeconds := 0;
while not ATask.Terminated do
begin
// stop after 10 seconds:
if ElapsedSeconds >= 10 then BREAK;
Inc(ElapsedSeconds);
ATask.Comm.Send(ElapsedSeconds);
Sleep(1000);
end;
end;
procedure TForm1.OTLMonitorTaskMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
// show elapsed seconds:
Self.Caption := IntToStr(msg.MsgID);
end;
procedure TForm1.OTLMonitorTaskTerminated(const task: IOmniTaskControl);
begin
FClockTask := nil;
Self.Caption := StopMessage;
end;
procedure TForm1.btnStartClockClick(Sender: TObject);
begin
if not Assigned(FClockTask) then // prevent multiple clock-tasks
begin
StopMessage := 'Automatically stopped after 10 seconds';
FClockTask := CreateTask(ShowElapsedSeconds, 'ShowElapsedSeconds').MonitorWith(OTLMonitor).Run;
end
else
begin
MessageDlg('Clock is already running!', mtInformation, [mbOK], 0);
{ Nice: The clock continues to run even while this message dialog is displayed! }
end;
end;
procedure TForm1.btnStopClockClick(Sender: TObject);
begin
if Assigned(FClockTask) then
begin
StopMessage := 'Stopped by the user';
FClockTask.Terminate;
FClockTask := nil;
end
else
MessageDlg('Clock is not running!', mtInformation, [mbOK], 0);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Assigned(FClockTask) then
begin
{ Do I need to terminate and nil the clock-task here?
Or will it be destroyed autmatically when the form closes? }
end;
end;
Primož Gabrijelčič, the author of "Parallel Programming with OmniThreadLibrary" writes:
"We should also handle the possibility of user closing the program by
clicking the ‘X’ button while the background scanner is active. We
must catch the OnFormCloseQuery event and tell the task to terminate.
procedure TfrmBackgroundFileSearchDemo.FormCloseQuery(Sender: TObject;
var CanClose: boolean);
begin
if assigned(FScanTask) then
begin
FScanTask.Terminate;
FScanTask := nil;
CanClose := true;
end;
end;"
This book is for sale at http://leanpub.com/omnithreadlibrary
Just learning some OpenGL with delphi and trying something simple but not getting a result, I belive i should get a dark green form. But when i run this i get nothing. No errors either. maybe missing something?
unit First1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls,OpenGL, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
GLContext : HGLRC;
ErrorCode: GLenum;
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
var
pfd: TPixelFormatDescriptor;
FormatIndex: integer;
begin
fillchar(pfd,SizeOf(pfd),0);
with pfd do
begin
nSize := SizeOf(pfd);
nVersion := 1; {The current version of the desccriptor is 1}
dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL;
iPixelType := PFD_TYPE_RGBA;
cColorBits := 24; {support 24-bit color}
cDepthBits := 32; {depth of z-axis}
iLayerType := PFD_MAIN_PLANE;
end; {with}
FormatIndex := ChoosePixelFormat(Canvas.Handle,#pfd);
SetPixelFormat(Canvas.Handle,FormatIndex,#pfd);
GLContext := wglCreateContext(Canvas.Handle);
wglMakeCurrent(Canvas.Handle,GLContext);
end; {FormCreate}
procedure TForm2.FormDestroy(Sender: TObject);
begin
wglMakeCurrent(Canvas.Handle,0);
wglDeleteContext(GLContext);
end;
procedure TForm2.FormPaint(Sender: TObject);
begin
{background}
glClearColor(0.0,0.4,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
{error checking}
errorCode := glGetError;
if errorCode<>GL_NO_ERROR then
raise Exception.Create('Error in Paint'#13+
gluErrorString(errorCode));
end;
end.
Since you request a single buffered context, you must call glFinish at the end of the rendering code, to commit your drawing commands to the implementation. However I strongly suggest you switch to using a double buffered context and instead of glFinish-ing you issue a wglSwapBuffers which implies a finish.
I have an (Delphi XE2) VCL app containing an object TDownloadUrl (VCL.ExtActns) to check several webpages, so I wonder if there is an equivalent object in FireMonkey, 'cause I wanna take advantage of rich features from this new platform.
A Firemonkey app demo using threads would appreciate. Thanks in advance.
Actions don't exist yet with FireMonkey.
BTW, you can create the same behavior with a code like this:
IdHTTP1: TIdHTTP;
...
procedure TForm2.MenuItem1Click(Sender: TObject);
const
FILENAME = 'C:\Users\Whiler\Desktop\test.htm';
URL = 'http://stackoverflow.com/questions/7491389/firemonkey-and-tdownloadurl';
var
// sSource: string;
fsSource: TFileStream;
begin
if FileExists(FILENAME) then
begin
fsSource := TFileStream.Create(FILENAME, fmOpenWrite);
end
else
begin
fsSource := TFileStream.Create(FILENAME, fmCreate);
end;
try
IdHTTP1.Get(URL, fsSource);
finally
fsSource.Free;
end;
// sSource := IdHTTP1.Get(URL);
end;
The commented lines can replace the others if you just need the source in memory...
If you want to use a thread, you can manage it like this:
unit Unit2;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, FMX.Menus;
type
TDownloadThread = class(TThread)
private
idDownloader: TIdHTTP;
FFileName : string;
FURL : string;
protected
procedure Execute; override;
procedure Finished;
public
constructor Create(const sURL: string; const sFileName: string);
destructor Destroy; override;
end;
type
TForm2 = class(TForm)
MenuBar1: TMenuBar;
MenuItem1: TMenuItem;
procedure MenuItem1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.fmx}
procedure TForm2.MenuItem1Click(Sender: TObject);
const
FILENAME = 'C:\Users\Whiler\Desktop\test.htm';
URL = 'http://stackoverflow.com/questions/7491389/firemonkey-and-tdownloadurl';
var
// sSource: string;
fsSource: TFileStream;
begin
TDownloadThread.Create(URL, FILENAME).Start;
end;
{ TDownloadThread }
constructor TDownloadThread.Create(const sURL, sFileName: string);
begin
inherited Create(true);
idDownloader := TIdHTTP.Create(nil);
FFileName := sFileName;
FURL := sURL;
FreeOnTerminate := True;
end;
destructor TDownloadThread.Destroy;
begin
idDownloader.Free;
inherited;
end;
procedure TDownloadThread.Execute;
var
// sSource: string;
fsSource: TFileStream;
begin
inherited;
if FileExists(FFileName) then
begin
fsSource := TFileStream.Create(FFileName, fmOpenWrite);
end
else
begin
fsSource := TFileStream.Create(FFileName, fmCreate);
end;
try
idDownloader.Get(FURL, fsSource);
finally
fsSource.Free;
end;
Synchronize(Finished);
end;
procedure TDownloadThread.Finished;
begin
// replace by whatever you need
ShowMessage(FURL + ' has been downloaded!');
end;
end.
Regarding this:
A Firemonkey app demo using threads would appreciate.
You can find a FireMonkey demo which is using Thread here: https://radstudiodemos.svn.sourceforge.net/svnroot/radstudiodemos/branches/RadStudio_XE2/FireMonkey/FireFlow/MainForm.pas
type
TImageThread = class(TThread)
private
FImage: TImage;
FTempBitmap: TBitmap;
FFileName: string;
protected
procedure Execute; override;
procedure Finished;
public
constructor Create(const AImage: TImage; const AFileName: string);
destructor Destroy; override;
end;
...
TImageThread.Create(Image, Image.TagString).Start;
if you don't have this demo in your sample directory, you can check it out from the subversion repository used in the link above.
You can using this code.
unit BitmapHelperClass;
interface
uses
System.Classes, FMX.Graphics;
type
TBitmapHelper = class helper for TBitmap
public
procedure LoadFromUrl(AUrl: string);
procedure LoadThumbnailFromUrl(AUrl: string; const AFitWidth, AFitHeight: Integer);
end;
implementation
uses
System.SysUtils, System.Types, IdHttp, IdTCPClient, AnonThread;
procedure TBitmapHelper.LoadFromUrl(AUrl: string);
var
_Thread: TAnonymousThread<TMemoryStream>;
begin
_Thread := TAnonymousThread<TMemoryStream>.Create(
function: TMemoryStream
var
Http: TIdHttp;
begin
Result := TMemoryStream.Create;
Http := TIdHttp.Create(nil);
try
try
Http.Get(AUrl, Result);
except
Result.Free;
end;
finally
Http.Free;
end;
end,
procedure(AResult: TMemoryStream)
begin
if AResult.Size > 0 then
LoadFromStream(AResult);
AResult.Free;
end,
procedure(AException: Exception)
begin
end
);
end;
procedure TBitmapHelper.LoadThumbnailFromUrl(AUrl: string; const AFitWidth,
AFitHeight: Integer);
var
Bitmap: TBitmap;
scale: Single;
begin
LoadFromUrl(AUrl);
scale := RectF(0, 0, Width, Height).Fit(RectF(0, 0, AFitWidth, AFitHeight));
Bitmap := CreateThumbnail(Round(Width / scale), Round(Height / scale));
try
Assign(Bitmap);
finally
Bitmap.Free;
end;
end;
end.