Version: 2006-09-16
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-2006 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
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.
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.
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.
If, when rounding to two decimal places, the nominal pre-rounding decimal value is liable to be of the form X.yz5, note that of those, only X.125, X.375, X.625, and X.875 can be stored exactly, and are the only true half-way cases.
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.
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.)
Henry Bartlett bravely explained all the maddening details in a Usenet article.
You should never create a tStrings object. Instead create one of its descendants like tStringList.
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';
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.
You can't. Use a tRichEdit instead.
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".
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.
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!
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.
See http://www.bancoems.com/mini_faq_examples.htm for example code and a component that does this.
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.)
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.
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.
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.
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. Alternatively, the new class could just always open keys read-only.
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:
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.
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 it, but then be careful that you know what you are doing.
See http://www.bancoems.com/mini_faq_examples.htm for example code.
Yes, you can, but there's a catch. The DLL will not use the same classes as the main program even when compiled from the same source. Objects will look the same, but their classes will not compare equal. That's why assigning a TFont value from the DLL to a TFont property in the main program (or the other way around) doesn't work: the Assign procedure is looking for the main program's TFont class, and never recognises the DLL's TFont object.
Using packages instead of normal DLLs solves this problem. Packages use the main program's classes. This is the preferred solution.
From the menu, select Project/Options, and pick the Compiler tab.
For debugging, turn all runtime error checking on, all messages on, and almost all debug information on. Using Debug DCUs is optional. Turn Optimization off.
In release code, range checking and overflow checking are still valuable.
All the options can also be set or cleared in the code using compiler directives. These then override the active settings from that point down. Combined with conditional compilation, this lets you use different settings in different cases without having to change the IDE's global settings each time.
These links may prove useful. Inclusion here should not necessarily be taken as an endorsement.