Delphi:
My personal Tips 4U
Let me share some experiences with you which I've found out during the
years !
..and if you have tips to add, correct or improve: don't
hesitate to tell me
!
Howto:
procedure GetBuildInfo(var V1, V2, V3, V4: Word);
function strBuildInfo: String;
procedure GetBuildInfo(var V1, V2, V3, V4: Word);
var
VerInfoSize,
VerValueSize,
Dummy : DWORD;
VerInfo : Pointer;
VerValue : PVSFixedFileInfo;
begin
VerInfoSize := GetFileVersionInfoSize(PChar(ParamStr(0)), Dummy);
GetMem(VerInfo, VerInfoSize);
GetFileVersionInfo(PChar(ParamStr(0)), 0, VerInfoSize, VerInfo);
VerQueryValue(VerInfo, '\', Pointer(VerValue), VerValueSize);
With VerValue^ do
begin
V1 := dwFileVersionMS shr 16;
V2 := dwFileVersionMS and $FFFF;
V3 := dwFileVersionLS shr 16;
V4 := dwFileVersionLS and $FFFF;
end;
FreeMem(VerInfo, VerInfoSize);
end;
function strBuildInfo: String;
var
V1, V2, V3, V4: Word;
begin
GetBuildInfo(V1, V2, V3, V4);
Result := IntToStr(V1) + '.' +
IntToStr(V2) + '.' +
IntToStr(V3) + '.' +
IntToStr(V4);
end;
// turn it off:
SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, 0);
// turn it on again:
SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, -1);
Caution: you must ensure the monitor being turned on again after you
use this command, otherwise the system must be rebooted!
If you want static bitmaps in your forms and the users are not allowed
to change them you can use a RES file instead of loading Bitmaps in the
Delphi IDE. This also makes your EXE smaller! Example:
Unit Unit1;
Interface
Uses
x,y,z;
Type
...
Implementation
{$R BitIco.res}
...
procedure ...
begin
// Watch out for upper and lower case
Image1.Picture.Bitmap.Handle := LoadBitmap(HInstance, 'Name of Bitmap');
// or:
Image1.Picture.Icon.Handle := LoadIcon(HInstance, 'Name of Icon');
...
end;
Don't forget that the names are treated case-sensitive!
The TabWidth property of the TListbox component sets the number of
dialog base units, usually pixels, for each tab character. Set it, to say,
a half of the ListBox' width to display two columns. When adding strings
to the ListBox, use the tab character (^I) at the desired position:
ListBox1.Items.Add('Column1'^I'Column2');
The imperfection of such approach is that the width of column is not
set automatically depending on the width of the string displayed, which is
simple to improve. Let's look at TextWidth method of TCanvas class: it
returns a width in pixels of a string passed as a parameter. Then we can
write with ListBox do begin
W := Canvas.TextWidth(Str);
if W > TabWidth then
TabWidth := W;
end;
(don't remember where I found this tip, sorry)
Your program might require to disable Ctrl-Alt-Del key sequence, for
example if you don't want your program to be unloaded from memory (it's up
to you to decide if your users will love you for that): and it is possible
by using the SystemParametersInfo API function. This function is used by
Control Panel to customize the Windows environment like setting keyboard-,
display-, sound setting parameters and others. Its syntax is as follows:
BOOL SystemParametersInfo(
UINT uiAction, // system parameter to query or set
UINT uiParam, // depends on action to be taken
PVOID pvParam, // depends on action to be taken
UINT fWinIni // user profile update flag
);
The meaning of each parameter is described in Win32 Developer's Reference (kbase.hlp).
Now, to do what we want, we must call this function like in the following procedure:
procedure DisableCtrlAltDel;
var
i : integer;
begin
i := 0;
{Disable Ctrl-Alt-Del}
SystemParametersInfo( SPI_SCREENSAVERRUNNING, 1, @i, 0);
end.
Don't forget to place WinProcs unit in "uses" clause of your unit. Remark: To disable
the Alt-Tab sequence, you must use the first parameter SPI_SETFASTTASKSWITCH and others are the same as above.
MS ackowledges that the current install problems are their bug and they have stated
that it will be addressed in a future beta. All InstallShield programs that use password
protected CABs will probably have problems. However, here's a workaround how to
bring Delphi 4 to work under Windows 2000:
- install Delphi 4 on Windows 2000 and ignore all error messages
- copy the runtime image directly from the CD
- go to the command line and change to
Common Files\Borland\Shared\Debugger
From there, type regsvr32 -u bordbk40.dll
and then regevr32 bordbk40.dll
..this should do the trick!
Preventing a user from switching to another application is possible under Windows 3.x and 9x
by tricking Windows into thinking a screen saver is running. This method does not work
under Windows NT and is not guaranteed to be available in future versions of Windows.
Many versions of Windows may also respond to a task switching trap installed by a CBT
(Computer Based Training) application hook. To prevent task switching under Windows NT,
you will need access to a third party keyboard device device driver.
VAR
OldValue: Longint;
[..]
// turns trap on:
SystemParametersInfo(97, True, OldValue, 0);
// turns trap off:
SystemParametersInfo(97, False, OldValue, 0);
Modify your *.DPR project file with the example below:
Program PrevInst;
USES
Forms, Windows, // Add Windows
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
VAR // Add Vars
MutexHandle: THandle;
hwind:HWND;
BEGIN
// Add below code
MutexHandle := CreateMutex(nil, TRUE, 'MysampleAppMutex'); // should be a unique string
IF MutexHandle <> 0 then
begin
IF GetLastError = ERROR_ALREADY_EXISTS then
begin
// MessageBox(0, 'Instance of this application is already running.',
// 'Application already running', mb_IconHand);
CloseHandle(MutexHandle);
hwind := 0;
repeat
// The string 'My app' must match your App Title (below)
hwind:=Windows.FindWindowEx(0,hwind,'TApplication','My app');
until (hwind<>Application.Handle);
IF (hwind<>0) then
begin
Windows.ShowWindow(hwind,SW_SHOWNORMAL);
Windows.SetForegroundWindow(hwind);
end;
Halt;
end
end;
// your/Delphi's window create / run code is below here:
Application.Initialize;
Application.Title := 'My app'; // this matches to above
Application.CreateForm(TForm1, Form1);
Application.Run;
END.
A shorter method (without automatic switching to your app's original instance) would be:
CreateMutex(nil,FALSE,'AnyNameHere');
IF GetLastError = ERROR_ALREADY_EXISTS THEN
begin
MessageDlg('Program is already running. You can not start more than one instance',
mterror,[mbOK], 0);
Halt(0);
end;
Application.Initialize;
If you have developed your program using Delphi's default settings and then
started it on a Computer running in a different Screen resolution, you will already
know that this can turn out to a problem causing not only short headaches.
But here is the solution that should fix your problems:
Design on large fonts, use only truetype fonts, set the forms Scaled property
to false. That works pretty well on smallfont systems. You just have to make
sure that your form does not become larger than the users screen size. This
can be handled by a simple check in the forms OnCreate procedure, call
SystemparametersInfo( SPI_GETWORKAREA, 0, @aRect, 0 );
Check if your forms width or height exceed the dimensions of aRect, if so you
set your forms Boundsrect := aRect; and the forms AutoScroll property to
true. It then gets scrollbars but that is better than having parts of the
form not accessible by the user.
More tips on this topic are linked from my "Delphi Tips" page.
Debugging is a pain, especially when you get uninformative errors from the operating system.
The following edited and partial answer demonstrates a generally useful technique when
you are getting AV's in "bad" situations like after closing your app etc.:
The access violation dialog should say that an access violation happened at XXXXXX: read of address YYYYYY.
Try running your program in the debugger. Pause the program and bring up the view CPU pane.
In the upper left pane, right click and select Go To Address. Go to the address that caused
the access violation. Look around a little bit and see if you can find where you are in the program.
Insert some breakpoints and try to close the program. When you hit a breakpoint, view the call stack and see if it gives any clues.
(Tip provided by Harold Howe)
This is not a good thing for an application to do unnecessarily...
Super := Application.Handle;
if SetPriorityClass(Super,REALTIME_PRIORITY_CLASS) THEN
Application.MessageBox('Didn''t work','',MB_OK);
Delphi 4 was the slowest Delphi we have ever seen, and Delphi 5 is not really faster, nor was Delphi 3.
Beside these versions, I'm still using Delphi 2 for some special tasks and every time I start it I'm amazed by its
incredible loading and general processing speed compared to the newer versions. And although
the raw compile and linking times have not increased measuruable, the required
resources Delphi 3/4/5 occupies make even that quite slow, especially at the first
compile after starting Delphi. Why?
Well, the main reason for Delphi's behaviour is the design of the OS under which
it runs: Microsoft Windows. Windows can be seen as the most "bloated" of today's
operating systems, containing an incredible amount of useless code, "funny" graphics,
animations and all the stuff which is required to keep even foreground threads slow
and resource-hungry.
Beside that, especially Delphi 3/4/5 itself has many built-in features which make it
slow, which can produce quite long "thinking pauses" from time to time and susceptible
for memory overflows. The goal of this section in my FAQ is to provide you a number
things you could check and avoid to prevent unnecessary resource consumption of the IDE.
Note: this section is not thought to provide you with tips that help to avoid
unnecessary resource consumption of your apps themselves. For that, please check
the sections "speed up your app and save resources"
or "reduce the .EXE-size of your app" on this page.
- develop under Windows NT.
Windows NT comes with a better memory management than Windows 95/98.
- put Resource-Meter (rsrccmtr.exe) in the tray area of your Windows Desktop.
This helps you to gain control about the point where Windows's resources are
filled up and after which the desktop "dies" so that you can close other apps
or opened forms before this happens.
- don't keep too many forms open while you are working in the IDE.
Each opened unit and form eats up resources - especially complex multi-page
forms can easily fill up to the half (!) of the available memory. After that,
not only your harddisks will have to do heavy work, but also you risk hanging
your PC when you run your app from within the IDE.
- Check "Minimize on run" in the Environment options of the IDE.
This will help to minimize fragmention of the Windows resources.
- disable unneeded "Code Insight" features
for these features, Delphi has to constantly open and lookup libraries, pop up
hint windows etc. etc. .. all stuff which will slow down your working speed a lot.
If you need these features, check out the related keyboard shortcuts - they will
give your Delphi the same power, but prevent making it unnecessarily slow.
- remove all unneeded components and packages from the IDE.
Each installed package and component uses valuable resources - even if you never
need it and it's "just a button" in your Delphi toolbar. And not only that - it
also slows down your Delphi startup a lot since Delphi does a lot of internal
reference checking when loading the required stuff.
- turn off Code Explorer.
Treeviews are slow by design. Turn off Delphi's built-in Treeview if you don't
really need it (you can still turn it on manually via "View"-"Code Explorer").
- finally: give Windoze at least 64 MB.
with less than that, programming with Delphi 3/4/5 is a pain and you might end up
as a trembling individual in a mental clinic.
- more tips? Please let me know!
There are 2 quite easy methods to save configuration data of your program: using
INI files (all Windows versions) or the Windows registry (for 32 bit programs).
If your program (or external parts of your program) have to run under Windows 3.x, too,
this is already my first argument why I'm always preferring INI files against the registry.
Another reason is easier maintenance: imagine having to tell a computer-newbie how to change
a damaged part of his program configuration in the registry during a support call - horrible!
Other reasons are much easier backup of the program including program configuration,
easiest overview about the saved settings, easy "low-level" editing capabilities of the
configuration (Notepad is enough). A commonly underestimated part is that INI files won't "blow up" your
registry - as you might know, Windoze doesn't really delete registry keys as they are
"deleted" -> it just "hides" them from your view. This leads to registry files
of 2-5 MB after a year of happily installing and "uninstalling" one application after
each other.... Unfortunately, there are still "programmers" out there who even use the
registy to save large amounts of binary data or even localized strings for their complete
applications (!) in the registry .. aargh ... let's talk about nicer things again.
My personal favourite for opening the INI files is:
MyIni := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
This has the advantage that the INI file will always "follow" the program,
even if I change the name of the EXE file.
There also exist 2 possible traps when using INI files:
- Windows 95/98 caches INI data. This might lead to annoying situations if you
want to access the INI file as a text file while it is opened or something like that.
In this case, add a TIniFile.UpdateFile; to your code which will flush the
INI data to disk (Delphi 4. With earlier versions, you can use TIniFile.WriteString(nil,nil,nil);.)
- INI files bigger than 32kb. If there is much data to save, Windows is
overwhelmed ;-). You will have to use a 3rd party component like TIniFile32 or BigIni.zip
(get it at the Delphi Super Page)
to read and write INI files of this size.
Issues which could lead to a decision against the use of INI files are: speed (using the registry
is faster due to its structure), better organization (tree structure), if you want to hide something
(it's easier to hide a "secret" registration string "somewhere" in the registry than in an
INI file, even if it's saved in another directory -> for better tips please read my Anti-Cracking FAQ) and the M$ Guidelines
which suggest to just use the registry under 32bit-Windows. Well, Bill's wishes and visions have
never influenced me too much, so I could easily resist until now. ;-)
Here is a very cool technique that not only gives you transparent forms but transparent forms
with insanely complex shapes (or, I should say, the appearance of). The following is only tested
in Delphi 1/2, but should also work without problems when using D3/4. And this is probably only
good for splash screens, because moving a window doesn't always force a redraw, which is the trick.
The trick is altering your form like this:
Add the following procedures to your form:
procedure paint; override;
procedure WMERASEBKGND(var Msg:TMEssage); message wm_erasebkgnd;
procedure TSplash.WMERASEBKGND(var Msg:TMEssage);
begin
msg.result:=1;
end;
Here is where the trickery comes - For this example I am using a component
that I wrote whose only function is holding a number of bitmaps. With this
component, several of my graphic controls can share the same bitmaps
resources but not be forced into the standard palette - as you would have
to if you were using .RES files. Anyways, I have two bitmaps - the first
bitmap is my splash form and the second bitmap is an 8-bit grayscale mask.
In the mask, black areas will be transparent, white areas will be clear and
any shades of gray will be semi transparent. It's important to note,
however, that the copymode settings used below do some weird crap, so it's
best to use a mask with little to no gray, although a little gray will work
and give your edges a more smooth anti-aliased appearance.
You also don't have ot use bitmaps, you can draw or whatever you want to do.
procedure TSplash.Paint;
var
t:TBitmap;
begin
// create an in-memory bitmap the same dimensions as my form
t:=TBItmap.Create;
t.width:=width;
t.height:=height;
// copy the area my form takes up from the desktop's image onto my drawing bitmap
BitBlt(t.canvas.handle,0,0,width,height,GetDC(GetDesktopWindow),left,top,SRCCOPY);
with t.canvas do
begin
copymode:=cmNotSrcErase;
draw(0,0,splashrez.bitmap2); // draw my bitmap mask first
copymode:=cmSrcErase;
draw(0,0,splashrez.bitmap1); // Draw my splash logo/regular bitmap
end;
canvas.draw(0,0,t); // draw the entire thing to the form's canvas in one shot to reduce flicker
t.free;
end;
And that's it! ;-)
(Tip provided by Jon Gilkinson)
Ever lost your Delphi 4 IDE setup?
In 32bit-Delphi, all IDE settings are stored in the registry under the branch
HKEY_CURRENT_USER\Software\Borland\Delphi\?.0
where ?.0 is 4.0 for Delphi 4, for example.
Since Regedit.exe can easily export and re-import branches of the registry,
you have an easy way to protect yourself against losing your carefully customized
IDE environment after crashes or when installing "your" Delphi on a new computer.
USES
ShellAPI;
..
procedure ExtractIcon(IcoFileName: String);
VAR
IcoFileName : String;
IcoHandle : THandle;
MyIcon : TIcon;
BEGIN
// the last parameter is the index of the icon
IcoHandle := ExtractIcon(Application.Handle, PChar(IcoFilename), Word(0));
// is there an icon in this file?
IF IcoHandle = 0 then begin
ShowMessage('No icon in this file!');
exit;
END;
// okay, so there is an icon in this file
MyIcon := TIcon.Create;
MyIcon.Handle := IcoHandle;
// how much space are we going to need for the icon?
BitBtn1.Glyph.Height := MyIcon.Height;
BitBtn1.Glyph.Width := MyIcon.Width;
// now draw the icon on the button
BitBtn1.Glyph.Canvas.Draw(2, 2, MyIcon);
// lets draw it on a TImage too...
Image1.Picture.Icon := MyIcon;
// oh, starts to make fun .. lets change the applications icon too! ;-)
Application.Icon := MyIcon;
// free the icon
MyIcon.Free;
END;
Sometimes it's required to check if a user has an active Internet connection, but you
don't want the DUN dialog box to popup by your code. Well, it was not easy to
find out a method for that, but finally a hint by Henri Fournier in the Newsgroups
gave me the idea for the following 2 powerful lines:
USES
WinInet;
..
..
function InternetConnected: Boolean;
CONST
INTERNET_CONNECTION_MODEM = 1; // local system uses a modem to connect to the Internet.
INTERNET_CONNECTION_LAN = 2; // local system uses a local area network to connect to the Internet.
INTERNET_CONNECTION_PROXY = 4; // local system uses a proxy server to connect to the Internet.
INTERNET_CONNECTION_MODEM_BUSY = 8; // local system's modem is busy with a non-Internet connection.
VAR
dwConnectionTypes : DWORD;
BEGIN
dwConnectionTypes :=
INTERNET_CONNECTION_MODEM +
INTERNET_CONNECTION_LAN +
INTERNET_CONNECTION_PROXY;
Result := InternetGetConnectedState(@dwConnectionTypes,0);
END;
That's all! Have fun..
Note from 05-Dec-98: I had to find that this solution only works under Windows 95/98 -
Since the required DLL routine doesn't seem to be implemented by Windows NT, this OS produces
an error during program startup when you refer to Wininet.
If anyone knows a better method to do Online-Checks, please let me know!
Need to make exact copies of records with some slight variations? This
in itself wouldn't be a problem, but if there are many tables that need this functionality
and they had a variety of types and structures, it can hit your nerves. The XBase solution
would have been to store all the fields into memory variables, append a new record, then replace
all the fields with the memory variables. As it turns out, that can be the solution to the problem
with Delphi as well.
The procedure introduced here, AppendCurrent, works this way:
- it creates a Variant Array
- it populates the array with each of field's values from the selected table
- it calls the Append method of the table
- it populates the table's fields with the values from the variant array
- it passes it back in the edit state ready for changing
The following listing shows you the code.
{************************************************
// procedure AppendCurrent
//
// Will append an exact copy of the current
// record of the dataset that is passed into
// the procedure and will return the dataset
// in edit state with the record pointer on
// the currently appended record.
************************************************}
Procedure AppendCurrent(Dataset:Tdataset);
VAR
aField : Variant ;
i : Integer ;
BEGIN
// create a variant Array
aField := VarArrayCreate([0,DataSet.Fieldcount-1],VarVariant);
// read values into the array
for i := 0 to (DataSet.Fieldcount-1) do
aField[i] := DataSet.fields[i].Value ;
DataSet.Append ;
// put array values into new the record
for i := 0 to (DataSet.Fieldcount-1) do
DataSet.fields[i].Value := aField[i] ;
END;
(Tip found in Delphi Developer)
For me, a nice add-on of each new Delphi version has always been the inclusion of the
latest Delphi advertisement video clips on the Delphi CD's. Borland/Inprise has always
had really cool and entertaining videos, you should definitely check them out.
The Delphi 4 CD, for example, contains the following clips:
- "Speed is.." (long version) in \Runimage\Delphi40\Demos\Coolstuf\SpeedIs.avi
- "The right tools.." in \Delphi16\Videos\Borland.avi
- "Speed is.." (short version) in \Delphi16\Videos\Delphi.avi
On the Delphi 3 CD, you can find
- "Just want to.." in \Info\Borland\Bor_GG.avi
and my personal favourites are definitely:
- "Racing Car" in DelCar2.avi and
- "Hans and Gunter" in DelCom2.avi
Unfortunately I don't remember where I found those initially. :-}
Btw., don't forget to browse your Delphi CD's for other goodies, too! You can find plenty
of interesting stuff like FAQ's, add-on libraries, Delphi mags (unfortunately just
on the earlier CD's) and, not the least, updated versions of Delphi 1 on them.
Maybe you've already asked yourself, how you get Delphi to accept two (or more) Icons ?
A 16x16 icon for when the small icon is required for display (ie. small icons in Explorer),
or a 32x32 icon for display when the large one is required (ie. large icons in Explorer).
Also, if you peek in M$ (and other) applications, you will see multiple
icons of differing sizes and palettes all under the same title.
Just check for the current resolution and change the icon handle of the
application... Of course, you have to create new icons in your resource
(for this "how to", please see tip "How to store "everything" in your EXE file").
Put this in the project (.DPR) file of your application source:
Application.Initialize;
Application.CreateForm(TForm1, Form1);
CASE GetDeviceCaps(GetDC(Form1.Handle), HORZRES) of
640 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON640');
800 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON800');
1024 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON1024');
1280 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON1280');
END;
Application.Run;
Well, that's all !
(Tip found in a msg of Gerry Jacobs.)
By supporting resource files, Delphi gives you a great way to store static file contents
like animated cursors, AVI videos, pictures or other nice typa things inside your .EXE files.
In the following example, the .AVI video file myavi.avi will be stored inside the .EXE file:
- Define a constant to be used to refer to the AVI:
CONST
ID_AVI_FILE = 123; { assign whatever number you want.}
- Create a resource file MyRes.RC. In the file, have the following line
(you can repeat steps 1 & 2 to add multiple AVI's):
IDS_AVI_FILE AVI myavi.avi
- Compile the RC file to a RES with the command:
BRC32 -r MyRes.RC
- Include the resulting MyRes.RES file in your project:
{$R MyRes.RES}
- Now we're going to add code to access the contained AVI video: put a
TAnimate component on the form, and assign it a name (in this example,
the AVI is named "AviClip"). Then, in the FormCreate() event, add the
following to start playing the clip:
WITH AviClip DO BEGIN
ResID := IDS_AVI_FILE; { Load AVI }
ResHandle := hInstance; { this line must be placed after assigning ResID }
Active := TRUE; { start playing immediately }
END;
Well.. that's all !
After popular demand, here comes a similar procedure to load a JPEG file from
a resource:
procedure LoadJPEGfromEXE;
var
MyJPG : TJPEGImage; // JPEG object
ResStream : TResourceStream; // Resource Stream object
begin
try
MyJPG := TJPEGImage.Create;
ResStream := TResourceStream.CreateFromID(HInstance, 1, RT_RCDATA);
MyJPG.LoadFromStream(ResStream); // What!? Yes, that easy!
Canvas.Draw(12,12,MyJPG); // draw it to see if it really worked!
finally
MyJPG.Free;
ResStream.Free;
end;
end;
See the second parameter of the CreateFromID procedure of the TResourceStream
component? It's simply the resource index. You can include more than one jpeg in your
executable just by adding a line for each jpeg (with a different index) in the resource
script (.RC) file.
And finally, a totally different procedure to play a WAVE file stored as resource:
var
FindHandle, ResHandle: THandle;
ResPtr: Pointer;
begin
FindHandle:=FindResource(HInstance, '<Name of your Ressource>', 'WAVE');
if FindHandle<>0 then begin
ResHandle:=LoadResource(HInstance, FindHandle);
if ResHandle<>0 then begin
ResPtr:=LockResource(ResHandle);
if ResPtr<>Nil then
SndPlaySound(PChar(ResPtr), snd_ASync or snd_Memory);
UnlockResource(ResHandle);
end;
FreeResource(FindHandle);
end;
end;
Another use for this technique can be to use this technique for program loaders
like Setup programs, self-patching or self-checking utilities.
Just add the second program to the first one as a RCDATA resource. When the first
program is started, it automagically extracts the second program to a temp file
and starts it. Here's the code for the magic:
-----SECOND.RC file listing
SECONDAPPEXE RCDATA "c:\Apps\Second\Second.EXE"
------ EOF
In a DOS Window:
C:\>BRCC32 FIRST.RC
In first.dpr add the following line:
{$R SECOND.RES}
Then, whenever you want to save Second.Exe to file, do the following:
VAR
SecRes : TResourceStream;
pTemp : pchar;
TempPath : string;
BEGIN
SecRes := TResourceStream.Create(hInstance,'SECONDAPPEXE',RT_RCDATA);
pTemp := StrAlloc(MAX_PATH);
GetTempPath(MAX_PATH, pTemp);
TempPath := String(pTemp);
StrDispose(pTemp);
SecRes.SaveToFile(TempPath+'Second.EXE');
SecRes.Free;
WinExec(PChar(TempPath+'Second.EXE'), SW_SHOW);
END;
Optionally, you can use CreateProcess instead of WinExec. You can also use the API
call GetTempFileName to make sure Second.EXE receives a unique filename in the
directory returned by GetTempPath. Also, in the code above, I am presuming that
the path returned by GetTempPath ends with a backslash ( \ ) character. You should
also check for that.
Last Tip partially provided by Alex Simonetti.
More information can be found in Borland's TI and QA files:
VAR
h: THandle;
begin
h := CreateEllipticRgn(40, 40, 300, 200);
SetWindowRgn(Handle,h,TRUE);
Well.. that's all !
Usually, you will search for a key word like the following:
procedure HelpSearch(sHelpName,sSearchKey:string);
var
pc:PChar;
begin
Application.HelpFile:=sHelpName;
pc:=StrAlloc(100);
StrPCopy(pc,sSearchKey);
Application.HelpCommand(HELP_PARTIALKEY, LongInt(pc));
StrDispose(pc)
end;
Instead, you could use following code:
procedure HelpSearch(sHelpName,sSearchKey:string);
var
pc:array[0..99] of char;
begin
Application.HelpFile:=sHelpName;
StrPCopy(pc,sSearchKey);
Application.HelpCommand(HELP_PARTIALKEY, LongInt(@pc))
end;
The important things is the low bounds of array must been 0.
Tip provided by Christo Tsvetanov.
In some situations you might have to display file names at a limited length - whether
this length is enough to show the full name or not. Delphi's function MinimizeName
helps you to "fit" this space by truncating and adding a "continue"-symbol to it.
Personally, I prefer using INI files for saving configuration data of my programs.
This gives me an easy way of helping them if I want to prevent that they had to
hack around in the dark deeps of the ever-growing Windows registry.
Here are two tips from my experiences with using INI files:
- Save your INI files in the main directory of your program
I think that preventing to spread files all over our user's PC's
is a good habit. Filling up his or her Windows directory with
one more INI file (often forgotten when removing programs !) isn't
required anymore if you just open/create your INI file(s) with the line:
MyIni := TIniFile.Create(ExtractFilePath(Application.ExeName)+'MyApp.INI');
- Opening INI files as text
In some cases it could be required that you open your INI file
as text. This might lead into problems if you concurrently do this
after your file has also been opened using the INI file functions since Windows works on a cached version
of the INI file once it was opened. It could happen that your changes
won't be saved correctly... I have spent many hours with searching for the
reason for those problems until I found the following way to flush Windows'
cache:
WritePrivateProfileString(NIL,NIL,NIL,'MyIni.INI');
If all three parameters are NULL, the function flushes the cache.
Note that the function always returns False after flushing, regardless of whether the
flush succeeds or fails.
- To indent/outdent a block of text:
- Mark the text
- [Control]-[Shift]-[I] to indent entire block
- [Control]-[Shift]-[U] to indent entire block
Bonus: It autorepeats! Cool..
- Keyboard recorder:
[Control]-[Shift]-[R]: record your keystrokes (press again to stop recording)
[Control]-[Shift]-[P]: playback recorded keystrokes
- [Control]-[click] on a token to navigate to the declaration
- [Control]-[Shift]-[Up/Down arrow] to navigate between function interface and implementation
- [Control]-[Shift]-[G] to insert a new GUID in the editor
- Hold down [Control] while dragging a window in the IDE to prevent it from docking
- [Esc] while dragging a window in the IDE to cancel the move/dock
- [Control]-[Space] to force Code Completion
- [Control]-[Shift]-[Space] to force Code Parameter Insight
- [Control]-[J] to use a Code Template
- [Alt]-[Shift]-[Up/Down arrow] to move cursor up/down and select the column above/beyond
- Use the wheel on your IntelliMouse (or compatible) to navigate in the editor
- All of the debugger window hot keys are [Control]-[Alt]-something - making them easy to remember
- [Control]-[Shift]-[C] early and often to complete class declarations and method implementations!
- [Control]-[E]: incremental search. When some letter is wrong - click [Backspace],
this will delete it, and continue typing. Delphi also remembers your last searched word, and you can repeate search with [F3]!
- Mark string in modile with [Ctrl]+[Shift]+<number from 0 to 9> (or [Control]+[K]+<number from 0 to 9>).
This will show a small green square with your number on the gutter. To find this, mark click [Control]+<your number>.
To delete it, back to the [Control]+[Shift]+<your number> when the cursor on the marked string.
- [Control]+[Shift]+[T] deletes the word right to cursor position
- [Control]+[Shift]+[Y] deletes the whole string right to cursor position
- [Control]+[Backspace] deletes the word left to cursor position
- To put multiple copies of the same component on a form:
- Hold down shift and click on the desired component (it goes flat)
- Then each click on the form leaves a new component!
- click on the 'cursor' component to turn it off!
Some of these tips found here,
thanks also for some contributions by Stas Kashepava and don't forget to read
the interesting article by Marco Cantu about Delphi 5-related stuff.
Hidden Secrets
- In Delphi 1 go to 'about' and do ALT + AND
and see a picture of a "Mr. Anders H." ;-) winking !
- or ALT + TEAM and see names of the team
- or ALT + DEVELOPERS and see developers
- or ALT + QUALITY and see names of quality team (Delphi 3+4 C/S only)
- or ALT + VERSION to see the internal version number (Delphi 2 only)
- or ALT + CHUCK and see something like an animated picture of Chuck J
A webpage dedicated to those "Easter Eggs" can be found here.
But now: Fun over .. back to work !<g>
(and don't forget to tell me if you found more secrets and "unknown" keystrokes !)
These tips were discovered by Frank Cowan and Tim Little.
To translate the Delphi 3.0 VCL messages, you need additional source files. They
are stored at Borland's website from where you can download them by clicking on the link
at my "Delphi updates and patches" page.
For example, to translate the button captions displayed by the MessageDlg function:
- copy consts.pas
- translate the SMsgDlg... strings
- put consts.pas in the project search path
- recompile the project
See the information on ResourceString in the help for more information.
(From the Borland website)
If you need to develop 16bit- and 32bit-versions of the same program simultaneously,
you just have to consider the following:
If you type the name of a unit in the Delphi code editor and add a point,
the code completion function enters in action and displays the types, variables,
procedures and function in the interface part of the named unit.
You can get the same effect by just pressing Ctrl+«space».
Tip: if you type 'self.' in an obect's method, you also get a list of
properties, variable, events, etc. - related to the "current" object.
Create a mini app (30k) called TODAY.EXE with the following code
(note this is a .DPR-file with the main form removed from the project !):
Program Today;
USES
Windows,SysUtils;
{$R *.RES}
VAR
f : TextFile;
i : Integer;
dd,mm,yy: word;
BEGIN
DecodeDate(Date,yy,mm,dd);
AssignFile(f,'c:\windows\Today.inc');
Rewrite(f);
WRITELN(f,'CONST');
WRITELN(f,'_Day : WORD = '+IntToStr(dd)+';');
WRITELN(f,'_Month: WORD = '+IntToStr(mm)+';');
WRITELN(f,'_Year : WORD = '+IntToStr(yy)+';');
CloseFile (F);
END.
Compile this project and move the resulting TODAY.EXE to the Windows directory.
Edit WIN.INI and add the following line to the [windows] section:
{ assuming "windows" as the name of your Win-Dir: }
run=c:\windows\today.exe
This executes TODAY.EXE each time windows is booted (presumed daily) and updates the
constants which are stored in "c:\today.inc" (an include file).
Now all you have to do to access these constants is to add the include file to the unit
you wish to use them in with the following statement:
{$I c:\windows\today.inc}
{ Now you could do the following...}
LblCompileDate.Caption :=
IntToStr(_Day)+'/'+IntToStr(_Month)+'/'+IntToStr(_Year);
(found somewhere on the web - don't remember where, sorry.)
When using Tables and Databases:
- index your table to speed up your searches !
- avoid reading the whole thing, better use
a) conditional indexes (preferred) or
b) queries
- sometimes using special algorithms is better than
doing manual searches / sorting on tables
- don't use too many fields per table !
Having more than 100, 120 fields in one table can slow down your app
because of exhaustive memory useage and forced data transfer.
Better split up the informations you need in more tables and
- open only the tables you need !
This one is a bit tricky to decide. Opening a table takes its time,
so if you had to open and close tables very often in a special part
of your program, better keep them opened permanently.
If your app is built of several parts (for example: one part for maintenance of your
clients, one for articles, one for suppliers, one for statistics etc.), then you shouldn't
keep ALL of your Tables opened permanently - instead open just the tables you need in the
FormCreate-procedure of the individual Form and close them again in its OnClose procedure.
This can also prevent locking and update-problems.
- when using ranges with Server databases:
Server databases are set oriented and, therefore, do not understand record navigation
very well. If you are trying to change the records in the range you should get much
better performance by using an UPDATE query (to see the SQL generated by SetRange use
the SQL Explorer).
The fastest way to change a bunch of records in a server table is alway with an UPDATE query
that runs entirely on the server and doesn't require you to read the records to client
and send the updates back.
- Generally: at some database operations using TQuery is much faster than performing
all and everything only by TTable methods. Try it !
- Make use of TTable.DisableControls !
Updating all of the controls connected to your dataset takes its time -
save this time by calling DisableControls .. after performing all the
required operations, call TTable.EnableControls (evt. followed by TTable.Refresh)
and everything will be updated again !
- Avoid OnCalcFields unless necessary !
This function gets called like you wouldn't believe - that's why it can slow
your app down enourmously...
Instead you could make use of the TDataSource.OnDataChange event to update
anything special on your form when a record changes.
- Order the results of queries by indexed fields !
Using dBase Tables with SQL can be very slow, because the Server processes
the query locally (= on the server) and then returns only the result, which is
not the case with dBase tables, which must be transported entirely through
the net to your client, processed on your client, which then shows the result.
If you have a slow net and large dBase tables this becomes very, very slow.
Using Paradox files instead can really speed up things if you have a primary
index in which you search for the value (the TQuery looks up for the primary
index and tries to use it !).
- Use QuickReports with tQuery !
It's faster than ReportSmith and gives unlimited queries.
(tQuery with ChartFX is also pretty cool!)
- Avoid tQuery in Database Grids and so forth
- it appears too slow.
- Could you make use of the SetRange function ?
It beats anything around for speed. Although it works on an IndexFieldName,
you can trick it by creating filtered indexes. This allows you to have special
choice combo-boxes that only display appropriate data.
(Table1.SetRange([Cust_A],[Cust_A]) will only show Cust_A data...)
For example IndexName: 'LiveOrders' (index on Cust_No for Paid=0 TAG LiveOrders)
'paidorders' (INDEX on Cust_No for Paid=1 TAG PaidOrders)
Both work as IndexFieldName='Cust_No', but you set the index as follows:
Table1.IndexName='LiveOrders';... Table1.SetRange([var],[var]);
(Apollo has a similar function that is even a bit more flexible.)
- Consider using 3rd party database engines (see Link at my Delphi Tools area)!
Engines like Advantage Database or Apollo not only use optimized search and query algorithms, they also support
combined, conditional and full-text indexes. Additionally you'll only need
2, relatively small DLL-files instead of the big-sized BDE.
Other tip collections about program optimization:
Save resources in TNoteBooks, TTabbedNotebooks and TTabSheets
NoteBooks, TTabbedNotebooks and TabSheets inhale a relatively large amount of
a computer's resources. To free the resources of all components which are
currently not displayed (because they are on a "hidden" page), place the
following piece of code in the TNotebook's TTabset TabClick-Event:
FOR i := 0 TO nbAdresses.Pages.Count-1 DO
IF i <> nbAdresses.PageIndex THEN
TfrmAdressForm(nbAdresses.Pages.Objects[i]).DestroyHandle;
Because Delphi keeps the objects stored internally, all of them will nevertheless
correctly be displayed when you change to the "cleaned" pages again.
To accomplish a similar task with TTabbedNotebooks, put the following at the end
of your tTabbedNotebook's onChange event (From Sid Gudes):
IF AllowChange THEN BEGIN
WITH TabbedNotebook1 DO BEGIN
LockWindowUpdate(Handle);
THintWindow(Pages.Objects[PageIndex]).ReleaseHandle;
TWinControl(Pages.Objects[NewTab]).HandleNeeded;
LockWindowUpdate(0);
END;
TabAt[nbPacket.PageIndex] := TabPacket.TabIndex;
END;
And to restore the handles:
IF Sender is TTabbedNotebook THEN
WITH TTabbedNotebook(Sender) DO BEGIN
CurrentPage := TWinControl(Pages.Objects[PageIndex]);
LockWindowUpdate(Handle);
TWinControl(Pages.Objects[NewTab]).HandleNeeded;
LockWindowUpdate(0);
END;
Some caveats on these methods:
- if you have any tComboBoxes with style of csDropDownList, you'll lose the values
in them (csDropDown works OK); you can write some code to cycle through the
components and save the values beforehand, then restore them afterwards.
- this frees resources as the user clicks through the pages/tabs, but at FormCreate
time all pages will be created, so the resource hit will be high at the beginning.
- carefully watch the edit mode of the tables of the form - in my apps I always lost
the edit mode after changing the pages/tabs and had to recall it.
General speed issues
- Use BeginUpdate / EndUpdate when doing large screen operations !
(btw: Since WM_SETREDRAW and LockWindowUpdate seem to accomplish the same task,
I think it is easier to stick with the BeginUpdate/EndUpdate Delphi methods than
dropping to the Windows API. Not to mention the fact that LockWindowUpdate can
cause horrendous flickering in any other apps visible on the screen :). )
General: saving resources
Some tips which - for example - can help you prevent this hated message
"Out of resources - can't create window" ...
- Avoid using Form-Autocreation !
If you create new forms for you project by selecting File..New.. with Delphi,
it creates code in your project file (.DPR - you can open it by pressing Ctrl-F4),
which will create each form automatically at the startup of your Application:
USES
MainForm in 'MainForm.pas',
ChildFrm1 in 'ChildFrm1.pas',
ChildFrm2 in 'ChildFrm2.pas',
(..etc..);
[..]
BEGIN
Application.CreateForm(TMainForm ,MainForm);
Application.CreateForm(TChildFrm1,ChildFrm1);
Application.CreateForm(TChildFrm2,ChildFrm2);
(..etc..);
END;
This doesn't only slow down the startup, but also uses huge amounts of memory !
Instead, let Delphi create only your "Main-Form" automatically, and remove
any other forms from the USES-clause, remove any other auto-creations from the
project source and create those other forms in the source of the main-form
"by hand" - and only where you really need it:
procedure MyMainForm.mnuClientsClick(Sender: TObject);
BEGIN
ChildForm1 := TChildForm1.Create(Self);
ChildForm1.ShowModal; { if it's a MDI-form, use ChildForm1.Tile; }
ChildForm1.Release; { will save additional resources ! }
END;
- Even applications less than 200kb in size use approx. 1 MB of RAM because
the Delphi RTL obtains much of it's variant support from OLEAUT32.DLL.
However, if you're not using OLE you can free those libs by putting
FreeLibrary(GetModuleHandle('OleAut32'));
in the OnCreate event of your MainForm or your project source. It will unload
OleAut32.dll and OLE32.dll, which use about one meg.
- Release memory of closed forms by putting either
MyForm1.Release;
in the calling form (after closing it) or by placing the line
Action := caFree;
in the OnClose-Event of your Form.
- Check: in your program there should never be more than 4 or 5 windows opened at the
same time. Each window consumes quite a lot of dynamic resources.
- Use TBevel's rather than TPanels !
The TPanel-component needs a separate window-handle whereas a TBevel is only a
"painted" are on your form. So you can again save resources by just "painting"
a Bevel on it instead of filling it with resource-eating Panels...
- Set "ParentFont=TRUE" on your forms !
By doing that, your app doesn't have to load and keep font-instances for each
individual component anymore.
Many of us had to find out that Windows (not a Delphi problem !) has serious limitations
about how many controls you can use on forms. Especially large forms with over 100, 120
controls (i.e.: Edit-Controls, Buttons, but also Panels and Images) can produce loading
problems ("error creating form xx").
Another problem is the limited size of the Data Segment, which is only 64 KB in size and
cannot be enlarged anyhow. Since the Delphi RTL already uses a few KB's of this space,
many of us already had contact to "Compiler error 49 ("Data Segment too large")...
How can you minimize the use of the Data Segment ?
The Data Segment commonly consists of the following:
- Global Variables
- Stack space (1 MB automatically -virtually- allocated)
- Local Heap
- PChar Literals
- Typed Constants
- Virtual Method Tables of objects & classes
You can :
- Try to make your strings smaller
Use Mystring: String[size] instead of unqualified strings.
Example: VAR StrX: String; { takes 255 bytes }
VAR StrX: String[5]; { takes up on ly 5 or 6 }
- Try to reduce your Stack space in the Linker options
Usually you can reduce it to a much smaller value without troubles.
- Split the data into different units.
- Put your things (see list above) in group blocks !
The Delphi compiler is smart enough not to link in any data that isn't used,
but this is only possible if everything in the block where this data was
declared isn't used. So, put your things that belong together in their own VAR..,
TYPE.. and CONST.. blocks to avoid linking them in if you might use only one part
of it.
- Allocate static data (see list above) on the heap and make it dynamic !
Means: use pointers and GetMem/New ! This will again decrease execution speed
and requires another layer of indirection (a pointer dereference).
Example: instead of coding
TYPE
a1: ARRAY[1..] OF
a2: ..
you should code:
TYPE
TArrRec = RECORD
a1: ARRAY[1..] OF
a2: ..
END;
VAR
ArrRec: ^TArrRec;
BEGIN
TRY
NEW(ArrRec);
{ use your Data like ArrRec^.a1[3]; here }
FINALLY
DISPOSE(ArrRec);
END;
END;
- Calculate values each time you need them and don't use lookup tables instead.
Well, this uses more code space and execution time, but fixes your bottleneck..
- Windows programs can eliminate the Data Size used up by PChar literals by using
String Tables. This also enables the translation (internationalisation) of the
application itself - Delphi already uses String Tables heavily !
- Move all your strings to a resource .RES file and load them dynamically.
- "What I usually do is to define one "global" object. It's usually a class - created
when the program starts and destroyed as it goes away.
All of the things that the various units of the program need to talk among themselves
and to each other are stored in Global. When the things that are global need to be
larger than 32k or so (even in Win95..transportability is still important to me..),
it becomes a method. Voila, now the class hides all of the details of physically
locating and storing/retrieving the global information - exactly, of course,
as a class ought to do.
It works. Even in Win95-land, it works." (by Sundial Services)
- More questions ? Maybe Klaus Hartnegg's 64 KB-FAQ can help you !
- Find related information on Borland's Pages: [1], [2] !
(Some hints to this topic came from Bob Swart)
- In Delphi 1, under "Options"/"Compiler"/"Linker", you set stack amd local heap
sizes. This is equivalent to the $M compiler directive in TP - and probably in
Delphi, too.
The trick is to fit these and your data area into 64k, i.e. make them both
16.384, and you have 32k left for variables. If you then get "Data segment
too large" (see above!), you have to shove some of your variables onto the
heap with Getmem() etc.
(by Jim Redmond)
- Use CASE.. statments rather than IF..ELSE.. clauses.
- In every .PAS-file that will be linked in your project, place the following line to the
top of your code:
{$D-,L-,O+,Q-,R-,Y-,S-}
Also add this line to your project source (.DPR).
{$D-} will prevent placing Debug info to your code.
{$L-} will prevent placing local symbols to your code.
{$O+} will optimize your code, remove unnecessary variables etc.
{$Q-} removes code for Integer overflow-checking.
{$R-} removes code for range checking of strings, arrays etc.
{$S-} removes code for stack-checking. USE ONLY AFTER HEAVY TESTING !
{$Y-} will prevent placing smybol information to your code.
After doing this, recompile the whole project - your .EXE-size should magically
have been reduced by 10-20 %..
- If you link in graphics, they don't need to have more than 16 or 256 colors.
- If the goal is really and only .EXE size, load your graphics by code ("manually")
instead of embedding them already during design time in each and every form which
uses them. A detailled description of this tip can be found on this page after
clicking here.
- If you include resource-files, they should only content the resources you really
need - and nothing more.
- If you use Delphi 1, finally run W8LOSS.EXE on your .EXE file (not required for 32bit-Apps).
- Delphi 2 produces larger .EXE sizes than Delphi 1 - 32 bit code demands its tribute..
- Delphi 3 produces larger .EXE sizes than Delphi 2 - main reason: market pressure
leads to more "fundamental" support of "bells and whistles" - quality and
usefulness/efficiency/productivity doesn't seem to be a real criteria in M$-times...
- Delphi 4 produces larger .EXE sizes than Delphi 3 - main reason: market pressure
leads to more "fundamental" support of "bells and whistles" - (..to be continued like above)
- check the "Show Hints" and "Show Warnings" options on the Project|Options|Compiler
page, then rebuild your project. It will show you every variable and proc/func that
isn't being used. You might be able to trim a little there, too.
- Clean your USES.. clauses for all unneeded units!
The so-called "SmartLinking" doesn't always remove all unused code.
In a large project you could possibly spare 100k in the EXE by that.
- Place Bitmaps/Glyphs/etc. in DLL's instead of in *.RES/*.DCR files !
If you place fx some large bitmaps in a .RES, these will compiled into the EXE.
Remove the .RES-declarations, instead place them in a new project like this:
LIBRARY My_Extern_RESes;
USES EmptyUnit;
{$R MyRes1.RES}
{$R MyRes2.RES}
...
INITIALIZATION
END.
The unit AEmptyUnit is a completely empty unit. You have to use a unit like this
because any other unit will place unnecessary code in the final DLL.
When you want to use an image (and typically, you only want to load it once),
you can do it like this :
MyHandle := LoadLibrary('My_Extern_RESes.DLL');
TButton1.Glyph.Handle := LoadBitmap(MyHandle,'My_Glyph');
Placing Glyphs, Bitmaps, String-const's etc. in DLL's can really reduce
a projects EXE-size. (Tip came from David Konrad)
- Basis rule: the more forms and units you add, the larger your exe becomes.
When you had one huge form, you only had one form class - even when you had 500+ things
on it! This creates only one runtime type information for that class. Now, when you
split this into 17 units, each class in that unit requires its own RTTI. Now, this runtime
information can get large, collectively, with a lot of redundant information compared to
when you only had one huge class. Also, if each of the 17 units had its own form, your end
product must contain resource information for all 17 of those forms even though most of
that information may be redundant. One reason why this happens is because the Delphi
compiler doesn't optimize resources - but I don't know of any compiler that does.
So, if you had two separate forms which are identical in look, you'll have 2 copies
of the resource in your .EXE.
This leaves some work for the programmer to be creative in maximizing resource reuseability.
(Tip came from Young Chung)
- Delphi 3 and 4 allows you to use packages (runtime libraries) which can be easily
enabled in your Project options. Especially if you have to update your application
often, this could be interesting for you: the runtime libraries just have to be deployed
once (there is even a chance that important Delphi packages like vcl30.dpl or vcl40.bpl
already exist on your user's computer) and to update your (then shrinked) executable
whenever an update is required (please consult the helpfile for further details).
- If your EXE file has to be super-small and you can go without Delphi's powerful
IDE and design features, you can also program without including the VCL (visual
component library) in your USES clause.
Here is a small EXE program which compiles to about 30 kb (by Frank Peelo):
Program HelloWin;
{ Standard Windows API application written in Object Pascal. }
Uses
Windows,
messages,
MMSystem;
Const
AppName : pChar = 'HelloWin';
Function WindowProc(Window:HWnd; AMessage, WParam, LParam:LongInt):
LongInt; StdCall; Export;
{ The message handler for the new window }
Var
h : hdc;
ps: tPaintStruct;
r : tRect;
Begin
Result := 0;
Case AMessage of
WM_Create:Begin
PlaySound('C:\WINDOWS\MEDIA\Musica Windows Start.wav', 0,
Snd_FileName or Snd_Async);
Exit;
End;
WM_Paint: Begin
h := BeginPaint(Window, Ps);
GetClientRect(Window, r);
DrawText(h, 'Hello Winblows!', -1, r,
DT_SingleLine or DT_Center or DT_VCenter);
EndPaint(Window, ps);
Exit;
End;
WM_Destroy:Begin
PostQuitMessage(0);
Exit;
End;
End;
Result := DefWindowProc(Window, AMessage, WParam, LParam);
End;
{ Register the window class }
Function WinRegister:Boolean;
Var
WindowClass: TWndClass;
Begin
WindowClass.Style := cs_HRedraw or cs_VRedraw;
WindowClass.lpfnWndProc := @WindowProc;
WindowClass.cbClsExtra := 0;
WindowClass.cbWndExtra := 0;
WindowClass.hInstance := HInstance;
WindowClass.hIcon := LoadIcon(0, idi_Application);
WindowClass.hCursor := LoadCursor(0, idc_Arrow);
WindowClass.hbrBackground := HBrush(GetStockObject(White_Brush));
WindowClass.lpszMenuName := NIL;
WindowClass.lpszClassName := AppName;
Result := RegisterClass(WindowClass)<>0;
End;
Function WinCreate:HWnd;
Var
HWindow:HWnd;
Begin
hWindow := CreateWindow(AppName,
'The Hello Program',
WS_OverlappedWindow,
CW_UseDefault,CW_UseDefault,
CW_UseDefault,CW_UseDefault,
0,0,HInstance,NIL);
If hWindow<>0 then Begin
ShowWindow(hWindow, CmdShow);
UpdateWindow(hWindow);
End;
Result := hWindow;
End;
{ Main: Set up window, then give it messages until done.}
Var
AMessage: TMsg;
hWindow: HWnd;
Begin
If not WinRegister then Begin
MessageBox(0, 'Register Failed', NIL, mb_Ok);
Exit;
End;
hWindow := WinCreate;
If hWindow=0 then begin
MessageBox(0, 'Register Failed', NIL, mb_Ok);
Exit;
End;
While GetMessage(AMessage, 0, 0, 0) do begin
TranslateMessage(AMessage);
DispatchMessage(AMessage);
End;
Halt(AMessage.WParam);
End.
There also exists a VCL replacement project named XCL linked from my "Delphi Tools"
page which could be of help here, too.
- If your main goal is distributable size, you could also consider
- using EXE/DLL packers like Blinker, ASPack, NeoLite etc. for program compression
- using alternative setup programs like INF-Tool to create your distribution package
(links to all those programs can be found on my Delphi Tools page.)
Start DELPHI32.EXE with parameter -ns ... and save invaluable millisecs !! :)
(works only with Delphi2 and Delphi3).
- You can drag Tables AND Fields onto your Form using the Database Explorer: Try it !
- By using bookmarks in the code editor you can jump around like you need it.
Set bookmarks by pressing <Ctrl>+<Shift>+"0".."9" and jump to them
by pressing <Ctrl>+"0".."9". (see Configuration help when you use other
keyboard settings than the default).
- In the Object designer you can jump to the parent of the highlighted component
by pressing <Esc>.
- You can easily change the "Source" of your forms by pressing Alt-F12 !
If you want to use Tables, Queries or DataSources in secondary forms, you could place a
new DataSource and write in the OnCreate-Event of the second form:
DataSource1.DataSet := Form1.Table1;
or you can assign the DataSource of the data-aware components to Form1.DataSource1.
From Delphi 2 on this is also possible with the Object Inspector as well. In Delphi 1
you loose design time view of the data.
Another way would be to use DataModules (Delphi 2 and higher). See the manual.
Richey's Delphi-Box at http://come.to/Delphi-Box
 | [e-mail] [PGP] [Copyright] Do not copy to other sites or include in commercial compilations without the written authorization from the author.
|