How to get/find the variable that caused Division By Zero error in delphi? - delphi-xe3

I know how to do basic exception handling. So i can raise a message on divide by zero using the 'try except' method.
What i would like to do is, find the variable that causes this error and then change its value on run time.
For Ex:
procedure Calculate();
var
a, b, c : Double;
begin
try
a := 4; //suppose i take this value from user and he enters 4
b := 0; //suppose i take this value from user and he enters 0
c := a/b;
ShowMessage(FloatToStr(c));
except
on E : EZeroDivide do
begin
ShowMessage('Exception message = '+E.Message);
//i am not sure how to identify that its variable 'b' that is causing the error and has to be changed by a default value
get(E....errorVaraiable);
E....errorVaraiable := 0.00001;
c := a/E....errorVariable;
ShowMessage(FloatToStr(c));
end;
end;
Please, can anyone help me with this?

Here's a modified version of your example that does what you want.
procedure Calculate();
var
a, b, c : Double;
begin
a := 4; //suppose i take this value from user and he enters 4
b := 0; //suppose i take this value from user and he enters 0
if IsZero(b) then
begin
ShowMessage('b cannot be 0')
end
else
begin
c := a/b;
ShowMessage(FloatToStr(c));
end;
end;

Related

Numbering with TRichEdit in Delphi

I am trying to implement numbering in TRichEdit component, Delphi. Ideally I want to get the same behavior as in these 3rd party component:
As you can see Numbering button works similar way as Bullet button. I mean it sets FirstIdent or LeftIdent (I am not sure) and put the numbers 1,2,3,... instead of bullets. When you move cursor to the left close to number it does not allow to move onto number but jumps one line up.
This is what I got so far:
procedure TMainForm.NumberingButtonClick(Sender: TObject);
var
i: Integer;
s: String;
begin
if NumberingButton.Down then
begin
Editor.Paragraph.Numbering := nsNone;
i := Editor.ActiveLineNo;
s := Editor.Lines[i];
insert(inttostr(i)+'. ', s, 1);
//Editor.Paragraph.LeftIndent := 10;
Editor.Paragraph.FirstIndent := 10;
Editor.Lines[i] := s;
end;
end;
But it does not work as I want. Anybody have any ideas?
This code works exactly how I expected:
procedure TMainForm.NumberingButtonClick(Sender: TObject);
var
i: Integer;
s: String;
fmt: TParaFormat2;
begin
FillChar(fmt, SizeOf(fmt), 0);
fmt.cbSize := SizeOf(fmt);
fmt.dwMask := PFM_NUMBERING or PFM_NUMBERINGSTART or
PFM_NUMBERINGSTYLE or PFM_NUMBERINGTAB;
if NumberingButton.Down then
fmt.wNumbering := 2
else
fmt.wNumbering := 0;
// wNumbering:
// 0 - no numbering
// 1 - bullet list (·, ·, ·, ...).
// 2 - Arabic numbers (1, 2, 3, ...).
// 3 - small letters (a, b, c, ...).
// 4 - capital letters (A, B, C, ...).
// 5 - small Roman numbers (i, ii, iii, ...).
// 6 - capital Roman numbers (I, II, III, ...).
// 7 - Unicode character sequence
fmt.wNumberingStart := 1;
// wNumberingStart:
// The number at which the numbering starts.
fmt.wNumberingStyle := $200;
// wNumberingStyle:
// Numbering Style
// 0 : 1)
// $100 : (1)
// $200 : 1.
// $300 : 1
// $400 : remove list
// $8000 : continues to number the list without changing the style
fmt.wNumberingTab := 1440 div 4;
// wNumberingTab:
// the space between number and paragraph text
Editor.Perform( EM_SETPARAFORMAT, 0, lParam( #fmt ) );
if BulletsButton.Down then
BulletsButton.Down := False;
end;
Thanks to www.decoding.dax.ru

Maple: RNG is not random

i was "finding Pi" with Monte Carlo Method, but the answer was incorrect. The oryginal code was:
RandomTools[MersenneTwister]: with(Statistics):
tries := 10000:
s := 0;
for i to tries do
if GenerateFloat()^2+GenerateFloat()^2 < 1 then s := s+1 end if;
end do:
evalf(4*s/tries)
It gives answer aroud 2.8-2.85
when I change the code to
s := 0;
x := Array([seq(GenerateFloat(), i = 1 .. tries)]);
y := Array([seq(GenerateFloat(), i = 1 .. tries)]);
for i to tries do
if x[i]^2+y[i]^2 < 1 then s := s+1 end if;
end do:
evalf(4*s/tries)
Then the answer is correct. I have no idea why i can't generate number in "for" loop.
I've founded that the mean of it is the same, but the variance is different.
For:
tries := 100000;
A := Array([seq(GenerateFloat(), i = 1 .. 2*tries)]);
s1 := Array([seq(A[i]^2+A[tries+i]^2, i = 1 .. tries)]);
Mean(s1);
Variance(s1);
s2 := Array([seq(GenerateFloat()^2+GenerateFloat()^2, i = 1 .. tries)]);
Mean(s2);
Variance(s2);
output is:
0.6702112097021581
0.17845439723457215
0.664707674135025
0.35463131700965245
What's wrong with it? GenerateFloat() should be as uniform as possible.
Automatic simplification is turning your,
GenerateFloat()^2+GenerateFloat()^2
into,
2*GenerateFloat()^2
before GenerateFloat() is evaluated.
One simple change to get it to work as you expected would be separate them. Eg,
restart:
with(RandomTools[MersenneTwister]):
tries := 10^4:
s := 0:
for i to tries do
t1,t2 := GenerateFloat(),GenerateFloat();
if t1^2+t2^2 < 1 then s := s+1 end if;
end do:
evalf(4*s/tries);
Another way is to use a slightly different construction which doesn't automatically simplify. Consider, single right quotes (uneval quotes) don't stop automatic simplification (which is a definition of the term if you want).
'f()^2 + f()^2';
2
2 f()
But the following does not automatically simplify,
a:=1:
'f()^2 + a*f()^2';
2 2
f() + a f()
Therefore another easy workaround is,
restart:
with(RandomTools[MersenneTwister]):
tries := 10^4:
s := 0:
a := 1;
for i to tries do
if GenerateFloat()^2 + a*GenerateFloat()^2 < 1 then s := s+1 end if;
end do:
evalf(4*s/tries);

Form resource not found after on-the-fly String-Resource translation

I have a problem which occurs only in a very small customer range and I would like to ask if you might give me a hint where the problem might be. The program works for 98% of the customers. Alas, it is not possible that I work with the customers to debug the issue, because their knowledge of Windows and computers is very basic. It is also not possible that I send multiple versions of the product to them, since they don't even know how to install software (the admins do all the stuff).
First of all, I translate all RT_STRING resources on-the-fly, so that the language-switching in the program also affects hardcoded stuff like "Yes", "No", "Cancel" etc., which would only be possible by compiling 2 EXE files.
The code (I have tried to left away as much unnecessary stuff as possible, but since I don't know where the problem is, I provided as much details for the bug as possible):
The ony-the-fly resource translation
procedure TranslateResources;
var
i: integer;
s: string;
{$IF NOT Declared(FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)}
const
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = $2000;
{$IFEND}
begin
// I copy all resources in a dummy DLL (without code), because
// 1) The resources are the only thing we need when changing the resource module
// 2) If the EXE code/debug sections are too long, BeginUpdateResource() will ruin the performance heavily
FTempFile := IncludeTrailingPathDelimiter(GetTempDirectory) + GetRandomString(8)+'.dll';
// Transfers all resources from ParamStr(0) into the dummy DLL at FTempFile
ReGenerateResourceFile(FTempFile);
// if necessary, remove readonly flag
SetFileAttributes(PChar(FTempFile), FILE_ATTRIBUTE_OFFLINE or
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED or
FILE_ATTRIBUTE_TEMPORARY );
for i := 0 to Length(RTLResStringTranslationArray)-1 do
begin
s := Translate(RTLResStringTranslationArray[i].TranslationID);
if s <> '' then
begin
// Translate the string
UpdateResString(RTLResStringTranslationArray[i].ResStrDescriptor.Identifier, s);
end;
end;
LoadNewResourceModule(FTempFile):
end;
procedure ReGenerateResourceFile(OutputFile: string);
var
hUpd: Cardinal;
rs: TResourceStream;
fs: TFileStream;
begin
// As template we use a dummy DLL which contains no code.
// We will implement all resources from ParamStr(0) into it, before we translate the strings.
rs := TResourceStream.Create(HInstance, 'DUMMYDLL', 'DLL');
fs := TFileStream.Create(OutputFile, fmCreate or fmOpenWrite);
try
fs.CopyFrom(rs, rs.Size)
finally
rs.Free;
fs.Free;
end;
// Transfer resources from our EXE into the dummy DLL file
hUpd := BeginUpdateResource(PChar(OutputFile), true);
try
EnumResourceTypes(hInstance, #_enumResTypesProc, hUpd);
finally
EndUpdateResource(hUpd, false)
end;
end;
// This is based on reinit.pas from Borland's RichEdit example; slightly modified
function LoadNewResourceModule(PatchedFile: string): LongInt;
var
NewInst: Longint;
CurModule: PLibModule;
begin
Result := 0;
// Win95: "Initialization routine failed"
// NewInst := LoadLibrary(PChar(PatchedFile));
NewInst := LoadLibraryEx(PChar(PatchedFile), 0, LOAD_LIBRARY_AS_DATAFILE);
CurModule := LibModuleList;
Result := 0;
while CurModule <> nil do
begin
if CurModule.Instance = HInstance then
begin
if CurModule.ResInstance <> CurModule.Instance then
FreeLibrary(CurModule.ResInstance);
// Win95: ERangeError
CurModule^.ResInstance := NewInst;
Result := NewInst;
Exit;
end;
CurModule := CurModule.Next;
end;
end;
// Based on http://stackoverflow.com/questions/1498658/modifying-a-string-in-resource-of-an-exe
// Modified
procedure UpdateResString(const AStringIdent: Integer; const ANewString: WideString);
var
ResData, TempData: TWordArray;
iSection, iIndexInSection: Integer;
i, iLen, iSkip, iPos: Integer;
begin
// Calculate the resource string area and the string index in that area
iSection := AStringIdent div 16 + 1;
iIndexInSection := AStringIdent mod 16;
ResData := ReadSectionCached(iSection);
// Calculate the position of the string
iLen := Length(ANewString);
iPos := 0;
for i := 0 to iIndexInSection do
begin
if iPos > High(ResData) then
begin
SetLength(ResData, iPos + 1);
ResData[iPos] := 0;
end;
if i <> iIndexInSection then
begin
iSkip := ResData[iPos] + 1;
Inc(iPos, iSkip);
end;
end;
// Put data behind strings into TempData
iSkip := 1{size} + ResData[iPos];
SetLength(TempData, Length(ResData) - (iPos + iSkip));
if Length(TempData) > 0 then
begin
CopyMemory(#TempData[0], #ResData[iPos + iSkip], Length(TempData)*SizeOf(TempData[0]));
end;
SetLength(ResData, iPos + (iLen + 1{size}) + Length(TempData));
// Overwrite string
ResData[iPos] := iLen;
Inc(iPos);
if iLen > 0 then
begin
CopyMemory(#ResData[iPos], #ANewString[1], iLen*SizeOf(ANewString[1]));
Inc(iPos, iLen);
end;
// Append TempData after our new string
if Length(TempData) > 0 then
begin
CopyMemory(#ResData[iPos], #TempData[0], Length(TempData)*SizeOf(TempData[0]));
end;
CacheSet(iSection, ResData);
end;
type
TGlobalData = record
GlobalPtr: Pointer;
Length: integer;
end;
function LoadResourcePtr(hModule: HMODULE; restype, resname: PChar; wIDLanguage: WORD): TGlobalData;
var
hFind, hRes: THandle;
begin
result.GlobalPtr := nil;
result.Length := -1;
hFind := Windows.FindResourceEx(hModule, restype, resname, wIDLanguage);
if hFind = 0 then RaiseLastOSError;
hres := Windows.LoadResource(hModule, hFind);
if hres = 0 then RaiseLastOSError;
result.GlobalPtr := Windows.LockResource(hres);
result.Length := Windows.SizeofResource(hModule, hFind);
end;
function _enumResLangsProc(hmodule: HMODULE; restype, resname: PChar; wIDLanguage: WORD;
lParam: LongInt): BOOL; stdcall;
var
rs: TGlobalData;
begin
rs := LoadResourcePtr(hmodule, restype, resname, wIDLanguage);
UpdateResource(lParam, restype, resname, wIDLanguage, rs.GlobalPtr, rs.Length);
result := true;
end;
function _enumResNamesProc(hmodule: HMODULE; restype, resname: PChar;
lParam: LongInt): BOOL; stdcall;
begin
EnumResourceLanguages(hmodule, restype, resname, #_enumResLangsProc, lParam);
result := true;
end;
function _enumResTypesProc(hmodule: HMODULE; restype: PChar;
lParam: LongInt): BOOL; stdcall;
begin
EnumResourceNames(hmodule, restype, #_enumResNamesProc, lParam);
result := true;
end;
{$R '..\dummydll\dummydll.RES'}
Then I use a wait form:
unit Wait;
interface
uses
...
type
TWaitForm = class(TForm)
...
end;
var
WaitForm: TWaitForm;
implementation
{$R *.dfm}
...
end;
The wait form will be called by dynamically showing the form:
procedure ShowWaitForm;
begin
...
{ I use my own _CreateForm function because it solves many workarounds for
juicy stuff like half-modal windows (which can be hidden without user action),
miscellaneous deadlocks etc. and to allow the form to be shown in a shared PAS file
without the requirement to add it to every DPR file where the WaitForm API is used. }
WaitForm := _CreateForm(TWaitForm, {Application.MainForm}AParent) as TWaitForm;
WaitForm.Show;
...
end;
function _CreateForm(InstanceClass: TCustomFormClass; AParent: TCustomForm): TCustomForm;
var
LOwner: TComponent;
begin
if Assigned(AParent) then
begin
LOwner := AParent;
end
else if Assigned(Application) then
begin
LOwner := Application;
end
else
begin
LOwner := nil;
end;
result := InstanceClass.Create(LOwner);
end;
The error message at 2% of the customers:
Resource TWaitForm was not found
However, other forms are working.
There are 2 theories I can think of:
1) Did the resource translation corrupt the DLL file / part of the RCData section? (Maybe a bug in the WinAPI's UpdateResource ?)
2) Is there a problem with the dynamic showing of the wait form (since other "static" forms are shown?)

How to prevent forms moving of screen

I use the following helper for preventing forms moving of screen and it is most of the time working OK. But if I open a wsNormal form in a MDI app then the form might show up of the area where it is supposed to be. I can then just move it a bit and then the unit here takes over and moves it in place.
My question is now: how can I either prevent this from happening or send a message to the form saying it is moving so the unit her can do its job.
unit U_FormsMove;
interface
uses
Messages, Windows, Forms;
{$M+}
type
TForm = class(Forms.TForm)
private
protected
procedure WMMoving(var message : TWMMoving); message WM_MOVING;
published
public
end;
implementation
function GetMovementArea: TRect;
var
MovementRect: TRect;
begin
if Application.MainForm.FormStyle = fsMDIForm then
Windows.GetWindowRect(Application.MainForm.ClientHandle, MovementRect)
else
SystemParametersInfo(SPI_GETWORKAREA, 0, #MovementRect, 0);
if MovementRect.Top < 150 then
MovementRect.Top := 150;
MovementRect.Top := MovementRect.Top + 5;
MovementRect.Left := MovementRect.Left + 5;
MovementRect.Right := MovementRect.Right - 5;
MovementRect.Bottom := MovementRect.Bottom - 5;
Result := MovementRect;
end;
{ TFormHelper }
procedure TForm.WMMoving(var Message: TWMMoving);
var
rec: ^TRect;
wrk: TRect;
begin
wrk := GetMovementArea;
rec := Pointer(Message.DragRect);
if rec^.Left < wrk.Left then
begin
rec^.Right := rec^.Right - (rec^.Left - wrk.Left);
rec^.Left := wrk.Left;
end
else if rec^.Right > wrk.Right then
begin
rec^.Left := rec^.Left - (rec^.Right - wrk.Right);
rec^.Right := wrk.Right;
end;
if rec^.Top < wrk.Top then
begin
rec^.Bottom := rec^.Bottom - (rec^.Top - wrk.Top);
rec^.Top := wrk.Top;
end
else if rec^.Bottom > wrk.Bottom then
begin
rec^.Top := rec^.Top - (rec^.Bottom - wrk.Bottom);
rec^.Bottom := wrk.Bottom;
end;
end;
end.

What causes problems with this piece of code?

I was just wondering why this certain problem happens to me. If you can help me, I will appreciate it.
Program factorial;
uses crt;
var
f, i: Integer;
begin
f:=1;
for i:=1 to 5 do
f:= f * i;
write(f);
readkey;
end.
Okay, That works fine and the result is 120. And it's true.
Now, here's the problem. If I asked the user to enter the value of the number, it won't work.
Program factorial;
uses crt;
var
i,r: Integer;
begin
write('Enter the number');
read(r);
for i:=1 to r do
r:= r * i;
write(r);
readkey;
end.
If I wrote 5, the output will be 600.
You are using the value r as the stopping condition of the loop and modifying the value in the loop.
Program factorial;
uses crt;
var
i,r, f: Integer;
begin
write('Enter the number');
read(r);
f := 1;
for i:=1 to r do
f:= f * i;
write(f);
readkey;
end.
You reuse the r variable. If you input 5 for r your program will in effect one to many times. You should start with 1 as the first f.
Program factorial;
uses crt;
var
i,r, f: Integer;
begin
write('Enter the number');
read(r);
f:=1
for i:=1 to r do
f:= f * i;
write(r);
readkey;
end.
try:
Program factorial;
uses crt;
var
i,r,x: Integer;
begin
write('Enter the number');
read(x);
r:=1
for i:=1 to x do
r:= r * i;
write(r);
readkey;
end.