                        - March '96 Issue -

Delphi-Talk: delphi-talk@bridge.net the very best of delphi-talk.
Summary March 1996
Final Update.

These questions and answers are taken from the delphi-talk of
delphi-talk@bridge.net and was built up from:
Marko Tietz
tietz@mailserv.rz.fh-merseburg.de
tietz@wing.tgm.ac.at

You can read it or download it from:
Marko Tietz Delphi Page: http://www.fh-merseburg.de/~tietz/delphi.html,
Delphi Superpages http://sunsite.icm.edu.pl/~robert/delphi/index.html
and others.

I can't guarantee that all answers are correctly! Please use all information
at your own risk!

For subscribing to Delphi-Talk:
Send a mail to Majordomo@bridge.net and write into body (subject line will
not be processed!) 'subscribe delphi-talk' (without ') or have a look at
http://www.qns.com/html/~delphi.

In my own case:
This file is dedicated to Claudia Junge also known as wurli ;-) (Feel like a
drunken sailor) PLEASE STAY BY MY SIDE!!! Love hurts infinity! :-(..(



                      -  DELPHI KNOWLEDGE BASE -


You can also download all dtXXXX.txt files as a Paradox database. It is called
Delphi Knowledge Base. Look at http://www.fh-merseburg.de/~tietz/delphi.html
dkb*.zip. * stands for the current version.
ACTUAL: All new versions will be spread to other web sites! I'm not able to
access my own homepage at the moment :-(. Please stay on tuned on Delphi-
Super-Pages website.

Delphi Knowledge Base archive contains:
- all questions and answers from dtXXX.txt files in a Paradox database,
- a database reader, which allows you to read, search, copy questions and
  answers
- a Paradox for Windows form to handle database within Paradox 7 or Paradox 7
  Runtime.

Delphi Knowledge Base current version:
- Database with all questions and answers from September 1995 to November 1995
- Database reader version 1.6a
- Paradox form version 1.0

Delphi Knowledge Base is free for all private programmers! I hope you enjoy it!

Delphi Knowledge Base 2.0 - The future watch:
- DKB will appear as 32-bit application for Windows 95 and Windows NT.
- DKB should be more than a database reader. Edit your own categories, answers
  and questions to paradox table within dkb.
- Uses BDE 32 bit.
- Will be out in May / June 1996.


                     - DELPHI KNOWLEDGEBASE WinHELP -

There is a nice WinHelp-File from Eric Miles (milese@usafe14.ramstein.af.mil)
on Delphi Superpages. It contains all questions and answers from dt0995.txt to
dt1295.txt. A look on it worth it! Good work, Eric!



Mail me:
- if you find this file useful
- if you want make some suggestions



NOTE:
- I don't give any support on delphi questions!!! Please subscribe to
  delphi-talk mailinglist or read delphi newsgroups. All delphi related questions
  will be unanswered, because I haven't enough time.
- My Delphi-Homepage won't contain my actual work on this field. I spread my
  software to some other homepages such as the very famous Delphi-Superpages.
- Let me say thank you for all replies (postcards, letters, e-mails). dt*.txt
  and DKB are a piece of Delphi know-how for you all!

=============================================================================

 Contents

 1. TMemoryStream
 2. Getting DDE to talk to Program Manger under Win95
 3. Printer margins
 4. TTimer question
 5. Knowing that Windows is exiting
 6. Screen Saver Register
 7. Capturing the Maximize message
 8. Sort a TStringList by Numerical Value
 9. Canceling The Key Press
10. Getting Deletes to cascade across linked tables
11. How many Child Windows are open
12. Assign a palette to a bitmap
13. Setup program (source)
14. Loading a bitmap from .res without losing palette
15. Getting disk information
16. Custom cursor
17. Create a unit without a form
18. Set focus while modal dialog is open
19. Make TAB act like ENTER in StringGrid
20. Resource problems encountered with TTabbedNotebook and TNotebook
21. Encryption Algorithm
22. Overriding ESC-key on a Form
23. Transparent bitmap brush
24. Setting PC Clock
25. Scroll Listbox programmatically
26. Detect deleted records in .DBF
27. dbGrid and Memo Fields
28. Cycle thru list of comps
29. Splitter bar
30. GetKeyBoardState
31. Destroy a Modal Form on Deactivate
32. Popup menu in dependence on mouse position
33. TabbedNotebook and common components on all pages
34. Hiding apps from taskbar
35. How to disable a tab (page) in a Notebook component
36. Time difference
37. 16 bit DLLs and Delphi 2.0
38. Obtain last digits in a number
39. Popup menu that construct from DB
40. Insert text in MEMO
41. Array of TPoints
42. LASTDRIVE Value
43. Callback functions
44. Name of the item in a TListBox
45. Moving the cursor only in the x-axis
46. DBGrid as Navigator
47. Open MDI Form at specific size
48. Date Math
49. Install BDE
50. Send Messages to a control
51. Display a customer database entry form from a work order form

=============================================================================

1. TMemoryStream
Q:
Could anyone give any pointers to let me use
TMemoryStream to save data - mainly lines of strings.

A:
Think of memory stream as a file that is located in memory.  So the writes
are very similar to the write command for files.  (Actually it is closer to
the blockwrite command.)

To put a string the slow way you could do the following:

    for i := 1 to Length(s) do memstream.Write(s[i], 1);

That would write the string one character at a time.  Simple and easy to
understand, but a bit slow.  A faster way would be to do the following:

    memstream.Write(s[1], Length(s));

The two lines do the same thing, they append characters to the stream.  If
you have never done a seek on the stream, they just append to the end.

Now to handle the line feeds you have to add them yourself:

    memstream.Write(#13, 1);
    memstream.Write(#10, 1);

Or you could do some sneaky things like this:

    procedure StreamWriteStr(var ms: TMemoryStream; s: string);
    begin
        ms.Write(s[1], Length(s));
    end;

    procedure StreamWriteLnStr(var ms: TMemoryStream; s: string);
    begin
        StreamWriteStr(ms, s + #13#10);
    end;

Or you could create you own descendant class of TMemoryStream with a method
to write strings.

[Jeffrey McArthur, j_mcarthur@bix.com]

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

2. Getting DDE to talk to Program Manger under Win95
Q:
Is there a special trick to getting DDE to establish a link with
program Manger using DDE, when uisng Win95 (V1.0 of delphi)?

A:
I've been using the following unit to manage program groups using dde to
progman. It's an adaption of Steve Texeira's(sp) code in Dephi Developers Guide.

have had success in 3.1 and '95.

unit Pm;

interface

uses
  SysUtils, Classes, DdeMan;

type
  EProgManError = class(Exception);

  TProgMan = class(TComponent)
  private
    FDdeClientConv: TDdeClientConv;
    procedure InitDDEConversation;
    function ExecMacroString(Macro: String): Boolean;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    Procedure CreateGroup(GroupName: String; ShowGroup:Boolean);
    procedure DeleteGroup(GroupName: String);
    procedure DeleteItem(ItemName: String);
    procedure AddItem(CmdLine, ItemName: String);
  end;

implementation

uses Utils;

const
  { Program Manager DDE macro strings }
  SDDECreateGroup   = '[CreateGroup(%s)]';
  SDDEShowGroup     = '[ShowGroup(%s, 1)]';
  SDDEDeleteGroup   = '[DeleteGroup(%s)]';
  SDDEDeleteItem    = '[DeleteItem(%s)]';
  SDDEAddItem       = '[AddItem(%s, "%s", %s)]';

constructor TProgMan.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  InitDDEConversation;
end;

destructor TProgMan.Destroy;
begin
  if Assigned(FDDEClientConv) then
    FDdeClientConv.CloseLink;
  inherited Destroy;
end;

function TProgMan.ExecMacroString(Macro: String): Boolean;
Begin
  StringAsPchar(Macro);
  Result := FDdeClientConv.ExecuteMacro(@Macro[1], False);
End;

Procedure TProgMan.InitDDEConversation;
begin
  FDdeClientConv := TDdeClientConv.Create(Self);
  If NOT FDdeClientConv.SetLink('PROGMAN', 'PROGMAN') then
    raise EProgManError.Create('Failed to establish DDE Link');
end;

Procedure TProgMan.CreateGroup(GroupName: String; ShowGroup:Boolean);
Begin
  { Delete the group if it exists }
  ExecMacroString(Format(SDDEDeleteGroup, [GroupName]));

  If NOT ExecMacroString(Format(SDDECreateGroup, [GroupName])) then
    raise EProgManError.Create('Could not create group ' + GroupName);
  If ShowGroup then
    If not ExecMacroString(Format(SDDEShowGroup, [GroupName])) then
      raise EProgManError.Create('Could not show group ' + GroupName);
End;

Procedure TProgMan.DeleteGroup(GroupName: String);
Begin
  if NOT ExecMacroString(Format(SDDEDeleteGroup, [GroupName])) then
    raise EProgManError.Create('Could not delete group ' + GroupName);
End;

Procedure TProgMan.DeleteItem(ItemName: String);
Begin
  if NOT ExecMacroString(Format(SDDEDeleteGroup, [ItemName])) then
    raise EProgManError.Create('Could not delete item ' + ItemName);
End;

Procedure TProgMan.AddItem(CmdLine, ItemName: String);
Var
  P: PChar;
  PSize: Word;
Begin
  PSize := StrLen(SDDEAddItem) + (Length(CmdLine) *2) + Length(ItemName) + 1;
  GetMem(P, PSize);
  try
    StrFmt(P, SDDEAddItem, [CmdLine, ItemName, CmdLine]);
    if NOT FDdeClientConv.ExecuteMacro(P, False) then
      raise EProgManError.Create('Could not add item ' + ItemName);
  finally
    FreeMem(P, PSize);
  end;
End;

end.

[-]

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

3. Printer margins
Q:
Does anybody know how to get, programmatically, the location of the canvas
on the piece of paper that emerges from the printer?  i.e. the size of the
top, left, right and bottom margins?

A:
procedure TPrtPvw.GetOffsetPrinter;
var
   pt: TPoint;
   tmpAncho,tmpAlto: longint;
begin
   Escape(hPrinter,GETPRINTINGOFFSET,0,Nil,@pt);
   gOffSetLeft:=pt.X;
   gOffSetTop:=pt.Y;
   Escape(hPrinter,GETPHYSPAGESIZE,0,Nil,@pt);
   tmpAncho:=pt.X;
   tmpAlto:=pt.Y;
   gOffSetRight:=tmpAncho-gOffSetLeft-Printer.PageWidth;
   gOffSetBottom:=tmpAlto-gOffSetTop-Printer.PageHeight;
end;

[Javier Esteban Acuna, jacuna@datamar.com.ar]

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

4. TTimer question
Q:
I try to make a scheduler for starting an application at a certain time. To
be more specific, this scheduler should call a certain phone number and
leave a message. So I used a tTimer, set the Interval to a large value
(longint), only to discover that this value always put the phone ringing
after about 1 minute, yes, 65.535 milliseconds.
Is there a way to build a timer that uses a longint value for the interval?
Or does anybody know of an example or a component I can use to reactivate a
program after a longer period of time?

A:
Interval is defined as a word, so 65,535 is the most you're going to get
into it.  Don't know about other components, but to use the timer for over
one minute try this:

   var
      numOfMinutes: integer;
   ...
   numOfMinutes := 30;
   timer1.interval := 60000;
   timer1.enabled := true;
   ...
   procedure Timer1Timer;
   begin
      dec (numOfMinutes);
      if numOfMinutes = 0 then begin 
         { do whatever needs to be done }
      end;
   end;

[Sid Gudes, cougar@roadrunner.com]

A:
unit Alarm;

{
Copyright (c) 1996 Eric Nielsen. All Rights Reserved.

Permission to use, copy, modify, and distribute this software for
NON-COMMERCIAL or COMMERCIAL purposes and without fee is hereby granted.
}

interface

uses
  SysUtils, WinTypes, WinProcs, StdCtrls, Controls, Classes, ExtCtrls, Forms;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    procedure Timer1Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure FormAlarm(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}
type
  TAlarm=class(TComponent)
  private
    FAlarmTime : TDateTime;
    FOnAlarm: TNotifyEvent;
    FTimer : TTimer;
    FEnabled : Boolean;
    procedure SetOnAlarm(Value: TNotifyEvent);
    procedure SetEnabled(Value: Boolean);
    procedure SetAlarm(Value: TDateTime);
    procedure AlarmTimer(Sender: TObject);
  protected
    procedure Alarm; dynamic;
    procedure UpdateAlarm;
  public
    property AlarmTime : TDateTime read FAlarmTime write SetAlarm;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property OnAlarm: TNotifyEvent read FOnAlarm write SetOnAlarm;
    property Enabled: Boolean read FEnabled write SetEnabled default False;
  end;

procedure TAlarm.SetOnAlarm(Value: TNotifyEvent);
begin
  FOnAlarm := Value;
  UpdateAlarm;
end;

procedure TAlarm.SetEnabled(Value : Boolean);
begin
  FEnabled := Value;
  UpdateAlarm;
end;

procedure TAlarm.SetAlarm(Value: TDateTime);
begin
  FAlarmTime := Value;
  UpdateAlarm;
end;

procedure TAlarm.Alarm;
begin
  If Assigned(FOnAlarm) then FOnAlarm(Self);
end;

procedure TAlarm.UpdateAlarm;
begin
  If Assigned(FOnAlarm) and FEnabled
    Then
      FTimer.Enabled := True
    Else
      FTimer.Enabled := False;
end;

constructor TAlarm.Create(AOwner: TComponent);
begin
  FTimer := TTimer.Create(Self);
  With FTimer do
    Begin
      Enabled := False;
      Interval := 250;
      OnTimer := AlarmTimer;
    End;
end;

destructor TAlarm.Destroy;
begin
  FEnabled := False;
  UpdateAlarm;
  FTimer.Free;
end;

procedure TAlarm.AlarmTimer(Sender: TObject);
begin
  If Now >= FAlarmTime
    Then
      Alarm;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Label1.Caption := DateTimeToStr(Now);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Label2.Caption := 'Waiting...';
  With TAlarm.Create(Self) do
    begin
      AlarmTime := StrToDateTime(Edit1.Text);
      OnAlarm := FormAlarm;
      Enabled := True;
    end;
end;

procedure TForm1.FormAlarm(Sender: TObject);
begin
  (Sender as TAlarm).Free;
  Label2.Caption := 'BEEP!';
end;


end.

[Eric Nielsen, htrsoft@midwest.net]

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

5. Knowing that Windows is exiting
Q:
I have been looking for a way to know how to differentiate between my app
closing on the users request and when Windows is closing my app because of
Windows shutting down.
Is there an easy way out, or do I have to write an event handler for the
TApplication object to look at the messages from Windows ?

A:
Windows sends a message (WM_QUERYENDSESSION) to every active
application when the user tries to shut down Windows.  If your 
program responds with a non-zero value, then Windows will close it.  
If you program returns a 0, that tells Windows not to shut down.  The 
DefWindowProc returns a non-zero value for you.  You can write a 
message handler for this message for this message that will keep 
Windows from shutting down if your program is active.

[Daniel Bradley, dbradley@itc-dal.com]

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

6. Screen Saver Register
Q:
How do I register my Screen saver so Windows picks it up in the control
panel Desktop Dialog. The Application Name begins with SCRNSAVE and it
still does not register.

A:
a) In the project file (*.dpr) add '{$D SCRNSAVE <saver name>} after the
uses clause.

b) On the main form, turn off the border and icon controls. In the 
activate method set the form left and top to 0, and set the Windowstate 
to wsMaximize.

c) In the form create method, set the application.OnMessage to a method 
that controls the deactivation of the screen saver. Set the 
application.OnIdle method to whatever display method for the saver.

d) In the form create method the command line should be tested for /c and 
/s. These are the command line parameters windows uses to define whether 
the screensaver should run or configure. (/c is for configuration)

e) Compile the program, and rename the .exe to .scr. Move it to the 
windows directory, and it should show up in the control panel.

[-]

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

7. Capturing the Maximize message
Q:
I need to intercept the maximize system message and change the size of my
form manually (it's got a funky size. It resembles Delphi's main form in
size and behavior).  Right now, I have code in my OnResize event that
changes the size after the fact to what I want.  This makes a nasty flash,
even on my DX4/100.  This app is going to run on much slower machines (486
DX/33 or SX/25) and this flash as it redraws the form twice would be nasty
at that speed.  I tried writing my own handler for it assigned it to message
SC_MAXIMIZE.  This gave me a duplicate virtual method error (?).

A:
Respond to the WMGetMinMaxInfo message.  Pretty simple, some example
code is  below.  You don't get the flash, it just restricts how far
you can drag window borders when resizing, maximising, etc.  Set the
sizes to whatever you like.

private
    { Private declarations }
    procedure WMGetMinMaxInfo( var Message :TWMGetMinMaxInfo ); message WM_GETMINMAXINFO;

procedure TCCentre.WMGetMinMaxInfo( var Message :TWMGetMinMaxInfo );
begin
  with Message.MinMaxInfo^ do
  begin
    ptMaxSize.X := 640;                {Width when maximized}
    ptMaxSize.Y := 96;                {Height when maximized}
    ptMaxPosition.X := 0;             {Left position when maximized}
    ptMaxPosition.Y := 0;             {Top position when maximized}
    ptMinTrackSize.X := 500;           {Minimum width}
    ptMinTrackSize.Y := 96;           {Minimum height}
    ptMaxTrackSize.X := 640;           {Maximum width}
    ptMaxTrackSize.Y := 150;           {Maximum height}
  end;
  Message.Result := 0;                 {Tell windows you have changed
  minmaxinfo} inherited;
end;

[Craig Francisco, Craig.Francisco@adm.monash.edu.au]

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

8. Sort a TStringList by Numerical Value
Q:
I cannot use the 'Sort' method in TStringList as I would like
to sort by Integer.
My TStringList is filled with numbers such as:
20
12
1
23
54
32
(of course, they're converted to string before being added to TStringList)
What is a fast algorithm to achieve this sort?  Thanks.
I normally have less than 50 items in my TStringList, if that is a factor.

A:
I think the implementation that I mailed out to a few people sorts an array
of integers, and so there would be no problem using it to sort a a string list
with integers. You'd end up doing a *lot* of conversions using StrToInt, which
is wasteful, so I would recommend that you create a

	type
		PInteger = ^Integer type,

store all of the StrToInt values in the TStringList.Objects array, and then when
you use the sort, do your comparisons based on

		PInteger(SL.Objects[Idx])^

The quicksort that TStringList uses (see CLASSES.PAS) uses a very simple
partition function, which is completely unaware of the data it's sorting.
It's using the midpoint index to begin to decide where to start partitioning,
which is just as reliable as picking a random number when deciding how to sort.
If, for example, you had a BIG list of items that was already sorted in the
reverse direction, and you used this quicksort on it, and would call itself
recursively once for every element in the list!  Now, when you take into account
that you're pushing a few items on the stack (the return address as well as the
parameters as well as the registers you are saving) it might not take too long
for your 16K of stack space to get eaten up (16,384 bytes divided by about maybe
32 bytes (and that's being pretty optimistic!) is about 2048 items before you
run the risk of killing the stack!).  The MaxListSize in CLASSES is 16380 (65520
div sizeof (Pointer)), so it's certainly possible to cause this problem.

I do want you guys to know that TStringList.Sort is declared as virtual, so if
you wanted to override it, you certainly could in a class derived from
TStringList.

I also want you to know that the odds of anyone having to sort this much data
(2000 items) seems pretty remote (correct me, anyone, if you've ever sorted more
than 2000 strings in an application).  The most reliable sort with the same
running time as QuickSort is a HeapSort.  They both run in O(N lg N) time,
whereas sorts like the InsertionSort (which someone mentioned) and BubbleSort
(which someone else mentioned) run in O(N^2) time, on the average.

The biggest differences between HeapSort and QuickSort, in terms of their run
time and storage are:

1) HeapSort only calls itself recursively at most lg N times, where as QuickSort
could call itself recursively N times (big difference, like 10 vs 1024, or 32 vs
2^32)

2) The worst case upper bound time on HeapSort is only O(N lg N), whereas in the
worst case for QuickSort, the running time is O(N^2).

Program follows:

{***********************************************************}

program H;

uses WinCrt, SysUtils;

  const
    min = 10;
    max = 13;
    maxHeap = 1 shl max;

  type
    heap = array [1..maxHeap] of integer;
    heapBase = ^heap;

  var
    currentSize, heapSize: integer;
    A: heapBase;

  procedure SwapInts (var a, b: integer);
  var
    t: integer;
  begin
    t := a;
    a := b;
    b := t
  end;

  procedure InitHeap (size: integer);
  var
    i: integer;
  begin
    heapSize := size;
    currentSize := size;
    Randomize;
    for i := 1 to size do
      A^[i] := Random(size) + 1; 
  end;

  procedure Heapify (i: integer);
  var
    left, right, largest: integer;
  begin
    largest := i;
    left := 2 * i;
    right := left + 1;
    if left <= heapSize then
      if A^[left] > A^[i] then
        largest := left;
    if right <= heapSize then
      if A^[right] > A^[largest] then
        largest := right;
    if largest <> i then
      begin
        SwapInts (A^[largest], A^[i]);
        Heapify (largest)
      end
  end;

  procedure BuildHeap;
  var
    i: integer;
  begin
    for i := heapSize div 2 downto 1 do
      Heapify (i)
  end;

  procedure HeapSort;
  var
    i: integer;
  begin
    BuildHeap;
    for i := currentSize downto 2 do
      begin
        SwapInts (A^[i], A^[1]);
        dec (heapSize);
        Heapify (1)
      end
  end;

type
  TAvgTimes = array [min..max] of TDateTime;
var
  sTime, eTime, tTime: TDateTime;
  i, idx, size: integer;
  avgTimes: TAvgTimes;
  

begin
  tTime := 0;
  i := min;
  size := 1 shl min;
  new (A);
  while i <= max do
    begin
      for idx := 1 to 10 do
        begin
          InitHeap (size);
          sTime := Time;
          HeapSort;
          eTime := Time;
          tTime := tTime + (eTime - sTime)
        end;
      avgTimes[i] := tTime / 10.0;
      inc (i);
      size := size shl 1;
    end;
end.

[Matthew A. Gessner, mgessner@aristar.com]

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

9. Canceling The Key Press
Q:
I'm using the OnKeyDown event (NOT OnKeyPress) of a DBEdit to trap the
Num Key Pad's Plus Minus pressing, and that means that the user wants
to increace or decrease the value in the field.
My problem is that after performing the operation - the control adds
'+' or '-' to the edit box. I tried Key := 0; (because I know that in
OnKeyPress you do something similiar: Key := #0) but no results.

A:
To stop the + or - from appearing in your Tedit window:

Tformz.EditzKeyDown(Sender:Tobject;Var key:word;Shift:TshiftState);
var
    save_key:byte;
begin
save_key := key;
KEY := $0;
If ((save_key = VK_ADD)or(save_key=VK_SUBTRACT)) then do whatever...
else key := save_key;
..
..
end;

I am not sure if the VK_ADD, VK_SUBTRACT are the VK values for the
keypad, thier definition shows up just after the VK_NUMPAD values.

These VK key values are listed in APT help, Virtual Key Codes. Not all
are supported by DELPHI.

..
implementation
const
    proof:integer = 0;   {Just for testing}
var
    key_sig:integer;     {Was the key the Numeric KeyPad + or - ?}
                         {A simple way for KeyDown and KeyPress to}     
                     {communicate}

{**************** Sense the key*******************}
procedure Tformxyz.EditzzzKeyDown(...var key:word...);
var
    save_key:byte;
begin
key_sig := 0;             {default value]
save_key := key;          {save key press if needed later}
if (key = VK_ADD) then key_sig := +1;
if (key + VY_SUBTRACT) then key_sig := -1;
end;

procedure Tformxyz.EditzzzKeyPress(...var key:char...);
var
    save_key:char;
begin
save_key := key;
key := #0;                                {Suppress printing...for now}
if key_sig = 0 then key := char(save_key) {Print the character}

{I just noticed the cast to char..left over from earlier trials,}
{should not be necessary}

else
    begin
    proof := proof + key_sig;         {Demonstration that it works}
    edityyy.text := inttostr(proof);
    end;
end;
..
..
end.

[Owen Gailar, mud2@ix.netcom.com]

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

10. Getting Deletes to cascade across linked tables
Q:
Getting Deletes to cascade across linked tables? How do I do this I even tried
referential integrity. When I delete a record in one table I want to delete the
record LINKED in the other tables.

A:
I found this procedure at the Borland site and it works in my projects

procedure TForm1.Table1BeforeDelete(DataSet: TDataset)
begin
   with Table2 do begin
     DisableControls;
     First;
     While not EOF do
        Delete;
     EnableControls;
   end;
end;

Where table1 is the parent and table2 is the child.

[-]

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

11. How many Child Windows are open
Q:
In my MDI application, I would like to trigger an event - mainly showing
some special status panel when ZERO child windows exist. Conversely, I would
like to hide this status panel when AT LEAST ONE Child Window exists.

A:
Just look at the MDIChildCount property of the TForm.
You can do that on the Screen.OnActiveFormChange event.

[Javier Esteban Acuna, jacuna@datamar.com.ar]

A:
Example (from online help):

  with Form1 do
    for I := 0 to MDIChildCount-1 do
      MDIChildren[I].Close;

So,
 if (MDIChildCount=0) then ShowTheStatusPanel
   else HideTheStatusPanel;

[-]

A:
Create a general proc called OnChildCountChange in you main form
put something like this in there

if MDIChildCount = 0 then
  Panel1.Show else Panel1.Hide;

in the Destroy and Create of your child form call that proc -

MainForm.OnChildCountChange;

to update the status of the status bar

[Ryan Peterson, rpetersn@usit.net]

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

12. Assign a palette to a bitmap
Q:
I use a resource file to load (256 colour) bitmaps into my application.
At 65.000 colours this is no problem. When i switch my screen to 256 colours
the palette of the bitmap becomes very weird.
If i place a timage component on the form and do a loadfromfile then the
palette is correct.
So the problem is that the palette won't update when i draw an image
with ' canvas.draw(0,0,bitmap1) ' where bitmap1 is a tbitmap.
Does anybody on the list know a answer to this problem!?

A:
If you are drawing on a TImage....

First you need to use the Image1.Picture.bitmap not the Image.Canvas.
The reason is the the Image1.Picture.Bitmap has a palette -Timage doesn't.
Then you need to assign the palette.  For example:
	//Set Width and Height before using Image1.Picture Bitmap's Canvas
var
	Bitmap: TBitmap;
begin

	Bitmap:=TBitmap.Create;
	Bitmap.LoadfromFile({'Whatever.bmp'});

	With Image2.Picture.bitmap do
	Begin
		Width:=Bitmap.Width;
		height:=Bitmap.Height;
		Palette:=Bitmap.Palette;
		Canvas.draw(0,0,bitmap);
		Refresh;
	end;
end;

If you are drawing on the Form's Canvas ...

	Canvas.Draw(0,0,Bitmap);
	SelectPalette(Form1.Canvas.handle,Bitmap.Palette,True);
	RealizePalette(Form1.Canvas.Handle);

[caracena@henge.com]

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

13. Setup program (source)
A:
{ this is a setup program for an application I wrote.
  Maybe it's useful to others who are about to write their
  own Setup-programs.

  Dr. Norbert Hartkamp
  hartkamp@uni-duesseldorf.de
  n-hartkamp@nadeshda.gun.de
}

{file setupscl.pas}
program Setupscl;

uses
  Forms,
  Setupsc1 in 'SETUPSC1.PAS' {Form1};

{$R *.RES}

begin
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

{file setupsc1.pas}
unit Setupsc1;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, LZExpand, DdeMan;

const ProgName   = 'SCL90.EXE';
      LZProgName = 'SCL90.EX_';
type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    Panel1: TPanel;
    Label1: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label2: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Panel2: TPanel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    ProgrammpfadEdit: TEdit;
    DatenpfadEdit: TEdit;
    Label11: TLabel;
    Label12: TLabel;
    Panel3: TPanel;
    Label14: TLabel;
    AuswerterEdit: TEdit;
    Label15: TLabel;
    Inst1Edit: TEdit;
    Inst2Edit: TEdit;
    Inst3Edit: TEdit;
    CheckBox1: TCheckBox;
    Panel4: TPanel;
    Image1: TImage;
    Label13: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    DdeClientConv1: TDdeClientConv;
    Image2: TImage;
    Label19: TLabel;
    Label20: TLabel;
    Label18: TLabel;
    Panel5: TPanel;
    Label21: TLabel;
    Label22: TLabel;
    Label23: TLabel;
    Label24: TLabel;
    Label25: TLabel;
    Label26: TLabel;
    procedure FormPaint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    DialogNo            : byte;
    WinDir              : array [0..144] of char;
    Programmverzeichnis : string[127];
    Datenverzeichnis    : string[127];
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormPaint(Sender: TObject);
var   Rows, Height:Integer;
begin
  Height:=(ClientHeight + 255) div 256;
  for Rows := 0 to 255 do
  begin
    Canvas.Brush.Color := RGB(0,0,Rows);
    Canvas.FillRect(Rect(0,(255-Rows)*Height,ClientWidth,((255-Rows)+1)*Height));
  end;
  Canvas.Font.Size := 32;
  Canvas.Font.Color:= clBlack;
  Canvas.Brush.Style := bsClear;
  Canvas.TextOut(13, 13, 'Setup SCL-90-Auswertung');
  Canvas.Font.Color:= clYellow;
  Canvas.TextOut(10, 10, 'Setup SCL-90-Auswertung');
  Canvas.Font.Size := 11;
  Canvas.Font.Style:= [fsBold];
  Canvas.Font.Color:= clWhite;
  Canvas.TextOut(10, ClientHeight-(ClientHeight div 20), '=AE N.Hartkamp, 1996');
end;

procedure TForm1.FormCreate(Sender: TObject);
var fileHandle: THandle;
    fileBuffer: Array [0..40] of Char;
begin
  BorderStyle := bsNone;
  WindowState := wsMaximized;
  BitBtn1.Top := trunc(ClientHeight * 0.90);
  BitBtn2.Top := trunc(ClientHeight * 0.90);
  BitBtn1.Left:= trunc(ClientWidth  * 0.80);
  BitBtn2.Left:= trunc(ClientWidth  * 0.60);
  DialogNo := 0;
  FillChar(WinDir, SizeOf(WinDir), #0);
  GetWindowsDirectory(WinDir, 144);
  ProgrammpfadEdit.Text := StrPas(WinDir);
  ProgrammpfadEdit.Text := ProgrammpfadEdit.Text[1] + ':\SCL90R';
  DatenpfadEdit.Text := ProgrammpfadEdit.Text;
  AuswerterEdit.Text := 'Anwendername';

  { Get user name and company name }
  fileHandle := LoadLibrary('USER');

  if fileHandle >= HINSTANCE_ERROR then begin
    If LoadString(fileHandle, 514, @fileBuffer, 40) <> 0 Then
       AuswerterEdit.Text :=3D StrPas(fileBuffer);
    FreeLibrary(fileHandle);
  end;

  Inst1Edit.Text := 'Bezeichnung der Institution';
  Inst2Edit.Text := 'Bezeichnung der Institution (Fortsetzung)';
  Inst3Edit.Text := '- z.B.: Angabe der Abteilung -';
  CheckBox1.Checked := true;

  Panel1.Left := (ClientWidth  div 2) - (Panel1.Width  div 2);
  Panel1.Top  := (ClientHeight div 2) - (Panel1.Height div 2);

  Panel2.Left := (ClientWidth  div 2) - (Panel2.Width  div 2);
  Panel2.Top  := (ClientHeight div 2) - (Panel2.Height div 2);

  Panel3.Left := (ClientWidth  div 2) - (Panel3.Width  div 2);
  Panel3.Top  := (ClientHeight div 2) - (Panel3.Height div 2);

  Panel4.Left := (ClientWidth  div 2) - (Panel4.Width  div 2);
  Panel4.Top  := (ClientHeight div 2) - (Panel4.Height div 2);

  Panel5.Left := (ClientWidth  div 2) - (Panel5.Width  div 2);
  Panel5.Top  := (ClientHeight div 2) - (Panel5.Height div 2);
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
begin
  Close;
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
var aResultStr : array[0..144] of char;
    strIniPath : array[0..144] of char;
    ResultStr  : string[144];
    fromStruct : TOFStruct;
    toStruct   : TOFStruct;
    ret        : word;
    fromHandle : integer;
    toHandle   : integer;
    BDEOk      : boolean;
    CurPath    : string[144];
    Ok         : Longint;
    i          : word;
    DDE_Link   : Boolean;
    Macro      : string;


  function iif(cond : boolean; exp1, exp2 : string) : string;
  begin
    if cond then result := exp1 else result := exp2;
  end;

begin
  if DialogNo = 0 then begin
    ret := GetProfileString('IDAPI', 'CONFIGFILE01', 'NIX', aResultStr,
 80);
    ResultStr := StrPas(aResultStr);
    BDEOk := (ResultStr <> 'NIX') and FileExists(ResultStr);
    if not BDEOk then begin
      Panel1.Hide;
      MessageDlg('Installationsfehler:'+#13#13+
                 'Bevor Sie das SCL-90 Auswertungsprogramm installieren'+
#13+
                 'k=F7nnen, m=B3ssen Sie die Database-Engine installieren
.'+#13#13+
                 'F=B3hren Sie dazu das Program SETUP.EXE von'+#13+
                 'der BDE-Diskette 1 aus.', mtError, [mbOk], 0);
      Close;
    end;
  end;
  Inc(DialogNo);
  case DialogNo of
    1: begin
         Panel1.Hide;
         Panel2.Show;
         ProgrammpfadEdit.SetFocus;
       end;
    2: begin
         GetDir(0, CurPath);
         Ok  := 0;
         ret := IOResult;
         {$I-}
         ChDir(ProgrammpfadEdit.Text);
         if IOResult <> 0 then ok := 1;
         ChDir(DatenpfadEdit.Text);
         if IOResult <> 0 then if ok = 0 then ok := 2 else ok := 3;
         if ok <> 0 then begin
           case ok of
             1 : CurPath := 'Das Programmverzeichnis ist ';
             2 : CurPath := 'Das Datenverzeichnis ist ';
             3 : CurPath := 'Programm- und Datenverzeichnis sind ';
           end;
           Panel2.Hide;
           if MessageDlg(CurPath + 'nicht vorhanden'+#13+
                        iif(ok=3,'Sollen die Verzeichnisse ', 'Soll das
 Verzeichnis ') +
                        'angelegt werden?', mtConfirmation, [mbYes, mbNo]
, 0) = mrNo
             then begin
               Panel2.Show;
               Dec(DialogNo);
               if odd(Ok) then ProgrammpfadEdit.SetFocus
                          else DatenpfadEdit.SetFocus;
             end else begin
               MkDir(ProgrammpfadEdit.Text);
               MkDir(DatenpfadEdit.Text);
               DialogNo := 2;
               ret := IOResult;
               Ok  := 0;
             end;
         end;
         ChDir(CurPath);
         if Ok = 0 then begin
           Panel2.Hide;
           Panel3.Show;
           AuswerterEdit.SetFocus;
         end;
       end;
    3: begin
         Panel3.Hide;
         Panel4.Show;
         Invalidate;
         Application.ProcessMessages;
         ResultStr := ProgrammpfadEdit.Text;
         if ResultStr[length(ResultStr)] <> '\' then ResultStr := ResultStr
         + '\';
         StrPCopy(strIniPath, ResultStr + 'SCL90.INI');
         WritePrivateProfileString('Passwort', 'Passwort', '=C1=C2=BF',
         strIniPath);

         StrPCopy(aResultStr, DatenpfadEdit.Text);
         WritePrivateProfileString('Vorgaben', 'Datenpfad', aResultStr,
         strIniPath);

         StrPCopy(aResultStr, Inst1Edit.Text);
         WritePrivateProfileString('Vorgaben', 'Inst1'    , aResultStr,
         strIniPath);

         StrPCopy(aResultStr, Inst2Edit.Text);
         WritePrivateProfileString('Vorgaben', 'Inst2'    , aResultStr,
         strIniPath);

         StrPCopy(aResultStr, Inst3Edit.Text);
         WritePrivateProfileString('Vorgaben', 'Inst3'    , aResultStr,
         strIniPath);

         StrPCopy(aResultStr, AuswerterEdit.Text);
         WritePrivateProfileString('Vorgaben', 'Auswerter', aResultStr,
         strIniPath);

         WritePrivateProfileString('Vorgaben', 'TWerte',    '1', strIniPath);

         fromHandle := LZOpenFile(LZProgName, fromStruct, OF_READ);
         ResultStr  := ProgrammpfadEdit.Text;
         if ResultStr[length(ResultStr)] <> '\' then ResultStr := ResultStr
         + '\';
         StrPCopy(aResultStr, ResultStr+ProgName);
         toHandle   := LZOpenFile(aResultStr, toStruct, OF_CREATE);
         ok := LZCopy(fromHandle, toHandle);
         if ok < 0 then begin
           case ok of
             LZERROR_BADINHANDLE  : ResultStr := 'Das Handle, das die
             Quelldatei bezeichnet, ist nicht g=B3ltig.';
             LZERROR_BADOUTHANDLE : ResultStr := 'Das Handle, das die
             Zieldatei bezeichnet, ist nicht g=B3ltig.';
             LZERROR_BADVALUE     : ResultStr := 'Der eingegebene Parameter
             ist au=DFerhalb des erlaubten Bereichs.';
             LZERROR_GLOBALLOC    : ResultStr := 'F=B3r die ben=F7tigten
             Puffer steht nicht gen=B3gend Speicher zu'+ 'Verf=B3gung.';
             LZERROR_GLOBLOCK     : ResultStr := 'Das Handle, das die
             internen Datenstrukturen bezeichnet, ist nicht'+
                                                 'g=B3ltig.';
             LZERROR_READ         : ResultStr := 'Die Quelldatei hat ein
             ung=B3ltiges Format.';
             LZERROR_UNKNOWNALG   : ResultStr := 'Die Quelldatei ist mi=
t einem unbekannten Algorithmus komprimiert worden.';
             LZERROR_WRITE        : ResultStr := 'Es steht nicht gen=B3=
gend Platz f=B3r die Ausgabedatei zur Verf=B3gung.';
           end;
           MessageDlg('Fehler beim Expandieren von SCL90.EXE:'+#13#13+
                      ResultStr, mtError, [mbOk], 0);
         end else
         begin
           Panel4.Hide;
           Ok := 0;
           with DdeClientConv1 do begin
             DDE_Link := FALSE;
             DDE_LINK := SetLink('ProgMan','ProgMan');
             if DDE_LINK = TRUE then
             begin
               OpenLink;
               Macro := ' [CreateGroup ("SCL-90-Auswertung")]';
               StrPCopy (aResultStr, Macro);
               if not ExecuteMacro(aResultStr, False) then
                 MessageDlg('Programmgruppe konnte nicht eingerichtet
                 werden...',
                            mtInformation, [mbOK],  0) else
               begin
                 Macro := ' [ShowGroup("SCL90AUS.GRP",1)]';
                 StrPCopy (aResultStr, Macro);
                 ExecuteMacro(aResultStr, False);
                 ResultStr  := ProgrammpfadEdit.Text;
                 if ResultStr[length(ResultStr)] <> '\'
                   then ResultStr := ResultStr + '\'+ProgName;

                 Macro := ' [AddItem('+ResultStr+
                          ', "SCL-90-Eingabe", )]';
                 StrPCopy (aResultStr, Macro);
                 if not ExecuteMacro(aResultStr, False) then
                   MessageDlg('Programm konnte nicht in Gruppe eingef=B3g=
t werden...',
                              mtInformation, [mbOK],  0) else
                 begin
                   Panel4.Hide;
                   Panel5.Show;
                 end;
               end;
               CloseLink;
             end; { if DDE_LINK = TRUE }
           end; { with DdeClientConv1 do }
         end; { ok > 0 }
       end { DialogNo = 3 }
    else Close; { => end of program }
  end; { of case }
end; { TForm1.BitBtn1Click }

end.

{ file setupscl.dfm }
object Form1: TForm1
  Left = 200
  Top = 99
  Width = 435
  Height = 300
  Caption = 'Form1'
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'System'
  Font.Style = []
  PixelsPerInch = 96
  TextHeight = 16
end


{ file setupsc1.dfm }
object Form1: TForm1
  Left = -4
  Top = -4
  Width = 648
  Height = 488
  BorderIcons = []
  Caption = 'Form1'
  Color = clBlack
  Font.Color = clBlack
  Font.Height = -43
  Font.Name = 'Arial'
  Font.Style = [fsBold, fsItalic]
  PixelsPerInch = 96
  WindowState = wsMaximized
  OnCreate = FormCreate
  OnPaint = FormPaint
  TextHeight = 49
  object BitBtn1: TBitBtn
    Left = 648
    Top = 512
    Width = 97
    Height = 33
    Caption = 'Weiter...'
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = [fsBold]
    ParentFont = False
    TabOrder = 0
    OnClick = BitBtn1Click
    Kind = bkOK
  end
  object BitBtn2: TBitBtn
    Left = 528
    Top = 512
    Width = 97
    Height = 33
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = [fsBold]
    ParentFont = False
    TabOrder = 1
    OnClick = BitBtn2Click
    Kind = bkCancel
  end
  object Panel1: TPanel
    Left = 128
    Top = 112
    Width = 400
    Height = 217
    TabOrder = 2
    object Label1: TLabel
      Left = 25
      Top = 40
      Width = 147
      Height = 16
      Caption = 'Herzlich Willkommen! '
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label3: TLabel
      Left = 25
      Top = 64
      Width = 294
      Height = 16
      Caption = 'Dieses Setup-Programm richtet das SCL-90-R'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label4: TLabel
      Left = 25
      Top = 80
      Width = 266
      Height = 16
      Caption = 'Eingabe- und Auswertungsprogramm auf '
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label5: TLabel
      Left = 25
      Top = 96
      Width = 126
      Height = 16
      Caption = 'Ihrer Festplatte ein.'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label2: TLabel
      Left = 25
      Top = 120
      Width = 337
      Height = 16
      Caption = 'Wenn Sie das Programm nicht installieren m=F7chten,'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label6: TLabel
      Left = 25
      Top = 136
      Width = 265
      Height = 16
      Caption = 'k=F7nnen Sie den Vorgang jetzt abbrechen.'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label7: TLabel
      Left = 25
      Top = 160
      Width = 341
      Height = 16
      Caption = 'Um fortzufahren dr=B3cken Sie bitte die Eingabetaste..=
.'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
  end
  object Panel3: TPanel
    Left = 120
    Top = 104
    Width = 401
    Height = 225
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    ParentFont = False
    TabOrder = 4
    Visible = False
    object Label14: TLabel
      Left = 16
      Top = 22
      Width = 64
      Height = 16
      Caption = 'Auswerter'
    end
    object Label15: TLabel
      Left = 16
      Top = 70
      Width = 260
      Height = 16
      Caption = 'Bezeichnung der Institution (drei Zeilen)'
    end
    object AuswerterEdit: TEdit
      Left = 16
      Top = 40
      Width = 177
      Height = 24
      TabOrder = 0
      Text = 'AuswerterEdit'
    end
    object Inst1Edit: TEdit
      Left = 16
      Top = 88
      Width = 345
      Height = 24
      TabOrder = 1
      Text = 'Inst1Edit'
    end
    object Inst2Edit: TEdit
      Left = 16
      Top = 120
      Width = 345
      Height = 24
      TabOrder = 2
      Text = 'Inst2Edit'
    end
    object Inst3Edit: TEdit
      Left = 16
      Top = 152
      Width = 345
      Height = 24
      TabOrder = 3
      Text = 'Inst3Edit'
    end
    object CheckBox1: TCheckBox
      Left = 16
      Top = 184
      Width = 161
      Height = 17
      Caption = 'Anzeige mit T-Werten'
      TabOrder = 4
    end
  end
  object Panel2: TPanel
    Left = 128
    Top = 112
    Width = 401
    Height = 185
    TabOrder = 3
    Visible = False
    object Label8: TLabel
      Left = 16
      Top = 16
      Width = 274
      Height = 16
      Caption = 'Bitte geben Sie das Programmverzeichnis'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label9: TLabel
      Left = 16
      Top = 32
      Width = 198
      Height = 16
      Caption = 'und das Datenverzeichnis ein:'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label10: TLabel
      Left = 16
      Top = 68
      Width = 71
      Height = 16
      Caption = 'Programm:'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label11: TLabel
      Left = 16
      Top = 100
      Width = 42
      Height = 16
      Caption = 'Daten:'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object Label12: TLabel
      Left = 14
      Top = 136
      Width = 341
      Height = 16
      Caption = 'Um fortzufahren dr=B3cken Sie bitte die Eingabetaste...'
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = [fsBold]
      ParentFont = False
    end
    object ProgrammpfadEdit: TEdit
      Left = 104
      Top = 64
      Width = 225
      Height = 24
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = []
      ParentFont = False
      TabOrder = 0
      Text = 'ProgrammpfadEdit'
    end
    object DatenpfadEdit: TEdit
      Left = 104
      Top = 96
      Width = 225
      Height = 24
      Font.Color = clBlack
      Font.Height = -13
      Font.Name = 'System'
      Font.Style = []
      ParentFont = False
      TabOrder = 1
      Text = 'DatenpfadEdit'
    end
  end
  object Panel4: TPanel
    Left = 152
    Top = 160
    Width = 337
    Height = 113
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    ParentFont = False
    TabOrder = 5
    Visible = False
    object Image1: TImage
      Left = 256
      Top = 40
      Width = 33
      Height = 33
      Picture.Data = {
        055449636F6E0000010001002020100000000000E80200001600000028000000
        2000000040000000010004000000000080020000000000000000000000000000
        0000000000000000000080000080000000808000800000008000800080800000
        C0C0C000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000
        FFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF333333FFFFFFFFFFFFFFFFF
        FFFFFFFFFF33FF33FFFFF11FFFFFF1111FFFFFFFFFF3FFFFFFFFFFF115FF11FF
        1155FFFFFFF33FFFFFFFFFFF115511F511F55FFFFFF33FFFFFFFFF11111511F5
        11F55FFFFFF33FFFFFFFF11FF11511F511F55444FFF33FFFFFFFF11F511F1145
        11F55FF4FFFF33FFFFF9911F5119119511F55FF4FFF3333FFFFFF11F51191199
        11F55FF4FFFFFFFFFFFFFF111199511114F55FF4FFFFFFFFFFFF999995996699
        55556666FFF3333FFFF99FF99F99449944F66FF6FF33FFF33FF99FF99F994499
        64F66F56F33FFFFFFFF99FF99F99F69966F66556F33FFFFFFFF99FF99F996699
        66566556F33FFFFFFFFF9999FFF9999566566556F333FFFFFFFFFF22FFFF6655
        66566556FF33FFFF3FFFFFFF22FF665566566556FFF33FFF33FFFFFFF22FF666
        62556666FFFFF333F3FFFFF222CCF255F2CCCC5FFFFFFFFFFFFFFF22FF22CC25
        5CCFFCC5F3F333FFFFFFFF22AA22FCCFACCAFCCFF33FF33FFFFFFF22FFACCCCC
        ACCAACC1FF3FF33FFFFFFF22FFCCA2CCACCAACCFFFFF333FFFFFFFF22ACCAACC
        ACCAACCFFFFF33FFFFFFFFFFAACCAACCACCAACCFFFF33FFFFFFFFFFFAACCAACC
        ACCAACCFFFF33FF3FFFFFFFFAAFCCCCAA1CCCCFFFFF33F33FFFFFFFFAAFFAA1A
        A11AA1FFFFFF333F3FFFFFFFFAAAA11FAAAA11FFFFFFFFFFFFFFFFFFFFFFFF11
        11FFF11100000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        00000000}
    end
    object Label13: TLabel
      Left = 24
      Top = 24
      Width = 117
      Height = 16
      Caption = 'Einrichtung l=F5uft...'
    end
    object Label16: TLabel
      Left = 24
      Top = 48
      Width = 166
      Height = 16
      Caption = 'Erstellen von SCL90.INI...'
    end
    object Label17: TLabel
      Left = 24
      Top = 72
      Width = 198
      Height = 16
      Caption = 'Kopieren der Programmdatei...'
    end
  end
  object Panel5: TPanel
    Left = 120
    Top = 80
    Width = 353
    Height = 281
    Font.Color = clBlack
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    ParentFont = False
    TabOrder = 6
    Visible = False
    object Image2: TImage
      Left = 24
      Top = 16
      Width = 33
      Height = 33
      Picture.Data =3D {
        055449636F6E0000010001002020100000000000E80200001600000028000000
        2000000040000000010004000000000080020000000000000000000000000000
        0000000000000000000080000080000000808000800000008000800080800000
        C0C0C000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000
        FFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF333333FFFFFFFFFFFFFFFFF
        FFFFFFFFFF33FF33FFFFF11FFFFFF1111FFFFFFFFFF3FFFFFFFFFFF115FF11FF
        1155FFFFFFF33FFFFFFFFFFF115511F511F55FFFFFF33FFFFFFFFF11111511F5
        11F55FFFFFF33FFFFFFFF11FF11511F511F55444FFF33FFFFFFFF11F511F1145
        11F55FF4FFFF33FFFFF9911F5119119511F55FF4FFF3333FFFFFF11F51191199
        11F55FF4FFFFFFFFFFFFFF111199511114F55FF4FFFFFFFFFFFF999995996699
        55556666FFF3333FFFF99FF99F99449944F66FF6FF33FFF33FF99FF99F994499
        64F66F56F33FFFFFFFF99FF99F99F69966F66556F33FFFFFFFF99FF99F996699
        66566556F33FFFFFFFFF9999FFF9999566566556F333FFFFFFFFFF22FFFF6655
        66566556FF33FFFF3FFFFFFF22FF665566566556FFF33FFF33FFFFFFF22FF666
        62556666FFFFF333F3FFFFF222CCF255F2CCCC5FFFFFFFFFFFFFFF22FF22CC25
        5CCFFCC5F3F333FFFFFFFF22AA22FCCFACCAFCCFF33FF33FFFFFFF22FFACCCCC
        ACCAACC1FF3FF33FFFFFFF22FFCCA2CCACCAACCFFFFF333FFFFFFFF22ACCAACC
        ACCAACCFFFFF33FFFFFFFFFFAACCAACCACCAACCFFFF33FFFFFFFFFFFAACCAACC
        ACCAACCFFFF33FF3FFFFFFFFAAFCCCCAA1CCCCFFFFF33F33FFFFFFFFAAFFAA1A
        A11AA1FFFFFF333F3FFFFFFFFAAAA11FAAAA11FFFFFFFFFFFFFFFFFFFFFFFF11
        11FFF11100000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        0000000000000000000000000000000000000000000000000000000000000000
        00000000}
    end
    object Label19: TLabel
      Left = 24
      Top = 64
      Width = 256
      Height = 16
      Caption = 'Die Einrichtung des SCL-Programms ist'
    end
    object Label20: TLabel
      Left = 24
      Top = 80
      Width = 103
      Height = 16
      Caption = 'abgeschlossen.'
    end
    object Label18: TLabel
      Left = 24
      Top = 112
      Width = 294
      Height = 16
      Caption = 'Das SCL-Programm verwendet ein Passwort, '
    end
    object Label21: TLabel
      Left = 24
      Top = 240
      Width = 260
      Height = 16
      Caption = 'Eingabetaste dr=B3cken, um zu beenden...'
    end
    object Label22: TLabel
      Left = 24
      Top = 128
      Width = 254
      Height = 16
      Caption = 'um bestimmte Programmfunktionen zu '
    end
    object Label23: TLabel
      Left = 24
      Top = 144
      Width = 63
      Height = 16
      Caption = 'sch=B3tzen.'
    end
    object Label24: TLabel
      Left = 24
      Top = 160
      Width = 300
      Height = 16
      Caption = 'Dieses Passwort k=F7nnen Sie jederzeit =F5ndern.'
    end
    object Label25: TLabel
      Left = 24
      Top = 176
      Width = 235
      Height = 16
      Caption = 'Das voreingestellte Passwort lautet:'
    end
    object Label26: TLabel
      Left = 144
      Top = 208
      Width = 39
      Height = 22
      Caption = 'SCL'
      Font.Color = clBlack
      Font.Height = -19
      Font.Name = 'Arial'
      Font.Style = [fsBold]
      ParentFont = False
    end
  end
  object DdeClientConv1: TDdeClientConv
    ServiceApplication = 'ProgMan'
    ConnectMode = ddeManual
    Left = 88
    Top = 32
  end
end

{ file scl90.ex_ (UUencoded) }
begin 644 SCL90.EX_
M4UI$1(CP)S-!90`H``#_35H``0$```#_"``0`/__"``)`/3Q]?!``@,.#1T+
M`03P,PU##5,-8`FZ$``._Q^T"<TAN`%,_\TAD)!4:&ES_R!P<F]G<F%M_R!R
M97%U:7)E_W,@36EC<F]S_V]F=3D"!7:6YD?V]W<RX-"B3@_?"Z#<H-V@WK\DY%
M!@'WTP#&`@(*`P,`7P`@`""@\_(#"1#W!P`-!P!8`(T`/Y8`I`"9`O\!_O#Q
M`!T0`@/]`*T1$!W_K1$9`/$(4`W_\0@C`.8`00V_L@,(``.``0,D7@D0$!P"
M@`(!#DL4W2<``!`<+&`$"$W_04E.24-/3@7?4T-,.3`J`@@`_PP`$0`:`"$`
M_R8```9+15)._T5,`T=3D$2015_U-%4@A+15E"ST]!4D25%*`;(/^_`,T_`?D0
MQ1'G;0_%$;8&Q1&.!\41K:C<$@0(Q1%KZ!*3[03%$5P%Q1&B#P&VQA"^#<41
MH0[%$>&J#"(9T!(PT!)*RA$"M!T0)B!A*B(M`24A1]8V(H\#)2&AW!$"_[;6
M$0)W]!$"?%0BW5;H$0*Y8"+22"(H^A%K`EAR(GUR(IT%`@'_"7-C;#DP+D4#
M6$5@#:(MLBW"+=3D(M(`S_+"!!8W1U86R?;'DL('2!`8(`;O]O=3D"!A(&9U;G]C
M=3D&EO;F%LA`7_+CP@271S(&_W;FQYC`!A<V]N_R!F;W(@8F5ILVYG`C#\(6%T
M_2!E?W)E(&UU<W0T,)\@<V]M9?X@.#`A_#`R1#`@<V5T=3D7!VA`9T;V4Q('5P
M'C'_9B!Y;W4@;&EU:T@P>64\86YD@C+W=3DV%N0C!O(&UO?V1I9GD@:70P,O\V
M('5S92!W:?MT:((Q<B!O=3DV[_(&%P<&QI8V'H$#$\,(,P)T<P9G)E]65S,61U
M,&\N+2"=3D09XP;6%Y3S!$,&[RW#)DAC/I,7)O<"#[;64),&QI;F4Z_QP@:&%R
M=3D&MA_VUP0'5N:2UD_W5E<W-E;&1O_W)F+F1E&R!N_2TB1FYA9&5S:+]D82YG
M=3D6XZ0`G_(%)E9V%R9'/_+!<@3F]R8F7O<G0@2"-$+"!-?RY$+A0@1/PR1?\L
M($=3DE<FUA;K]YFO__``"01U5_B>6_L`(>5Y!"?J)"OP(`#E=3DJE$B2K$,OLTVM
M0FS)3:-,CB3?3:U"R_]-K4,!%EVD2TDP*UVM0DU+7:-,:6%=3DTJU"<X%=3DK4*+
MEUS),?W`FD6#[`*+1@;?.T8$?0C*4(E&O_[K!HM&!-50BW]&_HGL7<($PUKY
M?M%=3DX5B`/F<``/]T$_\V7@"_3GT!I42C3`'K#!AA_B%E_S9,`;@0`%E0D$(^
M8Z-N-F,(/61I4CYC-V(%4VM=3DPY]`GC=3DA_S9N`9!"$6,0QAAKZPT883=3DA<&E>
M`/\QP%#_-D8!N/T"/62A'``K!B#_`/<F1@%0H1[ZQF`BRF!(`0,&2J$!6V4M
M9'5BIFU0M&"X^P$`MF!"`>C7_A@#<>!FM&(@``=3DQX&8'<9X$=3DD0!Z*01>P=3DS
M(MPE=3DW5B@#YE%&`*@/L^9A1@`^AW_[CU_^AE19]`'H`^9+\``'4#Z;.S8G:]
M"`MR)?Y0Z.%01E4(DG(&/G(1GG`RHG`V_E`(.\A@=3D0G*4=3D1@@W1YNG068Y)S
M)G;#=3D13V3G9V!B9ZH2``*RM&",MC(@N`!M=3DA`W#G,=3D)2'H3C=3D`BC(.;G<:,B
MY&?C4$W*!+D`@W+$8CX!0"%R__\V'`#H7_U0Z,N`_<]C0%>".H$>`)OH1F2`
M9_T^8T6!R_[$4@2A8``!1@3^_U`[!AH`?`:A]QH`*9>"]R88`/\#1@;$/CH!
M`__XB?B,PHE&_#N)5M]0_(M6XE?*5`\ZZ#+]-V'*4,=3DM&H(\J7)Y@(O_4E#_
M4!>`[CYCZ&3]RH5^!#;W_W7\'9#ZZ*;_?AJ0,<`VB47\*Y+_^C'`HQP`_P;S
M'@#08)R!=3D5G_#DT>.Y!@`).`0Y(%-Y#Y8+-C>8`D_XG'CO_"!E?_-A@`L'T@
M%7JA2`'WV!Z-!N-IP@)+@Y"`OV6\@+.2__J#?@8`=3DP/I_YH`Q'X()HH%_SP@
M<C\\_W<[L,B3MF`ZD'B!I?YID5B_)H@%_P8<PV$[ST;Z?@:YD_21!AC_`'4$
M5>@(_^O_2#P-=3D095Z/[__NL^/`AU+H/_/AP``'XE_P[TWY'?E%CGDB;&!2#V
M])+\??N3_.L,//,'=3D:5Q7&-&"/]._P;I7?__=3DOS__W;ZZ&/^@#XVZA1@!4"'
M!DN#C7X&HQ97)G9%@J26%)!"C8]^ZA97`7,$=3D)!""?_`=3D!^#?NP2=3D8\#Z)S\
MI:*@IY=3DCQO^#/F(``+``?O\!0(A&_8I&_?2(@J:4`I!'",!U+-_&!F8``6AS
M`^C;E/O\J73R#[6_^]X*L0"@<`'IH/\.;V(`OW$>8+]P'F#G_S9BE$/LK`8Q
MP/>)1OB00HA&^XK[1OL;H!.#?O@`?W8+_T[XL`@^8ZOK+,Z0*-*0),I02/](
M.T;X=3DAJ*5N_[BT;XR)`#^":?B!7_1OAPL#YC@/]^^PUT#8`^-[H48*6VL!IU
MGYR]^':VL747G[;&!0JJL!.P#3YCXW3XO(#"@'>D_I]`Q@9D``&A&/KF@!I#
M93H!B18\S0&R@097%,5SE8`^Y3B+<!JP9#]CN&#P]U"X`UQY@^P(QG\&9P`!
MZ)_Y?Y&_4@&9]SY&VF`@90!^@/GZ4&^1H5;:8)=3D&`4AUR":!P#"04%.A5'3!
MV6$B?<`U@<!_^O\V&@"A6-I@Z4B/PJK%]/ZP^(M&+?J7L'TXW(/^XX;3P-KO
MB/YEH8W[`I'\*_M&_E.E^NO`Z&'1^6?!1(%@PP(:D8M%LP8]BW#"<`I(^E%>
MZST!+M-`^E%0/0+'`'4+,=3D`,@/I10#WU`TK3`U'3,#T&`/MU!V.Q_NLD/0?[
M`'7V51<]!`!U40PETP%A_5$*W5<(PU.A!`F`^E`4@/^R""S1&O[WP*$^`9FY
M`@![]_D*<E7H3_]\TUH[T!)BH39`-F!$R]!1-L_09*#YP/Q`A<(*PFIH?-_2
MP`9UPJ,^WX!=3D!*?"HT`!?Y$84X*_4.BT]Z-")N(:?FR"4.BC]Z-$-F",7($-
M<'#W,X!S@T!P8MWW.8#HDOAH?#3XWN56).B(]S=3DAC7Y]W,RDBT;HHT;?@+_<
M`T;DHTC?@-[/HTH!N'S`D$+1X$U0NV:+R!3">,#!$G:_4.CN]EH#NX#^B[@A
MJ^@$/656QK[@&G+68\'6X+*G4.BMU>/^`<#^Q'X$)HE%RIB`_"#S!JGMXW/0
MH?]&`;$$T^`#PD36X"#S#.CHW>GSYM""D*?1X-%.\U'V#AWV$)XG]A+HSO8(
M:$#`=3D-\)@'X$`\2@H_>>WZ%`?0^*EX#@H(@_A7`!_P9BH9)AP]ZR^W/WN!&U
MIK``_7WGH?_'1OP!`/?K`__#@'[\T>?_T>>!QV0`'@?^RY`Z1@1U'R:*?T4!
M.D;_=3D18D`"\",.10)``#,0"T8/_H8/WK!H-^_.<,=3D</C4=3DKRQ@9ES`VQ<7-,
M]F-T<7.`]I12`71E/#9@.A/+-Y*C#1Z.`2``.8`3=3D0_!=3D&"WC-B0@W*.V)"`
M5KBIH+R0_[(.HU[G<`S^.M$&Z#;[Z:<`^ST/T`&`^^F<`%\]%0%U$/5S"JEP
M_^BK_.F'`#T4YP%U#I)R[P*7_.OG=3D#T%2M"I<78(Z+_C_.MD/20.$@C^\`%3
M_>M4/0(!_W4)BD8*4.AWW_[K1CT`+A66_OOK.'31!>C__NLW+CT(2A$)_W+0
M2=3D#?!>@3_^NZT`[_0W8,[0(C$4"#P`!6`<#_^HM6_%]>C6;K_A]'@`JEE0+$
M?O\&)H-]"`!T'Z\F_W4.HQ`,HQ`(#)!"F1`QP"/PKA-CL0'&X$J$EA2D%ZV3
MF1&)10K@MQ/"'?6D^AV2&(%]`O^QUW4:N/__NOO__R/P%":)51:ZMQ(8(_`:
MZR69$<>/10*RUS,K,R9&(%44,B3M$QP](!X1+:>432+9!3<@42"PUU`@!(#_
M`":-A8``C,+45_$](`XS)A`](!(FY\9%,$2*B'-2_S;]4F!@4`"_Y@`>=3DJF@
MNO]98/\V$&!@U1)@8!1@8!8`=3D/\V(9JS8">&R`">8YR40SR*_O.C@>R6`*$V
M`?^+%C@!HZX`B?,6L(9@BG.)`*&T[P`+!K9S<.E]`-^XY@",VGH3C;Y7:/\6
M;I`F8&`DTJ`!^LRD&&%R,Q9Y3,>?!T#`)XU^Z*BHMZ46MS*@I+;0->O3B(8^
MF(MP.M^AF@"C1+-AN`#U?Q,W1O@]HT@`QW\&2@`&`+\Z[2!`IT0>92U':UVC
M1EQDFNHCP[A0/63K(NLBOV.N`&V+3C"C-B/`.`$S(_Y*-%W+B``#`:'_`04`
M6P`#`*:O`?\`$:9`JZI``:JF0+BJ0!>F0,FJ0!:JID#.ND3?PD3DND3U6L)$
M^KI$!`*[0Q7R0*K%01KR1"OZ1##R1$&J^D1&\D10\D1A^D1FJO)$=3D_I$?/)$
MAO)$EZKZ1)SR1*WZ1++R1,.J^D3(\D30\D`2GD`RKP,#`">>0$"24$*^GD!0
M`P(`5YY`5JJB4"V>0&:24+2>0&TJHE`)GD!ZLE2!HE"U0*L!DZI4J))0*)Y`
MMZJ24$2>0-"24*.>0/&JDE"EGD#ZDE"GGD`$KP0#`*2>0"<"8$"JGD`[`F`^
MGD!:"F1OKA)C`(T$BU/T$F0150438SPZ8#V>0%$Z8'U\ID"4!?\`")Y`WQP&
M`@`AID"'!K?_`!J>0*4&0V.NU&I@36&_8F`"GD!E!W<#`&BF0(,'_[Q1*P"=3D
MBF`#ID"QBF!]857(@F!MGD#?@F!QGD"UZ8)@<J9`#0B+8Q+ZNF`%GD`J"`,`
M<%JF0"_"8P!?NF`;ID"E>;I@!DYANF`$ID#"ENID`PGK8PCZ8(UA+<KZ8!.F
M0$KZ8&5A70G47U">0&L:<)N>0!H*]EMC`PM38Z`+`@!]79Y`O@L#`+.>0%7*
M0G3>0G3Q0G3]0G2K!PQ#<Q]J=3D$9J=3D%*J:G1P:G1Z:G2&:G0'[PT#`&JF0)L-
M_\L`%)Y`LZ)PY6`!AF\.`P!KID#`#I-CJ<["<-5A^<)P!\1@1*\/_P`-?&!'
MVG(%*P!GVG`,?&!JZG(-$*KJ=3D'CR=3D(/:<`Y\8(:N"H(%`,[:<`]\8-'^&H(#
M`2@0`P`I*IY`."J`*IY`02J`36";`98J@*4!9V`J@"62GD"Q*H`=3D<;XJ@"5Q
MUDHJ@&R>0.0J@*UA[BJ`?K5@`101`P"NGD!5)GJ`K9Y`.7J`.:9`7T,1_P`*
MID!-DH!6K8``4I*`%:9`7)*$U6:2@!FF0&NB@P%][Q$!`#&>0(P1!*X@0`4`
MGY*`$'Q@HOC2@MZ-?&!%58OL'O\+P'1-C`:X`'^)-I@`B3Z:3##_G`")'IX`
MC`:'H``S4J4.,/*R]X`@_O*R,]*HP'4&0O^H`G4!0H@6PGX<0,``__^+Y>8P
M_[C_3,TA65OK_P0SR3/;H[(`_XO!"\-T#(/[__]T!X[#)HL>]H^@#K0*D+8`
M@S[UN%H"1EHV-KD*`/^@L@`RY+O5`/_H30"Y$`"AMK\`N]T`Z$%:,;OOX@#H
M.!*1N\0`?QY34%"X$!")`_J(D+1*D,0>K@",7<!:D!,SP$HPHX5`_[H`N-(`
M#E`&_U/+PS/2]_&`_\(P@/HZ<@.`_\('2X@7"\!U_^K#4&]R=3D&EO_VYS($-O
M<'ER_VEG:'0@*&,I_R`Q.3@S+#DR_R!";W)L86YDW/*"DK#HD@!#D7(#?I6@
MN,L`Z1;_':7_BTX(BUX*Z'_]`2BD!@"XS`#IV_;^\H*X`+24BP[_H@#C%8[!
M)@/_!@@`@](`)HMO#@H`.V:@=3D>M#DGCR@A.0B0,+TG4>9:+U&&N@.V^@<P0F
MH>4(=3D*CH0Y+W@%"CL/\#.P:D`'(?Z'](`'-%@SZD`^#_(*&P`XL>I@!_@^L,
M.\-W$L>@?^L+Z$,`<R;3H'_H(0!S'J&J73#_K`!T"/\VL`//_QZJ`#K0TZ!W
MM?]R`S/`F</_-K&H+)`5-#K0<@>)`_CMPSI@)/QEHA*.P=3D_H6@!S$W6G[NB_
M#@!R"^A%#I"B_P"+PXS"PU"A_Z8`Z+'_<C&._\(S__RX5%"K/S/`J[@,`&NQ
M6[#=3D+7"P4(S`9:(('G^.V8<&"@`?:['_6*M8P[L$`(O_\R:+'X/[`7+_(R:+
M5P(KT'+_[B:+#W0-B_OVYK")#3T@`HO/)F^)#"8I;Z#XP_>`]V7C9RRRCL.+
MV?\F@3X``%10=3D7]3]L,#=3D4Z^E[#__B:+-`OV=3D`;_.]YW\W0\)HG?-R:)1P)M
MHB:C_P@`!0P`.P:F_P!T0^@%`":)]QV+WYFP`W<")NL[-Z40BZ'P!R:+_T0"
M)@%'`OC#??FM`#O#=3D/A3'Y6Y[HRD);0SP(QDD!;^>*#:=3D!2AH@".]\`FH7B@
MPW7V)OV)6,",P*.B`.O_O#/`AP:Z`,OW@SZZWR`!RZ&Z_P#IP/R+]#:._BK`
M.U4"?P=3D\%-\F.P5R#Y/`!GS_"'\')CM%!'?_`<NXR0#IF/S_N-<`Z9+\!0#_
M!'(9*\1S%?>WV#8[BK!R#,;`#OZCH#:C#@#+N,K_`.EN_+HSTHO_W!XVQ'\(
M-L7W=3DP3\;+&PUZNX_8!QLJNKC45TJ^V,;;#__P32,\"Y_PX`\ZL&5P97^[E/
MDJ$)K#K(=3DO\$BLCC"*P*P/]T`ZKB^#+`JG[3,A_*"`"+W.7`CPHVBT<B\D+0
M[Q$,BD+0"([Q,[@2\!&.$+K_L=3D?K"+JRU^O/`[JSU_*"F1&+1?\"/;'7=3D!(]
MLO_7=3D`T]L-=3DT$/W'>\!F`.LD4@:_5P[H*`!:6M%5_0)<U;L0`.A4`,UT3R*P
MUT.1CR"P`,_K`K`!=3D-8L(G08]"LAAM`(C]%G`.L8_U"[%`#H&`!8_BG0#+L<
M`.@-`%RUVA31)O\9]X`#U)#W7P?#XL,$)L55_PPFBTT$)HL=3DW[0_S2%RQB!%
M"JY:T,=3D%"#;1!/G1"OK@,.X8Z#/))H=3D-]0@GX4`LX`<KP73'`[AE.^)([5CF
M`C/YP&GC&^*+'8/[!(]V!K0^@^./('_#-?<F@7\N(2XFBW?_""8[=3DPIT*QZ_
M!E-2)L57(N!?_PH'`]H#\OS__]`K\HS"6P<?;R:)=3DPCRD`C#C]'_:`##4%%2
M5P;_4^A"`5L'7UJ[65BUY76\PW_#0/ZMX;+7=3D3DFBT_^)N!_""O/*]%S_P0#
MRC/2!B;$_W<,`_ZP(/SS?ZHK_@<FB7^XX-]_!'4)4N[@\0#76P=3D:DZ#(XN)I
M`*H#]$@+]$$3^,$?\,C[,\"_X([:)L1?_PP#^_SSI"O[O-?B-O0-4%)6[N"<
M+D/P7EI8\I#`2_5TT_]>!KC=3D!C/2Z/_L_G4*)H-_&O);`7TGHH\@K#P-=3D/\,
M/!IT$3OS=3D?WSLO##._-T":S_/`IT`4XSP,/WN/(&J?<"`+[D+FDPZ%'_NO<U
MQ?6J];J\\PI_PP/H%<7U!E]3)O]?%`[DPS4"^1@[!:KT#+B)!XO_3@:+?@B+
M5@K_1^@]_HO'Q'Y?""O'2*J[T@;,\OT/TO`+JCOSX/++XP59`,/G\:KT"";O
MB@<RY%8`BU8&_RO0?@50Z%C^WIWP=3D`J+=3DF$!1NBSF/YR!.+!Q7]0D(GO#;@`
M/=3D'3#;`"^_\%+""SUW0"M/\\@'TP`'0)C3M5,"S@6HD%,R-1D?[1TR^+';@`
M1,WO(?;"@#,CB\B+9]IU%-L"*0`K`/,'^%$B.B`^((E-&(E=3D_QK'11S__\=3D%
M>1[W`(CB,](SR000_P)"S2$M@`"#[=3DJCH#/`X,#*B]#\!!%7$(V5@`"Y@/4`
M*.-SAN`SVSO8]W0@@'40&G0#0W_K\HO3*]"Y09"P5!-1$5SA%^",VNG`"O;E
MP`8V%/#\._=3DS_P<#\0/Y3D_]G_.D_([:9-`\T@@^M1`&-HI'!"[P.=3D!6O4`!
M)\1@'IY`,,R"?(>`Q&"$``4!4<1@4G6!QR!`S5%QHD"IGD!YGZ)`M8`!*P(%
M)(*Y-AX@C5%,`P6\@E>L+B`-<`%<+B`1Q&`6ZBI@''Q@&D8B`P%$4035<;O`
MVF`=3D?&`'7B)7!0`?VF`??&`B;B)7!0`UVF`>?&`X?B)7!0!2VF`@?&!7CB)`
MFBVJ+;HMRBW:+>0F!>0IQ8``,U"T@.0E6T$H2?]N86-T:79E(`<E<RG,4+5!
M6T$O/5M!_U105VEN0W)T^C`Y)3`P)P```27O`0`")\U0)```OP8C```')LQ0
M*/\``0$A``$"(O\``0,D`0$&(_<!`0<P.@0`(`+\,#WFB%)U;G1I;?]E(&5R
M<F]R(+\P,#`@873!,3"].LDQ+@`-"I5A)/RF0-U0`#P`_P`+5"91ID!4W%``
MF")`HS#!`>Q@ZX0&3?HF'4$`@#@!,?\D'D#`P,`D0"%!@%Q!$3-!0DM!&)%%
M05A,\]<S,S]82C-X0/_QQ1]\0!%_0&9`6$+Q%9__$?\1565!:T,1OU41]1'U
M7Y=3D%$;L1%:))\1_QL4)4O42X15$?$46D0/3^=3DT'_^9$?41D1Y9740O-J0<Y`
M&1&9?-1#FT,1F5$1%/5&_YF9E9EFF555^V9FZ$+YG_F?F;]$F43V;_9X0/,]
M/QU49/9O5IE"'5+?]IEF]F4W6&:9\V961E0-4/_YF97X5%)[0%1`(O__9E6(
M5%%Z06M!(G%5F4!I4?*/+_9F8A519T")0/)_(LSR5?+,S*9`_FI4(LPE7,_\
MQ?WS:%,BJB+\SZS?ROS/\S^90B+__ZS,S*S*K,'_ELE5S*+34<_W`&M!\B<J
MS*KC55A"JO%5F43\`&B)0JK\S,JAS/G,ET%I4JK_JAJAZQJA=3DD$_:T'ZJJ%O
M'ZJJ$5A)$1&!0``.36=3DM=3DVV';9=3DMIVVW;<=3DMN-=3DMXF<F,2`@$)0P`0,`Z`5`
@!4T1?2%],7U!?0!1?6%]<7V!?9%]H7VQ?<%]`-%]X7L`
`
end


[Dr.N.Hartkamp, hartkamp@uni-duesseldorf.de]

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

14. Loading a bitmap from .res without losing palette
Q:
Loading a bitmap from .res without losing palette?

A:
I'm going to manipulate your code a little bit in my explanation. Here goes:

procedure loadgraphic(naam:string);
var
  { I've moved these in here, so they exist only during the lifetime of the
    procedure. }
  HResInfo: THandle;
  BMF: TBitmapFileHeader;
  MemHandle: THandle;
  Stream: TMemoryStream;
  ResPtr: PByte;
  ResSize: Longint;
  null:array [0..8] of char;
  
begin
  { In this first part, you are retrieving the bitmap from the resource.
    The bitmap that you retrieve is almost, but not quite, the same as a
    .BMP file (complete with palette information). }

  strpcopy (null, naam);
  HResInfo := FindResource(HInstance, null, RT_Bitmap);
  ResSize := SizeofResource(HInstance, HResInfo);
  MemHandle := LoadResource(HInstance, HResInfo);
  ResPtr := LockResource(MemHandle);

  { Think of a MemoryStream almost as a "File" that exists in memory.
    With a Stream, you can treat either the same way! }

  Stream := TMemoryStream.Create;

  try
    Stream.SetSize(ResSize + SizeOf(BMF));

    { Next, you effectively create a .BMP file in memory by first writing
      the header (missing from the resource, so you add it)... }
    BMF.bfType := $4D42;
    Stream.Write(BMF, SizeOf(BMF));

    { Then the data from the resource. Now the stream contains a .BMP file }
    Stream.Write(ResPtr^, ResSize);

    { So you point to the beginning of the stream... }
    Stream.Seek(0, 0);

    { ...and let Delphi's TBitmap load it in }
    Bitmap:=tbitmap.create;
    Bitmap.LoadFromStream(Stream);

    { At this point, you are done with the stream and the resource. }
  finally
    Stream.Free;
  end;
  FreeResource(MemHandle);
end;

[Eric Nielsen, htrsoft@midwest.net]

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

15. Getting disk information
Q:
I was tring to get the serial number of a disk using Delphi, but the code does
not seem to work. It works only on a DOS window.

A:
I didn't find info regarding function 69h but I wrote something using
4409h:

type
  MIDPtr = ^MIDRec;
  MIDRec = Record
    InfoLevel: word;
    SerialNum: LongInt;
    VolLabel: Packed Array [0..10] of Char;
    FileSysType: Packed Array [0..7] of Char;
  end;

function GetDriveSerialNum(MID: MIDPtr; drive: Word): Boolean; assembler;
asm
  push  DS    { Just for safety, I dont think its really needed }
  mov   ax,440Dh { Function Get Media ID }
  mov   bx,drive    { drive no (0-Default, 1-A ...) } 
  mov   cx,0866h  { category and minor code }
  lds   dx,MID      { Load pointeraddr. } 
  call  DOS3Call   { Supposed to be faster than INT 21H } 
  jc    @@err
  mov   al,1           { No carry so return TRUE }
  jmp   @@ok
 @@err:
  mov   al,0           { Carry set so return FALSE }
 @@ok:
  pop   DS            { Restore DS, were not supposed to change it }
end;

procedure TForm1.NrBtnClick(Sender: TObject);
var
  Info: MIDRec;
begin
  Info.InfoLevel:=0; { Information Level }
  If GetDriveSerialNum(@Info,0) then  { Do something with it... }
    ListBox.Items.Add(IntToStr(Info.SerialNum)+' '+Info.VolLabel);
end;

[Leif Hellstroem, Leif@aivo.se]

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

16. Custom cursor
Q:
Could someone send me a sample program that uses a custom cursor...

A:
const
  SpinC_1    = MakeIntResource(10005);
  SpinC_2    = MakeIntResource(10006);
  SpinC_3    = MakeIntResource(10007);
  SpinC_4    = MakeIntResource(10008);

initialization
  Screen.Cursors[101] := LoadCursor(HInstance, SpinC_1);
  Screen.Cursors[102] := LoadCursor(HInstance, SpinC_2);
  Screen.Cursors[103] := LoadCursor(HInstance, SpinC_3);
  Screen.Cursors[104] := LoadCursor(HInstance, SpinC_4);
end;

to use them in side your code just:

  Screen.Cursor:=101; { or which one you want }

[Leif Hellstroem, Leif@aivo.se]

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

17. Create a unit without a form
Q:
I just discovered something interesting: One of the
"features" of Delphi that I was least enchanted with was its current
refusal to compile stand alone units that hadn't been added to a
project; where every previous version of Borlandish Pascal that
supported units would compile them separately with no problems.
On a hunch I tried closing down the current project, opening one of my
stand alone utility units, saving it with a .DPR extension, and
re-loading it as a project; Delphi complains that "USES clause is
missing or incorrect", but otherwise loads the unit source fine, and
then will compile it direcly to a .DCU without having to add it to
another project.  I haven't tried this with a component yet, but I'd bet
it would work too.  Apparently Delphi looks for the .DPR extension to
determine if it will separately compile code, rather than whether or not
the code is separately compilable: Bug or Feature?

A:
When opening a project, change the file type from *.DPR to *.PAS.
This will let you open a unit (*.pas), and you will be able to
modify, etc and compile okay, without any errors.

[Craig Francisco, Craig.Francisco@adm.monash.edu.au]

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

18. Set focus while modal dialog is open
Q:
I'm trying to create a lookup table for my users that will hang around
during the life of my application.  I've got one sticky problem: if I create
a data entry form and showModal it, then the lookup table can't receive
focus!

A:
The way I am seeing it, the form with the lookup table should be displayed when
you need it from either the main or data entry form by whatever means, a Lookup
Button etc.

I would envisage something like this:

in fMain.formCreate:
   fLookup := tFLookup.create (self);
   {show code removed from here}

in fMain.btn1Click:
   fEntry := tFentry.create (self);
   fEntry.showModal;

in fMain.LookupButtonClick:
   fLookup.showMODAL;

in fEntry.LookupButtonClick:
   fLookup.showMODAL;

in fLookup.DoneButtonClick:
   fLookup.Hide;

[Paul Taylor, paul@star.net.au]

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

19. Make TAB act like ENTER in StringGrid
Q:
I've tried putting code in the
OnKeyPress event of the Grid, but it does not make the focuseed cell
moved to the next cell as the TAB key does.

A:
This code moves it to next column.  On reaching end of column, it moves
to a new row.  On reaching very end of grid, it goes back to the very top -
of course, you can modify it to go to the next control.

procedure TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);
begin

  if Key = #13 then
    with StringGrid1 do
      if Col < ColCount-1 then {next column!}
        Col := Col + 1
      else if Row < RowCount-1 then begin {next Row!}
        Row := Row + 1;
        Col := 1;
      end else begin {End of Grid! - Go to Top again!}
        Row := 1;
        Col := 1;
        {or you can make it move to another Control}
      end;
end;

[pc@mailhost.net]

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

20. Resource problems encountered with TTabbedNotebook and TNotebook
Q:
Everybody knows about the resource problems encountered with TTabbedNotebook
and TNotebook. How can the be overcome?

A:
I use the following code. I have tested it by checking which TWinControls
have handles allocated at run time, and it does deallocate handles for
controls on inactive pages.
However, If I load ~6 forms, all with TTabbedNotebooks, I still get 'Unable
to create window' errors. Before this code, I could only open 2 windows. (I
have LOTS of controls on each page)
Does anybody know how many window handles are normally available? Also,
anybody have a way to find out how many are available/used ??

{in the OnChange event of TTabbedNotebook}

if AllowChange then
begin
        {Deallocate unneccessary window handles}
	with TTabbedNotebook(Sender) do
        	if PageIndex <> NewTab then
		begin
			LockWindowUpdate(Handle);
			THintWindow(Pages.Objects[PageIndex]).ReleaseHandle;
			TWinControl(Pages.Objects[NewTab]).HandleNeeded;
			LockWindowUpdate(0);

		end;
end;

[Dustin Caldwell, dustin@eddy.media.utah.edu]

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

21. Encryption Algorithm
Q:
I would like a Delphi unit algorithm that I can compile in... not those
external DLL, 'C' source etc.
I realize that home-brewed algorithms can be relatively weak.... but
can someone just send me some algorithms to start with?

A:
        const
           C1 = 52845;   {Used for encryption of Master Password string}
           C2 = 11719;
           Key = 1234;

        { Standard Decryption algorithm - Copied from Borland}
        function Decrypt(const S: String; Key: Word): String;
        var
          I: byte;
        begin
          Result[0] := S[0];
          for I := 1 to Length(S) do begin
            Result[I] := char(byte(S[I]) xor (Key shr 8));
            Key := (byte(S[I]) + Key) * C1 + C2;
          end;
        end;

        { Standard Encryption algorithm - Copied from Borland}
        function Encrypt(const S: String; Key: Word): String;
        Var
          I: byte;
        begin
          Result[0] := S[0];
          for I := 1 to Length(S) do begin
            Result[I] := char(byte(S[I]) xor (Key shr 8));
            Key := (byte(Result[I]) + Key) * C1 + C2;
          end;
        end;

[-]

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

22. Overriding ESC-key on a Form
Q:
I have a form on which a couple of TEdits are placed. Now i would
like to overrule the ESC button. Instead of closing the form, the text
in the active Edit-field should be deleted.

A:
	 (a) check the TEdit's OnKeyPress event for Key = #27
	 (b) if that fails, set the form's KeyPreview property

(set = make it true, reset means make it false) and then in the form's
OnKeyPress event, check for Key = #27.
If these fail, you could always use the windows message system to check for
all keypresses coming in to your application. I did it for a couple of apps for
some other keys, and it's not that hard.  Write if you want an example.

[Matthew A. Gessner, mgessner@aristar.com]

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

23. Transparent bitmap brush
A:
The following unit draws two bitmaps on a form.  One is used
as the background, and the second one as the foreground.  The
foreground bitmap is displayed as a "transparent" bitmap.

Read the comments for a complete (well sort of) explanation.

{ Purpose:  Display a transparent bitmap loaded from a file
  Author:   Michael Vincze (vincze@ti.com)
  Date:     04/20/95
  Usage:    Create a blank form, named Form1, compile and run.
  Limits:   This unit has been tested for both 16 and 256 color bitmaps.
            It is assumed that the lower left pixel of the bitmap represents
            the transparent color.
  Notes:    If this file is to be used for any purpose please leave
            this header intact and give credit to the author if used for
            any purpose.
            Please contact the author if any improvements are made.
            The author stakes no claim for this programs usefullness
            or purpose.
  Version:  1.00  04/20/95  Initial creation
}
unit Tbmpu;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    ImageForeGround: TImage;
    ImageBackGround: TImage;
  public
    { Public declarations }
  end;

procedure DrawTransparentBitmap (ahdc: HDC;
                                 Image: TImage;
                                 xStart, yStart: Word);

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure DrawTransparentBitmap (ahdc: HDC;
                                 Image: TImage;
                                 xStart, yStart: Word);
var
  TransparentColor: TColor;
  cColor          : TColorRef;
  bmAndBack,
  bmAndObject,
  bmAndMem,
  bmSave,
  bmBackOld,
  bmObjectOld,
  bmMemOld,
  bmSaveOld       : HBitmap;
  hdcMem,
  hdcBack,
  hdcObject,
  hdcTemp,
  hdcSave         : HDC;
  ptSize          : TPoint;
begin
{ set the transparent color to be the lower left pixel of the bitmap
}
TransparentColor := Image.Picture.Bitmap.Canvas.Pixels[0,
  Image.Height - 1];
TransparentColor := TransparentColor or $02000000;

hdcTemp := CreateCompatibleDC (ahdc);
SelectObject (hdcTemp, Image.Picture.Bitmap.Handle); { select the bitmap }

{ convert bitmap dimensions from device to logical points
}
ptSize.x := Image.Width;
ptSize.y := Image.Height;
DPtoLP (hdcTemp, ptSize, 1);  { convert from device logical points }

{ create some DCs to hold temporary data
}
hdcBack   := CreateCompatibleDC(ahdc);
hdcObject := CreateCompatibleDC(ahdc);
hdcMem    := CreateCompatibleDC(ahdc);
hdcSave   := CreateCompatibleDC(ahdc);

{ create a bitmap for each DC
}

{ monochrome DC
}
bmAndBack   := CreateBitmap (ptSize.x, ptSize.y, 1, 1, nil);
bmAndObject := CreateBitmap (ptSize.x, ptSize.y, 1, 1, nil);

bmAndMem    := CreateCompatibleBitmap (ahdc, ptSize.x, ptSize.y);
bmSave      := CreateCompatibleBitmap (ahdc, ptSize.x, ptSize.y);

{ each DC must select a bitmap object to store pixel data
}
bmBackOld   := SelectObject (hdcBack, bmAndBack);
bmObjectOld := SelectObject (hdcObject, bmAndObject);
bmMemOld    := SelectObject (hdcMem, bmAndMem);
bmSaveOld   := SelectObject (hdcSave, bmSave);

{ set proper mapping mode
}
SetMapMode (hdcTemp, GetMapMode (ahdc));

{ save the bitmap sent here, because it will be overwritten
}
BitBlt (hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);

{ set the background color of the source DC to the color.
  contained in the parts of the bitmap that should be transparent
}
cColor := SetBkColor (hdcTemp, TransparentColor);

{ create the object mask for the bitmap by performing a BitBlt()
  from the source bitmap to a monochrome bitmap
}
BitBlt (hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);

{ set the background color of the source DC back to the original color
}
SetBkColor (hdcTemp, cColor);

{ create the inverse of the object mask
}
BitBlt (hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, NOTSRCCOPY);

{ copy the background of the main DC to the destination
}
BitBlt (hdcMem, 0, 0, ptSize.x, ptSize.y, ahdc, xStart, yStart, SRCCOPY);

{ mask out the places where the bitmap will be placed
}
BitBlt (hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);

{ mask out the transparent colored pixels on the bitmap
}
BitBlt (hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);

{ XOR the bitmap with the background on the destination DC
}
BitBlt (hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);

{ copy the destination to the screen
}
BitBlt (ahdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0, SRCCOPY);

{ place the original bitmap back into the bitmap sent here
}
BitBlt (hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY);

{ delete the memory bitmaps
}
DeleteObject (SelectObject (hdcBack, bmBackOld));
DeleteObject (SelectObject (hdcObject, bmObjectOld));
DeleteObject (SelectObject (hdcMem, bmMemOld));
DeleteObject (SelectObject (hdcSave, bmSaveOld));

{ delete the memory DCs
}
DeleteDC (hdcMem);
DeleteDC (hdcBack);
DeleteDC (hdcObject);
DeleteDC (hdcSave);
DeleteDC (hdcTemp);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
{ create image controls for two bitmaps and set their parents
}
ImageForeGround := TImage.Create (Form1);
ImageForeGround.Parent := Form1;
ImageBackGround := TImage.Create (Form1);
ImageBackGround.Parent := Form1;

{ load images
}
ImageBackGround.Picture.LoadFromFile ('c:\delphi\images\splash\16color\earth.bmp');
ImageForeGround.Picture.LoadFromFile ('c:\delphi\images\splash\16color\athena.bmp');

{ set background image size to its bitmap dimensions
}
with ImageBackGround do
  begin
  Left := 0;
  Top := 0;
  Width := Picture.Width;
  Height := Picture.Height;
  end;

{ set the foreground image size centered in the background image
}
with ImageForeGround do
  begin
  Left := (ImageBackGround.Picture.Width - Picture.Width) div 2;
  Top := (ImageBackGround.Picture.Height - Picture.Height) div 2;
  Width := Picture.Width;
  Height := Picture.Height;
  end;

{ do not show the transparent bitmap as it will be displayed (BitBlt()ed)
  by the DrawTransparentBitmap() function
}
ImageForeGround.Visible := False;

{ draw the tranparent bitmap
  note how the DC of the foreground is used in the function below
}
DrawTransparentBitmap (ImageBackGround.Picture.Bitmap.Canvas.Handle, {HDC}
                       ImageForeGround, {TImage}
                       ImageForeGround.Left, {X}
                       ImageForeGround.Top {Y} );
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
{ free images
}
ImageForeGround.Free;
ImageBackGround.Free;
end;

end.

[Michael Vincze, mav@asd470.dseg.ti.com]

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

24. Setting PC Clock
Q:
Who can tell me how to SET the pc real time clock from a delphi program?

A:
{ SetDate sets the current date in the operating system. Valid  }
{ parameter ranges are: Year 1980-2099, Month 1-12 and Day      }
{ 1-31. If the date is not valid, the function call is ignored. }
procedure SetDate(Year, Month, Day: Word); assembler;
asm
	MOV	CX,Year
	MOV	DH,BYTE PTR Month
	MOV	DL,BYTE PTR Day
	MOV	AH,2BH
	INT	21H
end;

{ SetTime sets the time in the operating system. Valid          }
{ parameter ranges are: Hour 0-23, Minute 0-59, Second 0-59 and }
{ Sec100 (hundredths of seconds) 0-99. If the time is not       }
{ valid, the function call is ignored.                          }
procedure SetTime(Hour, Minute, Second, Sec100: Word); assembler;
asm
	MOV	CH,BYTE PTR Hour
	MOV	CL,BYTE PTR Minute
	MOV	DH,BYTE PTR Second
	MOV	DL,BYTE PTR Sec100
	MOV	AH,2DH
	INT	21H
end;

function SetSystemDateTime(Year, Month, Day, Hour, Minute, Second: word): integer;   export;
begin
  SetDate(Year, Month, Day);
  SetTime(Hour, Minute + 1, Second, 0);
  result := 1;
end;

[Dean Tessman, dean.tessman@cancerboard.ab.ca]

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

25. Scroll Listbox programmatically
Q:
I would like to scroll a list box programmatically by a button.
ie. when I click on the button, it's as though I've clicked on the
scrollbar arrow.

A:
Take a look at the LB_SETTOPINDEX Windows API message. Send it to the
desired listbox with the (zero-based) index in wParam.

Ex.
     SendMessage(ListBox.Handle,lb_SetTopIndex,10,0);

This will make the eleventh item appear as the first visible item.

[Ove.Back@derm.lu.se]

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

26. Detect deleted records in .DBF
Q:
How to detect deleted records in .DBF

A:
The routines are extracted from "Dtopics Database 1.10 from 3K computer
Consultancy "

<---------  Begin ------------>
 Dbase causes more 'special case' situations to the BDE than SQL tables
 and Paradox, due to its support of Expressions in indexes, etc.  ie.

1) Index Creation/recreation
    - DbiRegenIndexes( Table1.Handle ); { Regenerate all indexes }

    - create index (depends if expression exists or not)
    if (( Pos('(',cTagExp) + Pos('+',cTagExp) ) > 0 ) then
       Table1.AddIndex( cTagName, cTagExp, [ixExpression])  (<- ixExpression is a _literal_)
    else
       Table1.AddIndex( cTagName, cTagExp, []);

2) Master/Detail links on an expression child index
    - call BDE proc DbiLinkDetailToExp() instead of
      the usual  DbiLinkDetail() 

3) Packing Tables
    - with Table1 do
	  StrPCopy( TName, TableName );
	  Result := DBIPackTable( DbHandle, Handle, TName, szDBASE, TRUE );


4) Setting visiblity of Deleted records on/off (ie dBase SET 
   DELETED ON/OFF)
    - DbiSetProp( hDBIObj(Table1.Handle), curSOFTDELETEON,  LongInt(bValue));


5) Setting character matches partial/exact on/off (ie dBase SET 
   EXACT ON/OFF)
    - DbiSetProp( hDBIObj(Table1.Handle), curINEXACTON,   LongInt(bValue));
<------- End ---------->

And this is what I used exactly:

<------- Begin --------->
Q.   "How can I view dBASE records marked for deletion?"

A.   Call the following function on the AfterOpen event of the table. You 
     Must include DBITYPES, DBIERRS, DBIPROCS in the uses clause.  To call,
     send as arguments name of TTable and TRUE/FALSE depending to show/not  
     show deleted records. Ex:

     procedure TForm1.Table1AfterOpen(DataSet: TDataset);
     begin
       SetDelete(Table1, TRUE);
     end;

     procedure SetDelete(oTable:TTable; Value: Boolean);
     var
       rslt: DBIResult;
       szErrMsg: DBIMSG;
     begin
       try
          Table.DisableControls;
           try
             rslt := DbiSetProp(hDBIObj(oTable.Handle), curSOFTDELETEON,
             LongInt(Value));
             if rslt <> DBIERR_NONE then
               begin
                 DbiGetErrorString(rslt, szErrMsg);
                 raise Exception.Create(StrPas(szErrMsg));
               end;
           except
             on E: EDBEngineError do ShowMessage(E.Message);
             on E: Exception do ShowMessage(E.Message);
           end;
       finally
          Table.Refresh;
          Table.EnableControls;
       end;
     end;

Q.   "How can I create a column in the grid to which records in a dBASE
      table are marked for deletion?"

A.   Create a calculated field, then for the OnCalcField event of the
     table replace the calculated field you've created like so:

     procedure TForm1.Table1CalcFields(DataSet: TDataset);
     var
       RCProps : RecProps;
       Result : DBIResult;
     begin
       Result := DbiGetRecord(Table1.Handle, dbiNo

[-]

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

27. dbGrid and Memo Fields
Q:
How can I view the contents of a memo field within a dbGrid component (rather
than viewing <memo>).

A:
In the GetText event of the TMemoField put this

Text := GrabMemoAsString(TMemoField(Sender));

and put this function somewhere accessible

function GrabMemoAsString(TheField : TMemoField): String;
begin
if TheField.IsNull then
  Result := '' else
  with TBlobStream.Create(TheField, bmRead) do
    begin
      if Size >= 255 then
        begin
          Read(Result[1], 255);
          Result[0] := #255;
        end else
        begin
          Read(Result[1], Size);
          Result[0] := Chr(Size);
        end;
      Free;
      while Pos(#10, Result) > 0 do
        Result[Pos(#10, Result)] := ' ';
      while Pos(#13, Result) > 0 do
        Result[Pos(#13, Result)] := ' ';
    end;
end;

[Ryan Peterson, rpetersn@usit.net]

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

28. Cycle thru list of comps
Q:
procedure TForm1.OnCreate(whatever)
var i : Integer
begin
  for i := 1 to 5 do
  Edit[i].X := 10; {received compiler error here, didn't recognize the Edit
control}
end;

What wrong here??

A:
procedure TForm1.FormCreate(Sender: TObject);
var
  I : integer;
begin
     for I:= 0 to ComponentCount -1 do
        if (Components[I] IS TEdit) then
           (Components[I] AS TEdit).{Yourparameter} := {your value};
end;


If you need to identify a particular set of edit components, you can put 
them on a panel & use something like

procedure TForm1.FormCreate(Sender: TObject);
var
  I : integer;
begin
     with MyPanel do
       for I:= 0 to ControlCount -1 do
          if (Controls[I] IS TEdit) then
             (Controls[I] AS TEdit).{Yourparameter} := {Your value};
end;

[Shane Mulo, mulos@dpi.qld.gov.au]

A:
The major one is that Edit1, Edit2 ect is not the same as
Edit[1], Edit[2]. If you want to access a series of controls as an array,
you have to put them into a TList.

MyArr := TList.Create;
MyArr.Add(Edit1);
MyArr.Add(Edit2);
  ...

For i := 0 To MyArr.count - 1 Do
  (MyArr.items[i] As TEdit).Enabled := False;

MyArr.Free;

[Mike Chapin, mchapin@vcn.com]

A:
procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to ComponentCount -1 do
     if Components[I] is TEdit then
       TEdit(Components[I]).Whatever := 10;
end;

[Leslie Tsang, lesliet@HK.Super.NET]

A:
To access them just use:
TButton(mylist.items[i]).property := sumpin;
or
TButton(mylist.items[i]).method;

This is great for doing batch operations on the components or accessing them
in a linear fashion. For doing what you want there is a much easier way of
handling the problem and one that can be done at design time. Set the tag
property and take advantage of the fact that all components are derived from
TComponent.

Procedure TMyForm.MyButtonHandler(Sender: TObject);
Begin
  Case (Sender As TComponent).Tag Of
    1 : { do something }
    2 : { do something else }
     .
     .
  End;
End;

Just point the OnClick event to MyButtonHandler for all the buttons you want
to use this common handler.

[Mike Chapin, mchapin@vcn.com]

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

29. Splitter bar
Q:
Do you know where I can find "the code for a splitter bar" ?

A:
I use a TOutline aligned to alLeft, with desktop to the right of it. A
panel is placed after the TOutline, and also aligned alLeft. This makes it
stick to the TOutline. I call the new panel 'splitter'. Make the splitter
very narrow, bevel it if you like, and set it's cursor to east-west.
Replace the TOutLine with what ever type of component you need.
Attach the following to the mouse events:

 procedure TMainForm.SplitterMouseMove(Sender: TObject; Shift: TShiftState;
   X, Y: Integer);
 begin
   if ssLeft in Shift then begin
     outline.Width := outline.Width + X;   {replace outline with your object}
   end;
end;

[Scott May, scottmay@gil.com.au]

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

30. GetKeyBoardState
Q:
How do I get the VK_INSERT state?

A:
Hope the following code helps. It shows that indeed, VK_INSERT can be used.
Your program will need to track the status and adjust your program to overwrite
or insert text.

  TFormInstructor = class(TForm)
{   Must never absolutely change position! Code depends on this!
    PanelScrollLock, PanelINS, PanelCAPS, PanelNUM }
    PanelScrollLock: TPanel;
    PanelINS: TPanel;
    PanelCAPS: TPanel;
    PanelNUM: TPanel;
    procedure Timer1Timer(Sender: TObject);
  private
    stToggles: array[0..3] of Bool;
  end;



{ Check keystate @ every timer tick.
  Won't receive any Windows messages informing that keystate has changed! }
procedure TFormInstructor.Timer1Timer(Sender: TObject);
const
 vkconsts: array[0..3] of Word=(vk_Scroll, vk_Insert, vk_Capital, vk_NumLock);
 PanelColor: array[Boolean] of TColor=(clGray, clBlack);
var
{ tmScrollLock, tmNumLock, tmCapital, tmInsert: Bool; }
 Toggles: array[0..3] of Bool; { Maybe able to use
[Low(vkconsts)..High(vkconsts)] }
 Panels: array[0..3] of TPanel absolute PanelScrollLock;
 I: Integer;
begin
 for I := Low(vkconsts) to High(vkconsts) do
  begin
   Toggles[I] := Bool(GetKeyState(vkconsts[I]) and 1);
   if stToggles[I]<>Toggles[I] then
    begin
     stToggles[I] := Toggles[I];
     Panels[I].Font.Color := PanelColor[Toggles[I]];
    end;
  end;
end;

[Chee Wee, chuacw@singnet.com.sg]

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

31. Destroy a Modal Form on Deactivate
Q:
I would like to destroy a Modal Form when the application is de-activated.
I've done some testing and research.  Basically, I would use Application
Deactivate event handler.
How do I simulate letting the Modal Form close peacefully?
For a non-modal form, it's so much less complicated... a hide will do.
Note my "Form1" is declared globally that Application.OnDeactivate can
see it.

A:
procedure TForm1.AppDeactivate(Sender: TObject);
var
  hw: HWnd;
  CurTask: THandle;
  WndStyle:Longint;
begin
  CurTask:=GetWindowTask(handle);
  hw:=GetWindow(GetDesktopWindow, GW_CHILD);
  while GetWindowTask(hw)<>CurTask do
    hw:=GetWindow(hw, GW_HWNDNEXT);
  while (hw<>handle) and (GetWindowTask(hw)=CurTask) do
  begin
    PostMessage(hw, WM_Close, 0, 0);
    hw:=GetWindow(hw, GW_HWNDNEXT);
  end;
end;

[Henry Kowk, guoxy@public.szptt.net.cn]

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

32. Popup menu in dependence on mouse position
Q:
I need to pop up a popup menu when a user stops highlighting text in a
TMemo..  Problem is i can not figure out who to get the mouse coordinates
with which to call PopUp (i want the menu to pop up at the place the mouse
is release).

A:
procedure TForm1.Memo1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var ClientPoint,ScreenPoint: TPoint;
begin
 if Memo1.SelLength>0 then
 begin
  ClientPoint.X := X;
  ClientPoint.Y := Y;
  ScreenPoint := ClientToScreen (ClientPoint);
  PopupMenu1.Popup (ScreenPoint.X, ScreenPoint.Y);
 end;
end;

[Luis F Hernandez, luis@ris.risinc.com]

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

33. TabbedNotebook and common components on all pages
Q:
I want to know how to define some component common to all the
pages on a tabbedNotebook without having to define them on every page
(like the OK and the Cancel Button from the TabbedNotebook template).
I would like to have some labels and entry fields to be available on
all pages.

A:
The components that you want to appear on all pages have to be owned by
the parent of the TTabbedNotebook (often the TForm on which it appears),
and, apparently, also need to be created AFTER the TTabbedNotebook.  The
simplest way I've found to get this to happen is to place the
TTabbedNotebook, but be sure that you can still get at the parent (i.e.
don't set the .Align property yet), then place the buttons (or whatever)
on the parent area, then set the .Align property for the
TTabbedNotebook, the controls you placed after the TTabbedNotebook,
should then appear on all pages (actually, they exist "on top" of the
TTabbedNotebook as a whole. If you have already placed components,
I've found that using the "Edit/Send to Back" command with the
TTabbedNotebook selected, will bring the desired components on top. You
can also edit the .DFM file directly to make sure the creation order is
what you want.

[Stephen Posey, SLP@uno.edu]

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

34. Hiding apps from taskbar
Q:
Anyone can tell me how to remove an Application Icon & Title from the taskbar?

A:
ShowWindow(Application.Handle, SW_HIDE);

[Daniel Bradley, dbradley@itc-dal.com]

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

35. How to disable a tab(page) in a Notebook component
Q:
On my form I have a TTabbed notebook component. However at runtime I want
to be able to disable a page or two on the fly. How do I do that?

A:
In the OnChange event of your TTabbedNotebook place somethig like this:

if (NewTab = 0) and (IWantToDisableTab0) then
   AllowChange := False;

if (NewTab = 1) and (IWantToDisableTab1) then
   AllowChange := False;

...

Yes, you could use a Case construction, but you's still need an If
for each value.

[Sam Johnston, nerd@agt.net]

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

36. Time difference
Q:
var
  TimeStart : TDateTime;
  <snip>
procedure TimeTaken;
var
 tt : Double; {Have tried this as TDateTime}
begin
   tt := Now - TimeStart;
   Edit1.Text := FloatToStr(tt);
end;

OK, now matter what I do tt = Now  not the difference. What am I missing.

A:
I don't know when you execute TimeTaken.. Did you give some delayed time before
executing TimeTaken after execute SetTimeStart? If didn't then no wonder it
tt=Now..
I've tried your code, with some minor changes... and I always got the difference
between Now and TimeStart. But, I declare tt as TDateTime not as Double and
using OnTimer event to execute TimeTaken procs.
You can check it by trying the example below.

{*******************************************************************
 FILE : TIMEEX.PAS
 NOTE : Create form which contains 1 TTimer, and 6 TLabel.
        Set OnTimer Event of TTimer to TForm.Timer1.Timer
 ********************************************************************}
unit Time;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Label1: TLabel;           {Caption : 'Start :'}
    Label2: TLabel;           
    Label3: TLabel;           {Caption : 'Time : '}
    Label4: TLabel;
    Label5: TLabel;           {Caption : 'Elapsed time:'}
    Label6: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
    TimeStart : TDateTime;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
 TimeStart := Now;
 Label2.Caption := TimeToStr(Now);
end;

procedure TForm1.Timer1Timer(Sender: TObject); 
var
 tt : TDateTime;
begin
 Label4.Caption := TimeToStr(Now);
 tt:= Now - TimeStart;
 Label6.Caption:= TimeToStr(tt);
end;

end.

[Edhi Nugroho, edhinug@semarang.wasantara.net.id]

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

37. 16 bit DLLs and Delphi 2.0
Q:
I am wanting to know if it is possible to call functions in a 16bit DLL from
Delphi 2.0. I have tried to recompile a component which wraps a 16 bit DLL
to 32 bit code with no success.

A:
You can call a 16 bit dll from a 32 bit program (delphi 2.0 makes 32 bit
programs). And the program can run on win32s and win95 but not winNT since NT
does not allow ANY 32 bit code to call 16 bit code. So the only choice is to
keep working on the 16 -> 32 bit code conversion, one handy tool is PortTool
which scans your code files and tells you what parts of your 16 bit code must
change to work under win32.

[Calvin Smith, hawk@inf.net]

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

38. Obtain last digits in a number
Q:
I want to obtain the last 3 digits of a number
This is the code:

procedure TForm1.Button1Click(Sender: TObject);
var
  MyNumber: Word;
  TempNumber: LongInt;
begin
  TempNumber := 555444;
  MyNumber := Trunc(Frac(TempNumber / 1000) * 1000);
  ShowMessage(IntToStr(MyNumber));
end;

1- Why ShowMessage tell me 443 while 444 is the correct number?
2- Is there an easy way to obtain the last n digits?

A:
Convert your number to a string (IntToStr - if number is an integer)
then:
Assuming converted number is held in StringValue
Var
	tmpStr := String;
	LastThree := Integer;
Begin
	tmpStr := Copy(StringValue, len(StringValue)-4,3);
	LastThree := StrToInt(tmpStr);
End;

This should work with integers only.

[John Box, johnb@police.tas.gov.au]

A:
The 443 occurs because you're doing floting point operations, the
easy way to obtain the last three digits is:

MyNumber := MyNumber MOD 1000;

[O. Doevendans, odof2@fel.tno.nl]

A:
1. The fraction is not safe due to the limited accuracy of the intermediate
real number. If you get, e.g. 999999/1000 = 999.99899, your algorithm
will result in : .9989 * 1000 = 998.9 which truncates to 998. It would be
better to calculate:
MyNumber := Trunc(0.5 + Frac(TempNumber / 1000.0 ) * 1000)
or simply the equivalent:
MyNumber := Round(Frac(TempNumber / 1000.0 ) * 1000)
Alternatively, calculate:
MyNumber := TempNumber - Trunc(TempNumber / 1000.0 ) * 1000;

[Dr. Peter Wollenberg, ptpwol@krzsun.med-rz.uni-sb.de]

A:
procedure TForm1.Button1Click(Sender: TObject);
var
  MyNumber: Word;
  TempNumber: LongInt;
  tString : array[0..15] of Char;
  iLen : word;
begin
  TempNumber := 555444;
  StrPCopy(tString, IntToStr(TempNumber));
  iLen := Length(StrPas(tString));
  MyNumber := StrToInt(StrPas(@tString[iLen-3]));
  ShowMessage(IntToStr(MyNumber));
end;

[Joe H. Magill, joe@hotlanta.win.net]

A:
function ReadDigits(TheNumber:LongInt;NumDigits:byte):longint;
{Reads the last NumDigits of TheNumber}
var
   TempStr : string;
   TempInt : longint;
   ErrorCode : integer;
begin
  TempStr := IntToStr(TheNumber);
   val(copy(TempStr,length(TempStr)-(NumDigits-1),length(TempStr)),
          TempInt,ErrorCode);
  if ErrorCode = 0 then
    ReadDigits := TempInt
  else
    {do something about the error here}
end;

{as an example, I created this OnClick method to show the results in a 
label}

procedure TForm1.Button1Click(Sender: TObject);
begin
   {Read the last 2 digits of the number 555444}
   Label1.Caption := IntToStr(ReadDigits(555444,2));
end;

[Shane Mulo, mulos@dpi.qld.gov.au]

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

39. Popup menu that construct from DB
Q:
I just create a form and popup menu attached to it. Once user run the form,
it'll read a field from DB and update the popup menu.

A:
var m:TMenuItem;
    navidummy:TComponent;
  ..........................................................
 procedure  TMyForm.CreatePopUpMM(Sender: TObject);
begin
     Navidummy.free;
     Navidummy:=TComponent.create(self);
    
     While not NaviT.EOF do
     begin
      m := TMenuItem.create(navidummy);
      II:=II+1;
      with m do
       begin
            name :='MM'+IntToStr(II);
            caption := NaviT.Fieldbyname('MyWHAT').AsString ;
            tag := NaviT.Fieldbyname('MyTAG').AsInteger;
            visible:=True;
            OnClick:= NaviExec ;
       end;
      MyMenuItem.add(m);
      NaviT.Next;
     end;
     NaviT.Close;

end;

procedure TMyForm.NaviExec(Sender:TObject);
begin
     What.text := (Sender as TMenuItem).Caption; { There I get what I want ! }
     Key:= (Sender as TMenuItem).Tag ;
  
end;

[Tadas Vizbaras, tavizb@rc.lrs.lt]

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

40. Insert text in MEMO
Q:
Can it be true, that you can't insert a text in a TMemo at the cursor
position?
I wanted to use some buttons to insert standard phrases in a Memo field.
I solved the problem by using TEdit instead of TButton. Then I select the
text, copy it to  the clipboard, and paste from the clipboard to the
Memo.
That's OK, but I don't like to use the clipboard in the program. The user
might have stored something there too.

A:
Use the Windows API message-EM_REPLACESEL: (from Windows API Help)

EM_REPLACESEL
wParam = 0;                               /* not used, must be zero */
lParam = (LPARAM) (LPCSTR) lpszReplace;   /* address of new string  */

An application sends an EM_REPLACESEL message to replace the current
selection in an edit
control with the text specified by the lpszReplace parameter.

Parameter	Description
lpszReplace	Value of lParam. Points to a null-terminated string containing the
                            replacement text. { Pointer to the string }

Returns
This message does not return a value.

Comments
Use the EM_REPLACESEL message when you want to replace only a portion of the
text in an edit control.
If you want to replace all of the text, use the WM_SETTEXT message.

If there is no current selection, the replacement text is inserted at the
current cursor location.

[Eddie Shipman, nryan@inetport.com]

A:
Make a list with your standard phrases and use the "OnClick" or
the "OnMouseDown" Event in combination with "Alt", "Shift" or
"Ctrl".  Example: When the user press the "Alt" key in combination
with the Right button of your mouse, pop-up the phrases list and
insert the selected phrase in your TMemo component.

[Luis Hernandez, LUIS@ris.risinc.com]

A:
to insert a string into a memo :

procedure TForm1.Button1Click(Sender: TObject);
begin
     with Memo1 do begin
      SelStart:=10;
      SelLength:=0;
      SelText:='This is a string inserted into a memo, at 10th char position ';
   end;
end;

to insert a string AND replace some existing text :

procedure TForm1.Button1Click(Sender: TObject);
begin
     with Memo1 do begin
      SelStart:=10;
      SelLength:=20;
      SelText:='This is a string inserted, at 10th char position replacing 20 chars ';
   end;
end;

[Nitsan Kovalsky, nitsanko@netvision.net.il

A:
Put the text you want to insert into a PChar variable, then insert
the text into the memo, using the SetSelTextBuf command, where
SelStart is set to the postion of the cursor in the TMemo.
It works  great ..

Another thing, you can get around the 32K limit on the TMemo
component, if you by-pass the Lines.LoadfromFile method/command. Its
has an inbuilt limit of 32K. If you load the file you want into a
pointer, and using the SetTexBuf command/method, you can load up to
64K of text into a TMemo.

[Bruce W. Hearder, bwh@iinet.net.au]

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

41. Array of TPoints
Q:
I need to set up an array of TPoints.  Let's say I have 5 points
and I know their X and Y values.  How do I fill and array with those values
for Tpoints?

A:
Const
  ptarr : Array[0..4] Of TPoint =
   ((x:0; y:4),
      .
      .
    (x:4; y:4));

[Mike Chapin, mchapin@vcn.com]

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

42. LASTDRIVE Value
Q:
Does anyone know how to access the LASTDRIVE value from
CONFIG.SYS from within Delphi?  I don't mean parsing the
CONFIG.SYS file, I actually want to get the variable from
the system.

A:
program CrtApp;
uses WinCrt, winprocs;

function getlastdrive : byte; assembler;
asm
	mov	ah, $19
 	call    dos3call
        mov     dl, al
 	mov	ah, $e
 	call	dos3call
 	end;

begin
  Writeln(getlastdrive);
end.

The return value of GetLastDrive is the greatest of 5, the value of
LASTDRIVE= in Config.Sys or tha actual number of drives in the system.
With Novell the number is fixed at 32.

[George Blat, georgeb@brd.com]

A:
There's a way to do it using Int21 of DOS.
The function 0Eh, Set Default Drive, returns the number of logical 
drives.
A short func to do this could be:

  Function GetNoOfDrives: Integer; Assembler;
  asm
    Mov  ah,$19; { Get Default drive, used below }
    Call Dos3Call;
    Mov  dl,al; { Set default drive to default drive.. }
    Mov  ah,$0E; { Set Default Drive }
    Call Dos3Call;
    Xor  ah,ah;
  end;

[Leif Hellstroem, Leif@aivo.se]

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

43. Callback functions
Q:
Could someone explain to me how to setup callback functions?

A:
{function FindWindowHandle (HuntFor: string): HWnd;}
{----------------------------------------------------------------}
{ Hunts for a parent window with title containing the HuntFor    }
{ string.  Returns the window handle or 0 if none found.         }
{----------------------------------------------------------------}
    { The following indented code is logically a part of  }
    { FindWindowHandle but is placed here above the real  }
    { FindWindowHandle function heading as Borland do not }
    { allow nesting of callback functions.                }
    {-----------------------------------------------------}
     type
       PHuntRec = ^THuntRec;
       THuntRec = record
         HuntingFor: string;
         WindowFound: HWnd;
       end;

     function EnumWindowsFunc (WindowHandle: HWnd;
                         lParam: Longint): WordBool; export;
    {-----------------------------------------------------}
    { Callback function used by FindWindowHandle.         }
    {-----------------------------------------------------}
     var
       ATitle: array[0..255] of Char;
     begin
       GetWindowText(WindowHandle, PChar(@ATitle), 255);
       if StrContains(StrPas(PChar(@ATitle)),
         PHuntRec(lParam)^.HuntingFor, CaseInsensitive) then
       begin
         PHuntRec(lParam)^.WindowFound := WindowHandle;
         EnumWindowsFunc := false;     {stop looking}
       end
       else
         EnumWindowsFunc := true   {continue looking}
     end; {EnumWindowsFunc}

 function FindWindowHandle (HuntFor: string): HWnd;
 var
   Proc: TFarProc;
   HuntRec: PHuntRec;
 begin
   GetMem(HuntRec, SizeOf(THuntRec));
   HuntRec^.HuntingFor := HuntFor;
   HuntRec^.WindowFound := 0;
   Proc := MakeProcInstance(@EnumWindowsFunc, HInstance);
   EnumWindows(Proc, Longint(HuntRec));
   FreeProcInstance(Proc);
   FindWindowHandle := HuntRec^.WindowFound;
   FreeMem(HuntRec, SizeOf(THuntRec));
 end; {FindWindowHandle}

[Mike O'Hanlon, TMike@IAfrica.com]

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

44. Name of the item in a TListBox
Q:
How do I get the name of the item in a TListBox?

A:
Use the Items and ItemIndex properties:

Foo := Bar.Items[Bar.ItemIndex];

Foo would now contain the selected item in the listbox.

[Magnus Baeck, Ove.Back@derm.lu.se]

A:
ShowMessage(  Listbox1.Items[Listbox1.ItemIndex] );

[pc@mailhost.net]

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

45. Moving the cursor only in the x-axis
Q:
Does anyone know how to trap the movement of the mouse cursor, in
such a way that the programmer can specify that only the x coord will
change ?
I am currently looking at the OnMouseMove event, but can't seem to
find a way to specify the Y coord.
I need to be able to hold the cursor on a horizontal line and not
allow it to move in the y axis.

A:
In your OnMouseMove, go something like:

   if (y<>0) and (lockY) then begin 
     GetMouseCoords(NewX,NewY);
     NewY := NewY + y;      {or should that be minus?}
     SetMouseCoords(NewX,NewY);
   end;

'lockY' is a variable you declare and set true when you want to do this 
type of control.

Substitute the real functions that get and set the mouse co-ordinates in
the relevant spots.

[Scott May, scottmay@gil.com.au]

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

46. DBGrid as Navigator
Q:
I'd like to use a DBGrid as a navigating tool as follows:
 * Clients table with standard name, phone number etc
 * One DBText for each editable field
 * DBGrid contains just the name of the client
 * Click on a record in the DBGrid and the DBTexts are updated according
   to the record clicked.
At the moment, I'm using two DataSources, because I don't want phone
number etc to show in the DBGrid. However, when I click on the DBGrid,
because they're separate datasources, the DBTexts are not updated.
Is there any way I can do this? The clients table is going to be hundreds
of entries long, and paging through them is a pain. Also, editing them
all in a grid would result in a grid about 25 columns wide :-( and this is
also a pain.

A:
1. Place a table component on a blank form and link it to your Client table.

2. Place a Datasource component and link it to the table component above.

3. Place a grid component and link it the datasource component above.

4. Use the Fields Editor to create TField components for all the fields
   in the client table.

5. Set the Visible property of all the TField components, except Client
   Name (Or whatever field you would like displayed in the DBGrid), to
   False. The gird will now display only the Client Name.

6. Place DBEdit components below the grid to display fields from the
   Client table that you want the user to view or edit. They can use the
   same datasource as the grid.

The user can now use the grid to navigate and enter/edit data using the
DBEdits.

[VK Thakur, vkt@pobox.com]

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

47. Open MDI Form at specific size
Q:

A:
var
  ProjectWindow: TWndProject;
begin
  If ProjectActive=false then begin
    LockWindowUpdate(ClientHandle);
    ProjectWindow:=TWndProject.Create(self);
    ProjectWindow.Left:=10;
    ProjectWindow.Top:=10;
    ProjectWindow.Width:=373;
    ProjecTwindow.Height:=222;
    ProjectWindow.Show;
    LockWindowUpdate(0);
  end;
end;

Use LockWindowUpdate before creation and after creation is finished.

[Christian Feichtner, Christian Feichtner@jk.uni-linz.ac.at]

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

48. Date Math
Q:
Is there a function that will do simple date addition or will I have to
write a routine for it?

A:
procedure TForm1.MaskEdit1Exit(Sender: TObject);
var
  y, m, d : word;
begin
  decodedate(strtodate(maskedit1.text) +  11, y, m, d);
  maskedit2.text := inttostr(m) + '/' + inttostr(d) + '/' + inttostr(y);
end;

[Jerry Black, jblack@drmpark.com]

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

49. Install BDE
Q:
Does anybody know how you can install the BDE-files from your own
setup-program. I have finished a database-project, and I want to include all
the necessary files in my own install-program, so the end-users don't have
to install the BDE-distribution-disks apart of my database-application.
I'm talking of Delphi version 1.1 and the BDE that came with it. I have
found some help on how to make aliases programmaticaly, but not on how to
install the BDE programmaticaly.

A:
Here are the file names you need to install at runtime  .

Borland Database Engine
Unique File on BDE Disk #1
File Name :- IDAPICFG.PAK
Install Exe :-Setup.exe

Borland SQL Links
Unique File on SQL  Disk #1
File Name :- MNOVLWP.PAK
Install Exe :-Setup.exe

Borland ReportSmith Runtime
Unique File on RPT Disk #1
File Name :- INSTXTRA.PAK
Install Exe :-Setup.exe

Borland State that YOU must use their Install programs
when installing the runtime versions.

[Huet Bartels, HUET1@MDX.UK.AC]

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

50. Send Messages to a control
Q:
How do I send a window message to a control? I want to send
LVM_EDITLABEL to a TListView. Or does anyone know of any other way to
"manually" start editing of a label in a TListView?

A:
Use the NotifyControls(LVM_EDITLABEL) or...
  var
    msg : TMessage;
  begin
    msg.msg := LVM_EDITLABEL;
    msg.wParam := 0;
    msg.lParam := 0;
    msg.Result := 0;
    broadcast(msg);
  end;

please note that if you have a container such as a panel or page
control that contains other controls, the message won't be passed
through the container. You must code a message handler for each
container to receive and echo the message to the controls in that
container.

[Dave Fuller, dpfuller@icon.net]

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

51. Display a customer database entry form from a work order form
Q:
I am developing a work order program where in one of the fields on the
work order data entry form are "CUST LNAME" and "CUST FNAME". The
"CUST LNAME" field is in a DBLookupCombo box.
The DBLookupCombo box displays the records in the CUSTOMER.DB. If the
customer has no record in the CUSTOMER.DB (therefore new), the user
closes the DBLookupCombo box and has the option to click a "Add
Button".
Another form (lets call it form2) will then be displayed wherein the
user can insert the new customer's record by filling in a customer
database entry form.  When the user is done, the form closes and goes
back to the work order data entry form and finish the form.
My problem is:
1. What code do I write in the onclick event on the "Add Button" to
display and execute Form2?
2. Any uses statements that I have to implement?

A:
I've got a similar function in my project for location numbers for stock items

1. In the OnClick event I create the data entry form and then show it with
.ShowModal
Then I check .ModalResult -  if its mrOk, I post the record, otherwise I cancel

2. I put the unit name for my data entry form in my uses clause

Here's the basic outline of my code:

procedure TFrmItemNav.BtnChangeLocClick(Sender: TObject);
{var DlgItemLoc: TDlgItemLoc;}
begin
  DlgItemLoc := TDlgItemLoc.Create(FrmItemNav);
  DlgItemLoc.ShowModal;

  if DlgItemLoc.ModalResult = mrOk then
    {do whatever needs doing to Post}
  else 
    {cleanup and Cancel};

  DlgItemLoc.Free;
end;

[Chip Taylor, ctaylor@pbmo.net]

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