how to display progress bar in delphi? [duplicate] - delphi

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
how to display progress bar?
I created a steganalisys application and I want to add a progress bar to show how long the process works.
procedure TForm2.Button2Click(Sender: TObject);
var
q,x,y,leastSigBit,currentPixel,newPixelValue: integer;
pixels: PByteArray;
bmp: TBitmap;
begin
memo1.lines.clear;
Image2.Picture.Assign(Image1.Picture.Bitmap);
bmp := Image2.Picture.Bitmap;
for y := 0 to bmp.Height-1 do
begin
pixels := bmp.ScanLine[y];
for x := 0 to bmp.Width-1 do
begin
currentPixel := pixels[x];
leastSigBit := getBit(currentPixel, 0);
newPixelValue:=setBit(newPixelValue ,7,leastSigBit);
newPixelValue:=setBit(newPixelValue ,6,leastSigBit);
newPixelValue:=setBit(newPixelValue ,5,leastSigBit);
newPixelValue:=setBit(newPixelValue ,4,leastSigBit);
newPixelValue:=setBit(newPixelValue ,3,leastSigBit);
newPixelValue:=setBit(newPixelValue ,2,leastSigBit);
newPixelValue:=setBit(newPixelValue ,1,leastSigBit);
newPixelValue:=setBit(newPixelValue ,0,leastSigBit);
end;
pixels[x] := newPixelValue;
memo1.lines.append('pixel ke ' + inttostr(x) + ',' + inttostr(y) + ' desimal ' + inttostr(currentPixel) + ' (biner ' + toBinary(currentPixel) + ') ' +
' desimal baru ' + inttostr(newPixelValue) + ' (biner ' + toBinary(newPixelValue) + ')');
end;
end;
memo1.lines.append('All done!');
Button4.Enabled:=True;
Button2.Enabled:=False;
Button1.Enabled:=False;
Button5.Enabled:=True;
end;
how do I make a progress bar for the process? and where I have to put the command progress bar?

The correct way to do things like this is to do the computations in a background thread. Otherwise your GUI will freeze, and you might have trouble adding a Abort button. So you have to learn how to use threads (e.g., TThread) to do this properly. And your code must then be thread-safe, and you should only communicate between the thread code and the GUI in safe ways, e.g. using messages. The main ideas are found in my previous answer.
Anyhow, if you want to do this for educational purpouses or for private needs, perhaps the issues mentioned above aren't that severe. And then you can do simply like this:
procedure TForm2.Button2Click(Sender: TObject);
var
...
begin
ProgressBar1.Min := 0;
ProgressBar1.Max := bmp.Height;
ProgressBar1.Position := 0;
ProgressBar1.Step := 1;
for y := 0 to bmp.Height-1 do
begin
for x := 0 to bmp.Width-1 do
begin
end;
ProgressBar1.StepIt;
ProgressBar1.Update;
end;
end;
To try this, create a new VCL project. Add a TProgressBar and a TButton. In the OnClick event of the button, add the following code:
procedure TForm1.Button1Click(Sender: TObject);
var
i, j: integer;
begin
ProgressBar1.Min := 0;
ProgressBar1.Max := 100;
ProgressBar1.Position := 0;
ProgressBar1.Step := 1;
for i := 0 to 99 do
begin
for j := 0 to 200 do
begin
sleep(1);
end;
ProgressBar1.StepIt;
ProgressBar1.Update;
end;
end;
Be sure to notice the paramount backside of this approach, however. The application freezes during the entire 'computation'. You might not even be able to move the application window, and you will certainly not be able to interact with its GUI. Windows might even report the program as having freezed, and offer you the option to terminate it and send a bug report... Finally, since the entire GUI is down, there is no chance of adding a 'Stop computation' button. The solution? The dirty one is to use ProcessMessages and other filthy tricks. The proper one is to put the computation in its own thread, as already mentioned.

Related

Why is the second page of a fax truncated halfway using this code I use to send reports by fax?

I am trying to automate faxing of reports from database in application developed using Delphi XE4 using Async Professional Components. Reports usually are multi-page, two to four pages including the cover page. I followed the guidelines of Async Professional components and was able to send faxes quickly. The first page goes fine and status update reports it is accepted at the receiving end. However, after the first page, the second page is truncated below the top third. The status shows progress of transfer only to hang at 100% and the transfer never appears to be completed. The status finally shows Fatal Timeout. Any guide up to where the issue is appreciated.
function TfrmFaxDispatch.SendFax: Boolean;
var
Approutes: string;
SchRec: TSearchRec;
FaxPagesList: TStringList;
accession: string;
fax_number: string;
I: Integer;
begin
Approutes := ExtractFilePath(Application.ExeName);
FaxQry.Active := TRUE;
if FaxQry.RecordCount > 0 then
begin
fax_number := FaxQry.FieldByName('fax_number').AsString;
accession := FaxQry.FieldByName('accession_number').AsString;
if Get_Case_Status(accession) = PRELIM then
frmaedgeap.GenerateFinalDiagnosisReportFax(accession, TRUE, False)
else
frmaedgeap.GenerateFinalDiagnosisReportFax(accession, False, False);
FaxPagesList := TStringList.Create;
try
FaxPagesList.Sorted := TRUE;
FaxPagesList.Duplicates := dupIgnore;
if FindFirst(Approutes + 'APFReports\' + accession + '.*', faArchive,
SchRec) = 0 then
begin
repeat
FaxPagesList.Add(Approutes + 'APFReports\' + SchRec.Name);
until FindNext(SchRec) <> 0;
FindClose(SchRec);
end;
ApdSendFax1.PhoneNumber := fax_number;
ApdSendFax1.HeaderLine := DateTimeToStr(now) +
' AEdge Diagnostic Lab 123-3456';
ApdSendFax1.FaxFileList.Assign(FaxPagesList);
if ApdSendFax1.FaxFileList.Count > 0 then
begin
ApdSendFax1.ConcatFaxes(Approutes + 'APFReports\OUTFAX.APF');
ApdSendFax1.StartTransmit;
end;
finally
FaxPagesList.Free;
end;
end;
end;

Why in Firemonkey when create controls at runtime are display when finish iteration?

I create over 100 rectangles at runtine in code below;
var
RectT: TRectangle;
MyThread: TThread;
Layout1: TLayout;
begin
MyThread := TThread.CreateAnonymousThread(procedure()
begin
TThread.Synchronize(nil, procedure()
var
z, i: integer;
begin
z := 0;
for i := 0 to 99 do
begin
RectT := TRectangle.Create(Self);
RectT.Name := 'Rectangle' + IntToStr(i);
RectT.Align := TAlignLayout.Top;
RectT.Margins.Top := 6;
RectT.Position.Y := z;
RectT.Height := 20;
RectT.Parent := Layout1;
if (i mod 10) = 0 then Layout1.UpdateEffects;
inc(z, 20);
end;
end);
end);
MyThread.FreeOnTerminate := True;
MyThread.Start;
end;
Why didn't display the rectangle when is created and only are displayed when finish the iteration of all rectangles?.
First, you need to move the for loop in one thread and the creation of the rectangles in a Synchronize call, as Deltics has done. The difference is that you do not need the call to Repaint and you need to use the currentthread to pass the call for synchronization.
Try this (in OnClick event of a Button):
procedure TForm4.Button1Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure
var
I,z: Integer;
Total: Integer;
begin
Total := 0;
for I := 1 to 99 do
begin
TThread.Synchronize (TThread.CurrentThread,
procedure
var
RectT: TRectangle;
begin
RectT := TRectangle.Create(Self);
RectT.Name := 'Rectangle' + IntToStr(i);
RectT.Align := TAlignLayout.Top;
RectT.Margins.Top := 6;
RectT.Position.Y := z;
RectT.Height := 20;
RectT.Parent := Layout1;
Inc(z, 20);
end);
end;
end).Start;
end;
If this code is running on the main thread (which appears to be the case since you don't mention any threading) then the first opportunity that the FMX runtime has of visually updating the UI is when your code has itself finished running.
If you want the UI to update to display the rectangles as they are added, then you will need to re-write this to use an approach that allows the UI an opportunity to repaint periodically.
UPDATE
Your updated code in the question now involves a thread. However, in your posted code you Synchronize() all of the work in that thread. Synchronized code runs in the main thread so the consequence of synchronizing all of the work is to remove any benefit of the thread at all.
You are nearly there however.
A small change to your posted code so that the layout child objects are added in the thread, synchronizing only the repainting of the layout object itself periodically, then you get the result you are seeking:
var
MyThread: TThread;
begin
MyThread := TThread.CreateAnonymousThread
(
procedure()
var
z, i: integer;
RectT: TRectangle;
begin
z := 0;
for i := 0 to 999 do
begin
RectT := TRectangle.Create(Self);
RectT.Name := 'Rectangle' + IntToStr(i);
RectT.Align := TAlignLayout.Top;
RectT.Margins.Top := 6;
RectT.Position.Y := z;
RectT.Height := 20;
RectT.Parent := Layout1;
TThread.Synchronize(nil, procedure()
begin
Layout1.Repaint;
end);
inc(z, 20);
end;
end
);
MyThread.FreeOnTerminate := True;
MyThread.Start;
end;
I have increased the number of child objects in this demonstration of the approach to 999 since 99 was not enough to see any appreciable change in performance.
As written, the above code also repaints after every rectangle has been added, but this could be easily modified in a way similar to your posted code so that the layout is repainted only after "batches" of rectangles have been added:
if (i mod 10) = 0 then
TThread.Synchronize(nil, procedure()
begin
Layout1.Repaint;
end);
This is a simplistic approach, addressing the immediate problem of updating the UI to show the progress of some background changes made to that UI using this very simple test case. Whether this is actually the most appropriate approach in your specific case only you can really say.

Save Dbgrid Column Width Values to Ini and Reread them

I have Inherited form and a Ehlib dbgrid on it for selecting-listing records... The form is ready made for a lot of buttons and im using this form with different queries.
Like this...
If Dm.QrTmp.Active then Dm.QrTmp.Active:=False;
Dm.QrTmp.SQL.Clear;
Dm.QrTmp.SQL.Add(' SELECT ');
Dm.QrTmp.SQL.Add(' ch.cari_RECno AS KayitNo ');
Dm.QrTmp.SQL.Add(' FROM CARI_HESAPLAR ch ');
if FrmTmp=nil then FrmTmp:=TFrmTmp.Create(Self);
FrmTmp.StatusBar.Hide;
Dm.QrTmp.Open;
FrmTmp.DbGrid.DataSource:=Dm.DsQrTmp;
This query is cutted down but i have of course use a lot of fields. And Queries changes alot of time in the application.
The problem is column width. Manager wants to set column widths and restore them again. Actually my grid component supports save - restore column properties but as you can see my usage i m not using static columns. also i dont want to use xgrid.columns[0].width percent by percent.
Im using a ini in may app.
I want to add new section on it and named "Gridwidth"...
[Gridname]
Colwidths=x,y,z (where they are width values)
I'm now coding this line by line.
My write procedure is like this.
With dbgridx do
begin
For i:=0 to columns.count-1
begin
widthstr:=widthstr+Column[i].width+',';
end;
end;
Widthstr will be "15,23,45,67" etc...
But i want to know if this is good solution and if somebody know a better way and has some good code.
This should do it:
uses
IniFiles;
const
SETTINGS_FILE = 'Edijus\Settings.ini';
procedure TForm1.LoadDBGridColumnsWidth(const ADBGrid: TDBGrid);
var
_MemIniU: TMemIniFile;
_SettingsPath: string;
i, j: integer;
_ParentClass: TWinControl;
begin
_SettingsPath := GetHomePath + PathDelim + SETTINGS_FILE;
if (not Assigned(ADBGrid)) or (not Assigned(ADBGrid.DataSource)) or
(not Assigned(ADBGrid.DataSource.DataSet)) then
Exit;
_MemIniU := TMemIniFile.Create(_SettingsPath, TEncoding.UTF8);
try
_ParentClass := ADBGrid.Parent;
while not(_ParentClass is TForm) do
_ParentClass := _ParentClass.Parent;
for i := 0 to Pred(ADBGrid.DataSource.DataSet.Fields.Count) do
for j := 0 to Pred(ADBGrid.Columns.Count) do
begin
if (ADBGrid.DataSource.DataSet.Fields[i].FieldName = ADBGrid.Columns[j]
.FieldName) then
ADBGrid.Columns[j].Width :=
_MemIniU.ReadInteger(_ParentClass.Name + '_' + ADBGrid.Name,
ADBGrid.Columns[j].FieldName, 64);
end;
finally
FreeAndNil(_MemIniU);
end;
end;
procedure TForm1.SaveDBGridColumnsWidth(const ADBGrid: TDBGrid);
var
_MemIniU: TMemIniFile;
_SettingsPath: string;
i: integer;
_ParentClass: TWinControl;
begin
_SettingsPath := GetHomePath + PathDelim + SETTINGS_FILE;
if (not Assigned(ADBGrid)) or
(not ForceDirectories(ExtractFilePath(_SettingsPath))) then
Exit;
_MemIniU := TMemIniFile.Create(_SettingsPath, TEncoding.UTF8);
try
_ParentClass := ADBGrid.Parent;
while not(_ParentClass is TForm) do
_ParentClass := _ParentClass.Parent;
for i := 0 to Pred(ADBGrid.Columns.Count) do
if (ADBGrid.Columns[i].FieldName <> '') then
_MemIniU.WriteInteger(_ParentClass.Name + '_' + ADBGrid.Name,
ADBGrid.Columns[i].FieldName, ADBGrid.Columns[i].Width);
_MemIniU.UpdateFile;
finally
FreeAndNil(_MemIniU);
end;
end;

Delphi check if Tabsheet has finished loading data before making a clipboard picture

I am working in Delphi XE3. I have made a loop that goes through a Pagecontrol with 6 tabsheets, that has frames with a lot of edit boxes which load mdb data.
When looping through the pages I make a "screen cut" image of the active tab and place it on an image in fastreport.
Everything works great but when testing on some slower computers it makes all the frames but the data has not been loaded. How do I check that all data is loaded in frame edit components that is placed on the tab before going to next page?
The code looks like this:
begin
Screen.Cursor := crHourGlass;
p := PageControlKalkyl.ActivePageIndex; // Get page index
for i := 0 to 7 do begin
MyPage := frxReport1.FindObject('Page' + IntToStr(i)) as TfrxPage;
MyPage.Visible := True;
end;
try
for i := 0 to PageControlKalkyl.PageCount - 1 do
If PageControlKalkyl.Pages[i].TabVisible then
Begin
PageControlKalkyl.ActivePageIndex := i;
PageControlKalkyl.ActivePage.Repaint;
Bilder := 'Pic' + IntToStr(i);
if FLaddardata = False then //Check if page changed
Try
Bitmap := TBitmap.Create;
Assert(HandleAllocated);
DC := GetWindowDC(Handle);
Win32Check(DC <> 0);
Bitmap.SetSize(Width, Height);
Win32Check(BitBlt(Bitmap.Canvas.Handle, 0, 0, Width, Height, DC, 0, 0, SRCCOPY));
//Load data in to Images in Fastreport
if PageControlKalkyl.ActivePageIndex > 0 then
Begin
Ver:= 'Version NR: ' + Trim(DataModuleTrakop.ADOTableKALKYL.FieldByName('VERSION').AsString);
Raid:= 'Kalkyl ID: ' + Trim(DataModuleTrakop.ADOTableKALKYL.FieldByName('DENH').AsString);
RepImage := frxReport1.FindObject('Pic'+IntTostr(i)) as TfrxPictureView;
RepImage.Picture.Assign(Bitmap);
Rappid := frxReport1.FindObject('Rapdata' + IntToStr(i)) as TfrxMemoView;
Rappid.Font.Style:= [fsBold];
Rappid.Text := Ver +' '+Raid;
end;
Finally
ReleaseDC(Handle, DC);
Bitmap.Free;
End;
end
else
begin
MyPage := frxReport1.FindObject('Page' + IntToStr(i)) as TfrxPage;
MyPage.Visible := False;
end;
if Fskaparapport = True then
begin
Fskaparapport := False;
frxReport1.PrepareReport;
if FEpost = False then
frxReport1.ShowPreparedReport;
Screen.Cursor := crDefault;
end;
PageControlKalkyl.ActivePageIndex := p;
except
on E: Exception do
ShowMessage(E.Message);
end;
end;
Since you're using TADOTable, I suspect your table is configured to operate asynchronously.
This can be done via property ExecuteOptions: TExecuteOptions;
Of course, if you set ExecuteOptions := [];, your data should load synchronously, but with the unpleasant side-effect of blocking your UI.
The 'friendlier' option would be to hook the OnFetchComplete event which is decalred as follows: procedure (DataSet: TCustomADODataSet; const Error: Error; var EventStatus: TEventStatus) of object;
The exact specifics requires more information on what exactly you're trying to achieve. You could:
Call your report method directly from the OnFetchComplete handler.
Use your handler to enable a menu option / button / action that is disabled while the data is loading.
Use a synchronisation object (such as TSimpleEvent) and signal the event inside the OnFetchComplete handler. Then other code can simply call the WaitFor method blocking code until the event has been signalled.

My Delphi Program Seems to be Leaking

Ok, so I'm pretty new to Delphi (as you'll see from my code - try not to laugh too hard and hurt yourselves), but I've managed to make a little desktop canvas color picker. It works, kinda, and that's why I'm here :D
It seems to be leaking. It starts off using about 2 MB of memory, and climbs up about 2 kB per second until it reaches about 10 MB after 10 minutes or so. On my dual core 2.7 ghz cpu, it's using anywhere from 5% to 20% cpu power, fluctuating. My computer became unresponsive after running it for about 10 minutes without stopping the timer.
You can see in the source code below that I am freeing the TBitmap (or trying to, not sure if it's doing it, doesn't seem to be working).
procedure TForm1.Timer1Timer(Sender: TObject);
begin
GetCursorPos(MousePos);
try
Canvas1 := TCanvas.Create;
Canvas1.Handle := GetDC(0);
Pxl := TBitmap.Create;
Pxl.Width := 106;
Pxl.Height := 106;
W := Pxl.Width;
H := Pxl.Height;
T := (W div 2);
L := (H div 2);
Zoom := 10;
Rect1 := Rect(MousePos.X - (W div Zoom), MousePos.Y - (H div Zoom), MousePos.X + (W div Zoom), MousePos.Y + (H div Zoom));
Rect2 := Rect(0, 0, H, W);
Pxl.Canvas.CopyRect(Rect2, Canvas1, Rect1);
Pxl.Canvas.Pen.Color := clRed;
Pxl.Canvas.MoveTo(T, 0);
Pxl.Canvas.LineTo(L, H);
Pxl.Canvas.MoveTo(0, T);
Pxl.Canvas.LineTo(W, L);
Image1.Picture.Bitmap := Pxl;
finally
Pxl.Free;
end;
try
Pxl2 := TBitmap.Create;
Pxl2.Width := 1;
Pxl2.Height := 1;
Box1 := MousePos.X;
Box2 := MousePos.Y;
BitBlt(Pxl2.Canvas.Handle, 0, 0, 1, 1, GetDC(0), Box1, Box2, SRCCOPY);
C := Pxl2.Canvas.Pixels[0, 0];
Coord.Text := IntToStr(Box1) + ', ' + IntToStr(Box2);
DelColor.Text := ColorToString(C);
HexColor.Text := IntToHex(GetRValue(C), 2) + IntToHex(GetGValue(C), 2) + IntToHex(GetBValue(C), 2);
RGB.Text := IntToStr(GetRValue(C)) + ', ' + IntToStr(GetGValue(C)) + ', ' + IntToStr(GetBValue(C));
Panel1.Color := C;
finally
Pxl2.Free;
end;
end;
procedure TForm1.OnKeyDown(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
if Timer1.Enabled then
begin
Timer1.Enabled := false;
Panel2.Caption := 'Got it! Press Enter to reset.';
end
else
begin
Timer1.Enabled := true;
Panel2.Caption := 'Press Enter to lock color.';
end;
end;
end;
Note: The timer is set to run every 10 ms, if that makes any difference.
ANY and all help figuring out why this is leaking and using so much resources would be greatly appreciated!
You can nab the project here if you want it (Delphi 2010): http://www.mediafire.com/file/cgltcy9c2s80f74/Color%20Picker.rar
Thanks!
You never free your Canvas1 object, leaking both process heap and GDI obj. handles.
As user said above, TCanvas instance which owns DC of desktop window never freed, not releasing DC. I found another DC leak here:
BitBlt(Pxl2.Canvas.Handle, 0, 0, 1, 1, GetDC(0), Box1, Box2, SRCCOPY);
^^^^^^^^
This not solves memory leak but explains why Windows becomes unresponsive after 20 minutes (assuming previous issue has been patched already)
Every GetDC call requires ReleaseDC counter-part. GDI objects in the fact are even more precious than memory.
Ok, I found the solution (finally) after tinkering around with it a bit and following a few of the pointers on here. No one really hit it right on the head, but everyone was on the right track. The problem was that I was calling GetDC() inside the FUNCTION (and in earlier versions the timer procedure as well). Moving it outside of "try ... finally" while keeping it in the function (as suggested) still didn't yield results, but it was getting close and gave me the idea that actually worked. So I moved it a bit further away - into the Form's OnCreate event.
Here's the final code:
function DesktopColor(const X, Y: Integer): TColor;
begin
Color1 := TCanvas.Create;
Color1.Handle := DC;
Result := GetPixel(Color1.Handle, X, Y);
Color1.Free;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
GetCursorPos(Pos);
Rect1 := Rect(Pos.X - (W div Zoom), Pos.Y - (H div Zoom), Pos.X + (W div Zoom), Pos.Y + (H div Zoom));
Rect2 := Rect(0, 0, H, W);
Pxl.Canvas.CopyRect(Rect2, Canvas1, Rect1);
Pxl.Canvas.Pen.Color := clRed;
Pxl.Canvas.MoveTo(T, 0);
Pxl.Canvas.LineTo(L, H);
Pxl.Canvas.MoveTo(0, T);
Pxl.Canvas.LineTo(W, L);
Image1.Picture.Bitmap := Pxl;
Coord.Text := IntToStr(Pos.X) + ', ' + IntToStr(Pos.Y);
C := DesktopColor(Pos.X, Pos.Y);
DelColor.Text := ColorToString(C);
HexColor.Text := IntToHex(GetRValue(C), 2) + IntToHex(GetGValue(C), 2) + IntToHex(GetBValue(C), 2);
RGB.Text := IntToStr(GetRValue(C)) + ', ' + IntToStr(GetGValue(C)) + ', ' + IntToStr(GetBValue(C));
Panel1.Color := C;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Pxl := TBitmap.Create;
Canvas1 := TCanvas.Create;
DC := GetDC(0);
Pxl.Width := 106;
Pxl.Height := 106;
Canvas1.Handle := DC;
W := Pxl.Width;
H := Pxl.Height;
T := (W div 2);
L := (H div 2);
Zoom := 10;
Timer1.Enabled := True;
end;
procedure TForm1.OnKeyDown(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
if Timer1.Enabled then
begin
Timer1.Enabled := false;
Panel2.Caption := 'Got it! Press Enter to reset.';
end
else
begin
Timer1.Enabled := true;
Panel2.Caption := 'Press Enter to lock color.';
end;
end;
end;
procedure TForm1.OnDestroy(Sender: TObject);
begin
ReleaseDC(0, Canvas1.Handle);
ReleaseDC(0, Color1.Handle);
end;
And the final tally: drumroll CPU usage: 00% idle, 01% spikes if you move the mouse fast enough; Memory usage: ~3,500 kB solid, remaining unchanged. I even bumped the timer up from 10 ms to 5 ms and still get the same numbers.
Here's the final project with all the aforementioned fixes: http://www.mediafire.com/file/ebc8b4hzre7q6r5/Color%20Picker.rar
Thanks to everyone who helped, I greatly do appreciate it! I'm going to go ahead and open source the project for everyone who stumbles across this post and finds it useful. No license, do with it whatever you will. No credit necessary, but if you want to leave my name in there, that would be cool :D
Some comments on your code in DesktopColor
If the creation or GetDC fails, no resource will be locked and the unlock or free will generate an error, because you are trying to free a resource that does not exist.
The rule is that initialization should always be done before the try, because otherwise you will not know whether is is safe to deconstruct the entry.
In this case it's not a huge issue because GetxDC/ReleaseDC does not generate exceptions, it just gives back a 0 if unsuccesful.
Secondly I recommend putting in tests to make sure that your calls using DC's are succesful. When using Delphi objects you don't need that because the exceptions will take care of that, but Windows DC do not use exceptions, so you'll have to do your own testing. I recommend using assertions, because you can enable then in debug time and disable them when the program is debugged.
But because GetxDC never generates exceptions and to be consistent I'd recommend changing the code into:
{$C+} //enable assertions for debug purposes.
//or {$C-} //Disable assertions in production code
function DesktopColor(const X, Y: Integer): TColor;
var
Color: TCanvas;
Handle: THandle;
begin
Color := TCanvas.Create;
//If the create fails GetWindowsDC will not get stored anywhere
//and we cannot free it.
Handle:= GetWindowDC(GetDesktopWindow);
try
Assert(Handle <> 0);
Color.Handle := Handle; //Will generate an exception if create failed.
Handle := 0;
Result := GetPixel(Color.Handle, X, Y);
finally
//Free the handle if it wasn't transfered to the canvas.
if Handle <> 0 then ReleaseDC(0, Handle);
Color.Free; //TCanvas.Destroy will call releaseDC on Color.handle.
//If the transfer was succesful
end; {tryf}
end;
The same arguments apply to Timer1Timer.
Warning
When you disable assertions Delphi will remove the entire assert statement from your project, so don't put any code with side effects into an assert!
Links:
Assertions: http://beensoft.blogspot.com/2008/02/using-assert.html

Resources