Linking functions
Question
If I create a Delphi unit file (.PAS) with various functions and procedures
and use this unit in another unit but only use say one out of maybe five
functions will the other four be linked into the final .exe?
Answer
A:
Delphi has a very smart linker, which does not add unreferenced functions and
procedures to your final exe file.
This is actually better than almost all linkers used with C, which tend to
link in all functions in a given module (code file) if any one function is
referenced.
A:
I thought this to. But one of my forms had a "uses dll_link" where
dll_link was a component that used a dll. Although the component had
been removed from the form, the program would still crash if run
on a machine without the dll. Surely the linker should have stripped
all that code out, since I no longer called it, either explicitly,
nor, so far as I could follow, implicitly? Removing the "uses
dll_link" fixed the problem and reduced the exe size by 100k. So
obviously the linker hadn't stripped it out.
My conclusion is that the smart linker isn't quite smart enough to
strip all the unnecessary code out with a structure so convoluted
as delphi programs can get, what with my dumb coding, etc.
A:
If this is the case, why do you see mention of optimizing your compile size
by removing unneeded units (which delphi always includes by default) I have
seen size differences by doing this - why???
A:
I just ran a quick test on "Smart Linking" with Delphi. I created a "nothing"
app: one window, nothing else. In the FormCreate event, I put in two
variables and their initialization: one was a string, which I initialized to
'Hi there!' and the other was an Hwnd, which I initialized to "handle".
I created a second unit. In this unit, I included SysUtils, WinTypes, and
WinProcs. I created a function called "This". "This" takes two parameters:
an Hwnd and a String. It converts the string to a C-string, and calls
MessageBox. I did this because I wanted "This" do be a non-trivial function
(okay, it is trivial).
Anyway, the important thing is that there was no point where I called
"This". I did a "uses" in the form's unit for the second unit (where "This"
was sitting), but I never called "This".
I did a build, and noted the size of the executable.
Then, I went into the FormCreate procedure. I called "This" with the
variables I initialized earlier (the string and the window handle).
I did a build, and noted the size.
The second executable, where "This" was being called, was about 300 bytes
larger than the first build. So, it looks like, in that case, it stripped
out the unused fcn.
A:
The option "Optimize for size and load time" is a very different thing from
"smart-linking". Apparently most linkers waste a lot of space. It is a very
complicated thing to explain, and I don't understand all of it myself, but if
you want to read up on it, there's an article in MicroSoft Systems Journal,
the July 1993 issue, called "Liposuction your Corpulent Executables and
Remove Excess Fat". It is on the MSDN CD, if you have that. Anyway, to kind
of summarize the article, there is something known as "alignment", which is
similar to picking a good cluster size when creating a disk partition, that
often gets set poorly, wasting space. There are a number of other things as
well. Anyway, "Optimize for size and load time" is the same thing as running
the W8LOSS.EXE program (found in \Delphi\Bin) on your compiled app.
A:
One thing to remember is that when the linker does it's smart linking, it
doesn't actually run through the execution path of your code to see if a
particular function/procedure will be used. It only checks to see if the
function/procedure "could" be used. I haven't verified this, but there are
probably many such routines in the VCL that your program may never actually
use, but the code is linked in because the "possibility" exists that it
might be used.
In addition, I have noticed that a simple one-screen database application
quickly exceeds 500k, but when additional screens are added, the App does
not increase in size at the same rate.
A:
Types are not linked. They are only used by the compiler itself. Variables
would not be removed by the smart linker. And I think initialization code
will ALWAYS be called. When Delphi compiles a program, it is a two-step
process: first, all the units of the program are compiled to create the .DCU
files. Second, they are all linked together to get the .EXE file. It is
during this second step that any un-referenced functions/procedures are
removed. So, so far as you're concerned, when you're writing the program, it
really doesn't matter, because EVERYTHING is exported by the .DCU file.
Things will work as you would expect; there are no nasty surprises,
otherwise, it wouldn't be 'smart', and there would be an option to turn it
off.
Screen resolution
Question
Has anyone else noticed that your Delphi app looks wonderful on YOUR
windows setup, but gets screwed up on others?
Answer
A:
Here is an exerpt from Lloyd's Help File
==================================================
Screen Resolution
When designing forms, it is sometimes helpful to write the code so
that the screen and all of its objects are displayed at the same size
no matter what the screen resolution is. Here is some code to show
how that is done:
implementation
const
ScreenWidth: LongInt = 800; {I designed my form in 800x600 mode.}
ScreenHeight: LongInt = 600;
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
scaled := true;
if (screen.width <> ScreenWidth) then
begin
height := longint(height) * longint(screen.height) DIV
ScreenHeight;
width := longint(width) * longint(screen.width) DIV ScreenWidth;
scaleBy(screen.width, ScreenWidth);
end;
end;
Then, you will want to have something that checks to see that the
font sizes are OK. Before you change the font's size, you would need
to ensure the object actually has a font property by checking the
RTTI. This can be done as follows:
USES typinfo; {Add this to your USES statement.}
var
i: integer;
begin
for i := componentCount - 1 downto 0 do
with components[i] do
begin
if GetPropInfo(ClassInfo, 'font') <> nil then
font.size := (NewFormWidth DIV OldFormWidth) * font.size;
end;
end;
{This is the long way to do the same thing.}
var
i: integer;
p: PPropInfo;
begin
for i := componentCount - 1 downto 0 do
with components[i] do
begin
p := GetPropInfo(ClassInfo, 'font');
if assigned(p) then
font.size := (NewFormWidth DIV OldFormWidth) * font.size;
end;
end;
Note: not all objects have a FONT property. This should be enough
to get you started.
Note: The following are issue to bear in mind when scaling Delphi
applications (forms) on different screen resolutions:
* Decide early on in the form design stage whether you're going to
allow the form to be scaled or not. The advantage of not scaling is
that nothing changes at runtime. The disadvantage of not scaling is
that nothing changes at runtime (your form may be far too small or
too large to read on some systems if it is not scaled).
* If you're NOT going to scale the form, set Scaled to False.
* Otherwise, set the Form's Scaled property to True.
* Set AutoScroll to False. AutoScroll = True means 'don't change the
form's frame size at runtime' which doesn't look good when the
form's contents do change size.
* Set the form's font to a scaleable TrueType font, like Arial. MS
San Serif is an ok alternate, but remember that it is still a
bitmapped font. Only Arial will give you a font within a pixel of
the desired height. NOTE: If the font used in an application is not
installed on the target computer, then Windows will select an
alternative font within the same font family to use instead. This
font may not match the same size of the original font any may cause
problems.
* Set the form's Position property to something other than
poDesigned. poDesigned leaves the form where you left it at design
time, which for me always winds up way off to the left on my
1280x1024 screen - and completely off the 640x480 screen.
* Don't crowd controls on the form - leave at least 4 pixels between
controls, so that a one pixel change in border locations (due to
scaling) won't show up as ugly overlapping controls.
* For single line labels that are alLeft or alRight aligned, set
AutoSize to True. Otherwise, set AutoSize to False.
* Make sure there is enough blank space in a label component to allow
for font width changes - a blank space that is 25% of the length of
the current string display length is a little too much, but safe.
(You'll need at least 30% expansion space for string labels if you
plan to translate your app into other languages) If AutoSize is
False, make sure you actually set the label width appropriately. If
AutoSize is True, make sure there is enough room for the label to
grow on its own.
* In multi-line, word-wrapped labels, leave at least one line of
blank space at the bottom. You'll need this to catch the overflow
when the text wraps differently when the font width changes with
scaling. Don't assume that because you're using large fonts, you
don't have to allow for text overflow - somebody else's large fonts
may be larger than yours!
* Be careful about opening a project in the IDE at different
resolutions. The form's PixelsPerInch property will be modified as
soon as the form is opened, and will be saved to the DFM if you save
the project. It's best to test the app by running it standalone, and
edit the form at only one resolution. Editing at varying resolutions
and font sizes invites component drift and sizing problems.
* Speaking of component drift, don't rescale a form multiple times,
at design time or a runtime. Each rescaling introduces roundoff
errors which accumulate very quickly since coordinates are strictly
integral. As fractional amounts are truncated off control's origins
and sizes with each successive rescaling, the controls will appear
to creep northwest and get smaller. If you want to allow your users
to rescale the form any number of times, start with a freshly
loaded/created form before each scaling, so that scaling errors do
not accumulate.
* Don't change the PixelsPerInch property of the form, period.
* In general, it is not necessary to design forms at any particular
resolution, but it is crucial that you review their appearance at
640x480 with small fonts and large, and at a high-resolution with
small fonts and large before releasing your app. This should be
part of your regular system
compatibility testing checklist.
* Pay close attention to any components that are essentially
single-line TMemos - things like TDBLookupCombo. The Windows
multi-line edit control always shows only whole lines of text - if
the control is too short for its font, a TMemo will show nothing at
all (a TEdit will show clipped text). For such components, it's
better to make them a few pixels too large than to be one pixel too
small and show not text at all.
* Keep in mind that all scaling is proportional to the difference in
the font height between runtime and design time, NOT the pixel
resolution or screen size. Remember also that the origins of your
controls will be changed when the form is scaled - you can't very
well make components bigger without also moving them over a bit.
Run time errors
Question
Can anyone tell me where I can find a list of the various run-time errors
that Delphi [ 1 ] produces.
I cant find a simple list in either manuals or the on-line help. How is one
supposed to know what "Error 106" (or any other) is actually caused by?
Answer
A:
type
str31 = string[31];
function ErrMeaning (ResultCode: Integer): str31;
{----------------------------------------------------------}
{ Returns the meaning of the given result code. }
{----------------------------------------------------------}
const
NumOfEntries = 108;
type
ErrorEntry = record
Code: Integer;
Meaning: str31;
end;
ErrorMeaningsArray = array [1..NumOfEntries] of ErrorEntry;
const
MeaningsArray: ErrorMeaningsArray =
{DOS errors}
((Code: 1; Meaning: 'Invalid DOS function number'),
(Code: 2; Meaning: 'File not found'),
(Code: 3; Meaning: 'Path not found'),
(Code: 4; Meaning: 'Too many open files'),
(Code: 5; Meaning: 'File access denied'),
(Code: 6; Meaning: 'Invalid file handle'),
(Code: 7; Meaning: 'Memory control blocks destroyed'),
(Code: 8; Meaning: 'Insufficient DOS memory'),
(Code: 9; Meaning: 'Invalid memory block address'),
(Code: 10; Meaning: 'Invalid DOS environment'),
(Code: 11; Meaning: 'Invalid format (DOS)'),
(Code: 12; Meaning: 'Invalid file access code'),
(Code: 13; Meaning: 'Invalid data (DOS)'),
(Code: 15; Meaning: 'Invalid drive number'),
(Code: 16; Meaning: 'Cannot remove current directory'),
(Code: 17; Meaning: 'Cannot rename across drives'),
(Code: 18; Meaning: 'No more files'),
(Code: 19; Meaning: 'Disk write-protected'),
(Code: 20; Meaning: 'Unknown unit (DOS)'),
(Code: 21; Meaning: 'Drive not ready'),
(Code: 22; Meaning: 'Unknown DOS command'),
(Code: 23; Meaning: 'CRC error'),
(Code: 24; Meaning: 'Bad request structure length'),
(Code: 25; Meaning: 'Seek error'),
(Code: 26; Meaning: 'Unknown media type'),
(Code: 27; Meaning: 'Disk sector not found'),
(Code: 28; Meaning: 'Out of paper'),
(Code: 29; Meaning: 'Write fault'),
(Code: 30; Meaning: 'Read fault'),
(Code: 31; Meaning: 'General failure'),
(Code: 32; Meaning: 'File sharing violation'),
(Code: 33; Meaning: 'File lock violation'),
(Code: 34; Meaning: 'Invalid disk change'),
(Code: 35; Meaning: 'File control block unavailable'),
(Code: 36; Meaning: 'Sharing buffer overflow'),
(Code: 37; Meaning: 'Code page mismatch'),
(Code: 38; Meaning: 'Error handling EOF'),
(Code: 39; Meaning: 'Handle disk full'),
(Code: 50; Meaning: 'Network request not supported'),
(Code: 51; Meaning: 'Remote computer not listening'),
(Code: 52; Meaning: 'Duplicate name on network'),
(Code: 53; Meaning: 'Network name not found'),
(Code: 54; Meaning: 'Network busy'),
(Code: 55; Meaning: 'Network device no longer exists'),
(Code: 56; Meaning: 'NetBIOS command limit exceeded'),
(Code: 57; Meaning: 'Network adaptor error'),
(Code: 58; Meaning: 'Incorrect network response'),
(Code: 59; Meaning: 'Unexpected network error'),
(Code: 60; Meaning: 'Incompatible remote adaptor'),
(Code: 61; Meaning: 'Print queue full'),
(Code: 62; Meaning: 'Not enough space for print file'),
(Code: 63; Meaning: 'Print file deleted'),
(Code: 64; Meaning: 'Network name deleted'),
(Code: 65; Meaning: 'Access denied'),
(Code: 66; Meaning: 'Network device type incorrect'),
(Code: 67; Meaning: 'Network name not found'),
(Code: 68; Meaning: 'Network name limit exceeded'),
(Code: 69; Meaning: 'NetBIOS session limit exceeded'),
(Code: 70; Meaning: 'Temporarily paused'),
(Code: 71; Meaning: 'Network request not accepted'),
(Code: 72; Meaning: 'Print/disk redirection paused'),
(Code: 80; Meaning: 'File already exists'),
(Code: 82; Meaning: 'Cannot make directory entry'),
(Code: 83; Meaning: 'Fail on interrupt 24'),
(Code: 84; Meaning: 'Too many redirections'),
(Code: 85; Meaning: 'Duplicate redirection'),
(Code: 86; Meaning: 'Invalid password'),
(Code: 87; Meaning: 'Invalid parameter'),
(Code: 88; Meaning: 'Network data fault'),
{I/O errors}
(Code: 100; Meaning: 'Disk read error'),
(Code: 101; Meaning: 'Disk write error'),
(Code: 102; Meaning: 'File not assigned'),
(Code: 103; Meaning: 'File not open'),
(Code: 104; Meaning: 'File not open for input'),
(Code: 105; Meaning: 'File not open for output'),
(Code: 106; Meaning: 'Invalid numeric format'),
{Critical errors (Real or protected mode only)}
(Code: 150; Meaning: 'Disk is write protected'),
(Code: 151; Meaning: 'Unknown unit'),
(Code: 152; Meaning: 'Drive not ready'),
(Code: 153; Meaning: 'Unknown DOS command'),
(Code: 154; Meaning: 'CRC error in data'),
(Code: 155; Meaning: 'Bad drive request struct length'),
(Code: 156; Meaning: 'Disk seek error'),
(Code: 157; Meaning: 'Unknown media type'),
(Code: 158; Meaning: 'Sector not found'),
(Code: 159; Meaning: 'Printer out of paper'),
(Code: 160; Meaning: 'Device write fault'),
(Code: 161; Meaning: 'Device read fault'),
(Code: 162; Meaning: 'Hardware failure'),
{Fatal errors}
(Code: 200; Meaning: 'Division by zero'),
(Code: 201; Meaning: 'Range check error'),
(Code: 202; Meaning: 'Stack overflow error'),
(Code: 203; Meaning: 'Heap overflow error'),
(Code: 204; Meaning: 'Invalid pointer operation'),
(Code: 205; Meaning: 'Floating point overflow'),
(Code: 206; Meaning: 'Floating point underflow'),
(Code: 207; Meaning: 'Invalid floating pt. operation'),
(Code: 208; Meaning: 'Overlay manager not installed'),
(Code: 209; Meaning: 'Overlay file read error'),
(Code: 210; Meaning: 'Object not initialised'),
(Code: 211; Meaning: 'Call to abstract method'),
(Code: 212; Meaning: 'Stream registration error'),
(Code: 213; Meaning: 'TCollection index out of range'),
(Code: 214; Meaning: 'TCollection overflow error'),
(Code: 215; Meaning: 'Arithmetic overflow error'),
(Code: 216; Meaning: 'General Protection Fault'),
(Code: 217; Meaning: 'Unhandled exception'),
(Code: 219; Meaning: 'Invalid typecast'));
var
Low, High, Mid, Diff: Integer;
begin
Low := 1;
High := NumOfEntries;
while Low <= High do
begin
Mid := (Low + High) div 2;
Diff := MeaningsArray[Mid].Code - ResultCode;
if Diff < 0 then Low := Mid + 1 else
if Diff > 0 then High := Mid - 1 else
begin {found it}
ErrMeaning := MeaningsArray[Mid].Meaning;
Exit; {ErrMeaning}
end;
end; {while}
ErrMeaning := 'Error ' + IntToStr(ResultCode) +
' (meaning unknown)';
end; {ErrMeaning}
A:
I have filled in a few. These all come from 'Delphi Developers Guide'
by Pacheco and Teixeira.
> type
> str31 = string[31];
>
> function ErrMeaning (ResultCode: Integer): str31;
> {----------------------------------------------------------------}
> { Returns the meaning of the given result code. }
> {----------------------------------------------------------------}
> const
> NumOfEntries = 48;
> type
> ErrorEntry = record
> Code: Integer;
> Meaning: str31;
> end;
> ErrorMeaningsArray = array [1..NumOfEntries] of ErrorEntry;
> const
> MeaningsArray: ErrorMeaningsArray =
> {DOS errors}
> ((Code: 1; Meaning: 'Invalid DOS function number'),
> (Code: 2; Meaning: 'File not found'),
> (Code: 3; Meaning: 'Path not found'),
> (Code: 4; Meaning: 'Too many open files'),
> (Code: 5; Meaning: 'File access denied'),
> (Code: 6; Meaning: 'Invalid file handle'),
7 Memory Control Blocks Destroyed
8 Insufficient memory (NFI)
9 Invalid Memory Block Address
10 Invalid Environment
11 Invalid format
> (Code: 12; Meaning: 'Invalid file access code'),
13 Invalid Data
14 Reserved
> (Code: 15; Meaning: 'Invalid drive number'),
> (Code: 16; Meaning: 'Cannot remove current directory'),
> (Code: 17; Meaning: 'Cannot rename across drives'),
> (Code: 18; Meaning: 'No more files'),
19 Disk write protected
20 Unknown unit (not a Delphi unit - internal to DOS)
21 Drive not ready
22 Unknown command
23 CRC error
24 Bad request Structure Length
25 Seek Error
26 Unknown media type
27 Sector not found
28 Out of paper
29 Write fault
30 Read Fault
31 General Failure
32 Sharing violation
33 Lock violation
34 Invalid Disk Change
35 FCB unavailable
36 Sharing buffer overflow
37 Code page mismatch
38 Error handling EOF
39 Handle disk full ??
40..49 Reserved
(Code :50 ; Meaning : 'Network request not supported '),
(Code :51 ; Meaning : 'Remote computer not listening '),
(Code :52 ; Meaning : 'Duplicate name on network '),
(Code :53 ; Meaning : ' Network name not found '),
(Code :54 ; Meaning : 'Network busy '),
(Code : 55 ; Meaning : ' Network device no longer exists '),
(Code : 56 ; Meaning : ' NETBIOS command limit exceeded '),
(Code : 57 ; Meaning : ' Network adapter error '),
(Code : 58 ; Meaning : ' Incorrect network response '),
(Code : 59 ; Meaning : ' Unexpected network error '),
(Code : 60 ; Meaning : ' Incompatible remote adapter '),
(Code : 61 ; Meaning : ' Print queue full '),
(Code : 62 ; Meaning : 'Not enough space for print file '),
(Code :63 ; Meaning : 'Print file deleted '),
(Code : 64 ; Meaning : 'Network name deleted '),
(Code : 65 ; Meaning : 'Access denied '),
(Code : 66 ; Meaning : ' Network device type incorrect '),
(Code : 67 ; Meaning : 'Network name not found '),
(Code : 68 ; Meaning : 'Network name limit exceeded '),
(Code : 69 ; Meaning : 'NETBIOS session limit exceeded '),
(Code : 70 ; Meaning : 'Temporarily paused '),
(Code : 71 ; Meaning : 'Network request not accepted '),
(Code : 72 ; Meaning : 'Print or disk redirection is paused '),
(Code : 73..79 ; Meaning : 'Reserved '),
(Code : 80 ; Meaning : 'File already exists '),
(Code : 81 ; Meaning : 'Reserved '),
(Code : 82 ; Meaning : 'Cannot make directory entry '),
(Code : 83 ; Meaning : ' Fail on Interrupt 24 '),
(Code : 84 ; Meaning : ' Too many redirections '),
(Code : 85 ; Meaning : ' Duplicate redirection '),
(Code : 86 ; Meaning : ' Invalid password '),
(Code : 87 ; Meaning : 'Invalid parameter '),
(Code : 88 ; Meaning : 'Network data fault '),
> {I/O errors}
> (Code: 100; Meaning: 'Disk read error'),
> (Code: 101; Meaning: 'Disk write error'),
> (Code: 102; Meaning: 'File not assigned'),
> (Code: 103; Meaning: 'File not open'),
> (Code: 104; Meaning: 'File not open for input'),
> (Code: 105; Meaning: 'File not open for output'),
> (Code: 106; Meaning: 'Invalid numeric format'),
> {Critical errors (Real or proteted mode only)}
> (Code: 150; Meaning: 'Disk is write protected'),
> (Code: 151; Meaning: 'Unknown unit'),
> (Code: 152; Meaning: 'Drive not ready'),
> (Code: 153; Meaning: 'Unknown DOS command'),
> (Code: 154; Meaning: 'CRC error in data'),
> (Code: 155; Meaning: 'Bad drive request struct length'),
> (Code: 156; Meaning: 'Disk seek error'),
> (Code: 157; Meaning: 'Unknown media type'),
> (Code: 158; Meaning: 'Sector not found'),
> (Code: 159; Meaning: 'Printer out of paper'),
> (Code: 160; Meaning: 'Device write fault'),
> (Code: 161; Meaning: 'Device read fault'),
> (Code: 162; Meaning: 'Hardware failure'),
> {Fatal errors}
> (Code: 200; Meaning: 'Division by zero'),
> (Code: 201; Meaning: 'Range check error'),
> (Code: 202; Meaning: 'Stack overflow error'),
> (Code: 203; Meaning: 'Heap overflow error'),
> (Code: 204; Meaning: 'Invalid pointer operation'),
> (Code: 205; Meaning: 'Floating point overflow'),
> (Code: 206; Meaning: 'Floating point underflow'),
> (Code: 207; Meaning: 'Invalid floating pt. operation'),
> (Code: 208; Meaning: 'Overlay manager not installed'),
> (Code: 209; Meaning: 'Overlay file read error'),
> (Code: 210; Meaning: 'Object not initialised'),
> (Code: 211; Meaning: 'Call to abstract method'),
> (Code: 212; Meaning: 'Stream registration error'),
> (Code: 213; Meaning: 'TCollection index out of range'),
> (Code: 214; Meaning: 'TCollection overflow error'),
> (Code: 215; Meaning: 'Arithmetic overflow error'),
> (Code: 216; Meaning: 'General Protection Fault'));
217 Unhandled Exception
219 Invalid typecast
> var
> i: Integer;
> begin
> for i := 1 to NumOfEntries do
> if MeaningsArray[i].Code < ResultCode then
> Continue {with next iteration of FOR loop}
> else
> begin
> if MeaningsArray[i].Code = ResultCode then
> begin
> ErrMeaning := MeaningsArray[i].Meaning;
> Exit; {ErrMeaning}
> end
> else {Code in array > ResultCode}
> Break; {out of FOR loop}
> end;
> ErrMeaning := 'Error ' + IntToStr(ResultCode) +
> ' (meaning unknown)';
> end; {ErrMeaning}