FAQ: comp.lang.pascal.delphi.misc miniFAQ (full version)

From: Evil miniFAQ Boss (miniFAQ_at_mfw.dds.nl)
Date: 01/01/05


Date: 01 Jan 2005 02:59:25 GMT

FAQ FOR NEWSGROUPS: COMP.LANG.PASCAL.DELPHI.MISC, ALT.COMP.LANG.BORLAND-DELPHI

Version: 2004-05-31
First maintainer: Esther Michaels
Current maintainer: Maarten Wiltink (Evil miniFAQ Boss)
Posting schedule: Short version every Sunday, full version every first of
        the month

Copyright 2001-2004 the current maintainer, all rights reserved except the
        right to re-distribute the current document. Even better: give out the
        URL.

--------------------------------------------------------------------------------

Latest version:
        http://www.bancoems.com/CompLangPascalDelphiMisc-MiniFAQ.htm
Plain text version:
        http://www.bancoems.com/CompLangPascalDelphiMisc-MiniFAQ.txt
Examples page:
        http://www.bancoems.com/mini_faq_examples.htm

--------------------------------------------------------------------------------

CONTENTS
1. How do I start another program from my program?
2. How to stop a second instance of my program from executing? (Also: HPrevInst
        doesn't seem to work, why?)
3. Floating-point work - i.e. with single, real, real48, double, extended -
        gives long decimals or wrong numbers. Why?
4. Is there a limit on the number of characters allowed in a tMemo?
5. Why do I get an "abstract error" when I try to use a tStrings variable?
6. How do I access a component/property/field of Form1 from Form2?
7. How do I change the background, font, general appearance of an entry in a
        tListView, tTreeView, tListBox, tCombobox list, tStringGrid, etc.?
8. How do I change the formatting of a line/word/character in a tMemo?
9. Why does the debugger show my For loop variable counting down, not up?
10. How do I get the path of my executable? And, how do I retrieve the command
        line parameters?
11. When my program is in a loop it doesn't respond to user input or update its
        display. Can I change this?
12. How do I access the compilation date in my program?
13. How do I access the VersionInfo tab's data at runtime?
14. Does Delphi have an equivalent to the VB Split function?
15. I create a label/edit/some other control at run-time but it doesn't appear
        on the form. Why?
16. How do I use streams?
17. How do I declare an event handler for an object that I create at runtime?
18. When using the TRegistry component under Windows NT, Windows 2000, or
        Windows XP, a user with less than Administrator rights is unable to
        access information stored in the HKEY_LOCAL_MACHINE hive. How can I
        work around this?
19. When drawing text on a canvas, how do I determine the resulting text size?
20. How can I loop through the components/labels/edit-boxes on my form? (Also:
        Tag is unused, what is it good for?)

USEFUL LINKS

--------------------------------------------------------------------------------

1. How do I start another program from my program?

The most common methods of doing this employ either ShellExecute or
CreateProcess, both of which are documented in the WindowsSDK help
file. ShellExecute is quite versatile and has the advantage of being
simple and able to open or print any file for which there is a file
association registered on the system. CreateProcess is a little more
complex but provides much better control over the process. Information
returned by CreateProcess can be used to pause a program until the
called program has completed (using WaitForSingleObject). See
http://www.bancoems.com/mini_faq_examples.htm for example code showing
how to call each of these procedures.

--------------------------------------------------------------------------------

2. How to stop a second instance of my program from executing? (Also:
HPrevInst doesn't seem to work, why?)

HPrevInst only works in Delphi 1. There are several methods of handling
this in Delphi 2 and above. The most common recommendation is to use a
mutex (see the Windows SDK help). See
http://www.bancoems.com/mini_faq_examples.htm for example code.

--------------------------------------------------------------------------------

3. Floating-point work - i.e. with single, real, real48, double, extended -
gives long decimals or wrong numbers. Why?

Because computers are finite, all floating point formats are approximations.
Values that have no exact representation are simply replaced by the value
closest to it. It's like replacing "1/3" by "0.3333", and the computer has
no thoughts about what that number means. Multiplying it by 3 again gives
0.9999, not 1. Also, Trunc(0.9999) will return 0, not 1.

Comparing floating point numbers should not be done by testing for exact
equality, but for a difference being smaller than the inaccuracy in the
computed numbers. Errors start larger for shorter types and grow further
as more calculations are performed with inexact numbers.

Testing for exact equality is appropriate when possible copies are compared.
But this is not often; even assigning a literal constant to a variable in
source loses precision as determined by the variable's type.

If you must have precision use the Currency type (4 decimals) or use an
integer type and scale the results. When it's just a question of displaying
a result, use one of the formatting functions that allow you to specify
the number of decimal places to display - Format and FormatFloat. See
http://www.efg2.com/Lab/Library/Delphi/MathInfo for more info on this and
other math related questions.

--------------------------------------------------------------------------------

4. Is there a limit on the number of characters allowed in a tMemo?

Yes. The limit depends on the operating system and can be as low as 32KB.
If you need to have more than 32KB in a memo, use a tRichEdit instead.
Set the rich edit's PlainText property to True and its MaxLength property
to an appropriately large value, e.g. RichEdit1.MaxLength := MaxInt - 2;
allows the RichEdit1 to work with ~2GB. (Setting MaxLength to 0 as
suggested for tCustomEdit in the help doesn't work for rich edits.)

--------------------------------------------------------------------------------

5. Why do I get an "abstract error" when I try to use a tStrings variable?

You should never create a tStrings object. Instead create one of its
descendants like tStringList.

--------------------------------------------------------------------------------

6. How do I access a component/property/field of Form1 from Form2?

Add the Form1 unit to the Uses clause in the implementation section of Form2.
(By default there is no uses clause so you may have to add one - do so just
after the '{$R *.DFM}' line.) You can then reference items belonging to
Form1 by prefixing them with 'Form1.', e.g.,
Form1.Label1.Caption := 'Hi there, I was set from Form2';

--------------------------------------------------------------------------------

7. How do I change the background, font, general appearance of an entry in a
tListView, tTreeView, tListBox, tCombobox list, tStringGrid, etc.?

You have to use the owner draw event (typically OnDraw<something>) to
draw the items. This means that you have to draw each item when the
control asks you to do so. See
http://www.bancoems.com/mini_faq_examples.htm for example code.

--------------------------------------------------------------------------------

8. How do I change the formatting of a line/word/character in a tMemo?

You can't. Use a tRichEdit instead.

--------------------------------------------------------------------------------

9. Why does the debugger show my For loop variable counting down, not up?

The Delphi optimizer may generate loop code that decrements an internal
counter instead of your variable. This behavior is guaranteed not to alter
the correctness of your program. Ignore the debugger value and consider
the For loop variable "unavailable due to optimization".

--------------------------------------------------------------------------------

10. How do I get the path of my executable? And, how do I retrieve the
command line parameters?

Application.ExeName will return the full name of your executable. You can
get the path portion using ExtractFilePath. ParamStr(0) will return the
same value as Application.ExeName. To retrieve other command line arguments
use the ParamCount and ParamStr functions. See also FindCmdLineSwitch and
CmdLine in the help.

--------------------------------------------------------------------------------

11. When my program is in a loop it doesn't respond to user input or update
its display. Can I change this?

Yes. In your loop you need to add a call to the Application.ProcessMessages
method. This will allow your application to process Windows messages,
including those generated by user actions. There are two significant
caveats. First, since Windows messages often translate into calls to
event handlers your program may begin to do things at inappropriate
times. Make sure that the user can't initiate actions that will interfere
with the loop while the loop is active. In particular, note the following
sentence, taken from Delphi 3's help file on TApplication.Terminated:
"For applications using calculation-intensive loops, call
Application.ProcessMessages periodically, and also check
Application.Terminated to determine whether or not to abort
the calculation so that the application can terminate."
The second caveat is that calling Application.ProcessMessages can be
relatively expensive and may slow the program. In a fast (tight) loop
you may not want to call the method on each iteration. If you only want
to update the display and not handle user input you can use the Update
method (Delphi 3 and up) of the control covering the part of the display
you want to update. Remember that this will also slow down the loop!

--------------------------------------------------------------------------------

12. How do I access the compilation date in my program?

There are a number of ways to achieve this, depending on the architecture
of your program. If the program consists of a single executable whose
creation date is guaranteed never to change after compilation, then the
following code will be sufficient:
DateToStr(FileDateToDateTime(FileAge(Application.ExeName))).
This returns a string containing the date of the .exe file, formatted
using the host computer's current date format setting. However, these
circumstances don't hold for most programs. In these cases one solution
is to create a file, perhaps called Today.pas, which holds a single
constant: const COMPILE_DATE = '2001-11-17'; This file should be updated
daily, perhaps at boot time. In units of your program which need to refer
to the compilation date, you add the file to the "uses" clause, and then
simply refer to the constant as you would any other. For example, an About
box might contain a TEdit whose job is to display the compilation date:
Edit1.Text := COMPILE_DATE; This will then show the compilation date in
your About box.

--------------------------------------------------------------------------------

13. How do I access the VersionInfo tab's data at runtime?

See http://www.bancoems.com/mini_faq_examples.htm for example code and a
component that does this.

--------------------------------------------------------------------------------

14. Does Delphi have an equivalent to the VB Split function?

There is no direct equivalent. However one can use the CommaText property
of a tStrings to achieve the same result.

Example:

  procedure Split(const aString, Delimiter: string; Results: tStrings);
  begin
    Results.CommaText:='"' + StringReplace(aString, Delimiter, '","', [rfReplaceAll]) + '"';
  end;

(Note: the Results parameter is declared as tStrings. If you are using a
variable as the actual parameter, (as opposed to a VCL control's property,
e.g. tMemo.Lines, tListBox.Items, and tComboBox.Items), see question 5.)

--------------------------------------------------------------------------------

15. I create a label/edit/some other control at run-time but it doesn't
appear on the form. Why?

The most common cause of this problem is failure to assign, or correctly
assign, the Parent property. When one creates a control at run-time the
Owner (passed as a parameter to the constructor) should in almost all
circumstances be the form. Once the control has been constructed one must
then assign it a Parent. In most circumstances the Parent should also be
the form. However, if one wants the control to be on a Panel or in a
GroupBox then the Panel/GroupBox control should be made the Parent.

--------------------------------------------------------------------------------

16. How do I use streams?

Alan Lloyd has written an excellent introduction to streams in the form of
a help file, available from http://www.bancoems.com/mini_faq_examples.htm.

--------------------------------------------------------------------------------

17. How do I declare an event handler for an object that I create at runtime?

Declare the event handler with the appropriate type signature (e.g. an
OnClick handler will be of type TNotifyEvent), then once you have created
the object simply assign the handler to the OnClick property of the object,
like this: MyRuntimeButton.OnClick := MyRuntimeButtonClickHandler; See
the examples file at http://www.bancoems.com/mini_faq_examples.htm for an
example.

--------------------------------------------------------------------------------

18. When using the TRegistry component under Windows NT, Windows 2000, or
Windows XP, a user with less than Administrator rights is unable to access
information stored in the HKEY_LOCAL_MACHINE hive. How can I work around this?

The problem is caused by the fact that TRegistry (and the derived TRegIniFile)
always opens a key with KEY_ALL_ACCESS privileges, even if only KEY_READ would
be needed.

In Delphi 5+, you can specify the access you require using the Access property.

You can also avoid this by going back to using the WinAPI registry functions
(RegOpenKey et. al.), or in Delphi versions that don't already have it, add an
Access property to a new class derived from TRegistry. Alternatiely, the new
class could just always open keys read-only.

--------------------------------------------------------------------------------

19. When drawing text on a canvas, how do I determine the resulting text size?

Create a bitmap and allocate a font to its canvas. To get the height of
the font elements, call the WinAPI function GetTextMetrics using the
bitmap's handle. Use TCanvas.TextWidth to get character widths. Some
printing terms are used in GetTextMetrics:

* Ascent is the pixel height of characters, including any diacritics,
  above the base height.
* Descent is the pixel amount below the baseline, in letters like "y" and "g".
* Internal leading (pronounced "ledding") is how much higher diacritics go
  than capitals.
* External leading is the minimum empty space between two lines above
  each other: between descent for the top line and capitals or diacritics
  for the bottom line.

So the vertical margins in a TEdit are TEdit.Height - (Ascent + Descent),
but this dimension is neither constant nor the same as External Leading.

All values from GetTextMetrics will be in pixels. A call to GetDeviceCaps
can tell you how many pixels there are in an inch on your output device.

-----------------------------------------------------------------------------

20. How can I loop through the components/labels/edit-boxes on my form?
(Also: Tag is unused, what is it good for?)

Loop through the Components array property of the form. To process only
one kind of component, test the class of each using the "is" operator.
To indicate special processing for any particular component, you can set
its Tag property in the Object Inspector.

If you use Tag to signify that a component should be treated specially
in your loop, use zero to mean that it should be ignored, to prevent
surprises later when you may add components to the form. Tag is declared
as LongInt and its default value is zero. If a 4-byte LongInt is not
enough for your purposes, you can store an object reference or a pointer
to a record in in, but then be careful that you know what you are doing.

See http://www.bancoems.com/mini_faq_examples.htm for example code.

--------------------------------------------------------------------------------

Links

These links may prove useful. Inclusion here should not necessarily be taken
as an endorsement.

Newsgroups Etiquette
        http://www.uwasa.fi/~ts/http/tsfaq.html
How to ask questions to maximum effect
        http://www.catb.org/~esr/faqs/smart-questions.html
Borland Delphi Community - TI, FAQ, links, articles
        http://community.borland.com/delphi/0,1419,1,00.html
Borland Delphi Newsgroups
        http://newsgroups.borland.com/cgi-bin/dnewsweb?cmd=listall&group=borland.public.delphi.&utag= (but first read the posting guidelines, at http://www.borland.com/newsgroups/guide.html)
Google - Newsgroups archives
        http://groups.google.com/groups?hl=en&lr=&safe=off&group=comp.lang.pascal.delphi.misc
        http://groups.google.com/
The Delphi Bug List - bugs and gotchas
        http://www.jrsoftware.org/buglist/bugsall.htm
Undu - Delphi articles, tips, techniques, links
        http://www.undu.com/
Deborah Pate's Pages - Word, Excel, etc. automation
        http://www.djpate.freeserve.co.uk/Automation.htm
Kyle Cordes BDE Alternatives Guide
        http://www.kylecordes.com/bag/index.html
Delphi Printing Info and Links
        http://www.efg2.com/Lab/OtherProjects/PrinterProjects.htm
BDE Support Page
        http://www.bdesupport.com/
Microsoft MSDN - Win API documentation
        http://msdn.microsoft.com/
Project JEDI - open source code library
        http://www.delphi-jedi.org/
Delphi Super Page (DSP) - components, links
        http://delphi.icm.edu.pl/
        http://community.borland.com/homepages/dsp
Torry's Delphi Pages - components, links
        http://www.torry.net/
ICS (Internet Component Suite) - internet components
        http://overbyte.delphicenter.com/frame_index.html
Indy (Internet Direct, was Winshoes) - internet components
        http://www.nevrona.com/Indy
Timo Salmi's Pascal programming material (a little dated but good general Pascal knowledge)
        http://www.uwasa.fi/~ts/http/http2.html#programming
Martin Harvey's tutorial on multi-threading
        http://www.pergolesi.demon.co.uk/prog/threads/ToC.html
Henry Bartlett's page on Delphi-related newsgroups
        http://www.hotkey.net.au/~hambar/habit/newsgroups.htm

--------------------------------------------------------------------------------

VERSION HISTORY

2004-May-31: Removed link to Tutorial Times (with thanks to Henry Bartlett).
        Added link to Eric Raymond's page on asking questions the smart way
        (with thanks to Martin Strand). Added a paragraph to Q3 (with thanks
        to John Stockton). Updated copyright notice (with thanks to Bruce
        Roberts).
2003-Oct-07: Updated copyright notice (with thanks to John Stockton).
2003-Sep-21: HTML tidied. Added copyright notice. Removed dead link from Q12.
        Rephrased Q20.
2003-Jul-29: Example date in Q12 changed to an unambiguous value. Reordered
        and slightly changed Q18. Rephrased Q19 and added note about units.
2003-Jun-27: Rephrased Q3, again. Added Q18 (with thanks to Dan Donoghue),
        Q19 (with thanks to Alan Lloyd), and Q20 (with thanks to Henry
        Bartlett). Added a link to Henry Bartlett's Delphi newsgroups page.
        Reordered document. Removed introduction.
2002-Nov-16: Rephrased Q3.
2002-Jul-27: Reformatted into HTML.
2002-May-10: Minor corrections, added links to Borland Delphi newsgroups
        and posting guidelines.
2002-Apr-21: Added Q17.
2002-Apr-11: Minor corrections, added link to Martin Harvey's multi-threading
        tutorial.
2002-Apr-06: Added Q16 (with thanks to Alan Lloyd for the referenced file)
2002-Jan-13: Added Q14 and Q15 (with thanks to Bruce Roberts), and updated
        introduction to fit with abbreviated-posting policy.
2001-Nov-22: Added Q13 (with thanks to Daniel Rutten).
2001-Nov-11: Added Q12 (with thanks to John Stockton).
2001-Oct-30: Updated Links section.
2001-Apr-26: Added reference to FindCmdLineSwitch and CmdLine in Q10.
2001-Apr-25: Formatted to 80 characters per line, added preamble, made
        html version.
2001-Apr-04: Initial version (kindly contributed by Bruce Roberts).

--------------------------------------------------------------------------------

END


Quantcast