delphi app freezes whole win7 system - delphi

i have a simple program that sorts a text file according to length of words per line
this program works without problems in my xp based old machine
now i run this program on my new win7/intel core i5 machine, it freezes whole system and back normal after it finishes it's work.
i'v invastigated the code and found the line causing the freeze
it was this specific line...
caption := IntToStr(i) + '..' + IntTostr(ii);
i'v changed it to
caption := IntTostr(ii); //slow rate change
and there is no freeze
and then i'v changed it to
caption := IntTostr(i); //fast rate change
and it freeze again
my procedure code is
var tword : widestring;
i,ii,li : integer;
begin
tntlistbox1.items.LoadFromFile('d:\new folder\ch.txt');
tntlistbox2.items.LoadFromFile('d:\new folder\uy.txt');
For ii := 15 Downto 1 Do //slow change
Begin
For I := 0 To TntListBox1.items.Count - 1 Do //very fast change
Begin
caption := IntToStr(i) + '..' + IntTostr(ii); //problemetic line
tword := TntListBox1.items[i];
LI := Length(tword);
If lI = ii Then
Begin
tntlistbox3.items.Add(Trim(tntlistbox1.Items[i]));
tntlistbox4.items.Add(Trim(tntlistbox2.Items[i]));
End;
End;
End;
end;
any idea why ? and how to fix it?
i use delphi 2007/win32

Is this happening inside an event handler on a form? I'm going to guess taht it is. In that case, "Caption" is in the scope of the form. The form's caption text isn't managed by the VCL, but by Windows, and if you're sending a new WM_SETTEXT message on every iteration of the loop.
A thorough explanation of why this is doing what it's doing would require knowledge of Windows internals that I don't have, but if I were to take a guess, I'd say it's something like this:
Every time you send that WM_SETTEXT message with a new caption, Windows checks to make sure it's not identical to the existing caption. If it is, it can exit immediately. That's why the infrequent change (the one that only uses ii) doesn't slow your system down. But if it does change on every iteration, then Windows has to perform some sort of task switch in order to change it.
As for why that would bog down the entire system under a Vista kernel (including Win7) but not XP, that's completely outside my area of expertise. But if you're trying to do this as some sort of progress indicator, there are better ways, especially if this loop is as tight as it looks.
The best way to handle progress updates in a tight loop is to count iterations and only fire once every X times. (100 or 1000 can be good values for X, depending on how many times it's running and how fast the whole thing takes.) This is basically what the ii only option does. You could also try putting a Progress Bar on the form to measure progress instead of doing it through the form's caption.

Changing a Form's caption releases a whole bunch of actions - especially under Vista and Win7 with Aero active.
A quick try would be using a TLabel instead for displaying progress. Something like
Label1.caption := IntToStr(i) + '..' + IntTostr(ii); //problemetic line
Label1.Refresh; // or Repaint
should do the trick unless your label is transparent or on a glass area.
It would probably be best to follow Mason Wheeler's advice and use a progressbar. As the overall number of iterations is 15*TntListBox1.items.Count you can calculate the progress value quite easily.

First: you forget tntlistbox3.items.BeginUpdate/tntlistbox3.items.EndUpdate calls (same for tntlistbox4).
Second: Why does my program run faster if I click and hold the caption bar?
Solution (example):
const
UpdateInterval = 500; // half a second
var
...
LastUpdate: Cardinal;
begin
...
LastUpdate := GetTickCount + 100000; // forces first update
For ii := 15 Downto 1 Do //slow change
Begin
For I := 0 To TntListBox1.items.Count - 1 Do //very fast change
Begin
if (GetTickCount > (LastUpdate + UpdateInterval)) or
(GetTickCount < LastUpdate) then
caption := IntToStr(i) + '..' + IntTostr(ii); //problemetic line
...
end;
end;

Related

RichEdit 2.0's usage of single CR character as linebreak throws off SelStart calculations (Delphi XE2)

When transitioning from Delphi 2006 to Delphi XE2, one of the things that we learned is that RichEdit 2.0 replaces internally CRLF pairs with a single CR character. This has the unfortunate effect of throwing off all character index calculations based on the actual text string on the VCL's side.
The behavior I can see by tracing through the VCL code is as follows:
Sending a WM_GETTEXT message (done in TControl.GetTextBuf) will return a text buffer that contains CRLF pairs.
Sending a WM_GETTEXTLENGTH message (done in TControl.GetTextLen) will return a value as if the text still contains CRLF characters.
In contrast, sending an EM_SETSELEX message (i.e. setting SelStart) will treat the input value as if the text contains only CR characters.
This causes all sorts of things to fail (such as syntax highlighting) in our application. As you can tell, everything is off by exactly one character for every new line up to that point.
Obviously, since this is inconsistent behavior, we must be missing something or doing something very wrong.
Does anybody else has any experience with the transition from a RichEdit 1.0 to a RichEdit 2.0 control and how did you solve this issue? Finally, is there any way to force RichEdit 2.0 to use CRLF pairs just like RichEdit 1.0?
We also ran into this very issue.
We do a "mail merge" type of thing where we have templates with merge codes that are parsed and replaced by data from outside sources.
This index mismatch between pos(mystring, RichEdit.Text) and the positioning index into the RichEdit text using RichText.SelStart broke our merge.
I don't have a good answer but I came up with a workaround. It's a bit cumbersome (understatment!) but until a better solution comes along...
The workaround is to use a hidden TMemo and copy the RichEdit text to it and change the CR/LF pairs to CR only. Then use the TMemo to find the proper positioning using pos(string, TMemo) and use that to get the selstart position to use in the TRichEdit.
This really sucks but hopefully this workaround will help others in our situation or maybe spark somebody smarter than me into coming up with a better solution.
I'll show a little sample code...
Since we are replacing text using seltext we need to replace text in BOTH the RichEdit control and the TMemo control to keep the two synchronized.
StartToken and EndToken are the merge code delimiters and are a constant.
function TEditForm.ParseTest: boolean;
var TagLength: integer;
var ValueLength: integer;
var ParseStart: integer;
var ParseEnd: integer;
var ParseValue: string;
var Memo: TMemo;
begin
Result := True;//Default
Memo := TMemo.Create(nil);
try
Memo.Parent := self;
Memo.Visible := False;
try
Memo.Lines.Clear;
Memo.Lines.AddStrings(RichEditor.Lines);
Memo.Text := stringreplace(Memo.Text,#13#10,#13,[rfReplaceAll]);//strip CR/LF pairs and replace with CR
while (Pos(StartToken, Memo.Text) > 0) and (Pos(EndToken, Memo.Text) > 0) do begin
ParseStart := Pos(StartToken, Memo.SelText);
ParseEnd := Pos(EndToken, Memo.SelText) + Length(EndToken);
if ParseStart >= ParseEnd then begin//oops, something's wrong - bail out
Result := true;
myEditor.SelStart := 0;
exit;
end;
TagLength := ParseEnd - ParseStart;
ValueLength := (TagLength - Length(StartToken)) - Length(EndToken);
ParseValue := Copy(Memo.SelText, (ParseStart + Length(StartToken)), ValueLength);
Memo.selstart := ParseStart - 1; //since the .text is zero based, but pos is 1 based we subtract 1
Memo.sellength := TagLength;
RichEditor.selstart := ParseStart - 1; //since the .text is zero based, but pos is 1 based we subtract 1
RichEditor.sellength := TagLength;
TempText := GetValue(ParseValue);
Memo.SelText := TempText;
RichEditor.SelText := TempText;
end;
except
on e: exception do
begin
MessageDlg(e.message,mtInformation,[mbOK],0);
result := false;
end;
end;//try..except
finally
FreeAndNil(Memo);
end;
end;
How about subtracting EM_LINEFROMCHAR from the caret position? (OR the position of EM_GETSEL) whichever you need.
You could even get two EM_LINEFROMCHAR variables. One from the selection start and the other from the desired caret/selection position, if you only want to know how many cl/cr pairs are in the selection.

How to handle large String Grids?

I find that I might have to use a String Grid of less than 10 columns, but about 50k rows.
Experiments have showed it to be a very unresponsive CPU hog.
Any pointers?
Code or components?
Preferably Delphi (7) build in or TMS (for which I have a license) or FOSS (for commercial use).
Update: please don't just tell me to use Virtual Tree View, etc. Please tell me why, so that I can learn something. Thanks.
I don't think the problem came from adding this number to the TStringGrid.
Adding 100k rows took less than 1 second (700ms) (Not high end PC, just Dual Core).
procedure TForm1.btn1Click(Sender: TObject);
Const
arr : array[1..5] of string = ('One','Two','Three','Four','Five');
Rows = 100000;
var
I: Integer;
F,E : Integer;
begin
StringGrid1.RowCount := Rows;
F := GetTickCount;
for I := 0 to Rows do
begin
StringGrid1.Cells[1,I] := Arr[1] + IntToStr(I);
StringGrid1.Cells[2,I] := Arr[2]+ IntToStr(I);
StringGrid1.Cells[3,I] := Arr[3]+ IntToStr(I);
StringGrid1.Cells[4,I] := Arr[4]+ IntToStr(I);
StringGrid1.Cells[5,I] := Arr[5]+ IntToStr(I);
end;
E := GetTickCount;
ShowMessage(Inttostr(E-F));
end;
I think the slowness in your code, do you bring the data from database? if so this is will be the bottleneck of your code, also adding 50k to WHATEVER GRID to show for users called "Bad practice".
And it's hard to tell you why that's slow without showing any code.
The TListView component in virtual mode is recommended frequently (I have not tried it myself but it sounds rather easy to implement)
If you are interested in virtual treeview you should checkout the whole virtual treeview trunk from http://code.google.com/p/virtual-treeview/source/checkout. You will find Demos\Advanced subdirectory where and Demo application showing the virtual treeview functionality, for example how to use virtual treeview as a grid.
You need SVN to checkout the code from googlecode. If you have never used SVN download and install TortoiseSVN
You may want to look at Virtual Treeview, which is built for high volume:
http://www.delphi-gems.com/index.php?option=com_content&task=view&id=12&Itemid=38

Delphi: Problems with TList of Frames

I'm having a problem with an interface that consists of a number of frames (normally 25) within a TScrollBox.
There are 2 problems, and I am hoping that one is a consequence of the other...
Background:
When the application starts up, I create 25 frames, each containing approx. 20 controls, which are then populated with the default information. The user can then click on a control to limit the search to a subset of information at which point I free and recreate my frames (as the search may return < 25 records)
The problem:
If I quit the application after the initial search then it takes approx. 5 seconds to return to Delphi. After the 2nd search (and dispose / recreate of frames) it takes approx. 20 seconds)
Whilst I could rewrite the application to only create the frames once, I would like to understand what is going on.
Here is my create routine:
procedure TMF.CreateFrame(i: Integer; var FrameBottom: Integer);
var
NewFrame: TSF;
begin
NewFrame := TSF.Create(Self);
NewFrame.Name := 'SF' + IntToStr(i);
if i = 0 then
NewSF.Top := 8
else
NewSF.Top := FrameBottom + 8;
FrameBottom := NewFrame.Top + NewFrame.Height;
NewFrame.Parent := ScrollBox1;
FrameList.Add(NewFrame);
end;
And here is my delete routine:
procedure TMF.ClearFrames;
var
i: Integer;
SF: TSF;
begin
for i := 0 to MF.FrameList.Count -1 do
begin
SF := FrameList[i];
SF.Free;
end;
FrameList.Clear;
end;
What am I missing?
As you are taking control over the memory allocation of the Frames you are creating by Free'ing them, so there's no need to provide Self as the owner parameter in the create constructor. Pass nil instead to prevent the owner trying to free the frame.
Also, don't like the look of your ClearFrames routine. Try this instead:
while FrameList.count > 0 do
begin
TSF(Framelist[0]).free;
Framelist.delete(0);
end;
Framelist.clear;
If you want to know why your app is taking so long to do something, try profiling it. Try running Sampling Profiler against your program. The helpfile explains how to limit the profiling to only a specific section of your app, which you could use to only get sampling results on the clearing or creating parts. This should show you where you're actually spending most of your time and take a lot of the guesswork out of it.

TListView performance issues

I tried to use a TListView component to display rather large data lists (like 4000 rows large), and creating the list is incredibly slow - it takes something like 2-3 secs, which makes the UI all laggy and close to unusable.
I fill the TListView.Items inside a BeginUpdate/EndUpdate block, with only preallocated strings - I mean : I build a list of all strings to store (which takes no humanly noticeable time), then I put them in the TListView.
I wish to display the TListView's content in vsReport mode with several columns.
The code looks like this :
MyList.Items.BeginUpdate;
for i := 0 to MyCount - 1 do
begin
ListItem := MyList.Items.Add;
ListItem.Caption := StrCaptions[i];
ListItem.SubItems.Add(StrSubItems1[i]);
ListItem.SubItems.Add(StrSubItems2[i]);
end;
MyList.Items.EndUpdate;
Is there some other hack I missed in the TListView component's logic ? or should I just forget about using this component for performances ?
You can use listview in virtual mode. Have a look at the virtuallistview.dpr demo.
You can try Virtual Treeview component. It says "Virtual Treeview is extremely fast. Adding one million nodes takes only 700 milliseconds"
Use separate structure for holding your data. Set OwnerData of TListView to True.
#4000 rows I get only ~700 ms (D2009) times. For more responsiveness you could separate to other thread or add dirty Application.ProcessMessages() into loop.
rows generated with this code in 16 ms:
MyCount := 4000;
dw := GetTickCount();
for i := 0 to MyCount - 1 do begin
StrCaptions.Add('caption'+IntToStr(i));
StrSubItems1.Add('sub1'+IntToStr(i));
StrSubItems2.Add('sub2'+IntToStr(i));
end;
ShowMessageFmt('%u ms', [GetTickCount() - dw]);
Printed with:
MyList.Clear;
dw := GetTickCount();
MyList.Items.BeginUpdate;
for i := 0 to MyCount - 1 do
begin
ListItem := MyList.Items.Add;
ListItem.Caption := StrCaptions[i];
ListItem.SubItems.Add(StrSubItems1[i]);
ListItem.SubItems.Add(StrSubItems2[i]);
end;
MyList.Items.EndUpdate;
ShowMessageFmt('%u ms', [GetTickCount() - dw]);
EDIT:
I inserted Application.ProcessMessages() into print, but somewhy performance stays same

Delphi(2006) Loop Help

So What I'm essentially trying to do is have something happen 70% of the time, another few things happen 10% of the time each if that makes sense but my app doesn't seem to do any of the actions I'm guessing I'm misunderstanding the loop syntax or something, anyway if anyone could take a look and maybe give me some advice
per1 := 70;
per2 := 77;
per3 := 84;
per4 := 91;
per5 := 100;
per6 := Random(2) + 1;
randomize;
RandPer:= Random(100);
randomize;
RandPer2 := Random(100);
if RandPer2 <= 70 then begin
If RandPer <= per1 then begin
Functiontest(1);
end Else If RandPer <= per2 then begin
Functiontest(3);
end Else begin If RandPer <= per3 then begin
Functiontest(5);
end Else begin If RandPer <= per4 then begin
Functiontest(6);
end Else begin If RandPer <= per5 then begin
Functiontest(9);
end;
end;
end;
end;
You don't have any loop syntax, so that's certainly a possible source of your confusion.
Do not call Randomize multiple times. It reinitializes the random seed each time you do, and that's based on the system clock. If your code runs faster than the clock advances, then your several calls to Randomize will actually reset the random seed to the same value it had before, resulting in repeated Random calls returning the same value.
The help advises you to call Randomize just once at the start of your program. If you are writing a unit or component and you are not in charge of the whole program, then do not call Randomize at all. Instead, document that consumers of your code should call it themselves.
If you are writing a DLL and not using run-time packages, then call Randomize in an initialization function that your DLL exports; consumers of your DLL won't have access to your DLL's copy of the Delphi run-time library.
Also, if you want something to happen 70 percent of the time, then you should check whether your value is strictly less than 70. The possible return values of Random include zero; 70 percent of the results will be between 0 and 69 inclusive. Allowing 70 will actually make the event happen 71 percent of the time.
Finally, your calculations of 10 percent of the time don't make sense to me. You have three events that will happen 7 percent of the time, and one that will happen 9 percent of the time. You can't have four events that each happen 10 percent of the time when you only have 30 percent remaining. Do you mean for each event's frequency to be measured independently of the others? If so, then do not link all your conditional tests together with else; Use completely a separate if statement for each one.
I just modified CharlesF code to do what you need.
Hope CharlesF won't mind.
begin
randomize;
for i := 0 to NumberOfTimesNeed do
begin
R := Random(100);
case R of
0..69 : Functiontest(1); // this will fire 70% of NumberofTimes
70..79 : Funciotntest(2); // 10 percent
80..89 : Funciotntest(3); // 10 percent
90..94 : Funciotntest(4); // 5 percent
// and so on ...
end;
end;

Resources