                        - December '95 Issue -

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

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

You can read it or download it from:
http://www.fh-merseburg.de/~tietz/delphi.html

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



                      -  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 actual version.

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 for Windows 5.0
  or Paradox for Windows 5.0 Runtime.

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

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



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




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

 Contents

 1. Data aware outline control
 2. Edit in StringGrid
 3. Overriding the Create method
 4. How to detect a row focus change in TDBGrid
 5. Associate an Icon with a component
 6. How to reset a Timer halfway thru
 7. MS Binary Format / IEEE conversion
 8. Code for age
 9. Caps Lock
10. ASCII delimited file memo field into a DB table memo
11. Check whether mouse is over client area
12. Terminate vs. PostMessage(Handle, wm_Close, 0, 0)
13. Asynchronous communication
14. Name / caption property & emulating the caption
15. Converting Icons to Glyphs
16. Override vs Redefine
17. ChartFX
18. Form.TForm.Create(???)
19. Delphi Graphics
20. Problems with AddIndex
21. Array of the Image Object
22. Does this form exist
23. Interrupt Service Routine
24. How to detect program is already running
25. TPolygon Object OnClick algorithm question
26. Pass a record problem
27. Problems with GetSystemMetrics(SM_CYMENU)
28. Hard disk serial number
29. Why no free
30. Combobox problems
31. TotalSystemMemory
32. Image resizing and displaying
33. Stuffing Keystrokes into buffer
34. How to dim the colors of a glyph in a button
35. Function pointer
36. Insert / overwrite
37. How do I get a string out of a memo field
38. How can I trap a system error message
39. GetFileSize
40. Freeing form
41. Find window problem
42. Uses in DLLs
43. File Sharing question
44. How Can I Make These Graphics Faster
45. Freeing Pointers to Constants
46. Obtaining Windows Version
47. Creating and selecting palettes
48. Convert date to number in milliseconds
49. Make mouse snap to grid while drawing
50. Converting Real to a fraction of two integers
51. Ascii code for eof
52. Including a wave file in a Delphi EXE
53. How do I disable mouse cursor
54. Loading a listbox with program groups
55. Mouse cursor position
56. Master-Detail form
57. Change Grid Cell Color
58. Shift Tab don't activate onexit event
59. StretchDraw example
60. Graph to clipboard
61. Icons loaded and converted for TBitBtn no white
62. TEdit and OnEnter event
63. How do I create a component like TField
64. Create a Paradox table
65. Difference between two dates
66. How do you keep the user from resizing a form
67. String handling
68. Transparent Forms and Bitmaps
69. Task ID
70. Accessing notebook pages
71. Resizing (Dynamic) Arrays
72. From Sizes on different platforms
73. Which driver a TDatabase is connected to
74. PChar from TMemoField
75. Time problems
76. System colors
77. Use Free with records
78. Change delete behavior in Memo
79. Fast way to Clear a TCanvas

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

1. Data aware outline control
Q:
Another quick query, does anyone know of a data aware outline control?
My preference is obviously for a freeware one but shareware would do
if the source is available after registration.
If not, does anyone have any bright ideas on a method to store in a
database and display in an outline a relationship tree of undefined
depth?
To explain...we have a table of individuals and wish to relate
them to each other in a corporate hierarchy, ie. Bob reports to Peter,
Peter has Jane and Simon working for him etc... This structure could
have any number of levels and may have to start centred on any person,
at any level.

A:
I've done something like you would like to do.
I cannot explain everything but I can give you an idea on how to do.
You must have a table that make a relation between the individuals.
If Peter has Jane and Simon working for him you must have a table (RELATION)
with these two records

Master  Slave   ------- fields name
Peter     Jane
Peter     Simon

If George and Elisa work for Jane then the table becomes:
Master  Slave   ------- fields name
Peter     Jane
Peter     Simon
Jane      George
Jane      Elisa

and so on.

When you have to construct the tree starting at Peter you've to add a main
node called Peter in the tree an position the RELATION table on the first
record where Master = Peter. Than add a child node 
for each record that satisies the condition Master = Peter.
After adding a child you've to see if this child has child himself. The
child becomes now a probably father
so you've to position the RELATION table on the first record where Master =
child and so on recursively.
This grants you to build a correct tree.
Example

AddFather('Peter')
AddChild('Peter',1)


Procedure AddFather(Name: String)
Begin
  Tree.Add(Name);
End;

Procedure AddChildr(Name: String, Index:Integer)
Begin
  Relation.FindKey([Name])
  while RelationMaster.AsString = Name do
  Begin
    Tree.AddChild(Index,RelationSlave.AsString);
    AddChild(RelationSlave.AsString,Tree.ItemsCount);
    Relation.Next;
  End;
 End;

Maybe there's some error but the this is the way.

[Simone Mori, simone@softeam.it]

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

2. Edit in StringGrid
Q:
An annoying problem with StringGrid:
I'm using a stringgrid to display some data, and i start the stringgrid with
GoEditing set to false (The user can't edit the data in the stringgrid),
and selectrow to true (The user can select only whole row).
I placed an "EDIT" button so when the user presses it, i set the GoEditing to
true & selectrow to false and now the user can edit the data in the table.
All is well up to now.
The problem:
When the user pressed EDIT, and edits a cell, then presses EDIT again to
switch back to normal row selecting mode, the cell that was last edited, stays
in an inverted color, meaning , if the focused cell is blue and the rest is
white, then after the user edits a cell and presses EDIT again , the whole line
is blue EXCEPT the cell that was last edited, which remains white.
I should add that the stringgrid i'm using is a modified stringgrid which each
col. can be alligned diffrently (first col. can be rightjustified, second left
justified, third centered justified and so on...) but i don't think this has
anything to do with my problem.

A:
Haven't tried these, but two possibilities come to mind:

1) On the second edit press, change focus to another field (eg. x.focus
where x is not the grid), reset goEditing and selectRow, then change
focus back to the grid.  (This technique has worked for me in several
places, eg. grids, memos.)

2) On the second Edit press, after resetting goEditing and selectRow,
try creating a tGridRect spanning the row you want to highlight, the
doing grid.Selection := gridRect;

[Sid Gudes, cougar@roadrunner.com]

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

3. Overriding the Create method
Q:
When creating a new TWindow descendant, overriding the create method to add
the object initilization is very easy, just:

Procedure TMyWinControl.create;
begin

inherited
...additional stuff

end;

But, when creating a TOBject descendant, the create method is not virtual, so...
How can I do the same to initialize my new object?
Is the following code rigtht?

Procedure TMyTojectDescendant.create;
begin
inherited; {is this right??}
{Initilization code}

end;

A:
Yes, but you cannot use inherited without a procedure name in these
places. You should write 'inherited Create. The difference is when
someone else (Delphi for example) calls your Create constructor.
Simplifying, caller is not able to create the correct instance of
TObject, because TObject's constructor is not virtual. More precisly
constructor is not a method of the object but of its' class.

Oh, following code will explaine it better:

type
  TComponentClass = class of TComponent;

procedure CreateInstanceExample(AClass : TComponentClass);
begin
  AClass.Create(nil).Show;
end;

begin
  CreateInstanceExample(TForm);
    { Here the procedure creates and shows a from }
end.

[Krzysztof Hryniewiecki, kh@lodz.pdi.net]

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

4. How to detect a row focus change in TDBGrid
Q:
There is (very surprisingly) no event triggered when the user changes the
focus from one row to another in a TDBGrid. Am I missing something here? Has
anyone got an easy way?

A:
You use the OnDataChange event of the Datasource to which the DBGrid is attached.
If the State in the event is dsBrowse then you've gone to a new row (or just
opened the table).

Why not have an event in the grid?  Because the grid may not be the only control
displaying data from the current row and might not be the only way to move from
row to row.  Using the Datasource give centralized event handling.

As to your question about a single click, not sure what you're trying to do, but
you can use TDatasource.OnDataChange to capture row changes and
TDBGrid.OnColEnter/Exit to capture column changes.

[Brian Murray, murray@uansv3.vanderbilt.edu]

A:
The following works for me:

1. To detect row change, use the TDataSource's OnDataChange event.
   OnDataChange occurs whenever scrolling or clicking on a different row
   happens.  The event handler is something like this:

     procedure Form1.DSrc1DataChange(Sender: TObject; Field: TField);

   where Field is the column in which change occured.

   The TTable's fields can be used for comparing the currently selected
   row's fields (key) with whatever your requirement is. The TDBGrid's
   Fields property can also be used the same way. For instance:

     if tbl1.Fields[0].AsString = 'BlaBlaBla' then ...
   or, if dbGrid1.Fields[I].IsNull then ...

2. For column change, use TDBGrid's OnColExit & OnColEnter. The
   TDBGrid's properties SelectedField and SelectedIndex can be used to
   determine the currently selected column.

   When a different column on a different row is selected, you get
   OnColExit, OnColEnter, and then OnDataChange.

3. You can also do some fancy stuff by using the TDBGrid's
   OnDrawDataCell event which occurs when a cell is selected or
   when the grid is scrolled. The event handler looks like:

     procedure Form1.dbGrid1DrawDataCell(Sender: TObject; Rect: TRect;
               Field: TField; State: TGridDrawState);

   But of course you get a lot of draw events when cell changes, and
   you have to do your own filtering.

4. If you don't have a problem in creating "101 variations" on the
   standard components - which I don't 8-) then try this. It is
   easier.

   To access row or column index of the selected cell, you can
   derive a class from TCustomGrid and publish its Row, Col
   run-time properties (current grid row and column, not table's!!):

     type
       TSampleDBGrid = class(TCustomGrid)
       public
         property Col;
         property Row;
       end;

   in some procedure or event handler, do a typecasting:

     var
       G: TSampleDBGrid;
     begin
       G := TSampleDBGrid(myDBGrid1);
       if G.Row = I then ...
       if G.Col = J then ...
       
   This is because TDBGrid is a descendant of TCustomGrid, which
   has several properties on the grid coordinates, but aren't
   published in TDBGrid.

[Lawrence K. Lee, klee@inetnw.com]

A:
From what I can see, you have to do it programmatically.  OTTOMH,
assuming the grids already exist and you have access to the
underlying ttable::

   grid.colcount := dbGrid.fieldcount;
   table.first;
   row := 0;
   while not table.eof do begin
      grid.rowcount := row + 1;
      for i := 0 to grid.colcount-1 do
          grid.cells[i,row] := dbGrid.fields[i].asString;
      table.next;
      inc (row);
   end;

May be some latent bugs in this, but it should do the trick.

[Sid Gudes, cougar@roadrunner.com]

A:
Look at the following code and see if it will help. It takes the 'Name'
property of a control and then places it into the 'Caption' property of a
label.

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Edit1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Edit2MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  close;
end;

procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Label1.Caption := TEdit(Sender).Name;
end;

procedure TForm1.Edit2MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Label1.Caption := TEdit(Sender).Name;
end;

end.

[Patrick Allen, patrick@blueridge.com]

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

5. Associate an Icon with a component
Q:
I am creating my own components. Does anyone have an idea how to associate an
icon with a component?

A:
If you are refering to the pic on the comp palette, you create a bitmap in a
resource file (ext .dcr for delphi component resource). It should be 24
pixels square

[Mitch Cant, pokada@Direct.ca]

A:
    See Adding Palette Bitmaps on page 77 of the Component
    Writer's Guide.

    IMPORTANT NOTE
    ================

    However, note the following misleading sentence in the
    middle of that page:

        "The resource names are not case-sensitive, but by
        convention, they are usually in upper case letters".

    In  my experience there's a particularly good reason for the
    convention:

    IT DOESN'T WORK IF YOU DON'T USE UPPER CASE.

    This may avoid gnashing of teeth.


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

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

6. How to reset a Timer halfway thru
Q:
My timer is set to 5000 (5 seconds), halfway, thru' this, an event occurs
and I need to reset this Timer back to time zero again...

A:
Timer1.Enabled := False;
Timer1.Enabled := True;

This will reset the timer for its full duration.

BTW, changing the interval (to a different value) also resets the
timer.

[Eric Nielsen, htrsoft@midwest.net]

A:
You may enable or disable your timer component, setting its
property, like this:

  Timer1.Enabled := True; { or False, if you want to disable it }

But it will still continue with its 5 secs. If you want to change this,
set another property, the interval one, like this:

  Timer1.Interval := 100;

[Marcus Vinicius Neves, mneves@tpd.puc-rio.br]

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

7. MS Binary Format / IEEE conversion
Q:
Couple of questions:
1.  Does Delphi store real numbers in "Microsoft Binary Format" or "IEEE"?
2.  How can I call a small segment of assembler code within my Delphi code?
Can I embed assembler routines within Delphi?  Please note my assembler
exposure is very limited, but I have also wondered how I can do this, in
both C and now Delphi.

A:
"whatever the base-level machine uses" is not so straightforward before
Intel's 80x87 numeric coprocessors came along.  I'm not sure if the 80x86
processors had any native instructions to perform floating point
arithmetic.  This could be why Microsoft created their own proprietary
format for floating point numbers; they had to do all the arithmetic
themselves, using their own runtime library.  Today, the 80x87 makes the
arithmetic automatic, and IEEE is now the standard.

Delphi does store the following floating point types in IEEE format:
  Single      4 bytes
  Double      8 bytes
  Extended   10 bytes

Note that Real (6 bytes) is not on this list.  I may be wrong, but I
believe Real is an intrinsic Pascal type; its existence may predate the
80x87.

[Aside:  Delphi's online help says that, by default (via the $N+ compiler
directive), the compiler will generate code to perform ALL floating point
calculations using 80x87 instructions, including Real types.  So either the
compiler will generate calls to a runtime library to handle Real types, or
else I am completely wrong about the above! :) ]

Anyway, in checking Visual Basic's online help, I see that its data types
also include Single and Double, which are also IEEE, and are identical to
Delphi's Single and Double types.  However, there is no mention of
"Microsoft Binary Format".

I then dropped down to DOS and ran QBasic, which is Microsoft's old
QuickBasic interpreter that is now included in DOS.  If you check its
online help, you will see the following:

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
MKSMBF$ and MKDMBF$ convert IEEE-format numbers to Microsoft-Binary-format
numeric strings that can be stored in FIELD statement string variables.
CVSMBF and CVDMBF convert those strings back to IEEE-format numbers.

MKSMBF$(single-precision-expression!)
MKDMBF$(double-precision-expression#)
CVSMBF (4-byte-numeric-string)
CVDMBF (8-byte-numeric-string)

   Function    Returns
   ========    ============================================================
   MKSMBF$     A 4-byte string containing a Microsoft-Binary-format number
   MKDMBF$     An 8-byte string containing a Microsoft-Binary-format number
   CVSMBF      A single-precision number in IEEE format
   CVDMBF      A double-precision number in IEEE format

   These functions are useful for maintaining data files created with
   older versions of Basic.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

So, to sum up, if you want to access your "MetaStock" files, I think you
have 3 choices.

1.  Write your program in QBasic/DOS

2.  Find substitutes (hopefully compatible with Delphi) for the conversion
    functions mentioned above.

3.  Write these functions yourself.  You will have to find documentation
    for the bitwise layout of the old "Microsoft Binary Format" Single and
    Double types, perhaps in MS's old Basic manuals.  [Double-ouch!]

[Bernie Mondschein, mondschein.bernie@ehccgate.sandoz.com]

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

8. Code for age
Q:
1. How do I use this function, where do I put it. I have a table listing
the birthdays in one column but not a column for age. Would like to have
a field on a form/notebook showing the age automatically on run.

2. What about the ages of those less than 1 year. How do I show the age
in months for those occasions?

A:
Double-click on your TTable or TQuery component on your form to go to the
Fields Editor dialog (or right-click and select Fields Editor).  Add all the
fields that you will be working with in the form (even those you don't want 
to be visible but need access to--you can set the visible property to false 
for any fields you wish to suppress).  Then click on "Define..." to add a 
calculated field.  Type in a name for the calculated field that is not the 
same as any fields in the table, select a type (probably StringField) and 
enter a length (20 should be fine).  Make sure the 'calculated' box is 
checked.  Then add an event-handler to your TTable or TQuery object for 
'OnCalcFields'.  In this handler you will look at one of the real fields in
your table, do a calculation, and put the results into your calculated field 
object that you just created.  This will cause it to show up in the TDBGrid 
or you can add a TDBText control to display the value if you're using a data 
entry form rather than a grid.  

So far as displaying months as well as years, hear's a function which should 
do the trick.  Since not all the months are the same length, I just took an 
average, so it isn't going to be perfectly accurate, but for most people it 
should suffice:

function AgeStr(aDate: TDateTime): string;
var
  DaysOld : Double;
  Years,
  Months  : Integer;
begin
  DaysOld := Date - aDate;

  Years := Trunc(DaysOld / 365.25);
  DaysOld := DaysOld - (365.25 * Years);
  Months := Trunc(DaysOld / 30.41);

  Result := Format('%d years, %d months',[Years, Months]);
end;

So, in my case, my OnCalcFields method looked like this:

procedure TEntryForm.TableNameOrderCalcFields(DataSet: TDataset);
begin
  TableNameOrderAge.AsString := AgeStr(TableNameOrderDateOfBirth.AsDateTime);
end;

[Dan Butler, Dan_Butler@msn.com]

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

9. Caps Lock
Q:
How to control caps lock key?

A:
In Windows enviroment, you can look at the keyboard lights values, but you can't
set it, because Windows intercept your peek in the memory and blocks it (I tryed
under Windows 95, maybe under Windows 3.11 it works). However, you should be able 
to look at the status.

Try to put this simple code in a function:

const
   SCROLLLOCK = 1;
   NUMLOCK    = 2;
   CAPSLOCK   = 4;

var
   Status:  Byte;
   PntK:    ^Byte;
begin
     PntK := Ptr($40, $97);		{directly point in memory}
     Status := Byte(PntK^);		{read the status}
     if (NUMLOCK and Status) = NUMLOCK then	{if NUM LOCK is on}
         Status := Status and (255 - NUMLOCK)	{turn it off}
     else
         Status := Status or 2;			{turn it on}
     Pntk^ := Status;				{poke in memory (don't works)}
end;

[Marco Ermini, marko@mailserver.softeam.it]

A:
I use this procedures to turn on the caps lock if it isn't already on when
the user enters my DBloockup combo.  This gets rid of the nasty problem
of case-sensitive indexes.

procedure TMainForm.StudentLookupEnter(Sender: TObject);
Var Level : Integer;
    KeyState : TKeyBoardState;
begin
  {check if caps-lock is on - if not turn it on}
  Level := GetKeyState(VK_CAPITAL);
  GetKeyboardState(KeyState);
  CapsLockStatus := KeyState;
  If Level = 0 then
    begin
      KeyState[VK_CAPITAL] := 1;
      setKeyboardState(KeyState);
    end;
end;

[Dave Mansell, st9547d6@pilot.stu.cowan.edu.au]

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

10. ASCII delimited file memo field into a DB table memo
Q:
How do you get an ASCII delimited file Memo field into a DB Table Memo?
I create the Table and then populate it with the fields depending on the
type read on the first line of the ASCII delimited file.
I know that you can't do :
Table.Fields[MemoField].AsString := ASCII.MemoString;

A:
You need to use the getTextBuf procedure. Here's the example from
online help:

This example copies the text in an edit box into a null-terminated string,
and puts this string in another edit box when the user clicks the
button on the form.

procedure TForm1.Button1Click(Sender: TObject);
var
  Buffer: PChar;
  Size: Byte;
begin
  Size := Edit1.GetTextLen;       {Get length of string in Edit1}
  Inc(Size);                      {Add room for null character}
  GetMem(Buffer, Size);           {Creates Buffer dynamic variable}
  Edit1.GetTextBuf(Buffer,Size);  {Puts Edit1.Text into Buffer}
  Edit2.Text := StrPas(Buffer);   {Converts Buffer to a Pascal-style string]
  FreeMem(Buffer, Size);          {Frees memory allocated to Buffer}
end;

[Sam Johnston, sam@cosmos.ab.ca]

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

11. Check whether mouse is over client area
Q:
I want my application to know when the mouse cursor is no longer
hovering over the Client area of my application window.

A:
On the Form's OnMouseMove do:

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  P : TPoint;
begin
  P.X := X;
  P.Y := Y;
  if PtInRect (ClientRect,P) then {or boundsrect for whole window rect}
    MouseCapture := True
  else
    begin
      MouseCapture := False;
      ShowMessage ('It''s not over me anymore');
    end;
end;

[Daniel Polistchuck, danpol@carajas.homeshopping.com.br]

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

12. Terminate vs. PostMessage(Handle, wm_Close, 0, 0)
Q:
Would someone care to elucidate on the differences and advantages or
disadvantages of closing applications with either of these two calls,
are they the same thing?  Do they both clean up properly?

A:
Calling application.terminate sets the value of 'terminated', while
closing the window directly does not. If you're doing direct WM
posts, this lets you trap the event.

[Sam Johnston, sam@cosmos.ab.ca]

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

13. Asynchronous communication
Q:
I would try to compare VB and delphi about communication with
asynchronous port (modem by example). I didn't find an objet or
something like that to opencomm, writecom, readcomm, closecomm.

A:
unit Comm;

interface
uses Messages,WinTypes,WinProcs,Classes,Forms;

type
  TPort=(tptNone,tptOne,tptTwo,tptThree,tptFour,tptFive,tptSix,tptSeven,
         tptEight);
  TBaudRate=(tbr110,tbr300,tbr600,tbr1200,tbr2400,tbr4800,tbr9600,tbr14400,
             tbr19200,tbr38400,tbr56000,tbr128000,tbr256000);
  TParity=(tpNone,tpOdd,tpEven,tpMark,tpSpace);
  TDataBits=(tdbFour,tdbFive,tdbSix,tdbSeven,tdbEight);
  TStopBits=(tsbOne,tsbOnePointFive,tsbTwo);
  TCommEvent=(tceBreak,tceCts,tceCtss,tceDsr,tceErr,tcePErr,tceRing,tceRlsd,
              tceRlsds,tceRxChar,tceRxFlag,tceTxEmpty);
  TCommEvents=set of TCommEvent;

const
  PortDefault=tptNone;
  BaudRateDefault=tbr9600;
  ParityDefault=tpNone;
  DataBitsDefault=tdbEight;
  StopBitsDefault=tsbOne;
  ReadBufferSizeDefault=2048;
  WriteBufferSizeDefault=2048;
  RxFullDefault=1024;
  TxLowDefault=1024;
  EventsDefault=[];

type
  TNotifyEventEvent=procedure(Sender:TObject;CommEvent:TCommEvents) of object;
  TNotifyReceiveEvent=procedure(Sender:TObject;Count:Word) of object;
  TNotifyTransmitEvent=procedure(Sender:TObject;Count:Word) of object;

  TComm=class(TComponent)
  private
    FPort:TPort;
    FBaudRate:TBaudRate;
    FParity:TParity;
    FDataBits:TDataBits;
    FStopBits:TStopBits;
    FReadBufferSize:Word;
    FWriteBufferSize:Word;
    FRxFull:Word;
    FTxLow:Word;
    FEvents:TCommEvents;
    FOnEvent:TNotifyEventEvent;
    FOnReceive:TNotifyReceiveEvent;
    FOnTransmit:TNotifyTransmitEvent;
    FWindowHandle:hWnd;
    hComm:Integer;
    HasBeenLoaded:Boolean;
    Error:Boolean;
    procedure SetPort(Value:TPort);
    procedure SetBaudRate(Value:TBaudRate);
    procedure SetParity(Value:TParity);
    procedure SetDataBits(Value:TDataBits);
    procedure SetStopBits(Value:TStopBits);
    procedure SetReadBufferSize(Value:Word);
    procedure SetWriteBufferSize(Value:Word);
    procedure SetRxFull(Value:Word);
    procedure SetTxLow(Value:Word);
    procedure SetEvents(Value:TCommEvents);
    procedure WndProc(var Msg:TMessage);
    procedure DoEvent;
    procedure DoReceive;
    procedure DoTransmit;
  protected
    procedure Loaded;override;
  public
    constructor Create(AOwner:TComponent);override;
    destructor Destroy;override;
    procedure Write(Data:PChar;Len:Word);
    procedure Read(Data:PChar;Len:Word);
    function IsError:Boolean;
  published
    property Port:TPort read FPort write SetPort default PortDefault;
    property BaudRate:TBaudRate read FBaudRate write SetBaudRate
      default BaudRateDefault;
    property Parity:TParity read FParity write SetParity default ParityDefault;
    property DataBits:TDataBits read FDataBits write SetDataBits
      default DataBitsDefault;
    property StopBits:TStopBits read FStopBits write SetStopBits
      default StopBitsDefault;
    property WriteBufferSize:Word read FWriteBufferSize
      write SetWriteBufferSize default WriteBufferSizeDefault;
    property ReadBufferSize:Word read FReadBufferSize
      write SetReadBufferSize default ReadBufferSizeDefault;
    property RxFullCount:Word read FRxFull write SetRxFull
      default RxFullDefault;
    property TxLowCount:Word read FTxLow write SetTxLow default TxLowDefault;
    property Events:TCommEvents read FEvents write SetEvents
      default EventsDefault;
    property OnEvent:TNotifyEventEvent read FOnEvent write FOnEvent;
    property OnReceive:TNotifyReceiveEvent read FOnReceive write FOnReceive;
    property OnTransmit:TNotifyTransmitEvent read FOnTransmit write FOnTransmit;
  end;

procedure Register;

implementation

procedure TComm.SetPort(Value:TPort);
const
  CommStr:PChar='COM1:';
begin
  FPort:=Value;
  if (csDesigning in ComponentState) or
     (Value=tptNone) or (not HasBeenLoaded) then exit;
  if hComm>=0 then CloseComm(hComm);
  CommStr[3]:=chr(48+ord(Value));
  hComm:=OpenComm(CommStr,ReadBufferSize,WriteBufferSize);
  if hComm<0 then
  begin
    Error:=True;
    exit;
  end;
  SetBaudRate(FBaudRate);
  SetParity(FParity);
  SetDataBits(FDataBits);
  SetStopBits(FStopBits);
  SetEvents(FEvents);
  EnableCommNotification(hComm,FWindowHandle,FRxFull,FTxLow);
end;

procedure TComm.SetBaudRate(Value:TBaudRate);
var
  DCB:TDCB;
begin
  FBaudRate:=Value;
  if hComm>=0 then
  begin
    GetCommState(hComm,DCB);
    case Value of
      tbr110:DCB.BaudRate:=CBR_110;
      tbr300:DCB.BaudRate:=CBR_300;
      tbr600:DCB.BaudRate:=CBR_600;
      tbr1200:DCB.BaudRate:=CBR_1200;
      tbr2400:DCB.BaudRate:=CBR_2400;
      tbr4800:DCB.BaudRate:=CBR_4800;
      tbr9600:DCB.BaudRate:=CBR_9600;
      tbr14400:DCB.BaudRate:=CBR_14400;
      tbr19200:DCB.BaudRate:=CBR_19200;
      tbr38400:DCB.BaudRate:=CBR_38400;
      tbr56000:DCB.BaudRate:=CBR_56000;
      tbr128000:DCB.BaudRate:=CBR_128000;
      tbr256000:DCB.BaudRate:=CBR_256000;
    end;
    SetCommState(DCB);
  end;
end;

procedure TComm.SetParity(Value:TParity);
var
  DCB:TDCB;
begin
  FParity:=Value;
  if hComm<0 then exit;
  GetCommState(hComm,DCB);
  case Value of
    tpNone:DCB.Parity:=0;
    tpOdd:DCB.Parity:=1;
    tpEven:DCB.Parity:=2;
    tpMark:DCB.Parity:=3;
    tpSpace:DCB.Parity:=4;
  end;
  SetCommState(DCB);
end;

procedure TComm.SetDataBits(Value:TDataBits);
var
  DCB:TDCB;
begin
  FDataBits:=Value;
  if hComm<0 then exit;
  GetCommState(hComm,DCB);
  case Value of
    tdbFour:DCB.ByteSize:=4;
    tdbFive:DCB.ByteSize:=5;
    tdbSix:DCB.ByteSize:=6;
    tdbSeven:DCB.ByteSize:=7;
    tdbEight:DCB.ByteSize:=8;
  end;
  SetCommState(DCB);
end;

procedure TComm.SetStopBits(Value:TStopBits);
var
  DCB:TDCB;
begin
  FStopBits:=Value;
  if hComm<0 then exit;
  GetCommState(hComm,DCB);
  case Value of
    tsbOne:DCB.StopBits:=0;
    tsbOnePointFive:DCB.StopBits:=1;
    tsbTwo:DCB.StopBits:=2;
  end;
  SetCommState(DCB);
end;

procedure TComm.SetReadBufferSize(Value:Word);
begin
  FReadBufferSize:=Value;
  SetPort(FPort);
end;

procedure TComm.SetWriteBufferSize(Value:Word);
begin
  FWriteBufferSize:=Value;
  SetPort(FPort);
end;

procedure TComm.SetRxFull(Value:Word);
begin
  FRxFull:=Value;
  if hComm<0 then exit;
  EnableCommNotification(hComm,FWindowHandle,FRxFull,FTxLow);
end;

procedure TComm.SetTxLow(Value:Word);
begin
  FTxLow:=Value;
  if hComm<0 then exit;
  EnableCommNotification(hComm,FWindowHandle,FRxFull,FTxLow);
end;

procedure TComm.SetEvents(Value:TCommEvents);
var
  EventMask:Word;
begin
  FEvents:=Value;
  if hComm<0 then exit;
  EventMask:=0;
  if tceBreak in FEvents then inc(EventMask,EV_BREAK);
  if tceCts in FEvents then inc(EventMask,EV_CTS);
  if tceCtss in FEvents then inc(EventMask,EV_CTSS);
  if tceDsr in FEvents then inc(EventMask,EV_DSR);
  if tceErr in FEvents then inc(EventMask,EV_ERR);
  if tcePErr in FEvents then inc(EventMask,EV_PERR);
  if tceRing in FEvents then inc(EventMask,EV_RING);
  if tceRlsd in FEvents then inc(EventMask,EV_RLSD);
  if tceRlsds in FEvents then inc(EventMask,EV_RLSDS);
  if tceRxChar in FEvents then inc(EventMask,EV_RXCHAR);
  if tceRxFlag in FEvents then inc(EventMask,EV_RXFLAG);
  if tceTxEmpty in FEvents then inc(EventMask,EV_TXEMPTY);
  SetCommEventMask(hComm,EventMask);
end;

procedure TComm.WndProc(var Msg:TMessage);
begin
  with Msg do
  begin
    if Msg=WM_COMMNOTIFY then
    begin
      case lParamLo of
        CN_EVENT:DoEvent;
        CN_RECEIVE:DoReceive;
        CN_TRANSMIT:DoTransmit;
      end;
    end
    else
      Result:=DefWindowProc(FWindowHandle,Msg,wParam,lParam);
  end;
end;

procedure TComm.DoEvent;
var
  CommEvent:TCommEvents;
  EventMask:Word;
begin
  if (hComm<0) or not Assigned(FOnEvent) then exit;
  EventMask:=GetCommEventMask(hComm,Integer($FFFF));
  CommEvent:=[];
  if (tceBreak in Events) and (EventMask and EV_BREAK<>0) then
    CommEvent:=CommEvent+[tceBreak];
  if (tceCts in Events) and (EventMask and EV_CTS<>0) then
    CommEvent:=CommEvent+[tceCts];
  if (tceCtss in Events) and (EventMask and EV_CTSS<>0) then
    CommEvent:=CommEvent+[tceCtss];
  if (tceDsr in Events) and (EventMask and EV_DSR<>0) then
    CommEvent:=CommEvent+[tceDsr];
  if (tceErr in Events) and (EventMask and EV_ERR<>0) then
    CommEvent:=CommEvent+[tceErr];
  if (tcePErr in Events) and (EventMask and EV_PERR<>0) then
    CommEvent:=CommEvent+[tcePErr];
  if (tceRing in Events) and (EventMask and EV_RING<>0) then
    CommEvent:=CommEvent+[tceRing];
  if (tceRlsd in Events) and (EventMask and EV_RLSD<>0) then
    CommEvent:=CommEvent+[tceRlsd];
  if (tceRlsds in Events) and (EventMask and EV_Rlsds<>0) then
    CommEvent:=CommEvent+[tceRlsds];
  if (tceRxChar in Events) and (EventMask and EV_RXCHAR<>0) then
    CommEvent:=CommEvent+[tceRxChar];
  if (tceRxFlag in Events) and (EventMask and EV_RXFLAG<>0) then
    CommEvent:=CommEvent+[tceRxFlag];
  if (tceTxEmpty in Events) and (EventMask and EV_TXEMPTY<>0) then
    CommEvent:=CommEvent+[tceTxEmpty];
  FOnEvent(Self,CommEvent);
end;

procedure TComm.DoReceive;
var
  Stat:TComStat;
begin
  if (hComm<0) or not Assigned(FOnReceive) then exit;
  GetCommError(hComm,Stat);
  FOnReceive(Self,Stat.cbInQue);
  GetCommError(hComm,Stat);
end;

procedure TComm.DoTransmit;
var
  Stat:TComStat;
begin
  if (hComm<0) or not Assigned(FOnTransmit) then exit;
  GetCommError(hComm,Stat);
  FOnTransmit(Self,Stat.cbOutQue);
end;

procedure TComm.Loaded;
begin
  inherited Loaded;
  HasBeenLoaded:=True;
  SetPort(FPort);
end;


constructor TComm.Create(AOwner:TComponent);
begin
  inherited Create(AOwner);
  FWindowHandle:=AllocateHWnd(WndProc);
  HasBeenLoaded:=False;
  Error:=False;
  FPort:=PortDefault;
  FBaudRate:=BaudRateDefault;
  FParity:=ParityDefault;
  FDataBits:=DataBitsDefault;
  FStopBits:=StopBitsDefault;
  FWriteBufferSize:=WriteBufferSizeDefault;
  FReadBufferSize:=ReadBufferSizeDefault;
  FRxFull:=RxFullDefault;
  FTxLow:=TxLowDefault;
  FEvents:=EventsDefault;
  hComm:=-1;
end;

destructor TComm.Destroy;
begin
  DeallocatehWnd(FWindowHandle);
  if hComm>=0 then CloseComm(hComm);
  inherited Destroy;
end;

procedure TComm.Write(Data:PChar;Len:Word);
begin
  if hComm<0 then exit;
  if WriteComm(hComm,Data,Len)<0 then Error:=True;
  GetCommEventMask(hComm,Integer($FFFF));
end;

procedure TComm.Read(Data:PChar;Len:Word);
begin
  if hComm<0 then exit;
  if ReadComm(hComm,Data,Len)<0 then Error:=True;
  GetCommEventMask(hComm,Integer($FFFF));
end;

function TComm.IsError:Boolean;
begin
  IsError:=Error;
  Error:=False;
end;

procedure Register;
begin
  RegisterComponents('Additional',[TComm]);
end;

end.

[Piotr Markiewicz, piotr@homer.iinf.gliwice.edu.pl]

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

14. Name / caption property & emulating the caption
Q:
I need to use the name property to fill another property (like caption is
set by changing the name of a label) how is this done? I have to use a name
to create a link to another app for simplicity's sake I would like to
automatically use the component name. I would like to create the link at
create or load time but when I try, I get a blank for the name property

A:
Is something like this what you are looking for?

type
  TJJJ = class(TLabel)
  public
    constructor Create(AOwner: TComponent); override;
  end;

implementation

constructor TJJJ.Create(AOwner: TComponent);
begin
   inherited Create(AOwner);
   Caption := Name;
end;

[Jeremy L. Poteet, jpoteet@rdasun2.rurdev.usda.gov]

A:
1. Override the virtual SetName method that is inherited from TComponent
2. Make sure you call inherited SetName( NewValue ) to reuse the deafualt
name creation logic from Tcomponent
3. Augment the SetName Method with whatever code you need.


Example
-----------------------------------------------
unit edit;

interface

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

type
  TkeEdit = class(TDBEdit)
  private
    { Private declarations }
    FName : String;
  protected
    { Protected declarations }

 { override the virtual method }
    procedure SetName( const NewName : TComponentName ); override;

  public
    { Public declarations }
  published
    { Published declarations }
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Keen Edge', [TkeEdit]);
end;

procedure TkeEdit.SetName( const NewName : TComponentName );
BEGIN
        { reuse default TComponent SetName Logic }
     inherited SetName( NewName );

        ( 3. Augment SetNAme Logic }
        { always add the string 'Test' to the name whenever it is changed. }
    Text := 'Test' + Name;
END;

[Garth Tissington, tiss@IslandNet.com]

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

15. Converting Icons to Glyphs
Q:
I need to convert Icons(.ICO) into Bitmaps(.BMP) for use in Glyphs at
run-time.  I have seen an application to do this but it didn't come with
source code.  Does anyone know an easy way to do this?  I would prefer a
short code segment but a VCL component would also be an option.

A:
This is a small example with how to load an icon contained in a
file EXE in a Glyph of an SpeedButton and as cleaning the Glyph.

Sorry but the commentaries of the code source are in Spanish.

*****************************************
unit Procs;

interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, StdCtrls, Buttons, ExtCtrls, ShellAPI;

  procedure LlenaBoton(boton: TSpeedButton; Programa: string);
  procedure LimpiaBoton(boton: TSpeedButton);

  var
    {Botones de programas}
    Pic: TPicture;
    Fname : String;
    TempFile: array[0..255] of Char;
    Icon : TIcon;

implementation
uses ttotro;


procedure LlenaBoton(boton: TSpeedButton; Programa: string);
 var
   NumFiles, NameLength : integer;
   nIconsInFile : word;
   rBoton : TRect;
   oBitmap : TBitmap;
   oBitmap2: TBitmap;
   NombreBitmap: string;

 begin

  try
   screen.cursor := crHourglass;

     {Extrae el Icono}
     Icon := TIcon.Create;
     StrPCopy(TempFile, Programa);
     Icon.Handle := ExtractIcon(HInstance, TempFile, 0);

     {Crea una instancia de TPicture}
     Pic := TPicture.Create;
     {Asigna el icon.handle a la propiedad Pic.icon}

     Pic.Icon := Icon;


     {Configura el tamano del bitmap como el del icono y el del segundo
      bitmap con el tamano del boton}
     oBitmap := TBitMap.create;
     oBitmap2 := TBitMap.create;
     oBitmap2.Width := Icon.Width;
     oBitmap2.Height := Icon.Height;
     oBitmap.Width := boton.Width-4;
     oBitmap.Height := boton.Height-4;

     { Dibuja el icono en el bitmap }
     oBitmap2.Canvas.Draw( 0, 0, Pic.Graphic );
     rBoton.left := 1;
     rBoton.Top := 1;
     rBoton.right:= boton.Width-6;
     rBoton.Bottom := boton.Height-6;
     oBitmap.Canvas.StretchDraw(rBoton, oBitmap2);

     Boton.Hint := Programa;

     NombreBitmap := Copy(programa, 1, Length(programa)-3)+'BMP';
     {Salva el bitmap en un fichero}
     If Not FileExists(NombreBitmap) Then
        begin
         oBitmap.SaveToFile(ExtractFilePath(Application.ExeName)+ExtractFileName(NombreBitmap));
         Boton.Glyph := oBitmap;
        end
     else
        {Carga el BMP en el boton}
        Boton.Glyph.LoadFromFile(ExtractFilePath(Application.ExeName)+ExtractFileName(NombreBitmap));

   finally
     Icon.Free;
     oBitmap.Free;
     oBitmap2.Free;
     screen.cursor := crDefault;

   end; {main begin}
end;  {llenaboton}

procedure LimpiaBoton(boton: TSpeedButton);
 var
   oBitmap : TBitmap;
   rBoton : TRect;
 begin

  try
     {Configuara el tamano del bitmap como el del icono y el del segundo
      bitmap con el tamano del boton}
     oBitmap := TBitMap.create;
     oBitmap.Width := boton.Width-4;
     oBitmap.Height := boton.Height-4;
     Boton.Glyph := oBitmap;

   finally
     oBitmap.Free;
   end; {main begin}
end;  {limpiaboton}

end.

[Jose Manuel Prado Bravo, josema@lix.intercom.es]

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

16. Override vs Redefine
Q:
On Compuserve, someone brought up that you cannot override a static
method, however, you can redefine it.  Functionally, what is the difference.
I can see that you do not have access to the overriden method, but are there
any other differences?

A:
Yes, there is one other important difference.  The difference is that if the
ancestor object calls the method then it won't be calling your method, it
will be calling its own method (or higher up the tree if not defined in that
object).  A common practice in OOP is to define a base class that has has
virtual methods that have no definition, that are meant to be overridden in
descendant objects.  The base class will actually call these methods even
though they have no code in them,  and if you did not create a descendant
class you would get a run-time error if that method was ever called.  That is
what polymorphism is all about.

[Dan Butler, Dan_Butler@msn.com]

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

17. ChartFX
Q:
I have tried to introduce two spin
edit boxes to stand for the NSeries and NValues properties. When I
run the program, I try setting these properties to the value of the
spinedit.
I get the 'Error setting property at index #13 (and #14).  I assume this is
because I have not closed down the data before trying to alter the values.
I have read the instructions that came with it and am still non the wiser about
how to open and close channels.

A:
This is the code I use for setting up the chartfx.

  chart1.Opendata[cod_values]:=makelong(no_of_series,no_of_classes);

  {adjust series values}

  chart1.closedata[cod_values]:=0;

[Darryl Gove, djg1@soton.ac.uk]

A:
unit TstChart;

interface

uses=20
  WinTypes, WinProcs, Classes, Graphics, Forms, Controls, Menus,
  Dialogs, StdCtrls, Buttons, ExtCtrls, Tabs,
  ChartFX, {It seems to be necessary to include this for certain constants
           such as COD_VALUES}
  VBXCtrl, Chart2fx;

type
  TF_Chart =3D class(TForm)
    SpeedPanel: TPanel;
    ExitBtn: TSpeedButton;
    NB: TNotebook;
    TB: TTabSet;
    Chart1: TChartFX;
    Chart2: TChartFX;
    procedure ExitItemClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);

    procedure TBClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Procedure Build1( Ch : TChartFX );
    Procedure Build2( Ch : TChartFX );
  end;

var
  F_Chart: TF_Chart;

implementation

{$R *.DFM}

procedure TF_Chart.ExitItemClick(Sender: TObject);
begin
  Close;
end;

procedure TF_Chart.FormCreate(Sender: TObject);
begin
  TB.Tabs :=3D NB.Pages;

  NB.PageIndex :=3D 0;
  Build1( Chart2 );
  Build2( Chart2 ); {adds values, legends etc to Chart2}
end;

procedure TF_Chart.TBClick(Sender: TObject);
begin
   NB.PageIndex :=3D TB.TabIndex;
end;

Procedure TF_Chart.Build1( Ch : TChartFX );
begin
   {This procedure alters the properties that can be set at design
    time or run time.  The Design method of doing things is shown
    in the comments}

   with Ch do begin

      Adm[ CSA_GAP ] :=3D 25.0;

      {Design:  Use the AdmDlg propert to change the Y Gap}

      pType :=3D BAR or CT_LEGEND;
      {Design: Use the ChartType property to change from 1 - line
        to 2 - bar.}

      DecimalsNum[ CD_YLEG ] :=3D 0;
      {Design: Change the Decimals Property from 2 to 0}

      Stacked :=3D CHART_STACKED;
      {Design: Change the Stacked property from 0 - None to 1 - Normal}

      RightGap :=3D 20;
      {Design: Same}

      OpenData[ COD_COLORS ] :=3D 2;

      Color[ 0 ] :=3D clBlack;
      Color[ 1 ] :=3D clYellow;
      CloseData[ COD_COLORS ] :=3D 0; {Ugh!!}
      {Design: To change the colors of the 2 series:
        1)  Make sure ThisSerie is set to 0.  Change
             ThisColor to clBlack.
        2   Set ThisSerie to 1.  Change ThisColor to
             clYellow.}

      Title[ CHART_TOPTIT ] :=3D 'Articles vs Titles';
      Title[ CHART_LEFTTIT ] :=3D 'CCM';
      Title[ CHART_BOTTOMTIT ] :=3D 'Cards';
      {Design:  click on the TitleDlg property and

        set Top, Left and Bottom titles}
   end;
end;

Procedure TF_Chart.Build2( Ch : TChartFX );
{This procedure sets properties that cannot (as far as I can determine)
 be set at design time}
const
   XAbbrevs : array[ 0..4 ] of string[ 4 ] =3D
     ( 'Acc', 'Bar', 'Mas', 'Amex', 'Din' );
   SeriesTitles : array[ 0..1 ] of string[ 8 ] =3D
      ( 'Articles', 'Titles' );
   XTitles : array[ 0..4 ] of string[ 20 ] =3D

     ( 'Access', 'Barclaycard', 'Mastercard', 'American Express',
        'Diners' );
   {of course you would normally read xtitles and
    values from a database}
   Values : array[ 0..1, 0..4 ] of double =3D
      ( ( 50, 60, 70, 80, 90 ),
        ( 30, 35, 25, 37, 42 ) );
var
   i, SerieNo : integer;
begin
   with Ch do begin

      LegendWidth :=3D 120;

      {Set Number of series, number of values ******************}
      OpenData[ COD_INIVALUES ] :=3D MAKELONG( 2, 5 );

      CloseData[ COD_INIVALUES ] :=3D 0;
      {*********************************************************}

      OpenData[ COD_VALUES ] :=3D 2;
      {if you omit the above statement, (in which you enter the
      number of SERIES not VALUES), and the CloseData below,
      the assignment to Values does not create an error, but
      does not work!
      Assigning Values to Legend, KeyLeg works without an
      OpenData/CloseData}
      ThisSerie :=3D 0;
      for i :=3D 0 to 1 do

         SerLeg[ i ] :=3D SeriesTitles[ i ];
      for i :=3D 0 to 4 do=20
        begin
          Legend[ i ] :=3D XTitles[ i ];
          KeyLeg[ i ] :=3D XAbbrevs[ i ];
        end;
      SerieNo :=3D 0;
      for SerieNo :=3D 0 to 1 do=20
        begin
          ThisSerie :=3D SerieNo;
          for i :=3D 0 to 4 do
             Value[ i ] :=3D Values[ SerieNo, i ];
        end;

      CloseData[ COD_VALUES ] :=3D 0;
   end;
end;

procedure TF_Chart.FormResize(Sender: TObject);
var
   w, h : longint;
begin
   w :=3D NB.Width;
   H :=3D NB.Height;
   {enlarge/reduce chart size if necessary}
   Chart1.Width :=3D W - 18;
   Chart1.Height :=3D H - 12;
   Chart2.Width :=3D W - 18;
   Chart2.Height :=3D H - 12;

   {move Exitbutton close to right edge}
   ExitBtn.Left :=3D SpeedPanel.Width - 32;
end;

end.

[Marcus Vinicius Neves, mneves@tpd.puc-rio.br]

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

18. Form.TForm.Create(???)
Q:
I have some questions about creating a form,
when and/or why do I use code line 1,2,3 or 4????
(MainForm and NameOnForm are FormStyle = fsNormal).

Procedure MainForm.BtnOpenFormClick(Sender: TObject);
Begin
  1) NameOnForm:=TNameOnForm.Create(APPLICATION);
  2) NameOnForm:=TNameOnForm.Create(NAMEONFORM);
  3) NameOnForm:=TNameOnForm.Create(SELF);
  4) NameOnForm:=TNameOnForm.Create(???????????);
     NameOnForm.ShowModal;
     NameOnForm.Free;
end;

A:
Don't use (2)!  You'll either GPF or, if this is a second instance of
NameOnForm you'll lose your pointer to the first.

My understanding of how (1) and (3) differ is: For a showModal, (1)
and (3) are equivalent.  For a show, (1) would keep nameOnForm open
until the application is closed (or until the user closes
NameOnForm), while (3) would close nameOnForm when mainForm is
closed.

[Sid Gudes, cougar@roadrunner.com]

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

19. Delphi Graphics
Q:
I have a project to develop a small video format....
I've done some tests with Delphi and it takes approx. 4 seconds for it
to fill a 250 x 250 bitmap, one pixel at a time...this is obviously
too slow...there has to be other ways to do it.

A:
Well, I'm not a champ with graphics, but here is an idea or two.

If you are simply trying to display one image in a resonable amount
of time, create a TBitmap object, play around with it, and then when
you are ready to display it to the screen, call
Image.Canvas.Draw(0, 0, Bitmap) to copy it and draw it to the screen.
You see, the main time consumer is the painting of the screen, not in
the setting of the Image's attributes.  As a result, you want to set the
pixel's colors in a non-visible object (the TBitmap you created), and
then display the whole Bitmap at once.  Here is some code for a form
with a single Image component on it:

procedure TForm1.FormPaint(Sender: TObject);
Var
  TmpX, TmpY : Byte;
  MyImage : TBitmap;
begin
  Form1.Width := 260;    Form1.Height := 260;
  Image1.Width := 250;   Image1.Height := 250;
  Image1.top := 5;       Image1.width := 5;
  MyImage := TBitmap.Create;
  MyImage.Width := 250;  MyImage.Height := 250;
  FOR TmpX := 0 TO 249 DO
    FOR TmpY := 0 TO 249 DO
      MyImage.Canvas.Pixels[TmpX,TmpY] :=
            RGB(TmpX, 250 - TmpY, (TmpX + TmpY DIV 2));
  Image1.Canvas.Draw(0, 0, MyImage);
  MyImage.Free;
end;

If you want to do really fast graphics, look into the GDI (API)
functions and/or the WinG functions given out by Microsoft.  These
are a bit tedious to learn and use, and they are outside of Delphi's
domain.

[John C. Fowler, jcfowler@david.wheaton.edu]

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

20. Problems with AddIndex
Q:
I have problems creating a new secondary index.
I added field to the TTable then the table is created as by Table.Create. Next
I create the primary index with Table.AddIndex('PRIMARY','ID',[ixPrimary]);.
But when I want to create a secondary index I always get an error at runtime.

A:
I am using a paradox table on a local station.
I use the following commands:
Table.DatabaseName := 'ABC';
Table.TableName := 'TEST';
Table.CreateTable;
Table.AddIndex('Primary','ID',[ixPrimary]); (works fine)
Table.AddIndex('Number_IDX','NUMBER',[ixUnique]); (here I get a runtime error)

ID is a Long-Integer field
NUMBER is a char[15] field

[Markus Tondorf, phantom@dark-moon.rhein.de]

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

21. Array of the Image Object
Q:
How do I implement an array of objects...lets say I want 10 image
controls on my form, but don't want a different name for each one...in
VB there was an index property...how do I do it in Delphi?

A:
You cannot do this visually & straight off, but if you don't mind some coding
this is pretty simple:

type
  TForm1 = class(TForm)
    ...
  public
    images: array [1..10] of TImage;
    ...
  end;

procedure TForm1.FormCreate(...);
var i: integer;
begin
  ...
  for i := 1 to 10 do begin
    images[i] := TImage.Create(self);
    with images[i] do begin
      parent := self;
      tag := i; { makes it easier to detect e.g which 
      ... set other properties as required, e.g:
      OnClick := MyClickEventHndlr;
    end;
  end;
  ...
end;

To make sure you get all "uses" correct you may drop one such dynamic component
on your form, and then either delete it or set visible to false.

A more elaborate way is of course to design your own component to do the same.

[Tommy Ericson, teit@pi.se]

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

22. Does this form exist
Q:
How can I check to see if a form exists yet or not?  I have a button that
creates a subform and displays it. When the user clicks on close it does NOT
destroy it but just hides it. If the button is click again, I want to show
the form, not create a new instance. How can I tell if the form has been
created or not??

A:
Normally, dynamically instantiated  forms (or subforms in this case) are
created with a statement like...

        frmNewForm := TNewForm.Create( owner );

To achive what you have asked for, I can think of 3 possible solutions.
1) declare frmNewForm as a global var hence you can code:
        IF frmNewForm = NIL THEN
                frmNewForm := TNewForm.Create( owner );
        frmNewForm.Show;

or for those with an aversion to global vars

2) Find the refernce to frmNewForm that was saved in the components list of
the owner when TForm.Create( owner ) was called.
In most cases this will be the components array property of the main form.

3) If you do not have a main form that you want to be the owner of the
subform then use the TAppllication components CreateForm( TForm, refernce )
method. Later when you need to refer to the subform you can scan the
components array property of you application object. Find the form
by component name and retrieve the required reference.

[Garth Tissington, tiss@IslandNet.com]

A:
One important point, if you do release the object, you should set the
pointer to nil yourself.  Otherwise you may get a GPF next time round.

          frmNewForm.Release;
          frmNewForm := nil;

[Ch'ng Khong, Chng.Khong@cps.co.nz]

A:
If you specify the form's owner as 'Application' when you create it, you
can then iterate through Application's components to check if your form
still exists.

In order to do this, you can create the form either of these ways:

    Application.CreateForm(TSubForm, SubForm);
                - or -
    SubForm := TSubForm.Create(Application);

Then, you can check for your form's existence by iterating through 
Application's components:
     
    for i := 0 to Application.ComponentCount-1 do begin
        if Application.Components[i] = SubForm then ... {form exists};
     
     
Notes:
------
1. If you use 'Self' instead of 'Application' when creating the form, the
   form will not be a component of Application, so it won't be found when 
   you iterate through Application's components.
     
2. Instead of iterating through Application.Components, you could use
   'Application.FindComponent' *if* you've assigned a name to the form 
   (e.g., SubForm.Name := 'SubForm';) after you've created it.  
   'FindComponent' searches the Components array by *name*, and forms you 
   create are NOT given a name until you assign one.
     
3. Testing for a form's existence by using something like
   'if SubForm = nil' will NOT work UNLESS you specifically assign nil to 
   SubForm after you 'Free' it.  Delphi does NOT assign nil to forms when 
   it frees them.
     
[Bernie Mondschein, mondschein.bernie@ehccgate.sandoz.com]

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

23. Interrupt Service Routine
Q:
I am trying to write code which will install an interrupt service routine for
DOS interrupt 21H. I want my ISR to be called ANY time interrupt 21 is call
from any running program or the system itself.  Using the code below, I
don't seem to get any response at all.  I can't even get a GPF.
Any ideas, suggestions or pointers would be apreciated.

 procedure InitDOS21;
 begin
   PassCount := 0;
   GetIntVec($21, OldInt21);
   NewInt21 := @NewInt21ISR;
   SetIntVec($21, NewInt21);
 end;

 procedure ShutdownDOS21;
 begin
     Inc(PassCount);
     SetIntVec($21, OldInt21);
 end;

 procedure JmpOldISR(OldISR : Pointer);
 begin  { This procedure will jump from and ISR to the ISR vector passed.}
 			{ Taken from BREAKNOW.PAS. }
 	inline($5B/$58/$87/$5E/$0E/$87/$46/$10/$89/
   	$EC/$5D/$07/$1F/$5F/$5E/$5A/$59/$CB);
 end;

 procedure NewInt21ISR(Flags, CS, IP, AX, BX, CX, DX, SI,
      	DI ,DS, ES, BP: word);
 begin
     Inc(PassCount);
     { Do my processing }
     JmpOldISR( OldInt21);
 end;

A:
In TP6 and BP7 you needed to define your ISR like this:

procedure NewInt21ISR(...registers...); interrupt;

I've also seen people define them like this:

type
  IntRegisters = record
    case Byte of
      1 : (BP, ES, DS, DI, SI, DX, CX, BX, AX, IP, CS, Flags : Word);
      2 : (Dummy : Dummy5; DL, DH, CL, CH, BL, BH, AL, AH : Byte);
      end;

procedure NewInt21ISR(BP : WORD); interrupt;
var
  Regs : IntRegisters absolute BP;
begin
...
end;

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

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

24. How to detect program is already running
Q:
How do I prevent my program to run which is already running?

A:
Use the hPrevInst variable. If it is 0 then there is no previous instance
otherwise your app is already running somewhere.

So in your project file (Project1.DPR or similar) add an if statement like

IF hPrevInst <> 0 THEN BEGIN
  Application.CreateForm(TForm1, Form1);
  Application.Run;
END;

[Garth Tissington, tiss@IslandNet.com]

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

25. TPolygon Object OnClick algorithm question
Q:
I would like to make a polygon object that will allow the user to draw a
multiple side free form polygon.
I'm wondering if anyone has any ideas on how to write an algorithm to detect
if the user has clicked inside of the polygon for an OnClick Event.

A:
Check out the 2 WinAPI functions CreatePolygonRgn() and PtInRegion().
These will give you the capability you need. If you happen to have a
Compuserve account of susbscribe to Delphi Informant there is code in
the most recent issue on how to set up odd shaped mouse hot spots and
demonstrates how to use the above to functions.

[Mark Lussier, mlussier@best.com]

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

26. Pass a record problem
Q:
I have a Delphi application that has about two dozen simple type
declarations similar to the following:

type RecordA = record
  this             : Integer;
  that            : String;
  the_other : Integer;
end;

Each record type can have an essentially random number of fields in it.
I'm looking for a way to write a *generic* function that can be passed
a specific record variable and a 'field number' which will be able
to determine the name of the field, it's type and it's value.

A rough example:

recA    :  RecordA;
.....
recA.that  := 'Steve';
MyFunc(recA, 2);   { Give me details on the 2nd field in recA };

function MyFunc(rec: ????;  field : Integer);
begin
  { Do some magic }

  Label1.Caption := recordname;      { 'recA' }
  Label2.Caption := fieldname;           { 'that' }
  Label3.Caption := _type;                    { 'String' }
  Label4.Caption := value;                    { 'Steve' }
end;

A:
An idea, may not be what you're looking for, but here it is:

Define a base class, call it allrecs, eg.

   tAllrecs = class
      function getVal (field: integer): string; virtual;
   end;

Then derive a class for each record type, eg.

   recA = class (tAllrecs)
      this             : Integer;
      that            : String;
      the_other : Integer;
      function getVal (field: integer): string; virtual;
   end;

then for each class's function define what it returns:

   function recA.getVal (field: integer); string;
   begin
      case field of
         1: getVal := intToStr (this);
         2: getVal := that;
         3: getVal := intToStr (the_other);
      end;
   end;

Then you can define 

   function myFunc (rec: tAllrecs; field: integer);
   begin
      label2.caption := allrecs.getVal(field);
   end;

and you can then call myFunc with any class derived from tAllrecs, eg.
   myFunc (recA, 2);
   myFunc (recB, 29);

(Rather than a function, getVal will probably have to be a procedure with 3
var parameters so you can return name and type as well as value.)

BTW, please don't use "this" as a variable, it confuses us old C++
programmers :-)
("this" in C++ means the same as "self" in Delphi.)

Also, I did try this out so I have a small working project that does it.

[Sid Gudes, cougar@roadrunner.com]

A:
If you're willing to pass the whole record at a time, then set your
function/procedure to expect an 'array of const' (keeps typechecking
safe, warm & fuzzy). This is identical to 'array of TVarRec'.

See Delphi online help for the system constants defined for TVarRec.

[Sam Johnston, sam@cosmos.ab.ca]

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

27. Problems with GetSystemMetrics(SM_CYMENU)
Q:
I'm trying to capture the height of the menu bar using
GetSystemMetrics(SM_CYMENU).  This works fine for a single-line
menu bar, which the WinApi help says it should.  My problem occurs
when the window is shrunk horizontally until the once-single-line
menu has to double-up on itself, becoming a double-line menu.
How can I determine the total height of the menu bar?

A:
Check if TForm.ClientOrigin (or ClientRect) is moved wrt TForm.Top, e.g.

  YDiff := ClientOrigin.Y - Top;

I find that this will become 42, 61, 80 with a wide enough menu to be
made into three lines. (Add 20 minus 1 for the border line which isn't there.)

[Tommy Ericson, teit@pi.se]

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

28. Hard disk serial number
Q:
Is there anyone who knows how to obtain the hard disk serial number?

A:
I have an unit to get the Hd Name and the Hd Serial number for Borland
Pascal 7.0. I don't know if it's working with Delphi and the other thing is
that it is in dutch and I don't have time to translate it in englisch. Mayby
you can use it, otherwise drop it out of the window.

Unit HardDisk;

INTERFACE

FUNCTION  GetHardDiskNaam               : STRING;
FUNCTION  GetHardDiskSerieNummer        : STRING;
FUNCTION  GetHardDiskControlleNummer    : STRING;
PROCEDURE GetHardDiskGegevens;

CONST
  CodeerTabel : ARRAY[0..24] OF BYTE =
(3,1,2,1,4,1,3,2,6,4,6,5,1,2,6,4,2,6,3,4,6,2,4,1,2);

TYPE
  CharArray = ARRAY[0..24] OF CHAR;

VAR
  HardDiskGegevens          : ARRAY[1..256] OF INTEGER;
  HardDiskNaam              : CharArray;
  SerieNummer               : CharArray;
  ControlleNummer           : CharArray;
  C_HardDiskNaam            : STRING;
  C_HardDiskSerieNummer     : STRING;
  C_HardDiskControlleNummer : STRING;
  C_LicentieNaam            : STRING;

IMPLEMENTATION

 FUNCTION GetHardDiskNaam : STRING;
 VAR
   Teller : INTEGER;
   Lus    : INTEGER;
 BEGIN
    GetHardDiskNaam := '';
    Teller := 1;
    FOR Lus := 1 TO 18 DO
    BEGIN
      HardDiskNaam[Teller] := CHR( ( HardDiskGegevens[27+Lus] DIV 256 ));
      Inc(Teller);
      HardDiskNaam[Teller] := CHR( ( HardDiskGegevens[27+Lus] MOD 256 ));
      Inc(Teller);
    END;
    GetHardDiskNaam := HardDiskNaam;
 END;

 FUNCTION GetHardDiskSerieNummer : STRING;
 VAR
   Teller : INTEGER;
   Lus    : INTEGER;
 BEGIN
    GetHardDiskSerieNummer := '';
    Teller := 1;
    FOR Lus := 1 TO 8 DO
    BEGIN
      SerieNummer[Teller] := CHR( ( HardDiskGegevens[10+Lus] DIV 256 ));
      Inc(Teller);
      SerieNummer[Teller] := CHR( ( HardDiskGegevens[10+Lus] MOD 256 ));
      Inc(Teller);
    END;
    GetHardDiskSerieNummer := SerieNummer;
 END;

 FUNCTION GetHardDiskControlleNummer : STRING;
 VAR
   Teller : INTEGER;
   Lus    : INTEGER;
 BEGIN
    GetHardDiskControlleNummer := '';
    Teller := 1;
    FOR Lus := 1 TO 3 DO
    BEGIN
      ControlleNummer[Teller] := CHR( ( HardDiskGegevens[23+Lus] DIV 256 ));
      Inc(Teller);
      ControlleNummer[Teller] := CHR( ( HardDiskGegevens[23+Lus] MOD 256 ));
      Inc(Teller);
    END;
    GetHardDiskControlleNummer := ControlleNummer;
 END;

 PROCEDURE GetHardDiskGegevens;
 VAR
   Lus    : INTEGER;
 BEGIN
   WHILE ( Port[$1f7] <> $50) DO ;
   Port[$1F6] := $A0 ;
   Port[$1F7] := $EC ;
   WHILE ( Port[$1f7] <> $58 ) DO ;
   FOR Lus := 1 TO 256 DO
   BEGIN
     HardDiskGegevens[Lus] := Portw[$1F0] ;
   END;
 END;

END.

[Rene Groothuis, steelcover@dataweb.nl]

A:
unit Chiunit4;

interface

function Chk...(ParamIn ... ,=20
         ParamDatabaseNamePchar: pchar ): longint; export;

implementation

uses  SysUtils, DBTables, ExtCtrls ;

const
  ide_drive_C           =3D $00A0;
  ide_Data              =3D $1F0;
  ide_Error             =3D $1F1;
  ide_DriveAndHead      =3D $1F6;
  ide_Command           =3D $1F7;
  ide_command_readpar   =3D $EC;
  ide_Status            =3D $1F7;
  ide_status_busy       =3D $80;
  ide_status_ready      =3D $40;
  ide_status_error      =3D $01;
  ide_Fixed             =3D $3F6;
  ide_Fixed_Irq         =3D $02;

  IntervalleMinimum  =3D 0.0000232;
  { 0.000011574 =3D 1 seconde (.0001 (hh.mmss) (->DEG=3D.0002777) / 24) }
  { .0000174 =3D 1 1/2 sec }  { .0000232 =3D 2 sec }

type
  tIdeRec =3D Record
    rec : array[0..255] of word;
  end;

var
  ExitSave :  Pointer;
  IdeRec :    tIdeRec;

function ConvertToString : string;
var
   i,j : integer;
begin
     FillChar( Result, 20, ' ' ); Result[0] :=3D #20;
     for i :=3D 1 to 20 do
       begin
         j :=3D Trunc( (i-1) /2 )  +10 ;
         if Lo(IdeRec.Rec[j]) =3D (0)
            then Result[i]:=3D ' '
            else
            Result[i]:=3D Chr ( Lo( IdeRec.Rec[j] ) ) ;
         i :=3D i +1;
         if Hi(IdeRec.Rec[j]) =3D (0)
            then Result[i]:=3D ' '
            else
            Result[i]:=3D Chr ( Hi( IdeRec.Rec[j] ) ) ;
       end;
end;

function DoIt(Numero: string) : longint;
var
  portchar    :byte;
  boo         :Boolean;
  i           :integer;
  S,S1        :String;
begin
  Result:=3D 19 ; { fail per default }
  FillChar( IdeRec.Rec, 512, ' ' ) ;

  { en premier lieu v=E9rifier l'=E9tat }
         boo :=3D true;
         { poll DRQ wait }
         i :=3D 5000 ;
         repeat
           i :=3D i -1;
           portchar :=3D Lo(port[ide_status]) ; { get status }
         until
         ( i < 1 ) or not
         ( ( portchar AND ide_status_busy ) =3D ide_status_busy ) ;
         if i < 1 then
              begin
                 { Result:=3D 'status allways busy'; }
                 Result :=3D 180 ;
                 boo :=3D false;
              end;

  if boo then
  try
    { premi=E8rement disable drive interrupts }
  port[ide_Fixed] :=3D 0;

  port[ide_DriveAndHead] :=3D ide_drive_C ;  { set drive }
  portchar :=3D Lo(port[ide_status]) ; { get status }
  if portchar =3D $ff then begin
                         { Result:=3D 'set drive status $ff'; }
                         Result :=3D 11 ;
                         boo :=3D false;
                         end;

  if boo then
     begin
         { poll DRQ wait }
         i :=3D 1024 ;
         repeat
           i :=3D i -1;
           portchar :=3D Lo(port[ide_status]) ;
         until
         ( i < 1 ) or not
         ( ( portchar AND ide_status_busy ) =3D ide_status_busy ) ;
         if i < 1 then
              begin
                 { Result:=3D 'status allways busy'; }
                 Result :=3D 181 ;
                 boo :=3D false;
              end;
     end;

  if boo then
         { check if ready }
         if ( portchar AND ide_status_ready ) =3D 0
            then begin
                 { Result:=3D 'set drive status not ready'; }
                 Result :=3D 12 ;
                 boo :=3D false;
                 end;

  if boo then
         { ok now want to readIDE }
         { send ReadParameters command }
         port[ide_Command] :=3D ide_command_readpar ;

         { poll DRQ wait }
         i :=3D 5000 ;
         repeat
           i :=3D i -1;
           portchar :=3D Lo(port[ide_status]) ;
         until
         ( i < 1 ) or not
         ( ( portchar AND ide_status_busy ) =3D ide_status_busy ) ;
         if i < 1 then
              begin
                 { Result:=3D 'status allways busy'; }
                 Result :=3D 182 ;
                 boo :=3D false;
              end;

  if boo then
         { check if no error}
         if ( portchar AND ide_status_error ) =3D ide_status_error
            then begin
                 { Result:=3D 'drive status error after ReadPar'; }
                 Result :=3D 13 ;
                 boo :=3D false;
                 end;

  if boo then
         { check if ready }
         if ( portchar AND ide_status_ready ) =3D 0
            then begin
                 { Result:=3D 'after ReadPar drive status not ready'; }
                 Result :=3D 14 ;
                 boo :=3D false;
                 end;

  if boo then
        try
        { ok now read the buffer 256 word }
         for i :=3D 0 to 255 do
             begin
             IdeRec.Rec[i] :=3D ( portw[ide_Data] ) ;
             end;
        except
          on Exception do begin
                          { ShowMessage( 'Erreur portw i=3D '+intToStr(i)=
 ) ; }
                          boo :=3D false;
                          Result :=3D 15 ;
                          end;
          else begin
               boo :=3D false;
               Result :=3D 16 ;
               raise;
               end;
        end;

  if boo Then
     begin
      S :=3D ConvertToString;
      if length(Numero) < 20 then S1:=3D Numero +'                    '
                             else S1:=3D Numero;
      if CompareStr ( S, Copy(S1,1,20) ) =3D 0
         then Result :=3D 10
         else Result :=3D 17 ;
             { Result :=3D '('+S+')<>('+Copy(S1,1,20)+')' ; }
     end;
  finally
  { re-enable disk interrupts }
  port[ide_Fixed] :=3D ide_Fixed_Irq ;
  end;
END;

procedure MyExit; far;
{ reset disk parameters so other disk operations won't be desturbed in ca=
se
  of program abort }
begin
  ExitProc :=3D ExitSave;        { restore previous exitproc }
{  Port[ide_Command]:=3D$10;      { send command: reset current drive }
end;

function GetParam(ParamAlias: string): String;
var
   i : integer ;
   t : TTable ;
   S : String ;
begin
 Result :=3D '';
 try
  t :=3D nil;
  t :=3D TTable.Create(nil);
  t.DatabaseName :=3D ParamAlias;
  t.TableName :=3D  ...;
  t.TableType :=3D ttPARADOX;
  t.open;
...
 finally
  if Assigned(t) then t.free ;
 end;
end;

function FixParam(ParamAlias: string): boolean;
var
   i : integer ;
   t : TTable ;
   S : String ;
begin
 Result :=3D False;
 try
  t :=3D nil;
  t :=3D TTable.Create(nil);
  t.DatabaseName :=3D ParamAlias;
  t.TableName :=3D  ;
  t.TableType :=3D ttPARADOX;
  t.open;
  if=20
    begin
...         t.Edit;
         t.setFields([nil, S]);
         t.post;
    end;
  t.close;
  Result :=3D True;
 finally
  if Assigned(t) then t.free ;
 end;
end;

  {----------------------------------------------------}

function Chk...(ParamIn: ;
                  ParamDatabaseNamePchar: pchar ): longInt ;
var
   ParamString :  String; =20
   Temps :        Real;
   Ok :           boolean;
   i:             integer;
   S :            string[20];
   S6 :           string[6];
   r :            longInt;

Label
     Jump;
BEGIN
  Result:=3D 0 ;  { par d=E9faut }
 if Ok then
       i :=3D 0;
       repeat
           begin
           i :=3D i +1 ;
           r :=3D DoIt(Copy(ParamString,54,20)) ;
           if r =3D 10 then begin
                          Ok :=3D True ;
                          break
                          end
                     else begin
                          Ok :=3D False ;
                          Result:=3D r;
                          Continue;
                          end;
           end;
       until i =3D 3 ;
  If Ok
     then begin
          Ok :=3D FixParam(ParamDatabaseName) ;
          If Ok then else { Result :=3D 'FixParam fail'; }
                          Result :=3D 2 ;
          end;
  If Ok then Result :=3D 1 ;
END;

Begin
  ExitSave:=3D ExitProc;
  ExitProc:=3D @MyExit;
end.

[Guy Messely, gmessely@riq.qc.ca]

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

29. Why no free
Q:
My Pascal Ref manual states that calling Free is the correct (or better)
way to destroy these objects.

 TStage = class(TObject)
   constructor Create;
   destructor Destroy;
   ..
   ..
   ..
 end;

 *** NOTE that I did not override free!

So here is the question.  At program shutdown I call:  Stage.Free.  Stage
was created by  Stage := new TStage; I placed some debug output calls in
TStage.Destroy but I never see them!
If I call Stage.Destroy I do see them! What is the deal?

A:
You should declare your desctructor as
  destructor Destroy; override;

The Destroy method of TObject is a virtual method and can be overriden.  That
way when you call Free (even though you have not defined a Free method in
your class) it will, through the miracle of polymorphism, call the correct
Destroy method.

So, Stage.Free really executes TObject.Free.  Since you did not override the
definition of Destroy in TStage, TObject.Free calls TObject.Destroy.  If you
override Destroy in TStage then when TObject.Free calls Destroy, Delphi is
able to figure out that you wanted TStage.Destroy not TObject.Destroy.

[Brian Murray, murray@uansv3.vanderbilt.edu]

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

30. Combobox problems
Q:
I have a combo box. On the On Change event I have some processing, which if
OK will update the results, and the combo box selection is valid. However,
if the processing fails, the combo box selection should revert to the
previous value. How do I make it do this?

A:
Try saving the Index value in a variable in the OnEnter method or the OnCreate
method of the form.  Then, to reject the user's selection, simply
  ComboBox1.ItemIndex := var1;

[Ryan Peterson, rpetersn@usit.net]

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

31. TotalSystemMemory
Q:

A:
Unit MemInfo;

Interface

Procedure FreeMemory(Var lTotalMemory: LongInt; Var lFreeMemory: LongInt);


Implementation

Uses WinTypes, WinProcs, ToolHelp;

Function Min(Number1, Number2 : LongInt) : LongInt;
{Returns the minimum of Number1 & Number2}
Begin
     If (Number1 <= Number2) Then
        Min := Number1
     Else
        Min := Number2;
End; {end Function, Min()}

Procedure FreeMemory(Var lTotalMemory: LongInt; Var lFreeMemory: LongInt);
{Calculates and returns the amount of Total & Free Memory in bytes
 (ie. divide each by 1024 of Kilobytes)
 NB: Total Memory will be 0 if windows is running in Standard Mode since
     actual Total Memory is not able to be determined.}
Var
   lWinFlags : LongInt;
   mmiMemManInfo : TMemManInfo;
Begin
     {Initialise Variables}
     lTotalMemory := 0;
     lFreeMemory  := 0;
     lWinFlags    := GetWinFlags;
     If (0 <> (lWinFlags And WF_ENHANCED)) Then
     Begin
          {Initialise MemManInfo structure}
          mmiMemManInfo.dwSize               := SizeOf(TMemManInfo);
          mmiMemManInfo.dwLargestFreeBlock   := 0;
          mmiMemManInfo.dwMaxPagesAvailable  := 0;
          mmiMemManInfo.dwMaxPagesLockable   := 0;
          mmiMemManInfo.dwTotalLinearSpace   := 0;
          mmiMemManInfo.dwTotalUnlockedPages := 0;
          mmiMemManInfo.dwFreePages          := 0;
          mmiMemManInfo.dwTotalPages         := 0;
          mmiMemManInfo.dwFreeLinearSpace    := 0;
          mmiMemManInfo.dwSwapFilePages      := 0;
          mmiMemManInfo.wPageSize            := 0;
          MemManInfo(@mmiMemManInfo);  {Get Memory Manager Information}
          {Calculate Total Memory}
          lTotalMemory := (Min(mmiMemManInfo.dwTotalLinearSpace,
                               mmiMemManInfo.dwTotalPages +
mmiMemManInfo.dwSwapFilePages)
                           * mmiMemManInfo.wPageSize);

          {Calculate Free Memory}
          lFreeMemory := GetFreeSpace(0);
     End
     Else
     Begin
          {Total Memory = 0}
          lTotalMemory := 0;
          {Calculate Free Memory}
          lFreeMemory := GetFreeSpace(0);
     End; {end If-Then-Else}
End; {End Procedure, TotalMemory()}

End. {end of Unit, MemInfo}

[Jeremy Coleman, jeremy_coleman@signas.dpa.act.gov.au]

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

32. Image resizing and displaying
Q:
I've placed a TImage in a ScrollBox, and in my paint routine I have it
adjust the size of the Image component based on a zoom factor, then paint
the Image.
The scroll box reacts correctly to the changes in size, however the TImage
only paints the area corresponding to my 100% size when going to bigger zoom
factors (i.e. 200%).  So my image is chopped off, but like I said, the
scrollbox is doing everything correctly.  I've checked the height and width
properties of the image at run time, and everything is fine there, so I
can't understand what's happening.

A:
The size of the bitmap you have attached to the picture.graphic property
matters also. If you size the image contorl to say 320x200, but the bitmap
is only sized to 160x100 then all you'll be left with is that gray area
around the picture, unless you have the stretch property set to true.

  Here's a quick way around this.

procedure TForm.ZoomImage;
var Bitmap: TBitmap;
      DstRect: TRect;
begin
   { Here you want to set up a new bitmap with the proper proportions for your
     zoom factor and draw the image you want zoomed into it. }

   Bitmap := TBitmap.Create;
   Bitmap.Width := { insert your zoomed width here }
   Bitmap.Height := { insert your zoomed height here}

   Bitmap.Canvas.StretchDraw(Bitmap.Canvas.ClipRect,{insert the image you

      you want zoomed here});

   { Here you assign the new bitmap to the graphic property of the image box.
    The image box will automatically dispose of the resources used by its
    prior image. }

   Image1.Picture.Graphic := Bitmap;
   Image1.Invalidate;
end;

That should work, or at least give you an idea of how to implement what you
want. I just wrote this OTTOMH so no flames if I forgot something please.
Keep in mind that you don't need to do this in the onPaint event. I also
like the fact that the image box is smart enough to dispose of the old
bitmap when you assign it a new one. Incidently I think if you use the
Assign method instead of implicity assigning the bitmap you'll end up with a
copy of the new bitmap instead of actually having the image box use the one
just created..  You'll have to dispose of the new one then at the end of the
procedure.

[Jumpstile Turner, fortunat@interpath.com]

A:
The solution to problem was to adjust the TImage.Picture.Bitmap.Width and
the TImage.Picture.Bitmap.Height properties.

[Larry Standage, ls37450@goodnet.com]

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

33. Stuffing Keystrokes into buffer
Q:
I need my application to be able to "stuff keystrokes" into the keyboard
buffer.
My application needs to be able to do this while minimzed and the
keystrokes should effect the active Window and appear "typed".

A:
{this proc interrogates the numlock key and sets the state}
{according to the value of bOn}

procedure TIndexForm.ToggleNumLockKey(bOn: Boolean);
var
  KeyState : TKeyBoardState;
begin
  GetKeyboardState( KeyState );
  if bOn then KeyState[VK_NUMLOCK] := 1
     else KeyState[VK_NUMLOCK] := 0;
  SetKeyboardState( KeyState );
end;

[Patrick Allen, patrick@blueridge.com]

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

34. How to dim the colors of a glyph in a button
Q:
However, for various reasons, I can't create the 2,3,4 glyphs in a bitmap.
I just need to use the 1 glyph in 1 bitmap  and dim the color at run-time
- maybe using some calculations to reduce the color value..... or something.

A:
The best way is still to create more than one glyph in a bitmap, if
you're stuck on this one, try loading one of the provided sample
button glyphs into image editor, to see how it's done...

I presume though, that you probably have a TBitmap that you
dynamically assign to the bitmap at runtime. To generate a dimmed
glyph, you could draw black pixels on every alternate square, like
those on a chessboard. I think this is the way that 95 generates it's
dimmed screen when asking if a shutdown is needed. Your code to draw
a pixel onto the glyph would probably look something like this:
BitBtn1.Glyph.Canvas.Pixels[0,0] := clBlack;

and in a loop:
for i := 1 to BitBtn1.Height do
  for j := 1 to BitBtn1.Width do
    begin
     if (Trunc(j/2)*2) = j then BitBtn1.Glyph.Canvas.Pixels[j,
          Trunc(Frac(i/2)*2)] := clBlack;
    end;

[Carl Mes, carl.mes@pixie.co.za]

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

35. Function pointer
Q:
Is The a similar concept to pointers to functions in delphi?
I have a toolbar with speed buttons which can be used by a number of diffrerent
forms in an app. Depending on which form is currently active I would like to
execute a different funtion if a button is pressed. I would hate to do this
using a huge case statement, Ideally what I wanted to do is register a function
with the toolbar button when a form gets focus and then simply call that
function when the speed button is pressed.

A:
This is what I came up with when building a simple states machine:

This is a very simple example of using function pointers under
Borland Delphi to control program flow.  Just create a simple form with
one button and add the code from Unit1 to the unit created.  Add Unit2
to the project and compile.  Give me a yell if you have any problems.

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  CurrProc : LongInt;
  MyVal : LongInt;

implementation

uses Unit2;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  NewProc : LongInt;
  MyString : string;
begin
  CurrProc := 2;                { beginning point in proc table }
  MyVal := 0;                   { dummy variable }
  NewProc := 0;                 { return value for next index in proc
table }
  while CurrProc < 6 do
    begin
    { execute the current index in the proc table and get the next proc
}
    NewProc := ProcTable[CurrProc](MyVal);

    { this is just to track the values of NewProc and CurrProc }
    FmtStr(MyString, 'NewProc [%d]  CurrProc [%d]', [NewProc,
CurrProc]);
    MessageDlg(MyString, mtInformation, [mbOK], 0);

    { set the current proc to the returned proc }
    CurrProc := NewProc;
    end;

end;

end.

{ This is a simple example of defining an array of function pointers }

interface

type
  { define Procs as a function }
  Procs = function(var ProcNum : LongInt): LongInt;

var
  { declare the array of function pointers }
  ProcTable : Array [1..5] of Procs;

{ function interface definitions }
function Proc1(var MyVal : LongInt) : LongInt; far;
function Proc2(var MyVal : LongInt) : LongInt; far;
function Proc3(var MyVal : LongInt) : LongInt; far;
function Proc4(var MyVal : LongInt) : LongInt; far;
function Proc5(var MyVal : LongInt) : LongInt; far;


implementation

uses Dialogs;

function Proc1(var MyVal : LongInt) : LongInt;
begin
  MessageDlg('Proc 1', mtInformation, [mbOK], 0);
  Proc1 := 6;
end;

function Proc2(var MyVal : LongInt) : LongInt;
begin
  MessageDlg('Proc 2', mtInformation, [mbOK], 0);
  Proc2 := 3;
end;

function Proc3(var MyVal : LongInt) : LongInt;
begin
  MessageDlg('Proc 3', mtInformation, [mbOK], 0);
  Proc3 := 4;
end;

function Proc4(var MyVal : LongInt) : LongInt;
begin
  MessageDlg('Proc 4', mtInformation, [mbOK], 0);
  Proc4 := 5;
end;

function Proc5(var MyVal : LongInt) : LongInt;
begin
  MessageDlg('Proc 5', mtInformation, [mbOK], 0);
  Proc5 := 1;
end;

initialization

  { initialize the contents of the array of function pointers }
  @ProcTable[1] := @Proc1;
  @ProcTable[2] := @Proc2;
  @ProcTable[3] := @Proc3;
  @ProcTable[4] := @Proc4;
  @ProcTable[5] := @Proc5;

end.

[Chuck McKnight, mcknight@intellex.com]

A:
     I think I would do something like this:
     Declare in each form procedures that handle the buttonpresses, like
     procedure CutButtonPressed(Sender:TObject) of Object;
     Then I would simply assign the buttons' OnClick events to these
     procedures in the forms OnActivate event. This would be the oop way to
     do it, but if you don't like it, I think Delphi still has function
     pointers.

[Palle Due Larsen, pdl@vki.dk]

A:
Define a base class form with an abstract function declaration for each
of the functions you want to call from your toolbar. Then derive each
of your forms from that base class form, and provide definitions for those
functions.
Eg: (There might be a couple of syntax errors here - I haven't compiled it)
type
  TBaseForm = class(TForm)
  public
    procedure Method1; virtual; abstract;
end;

type
  TDerivedForm1= class(TBaseForm)
  public
    procedure Method1; override;
  end;

  TDerivedForm2= class(TBaseForm)
  public
    procedure Method1; override;
  end;

  procedure TDerivedForm1.Method1;
  begin
    ....
  end;

  procedure TDerivedForm2.Method1;
  begin
    ....
  end;

{To call the function from your toolbar, get
the currently active form and call Method1}
procedure OnButtonClick;
var
  AForm: TBaseForm;
begin
  AForm := ActiveForm as TBaseForm;
  AForm.Method1;
end

[Carter Daniel, DCARTER@X400.telkom400.inca.za]

-----------------------------------------------------------------------------
36. Insert / overwrite
Q:
I've looked through help files and vcl source code for
components, but I haven't found any information about toggling
between insert and overwrite modes in components.  It seems
like a fairly easy thing to do (perhaps an api call?) but I'm
at a loss.

A:
    Here are a couple of routines that should help.  When
    testing, you'll have to watch out for the Delphi IDE's own
    use of Insert and Overwrite.  I'm not sure whether that
    setting is stored in the VK_INSERT entry of the Virtual key
    code table or not (and it may be reset on a return to the
    IDE).

    Also, it goes without saying that many Delphi components
    may well pay no attention to the setting of this key.

 function LowOrderBitSet(Int: integer): boolean;
{----------------------------------------------------------------}
{ Tests whether the low order bit of the given integer is set.   }
{----------------------------------------------------------------}
 const
   LowOrderBit = 0;
 type
   BitSet = set of 0..15;
 begin
   if LowOrderBit in BitSet(Int)
     then LowOrderBitSet := true
     else LowOrderBitSet := false;
 end;

 function InsertOn: boolean;
{----------------------------------------------------------------}
{ Returns the status of the Insert key.                          }
{----------------------------------------------------------------}
 begin
   if LowOrderBitSet(GetKeyState(VK_INSERT))
     then InsertOn := true
     else InsertOn := false
 end;

 procedure ToggleInsert;
{----------------------------------------------------------------}
{ Toggles the status of the Insert key.                          }
{----------------------------------------------------------------}
 var
   KeyBoardState: TKeyBoardState;
 begin
   GetKeyboardState(KeyBoardState);
   if InsertOn
     then KeyBoardState[VK_INSERT] := 0
     else KeyBoardState[VK_INSERT] := 1;
   SetKeyboardState(KeyBoardState);
 end;

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

A:
The reason why the Delphi component doesn't support overwrite is that it is
simply using the multi-line edit control that is one of the standard Windows
controls, and it does not support this capability directly.  However, there
is a simple technique that I got from one of the Borland SE's not too long
ago.  Here's what you do:

1.  Add a boolean variable to the private section of your form, and call it
InsertOn.
2.  Add an 'OnCreate' event handler to the form, to initialize InsertOn to
TRUE.
3.  Add a TLabel control to display whether insert is on or not (optional).
4.  Add an 'OnKeyDown' event handler to your TMemo or TDBMemo control, and
insert the following code:

procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key = vk_Insert) and (Shift = []) then
  begin
    InsertOn := not InsertOn;
    if InsertOn then
      Label1.Caption := 'Insert'
    else
      Label1.Caption := 'Overwrite';
  end;
end;

5.  Add an 'OnKeyPress' event handler to your TMemo control, and code it like
this:

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  if (Memo1.SelLength = 0) and (not InsertOn) then
    Memo1.SelLength := 1;
end;

What happens is, if InsertOn is TRUE, then it selects one character so that
when the key gets inserted, it will be overwritten.  I think its a pretty
slick way to handle the problem.  Wish I could take credit for it!  Anyway,
this should take care of the problem.

I don't think there is any function called LowOrderBitSet in
Delphi--must have been his own function.  Also, as far as I know, the Insert
key is not a toggle in the same sense that the caps lock or num lock keys
are.  It is totally up to the app to decide what to do when it is pressed,
and if you want to make it toggle, you'll have to store the state of that
toggle yourself, the way I suggested (or some other way).

[Dan Butler, Dan_Butler@msn.com]

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

37. How do I get a string out of a memo field
Q:
I have a memo field in a table that I know is less than 255 bytes.
I need to stuff it into a string and nothting I do seems to work.

A:
  Memos := TStringList.Create;
  Memos.Assign(Table1Memo);
  MyString := '';
  for I:= 0 to Memos.Count-1 do
    MyString := MyString + Memos[I];
  Memos.Free;

[Cosimo Laddomada, mimmoladd@mail.clio.it]

A:
if Memo1.GetTextLen <= 255 then
  aStr := TEdit(Memo1).Text;

If you look at the source for TEdit, TCaption, and TMemo, none of them have a
property called Text or Caption; it is inherited from TControl.  TCaption
publishes Caption, while TEdit publishes Text.  TMemo doesn't publish either,
but we know the property is there, so we can get access to it by pretending
that the Memo object is a TEdit object (through a typecast), thus bypassing
the protection.  If the typecast offends you, do it like this:

function TForm1.GetStr: string;
var
  st: array[0..256] of char;
begin
  Memo1.GetTextBuf(st, sizeof(st));
  Result := StrPas(st);
end;

[Dan Butler, Dan_Butler@msn.com]

A:
When I referenced the string list from form one (see above ex. code) I was using the
.PAS filename for form1.  Turns out, I needed to use the Tform name instead - i.e.,
instead of FORM1 I needed to use FRMORDENTRY:

         If (frmOrdEntry.List1[0] < '00000' ) or (frmOrdEntry.List1[0] < '    0') then
                 Listbox1.Items.Add(frmOrdEntry1.List1[0]);


Funny thing is, in another section of code on Form2, I HAD to use the .PAS filename
to reference an array from form one.

[Deb Clark, DACLARK@freh-01.adpc.purdue.edu]

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

38. How can I trap a system error message
Q:
I am writing an installation program for an application that I
have, but if the disk if not in the drive I get a system error
message [Cancel] [Retry], is it possible to trap this?

A:
 function DisketteDriveReady (DisketteDrive: char): boolean;
{----------------------------------------------------------------}
{ Returns true if specified Diskette drive [A/a or B/b] is ready }
{ with a diskette inserted, otherwise false.  From a Delphi-Talk }
{ posting by Per Ola Svensson <po.svensson@mailbox.swipnet.se>   }
{----------------------------------------------------------------}
 var
   Drive: byte;
   SaveErrorMode: word;
 begin
   DisketteDriveReady := false;    {until proven otherwise}
   case DisketteDrive of
     'A', 'a':  Drive := 1;
     'B', 'b':  Drive := 2;
     else Exit;
   end; {case}
   SaveErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
   if DiskFree(Drive) <> -1 then
     DisketteDriveReady := true;
   SetErrorMode(SaveErrorMode);
 end; {DisketteDriveReady}

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

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

39. GetFileSize
Q:
Like everyone else (I suppose) I had patched around the lack
of a GetFileSize function and botched one together using
AssignFile, Reset, FileSize & CloseFile.
This merry morning I find that it doesn't work for files which
have the "Read only" attribute set.
I guess I could further botch it to work using FileGetAttr and
FileSetAttr, but I can't believe that there's nothing in the
Windows API.
Failing that, has anybody else written a nice clean GetFileSize
function that works properly for any file.   I'd prefer it to
work on an unopened file, or failing that on a File Handle, but
not from a "File Variable".
I don't want much, just the size in bytes.

A:
Here's a bit of code I use to determine info about a group of files:

var
  Fhnd2 : File ;
  sPath : String;
  tpath : string;
  SearchRec: TSearchRec;
  tempsearch : string;
  tempfiles : Integer;
  tempbytes : LongInt;
  wBytes : Word;
  sTemp : String ;
  iLen : Integer ;
  szString: Array[0..128] Of Char;
  ec : integer;

BEGIN

  {* Fetch System Directory *}
  MailManLogS('MailMan Begin');
  sTemp := ParamStr(0) ;
  iLen := Length(sTemp) ;
  WHILE sTemp[iLen] <> '\' DO
    DEC (iLen) ;
  StrPCopy(szString, sTemp) ;
  szString[iLen] := #0 ;
  SysDir := StrPas(szString) ;

  tempbytes := 0;
  tempfiles := 0;
  Files2bProc := 0;
  Bytes2bProc := 0;
  MailManLogS('Calculate Files To Be Processed');
  {* Find out how many files and bytes are to be processed *}
  tempsearch := SysDir + 'spool\witchcrf\d\*.*'  ;
  ec := FindFirst(tempsearch, faSysFile, SearchRec);
  While ec = 0 do
    begin
      if ((SearchRec.Name <> '.') and (SearchRec.Name <> '..')) then
        begin
          tempfiles := tempfiles + 1;
---->     tempbytes := tempbytes + SearchRec.Size;       <------
          TotalInBytes.Text := IntToStr(tempbytes);
          TotalInFiles.Text := IntToStr(tempfiles);
          MailManLogS('File-' + SearchRec.Name + '     Size-' + IntToStr(SearchRec.Size));

        end;
      ec := FindNext(SearchRec);
    end;

    MailManLogS('Total Files = ' + IntToStr(tempfiles) + '        Bytes = ' + IntToStr(tempbytes));

end;


All the syntax may not be right,  I just cut and paste a section of
one of my programs to demonstrate how the FindFirst Function
works.  It returns info about file in SearchRec which should
contain any info you want about a file.  I think it's exactly what
your looking for as the file doesn't have to be open.

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

A:
I have cobbled together something using FindFirst.
It returns a record of type TSearchRec.  This record contains a
variable Size which is the file size in bytes.  It may not be pretty
but it works.

function GetFileSize(FileName: string): Longint;
var
   SearchRec: TSearchRec;
begin
   if FindFirst(FileName, faAnyFile, SearchRec) = 0 then
      Result:=SearchRec.Size
   else
      Result:=-1;       {return an error, this can be anything less
                                     than zero}
end;

[Bill Shearman, 90121460@postoffice.csu.edu.au]

A:
If you like, you can pick one of these two for a start. The first is
a hack that changes the file attributes temporarily to allow the
read. The second uses the Windows API, but doesn't do any error 
checking.


Function FileGetSize1(Filename : String) : LongInt;
var
  F : File;
  OldFileAttr : Integer;
begin
  if FileExists(Filename)
    then
      begin
        OldFileAttr := FileGetAttr(Filename);
        FileSetAttr(Filename,OldFileAttr and (faReadOnly xor $FFFF));
        try
          AssignFile(F, Filename);
          Reset(F,1);
          Result := FileSize(F);
          CloseFile(F);
        finally
          FileSetAttr(Filename, OldFileAttr);
        end;
    end
  else
    Result := 0;
end;

Function FileGetSize2(Filename : String) : LongInt;
var
  FileHandle : Integer;
begin
  if FileExists(Filename)
    then
      begin
        FileName := FileName + chr(0);
        FileHandle := _lopen(@FileName[1], 0);
        Result := _llseek(FileHandle, 0, 2);
        _lclose(FileHandle);
      end
    else
      Result := 0;
end;

[Eric Nielsen, htrsoft@midwest.net]

A:
I didn't bother with AssignFile.

Function FileSizeInBytes(YourFile : String) : LongInt;
Var
  F : Integer;
Begin
  F:=FileOpen(YourFile,0);  { ReadOnly Mode }
  FilesizeInBytes := FileSeek(F,0,2);
  FileClose(F)
End;

Note: No error checking !!!

[Ray Cramer, RNC@Pol.ac.uk]

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

40. Freeing form
Q:
Am I right in believing that when a form is not visible it frees its
resources? Or do I have to Destroy it. How would i do that whithout
closing the app.

A:
        When a form isn't visible it is does set its resources free.
        What you need is to create it at run time.
        Use Release method, not Free.


Try this:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
     Form2: TForm;
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Form2 <> nil then
  begin
    Form2.Release;
    Form2:= nil;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if Form2 = nil then
  begin
    Form2 := TForm.Create(Application);
    Form2.Show;
  end;
end;

end.

[Francisco Torres, ftorres@cenpes.petrobras.gov.br]

A:
- In your main don't use this:

begin
Application.Create(myForm);
Application.Run;
end.

- But this:

begin
myForm := TmyForm.Create(Application);
		{ insert here what you want } 
myForm.Show;	{ ... or myForm.ShowModal }
		{ insert here what you want } 
myForm.Hide;	{ ... if you want to hide it }
		{ insert here what you want } 
myForm.Free;
end.

- You can "Create" and "Free" forms when you want and many times you want.

Remember to free-up all forms before ending your app!

[gefswtec@mbox.vol.it]

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

41. Find window problem
Q:
I am using FindWindow() to locate a window handle and I
am using PostMessage() to close the application.
The problem is that I can't seem to close Acrobat Reader
using FindWindow(nil, 'Acrobat Reader') and PostMessage()
whenever Acrobat Reader has a file opened.
Using winsight.exe, I noticed that Acrobat Reader changes
its classname and window title whenever a PDF file is
opened. How do I find Acrobat Reader's window handle and
send a message to close the application when the
classname and title change?

A:
You should use Getwindow() to cycle thru all the various windows and search
each one's 
name for acrobat reader, although this sounds like a chore it's really not
too tough and happens instantaneously when you run the program.

Here's the code to do it :
(* Function to get the handle of Adobe Acrobat, could be applied to any
 partial text window of a main program such as notepad, just change the
"Acrobat 
Reader" to the appropriate thing you're looking for, it will return 0 if the
window
wasn't found will return the handle if it was found     *)

Function GetAcrobatHwnd : word;

var
  hwndx :word;
  PString : Pchar;
  txtlength : integer;
  posit : byte;
begin{function}
 hwndx := Getdesktopwindow;
 GetWindow(hwndx,GW_CHild);
 While not done do
 begin
 Txtlength := GetWindowText(hwndx,PString,255);
 Posit := Pos("Acrobat Reader",Strpas(Pstring));
        If Posit > 0 then (* Acrobat was found, make the result the handle *)
        begin {if}  
        Result := hwndx;
        done := true;
        end;{if}
  if hwndx = Getwindow(hwndx,GW_Hwndlast) then (* Acrobat Isn't Running *)
    begin
    Result := 0;
    done := true;
    end;
hwndx := GetWindow(hwndx,GW_HWNDNEXT);
 end;{while}

end;{function GetAcrobatHwnd}

That function should cover it, let me know if you have any problems

[Michael Donohue, radtech@valleynet.net]

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

42. Uses in DLLs
Q:
If I keep 'uses Forms;' in a formless DLL project, the compiled DLL file is
about 140kb.  If I delete 'Forms' from the uses clause, it doesn't compile.
If I delete the whole uses clause, it compiles happily to about 2kb,
but occasionally the IDE complains about the lack of a uses clause.
Is it safe to have no uses clause?
The DLL at the moment is only an experiment, but I hope to use a DLL in my
program.

A:
You're not really following the proper syntax for a DLL.  Here's
the way to do it right:

You need at least two files-- the library file and the source
code file:

Library file:  mylib.dpr

  library MyLib;

  uses
    MyCode in 'MYCODE.PAS';

  exports
    MyFunc index 1;

  begin
  end.

Source file:  mycode.pas

  unit MyCode;

  interface

  function MyFunc( MyParam: string ): string; export;

  implementation

  function MyFunc( MyParam: string ): string;

  begin
  Result := 'This was just an example!';
  end;

  end.

Follow this format and you can't miss...  taken (loosely) from
the Delphi Developer's Guide from Borland Press/Sams Publishing.

[Brad Choate, choate@cswnet.com]

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

43. File Sharing question
Q:
I have a robot-type application which runs unattended, and I am having
some filesharing problems:
The robot opens a text file for append, then adds a line to it, then
closes it. If the file is in use by someone else, even for read, Windows
puts up a message saying Sharing Violation, retry/cancel, and the robot
is then hung. This happens in win31 with vshare.386 and in win95.
I have tried flagging the files as Shareable (they are on a Novell
server), this doesnt help. I have looked at the filemodes available, and
none seem to help. How can I trap this error in my program, and handle it
myself?

A:
Have you tried a try ... except block yet?

I had a similar, but not the same, problem.

Code like this worked fine...

try
  {open file code goes here}
  ...
except
   {exception handling code goes here}
   {something like MessageDlg('Cannot open file', mtError, [mbOk], 0)
    would do nicely :) }
   ...
end;

[Geoff Groube, geoff@hijinx.com.au]

A:
The Shareable netware attribute is used for EXE & COM files, and let
multiple users run one file.  This will not work for text files.

One method would be to check to see if the DOS "READ-ONLY" attribute is set.
Most DOS & Windows programs will set the flag after it opens a file to keep
everyone else out.  You can alternativly check for the NETWARE "READ-ONLY
file attribute.  This could be done with one of the NETWARE API components
that are floating around.  When your program finds one of these conditions
to be true, just have it wait a certain amount of time then check again.

[Guy M. Spillman Jr., gmspillm@cris.com]

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

44. How Can I Make These Graphics Faster?
Q:
How might I only draw to a specific part of the screen and only have that
portion repainted, instead of the entire bitmap repainted?  Just having a
16 x 16 portion of the bitmap would be speed demons quicker than repainting
the entire 416 x 216 bitmap...

A:
Here is a VCL-only method of doing it;  see if this
helps. The BitBlt is handled by CopyRect.

var
  BitMap : TBitmap;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Bitmap := TBitmap.Create;
  Bitmap.Width := 400;
  Bitmap.Height := 400;
  PaintBox1.Width := 200;
  PaintBox1.Height := 200;
  With Bitmap.Canvas do
    begin
      Pen.Color := clNavy;
      Ellipse(0,0,399,399);
    end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Bitmap.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Limit : Word;
  I : Word;
  PBBottom, PBRight : Word;
begin
  PBBottom := PaintBox1.Height - 1;
  PBRight := PaintBox1.Width - 1;
  Limit := Bitmap.Width - PaintBox1.Width;
  For I := 0 to Limit do
    PaintBox1.Canvas.CopyRect(Rect(0,0,PBRight,PBBottom),
                              Bitmap.Canvas, Rect(I,0,I+PBRight,PBBottom));
end;

[Eric Nielsen, htrsoft@midwest.net]

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

45. Freeing Pointers to Constants
Q:
I have been using using a listbox to store a database string and long
 integer.  The longint is store in the Object field:
    MyList.AddObject(sDescription, POINTER(lKey) );

Now, how do I reclaim the memory used by the lKeys stored in the Listbox?

A:
You don't have to.  The Data property of TList is simply a space to put an
address.  Since your just storing a number there and the number doesn't point to
any memory that you have allocated there is nothing to free/reclaim.  When the
TList items are deleted the space allocated to hold your pointer will be freed.

[Brian Murray, murray@uansv3.vanderbilt.edu]

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

46. Obtaining Windows Version
A:
Does anyone have any code handy for determining the version
of Windows?   I'm pretty sure it's got something to do with
the GetVersion API,  but I can't seem to figure out the
bitwise operations in Delphi to determine it in a LongInt!

A:
Here's a short program that demonstrates GetVersion.

 program Winvrsn;

 uses
   WinTypes,
   WinProcs,
   SysUtils;

 var
   WinVersion : Word;
   DosVersion : Word;
   VersionString : String;

 begin
   WinVersion := GetVersion and $0000FFFF;
   DosVersion := (GetVersion and $FFFF0000) shr 16;
   VersionString := 'DOS : ' + IntToStr(Hi(DOSVersion)) + '.' + IntToStr(Lo(DOSVersion)) + #13 +
                    'Windows : '+ IntToStr(Lo(WinVersion)) + '.' + IntToStr(Hi(WinVersion)) + #0;
   MessageBox(0, @VersionString[1],'Version Information', MB_ICONINFORMATION or MB_OK)
 end.

[Eric Nielsen, htrsoft@midwest.net]

A:
I don't know for certain what result to expect since WfW is not on my machine
anymore, but how about giving this snippet a try and let me know what
happens? It "cheats" by getting the version info from WINVER.EXE. For
Windows 95 it reads "4.00", for Windows 3.1 it says "3.10".

var
  VIHandle : LongInt;
  VSize : LongInt;
  VData : Pointer;
  VVers : Pointer;
  Len : Word;
  OutStr : String;

begin
  VSize := GetFileVersionInfoSize('WINVER.EXE', VIHandle);
  If VIHandle = 0
    Then OutStr := 'Windows Version : Unknown L1'
    Else
      Begin
        GetMem(VData, VSize);
        Try
          If not GetFileVersionInfo('WINVER.EXE', VIHandle, VSize, VData)
            Then OutStr := 'Windows Version : Unknown L2'
            Else
              If not VerQueryValue(VData,'\',VVers,Len)
                Then OutStr := 'Windows Version : Unknown L3'
                Else
                  With TVS_FIXEDFILEINFO(VVers^) do
                    OutStr := 'Windows Version : ' +
                      IntToStr((dwProductVersionMS and $FFFF0000) shr 16) +
                      '.' +
                      IntToStr(dwProductVersionMS and $0000FFFF) + #0;
        Finally
          FreeMem(VData, VSize);
        End;
      End;
    MessageBox(0,@OutStr[1],'Windows Version Test',MB_OK or MB_ICONINFORMATION);
end;

[Eric Nielsen, htrsoft@midwest.net]

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

47. Creating and selecting palettes
Q:
How do you create and use a Palette in Delphi. According to the help fn I
think you should use the CreatePalette and SetPaletteEntries and RealizePalette
but how?

A:
Below are functions that help to create a palette (an identity
palette, BTW) from an array of RGBQuads (such as you would find in 
the palette section of a .BMP file). I stole this from the WinG 
documentation, and converted it to Delphi. First call 
ClearSystemPalette, then you can get an identity palette by calling 
CreateIdentityPalette.

 If you plan to try palette animation, work in a 256-color mode, and 
change all the PC_NOCOLLAPSE entries below to PC_RESERVED.

Besides creating the palette, the other pieces to the puzzle are 

1. Override the form's GetPalette method, so that it returns the 
new palette.

2. Select and realize the new palette just before you paint.

  OldPal := SelectPalette(Canvas.Handle, NewPalette, False);
  RealizePalette(Canvas.Handle);  
  { Do your painting here }
  SelectPalette(Canvas.Handle, OldPal, False);

3. Remember to release the palette when you are done using 
DeleteObject

4. If you are used to using the RGB function to get color values, use 
the PaletteRGB function in its place.

function CreateIdentityPalette(const aRGB; nColors : Integer) : HPALETTE;
type
  QA = Array[0..255] of TRGBQUAD;
var
  Palette : PLOGPALETTE;
  PalSize : Word;
  ScreenDC : HDC;
  I : Integer;
  nStaticColors : Integer;
  nUsableColors : Integer;
begin
  PalSize := SizeOf(TLOGPALETTE) + SizeOf(TPALETTEENTRY) * 256;
  GetMem(Palette, PalSize);
  try
    with Palette^ do
      begin
        palVersion := $0300;
        palNumEntries := 256;
        ScreenDC := GetDC(0);
        try
          { For SYSPAL_NOSTATIC, just copy the color table into a PALETTEENTRY
            array and replace the first and last entries with black and white }
          if (GetSystemPaletteUse(ScreenDC) = SYSPAL_NOSTATIC)
            then
              begin
	        { Fill in the palette with the given values, marking each
                  with PalFlag }
                {$R-}
                for i := 0 to (nColors-1) do
                with palPalEntry[i], QA(aRGB)[I] do
                  begin
                    peRed := rgbRed;
                    peGreen := rgbGreen;
                    peBlue := rgbBlue;
                    peFlags := PC_NOCOLLAPSE;
                  end;

                { Mark any unused entries with PalFlag }
                for i := nColors to 255 do
                  palPalEntry[i].peFlags := PC_NOCOLLAPSE;

                { Make sure the last entry is white --
                  This may replace an entry in the array!}
                I := 255;
                with palPalEntry[i] do
                  begin
                    peRed := 255;
                    peGreen := 255;
                    peBlue := 255;
                    peFlags := 0;
                  end;

                { And the first is black --
                  This may replace an entry in the array!}
                with palPalEntry[0] do
                  begin
                    peRed := 0;
                    peGreen := 0;
                    peBlue := 0;
                    peFlags := 0;
                  end;
                {$R+}
              end
            else
              begin
	        { For SYSPAL_STATIC, get the twenty static colors into the
                  array, then fill in the empty spaces with the given color
                  table }

                { Get the static colors from the system palette }
                nStaticColors := GetDeviceCaps(ScreenDC, NUMRESERVED);
                GetSystemPaletteEntries(ScreenDC, 0, 256, palPalEntry);

                {$R-}
                { Set the peFlags of the lower static colors to zero }
                nStaticColors := nStaticColors shr 1;
                for i:= 0 to (nStaticColors-1) do
                  palPalEntry[i].peFlags := 0;

                { Fill in the entries from the given color table}
                nUsableColors := nColors - nStaticColors;
                for I := nStaticColors to (nUsableColors-1) do
                  with palPalEntry[i], QA(aRGB)[i] do
                    begin
                      peRed := rgbRed;
                      peGreen := rgbGreen;
                      peBlue := rgbBlue;
                      peFlags := PC_NOCOLLAPSE;
                    end;

                { Mark any empty entries as PC_NOCOLLAPSE }
                for i := nUsableColors to (255-nStaticColors) do
                  palPalEntry[i].peFlags := PC_NOCOLLAPSE;

                { Set the peFlags of the upper static colors to zero }
                for i := (256 - nStaticColors) to 255 do
                  palPalEntry[i].peFlags := 0;
              end;
        finally
          ReleaseDC(0, ScreenDC);
        end;
      end;
    { Return the palette }
    Result := CreatePalette(Palette^);
  finally
    FreeMem(Palette, PalSize);
  end;
end;


procedure ClearSystemPalette;
var
  Palette : PLOGPALETTE;
  PalSize : Word;
  ScreenDC : HDC;
  I : Word;
const
  ScreenPal : HPALETTE = 0;
begin
  PalSize := SizeOf(TLOGPALETTE) + SizeOf(TPALETTEENTRY) * 255; {256th = [0] }
  GetMem(Palette, PalSize);
  try
    FillChar(Palette^, PalSize, 0);
    Palette^.palVersion := $0300;
    Palette^.palNumEntries := 256;
{$R-}
    For I := 0 to 255 do
      With Palette^.palPalEntry[I] do
        peFlags := PC_NOCOLLAPSE;
{$R+}
    { Create, select, realize, deselect, and delete the palette }
    ScreenDC := GetDC(0);
    try
      ScreenPal := CreatePalette(Palette^);
      if ScreenPal <> 0
        then
          begin
            ScreenPal := SelectPalette(ScreenDC,ScreenPal,FALSE);
            RealizePalette(ScreenDC);
            ScreenPal := SelectPalette(ScreenDC,ScreenPal,FALSE);
            DeleteObject(ScreenPal);
          end;
    finally
      ReleaseDC(0, ScreenDC);
    end;
  finally
    FreeMem(Palette, PalSize);
  end;
end;

[Eric Nielsen, htrsoft@midwest.net]

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

48. Convert date to number in milliseconds
Q:
PDOXWin allows conversion of a dateTime to a number in milliseconds. Am
trying to write code in Delphi to do the same procedure. I have done the
following which results in date format:

procedure TForm1.Table1NewRecord(DataSet: TDataset);
begin
   Table1.FieldByName('MyField').AsDateTime :=
     EncodeDate(9999, 12, 31) - Now;
end;

Anyone suggest how the code should read if 'MyField' were a number field
and AsDateTime were to change to AsInteger?

How would I get a number that is in milliseconds?

A:
EncodeDate returns a TDateTime object, which is just a double. To get the
milliseconds since 1/1/0001 multiply the result by 86400000.0 Better yet, first
substract from a more recent base date, so as not to cause overflow.

[George Blat, georgeb@brd.com]

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

49. Make mouse snap to grid while drawing
Q:
Can anyone tell me where to start....
How do make "snap to grid" -procedure for OnMouseMove -event (or for any
event)(like in programs like Designer, AutoCAD  etc.)

A:
1.  Declare two variables to hold the user-desired grid sizes:

	nGridWidth : Integer;
	nGridHeight : Integer;

2.  If bSnapToGrid then

	2a.  calculate the closest point in the grid to the current
	x, y in the OnMouseUp event handler.

3.  Place the drawing component (left, top) at this position.

Give the user a method for setting their own grid preferences.

[Patrick Allen, patrick@blueridge.com]

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

50. Converting Real to a fraction of two integers
Q:
I am using a rather slow function (see below) to convert a real
value to a fraction of two integers. Does anybody know of a better and
faster method as the one here presented?

A:
I have written a program that does just that. It's a dos program. You
call it with the decimal number passed as a parameter. It will print
3 columns, the first will be the continued fractions and the next two
will be the numerator and denominator. You will have to convert it
yourself to a function that you can call from your program, but that
should be to difficult.

To see how it works, I suggest that you try it as follows from the
DOS command line:

CONTFRAC 3.141592654

program contfrac;       { continued fractions }
{$N+}
const
        order   = 20;
var
        y,
        lasterr,
        error,
        x               : extended;

        a               : array[0..order] of longint;
        i,j,
        n               : integer;
        op,
        p,
        q               : longint;

begin
        lasterr := 1e30;
        val(paramstr(1), y, n);
        if n <> 0 then
                halt;
        x := y;
        a[0] := trunc(x);

        writeln;
        writeln(a[0]:20, a[0]:14, 1:14);

{ this is where the smarts are }

        for i := 1 to order do begin
                x := 1.0 / frac(x);
                a[i] := trunc(x);
                p := 1;
                q := a[i];
                for j := pred(i) downto 0 do begin
                        op := p;
                        p := q;
                        q := a[j] * q + op;
                        end;
                error := abs(y - int(q) / int(p));
                if abs(error) >= abs(lasterr) then
                        halt;
                writeln(a[i]:20, q:14, p:14, error:10);
                if error < 1e-18 then
                        halt;
                lasterr := error;
                end;
end.

[George Blat, georgeb@brd.com]

A:
Here's an idea that I use that seems fast enough.  Here's the scheme:

We'll use the number 23.56.

Take your real number and do integer division by 1.

        23.56 div 1 = 23

Now subtract the result from the number you started with.

        23.56 - 23 = .56

To convert to int value just multiply by 100 if that is needed and recast.

             valA := (val div 100);
             valB := (valA - val); or valB := (valA - val) * 100;

                val = 23.56
                ValA = 23
                ValB = .56 or 56

[Monte Saager, kidvolt@teleport.com]

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

51. Ascii code for eof
Q:
What is the ascii code for the <eof> marker for a text file
the same thing as the ascii code for <eoln> is #13.

A:
The standard DOS EOF marker is control-Z, or ASCII character 26

[Sam Johnston, sam@cosmos.ab.ca]

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

52. Including a wave file in a Delphi EXE
Q:
I would like to include a Wave file with my EXE. However, I don't want to
distribute the wave file as a separate file. Is it possible to include this
in my EXE, and call it from my Delphi program? If so, how?

A:
First,you can create foo.rc which is plain text file.
1.wav ,2.wav are example. Please write your waves name here.

--- foo.rc
// WAVES
//
WAVE1 WAVE PRELOAD FIXED PURE "1.WAV"
WAVE2 WAVE PRELOAD FIXED PURE "2.WAV"
WAVE3 WAVE PRELOAD FIXED PURE "3.WAV"
WAVE4 WAVE PRELOAD FIXED PURE "4.WAV"

The first words are wave resource names. You must remember these names.
The last words are wave file names.

--- foo.rc

Next,run delphi\bin\brcc.exe for making foo.res.

Last,you must the following code in your source code:

--- unit1.pas (your code)
Unit1
 :
 :
implementation

{$R foo.res}    (* your waves resources here *)
 :
 :
--- unit1.pas (your code)

  Then,I show the code how to load the wave resources.

# How to load

      hrsr := FindResource(hinst, resname, 'WAVE');
      if hrsr = 0 then begin
          (* ERROR *)
      end
      else begin
        wave[num].hglb := LoadResource(hinst, hrsr) ;
        wave[num].lpstr := LockResource(wave[num].hglb) ;
        if wave[num].lpstr <> nil then
         wave[num].load := True
      end;

  (notes)
    resname : 'WAVE1','WAVE2',... wave resource name
    hinst   : value of System.HInstance
    num     : wave number. You can manage waves by number. 

# How to play waves

   if wave[num].load = True then begin
       sndPlaySound(wave[num].lpstr, SND_ASYNC or SND_MEMORY)
   end;


[Yoshihiro Oikawa, y-oikawa@str.hitachi.co.jp]

A:
You will need a resource editor such as Resource Workshop to load the
.WAV file into a resource file. In the example below, I called my
resource type "SOUND", but you could call it "WAV" or whatever you 
like. I think it needs to be all uppercase, as does the resource name 
itself.

BTW -- don't use the resource file made by Delphi. Make a new one, 
and add a resource statement for it in your file.

{$R SOUNDS.RES}

procedure PlayMyNoise;
var
  rhMyNoise : THandle;
  pMyNoise : Pointer;
  hMyNoise : THandle;

begin
  rhMyNoise := FindResource(HInstance, 'MYNOISE', 'SOUND');
  hMyNoise := LoadResource(HInstance, rhMyNoise);
  pMyNoise := LockResource(hMyNoise);
  sndPlaySound(pMyNoise, SND_SYNC or SND_MEMORY);
  FreeResource(hMyNoise);
end;

[Eric Nielsen, htrsoft@midwest.net]

A:
If you have Resource Workshop, you're in luck. You
need to create a new resource type and load in the wave file and save the 
resource with a filename that's different from your project. Then in your unit, 
following the {R *.DFM) add the line {R WHATEVERYOUNAMEDYOURFILE.RES}. Then 
comes the fun part of how to use it. I worked on this a long time but finally 
got it right. 

Following is a short unit that will load a wave file as a resource and play it 
for as long as you hold your mousebutton down. Hope it helps!

Syl

unit Wavemain;

interface

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

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure SpeedButton1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure SpeedButton1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormDestroy(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    p : pointer;
    HResource : THandle;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}
{$R LAUGH.RES}

procedure TForm1.FormCreate(Sender: TObject);
var
  RName : array[0..20] of char;
  RType : array[0..20] of char;
begin
  StrPCopy(RName,'LAUGH');     {the name you gave the resource in Workshop}
  StrPCopy(RType,'WAVSOUND');  {the name you gave the new 'type' in Workshop}
  try
    HResource := LoadResource(HInstance,(FindResource(HInstance,RName,RType)));
    P := LockResource(HResource);
  except
    GlobalFree(HResource);
  end;
end;

procedure TForm1.SpeedButton1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  SndPlaySound(P,snd_Async or snd_Loop or snd_Memory);  
end;

procedure TForm1.SpeedButton1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  SndPlaySound(nil,0);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  try
    GlobalUnlock(HResource);
  except
    abort;
  end;
  try
    GlobalFree(HResource);
  except
    abort;
  end;

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  SndPlaySound(nil,0);
end;

end.

[cricket@richmond.infi.net]

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

53. How do I disable mouse cursor
Q:
How do I disable mouse cursor on the image-component?

A:
You can also use the ShowCursor API Function set to false (-1). To enable
again call the function again with
True arguement (0).

But you must remember that the cursor will be unavailable to ALL windows
with this function! You must reset the
value if someone leaves the specific area of your Application that you want
the cursor disabled.
I would put it in the LostFocus event of my window or control or in test
cursor coords in the mousemove event to see if it
is out of the area. This is a little bit more work, but it lets Windows
handle the null cursor.

[Eddie Shipman, nryan@slip.net]

A:
I think that the simplest method to 'disable' the mouse is to create
a custom cursor wich is an empty image, then you assign this cursor
when the mouse is over the windowed control that you want (in your
case, a TImage component).
I use this technique to hide the mouse cursor in a program that
displays an animated image projected on a maxi-screen. Then I use a
right-click to show a menu that permits to re-display the cursor.
It works well and it's functional.

You should create a cursor with Resource Wokshop or similar resource
editors, and fit it in your RES. Then you must define a constant value
for your cursor that is greater then the standars Windows cursor
constant (for instance, 6 or 7):

const
   crNullCursor = 6;

Then in the create event, load the cursor:

Screen.Cursors [crNullCursor] := LoadCursor (hInstance, 'EMPTY');

Assuming that 'EMPTY' is the name for your custom cursor in the RES.
Then you can toggle for different Windows and custom cursor on every
windowed control, assigning the proper Cursor property.

TImage1.Cursor := crNullCursor;
TImage1.Cursor := crDefault;
etc...

[Marco Ermini, marko@mailserver.softeam.it]

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

54. Loading a listbox with program groups
Q:
So my question is,  how can I load a list box with all existing
program groups on a system?

A:
I am not sure if this approach will be helpful if the application
that ran before yours added any  groups.  i.e. whether groups get 
written to disk immediately, or only on exit from Windows....  You 
can try it out... if it works, great.... if not, and if you would 
like some programmatic control, here is some code that I pulled out 
of one of my programs...

------code---------
Procedure TProgMan.ReadGroups;
Var
  GroupData : PChar;
  TmpStr : String;
  i : integer;
begin
 GroupData := FDDEClient.RequestData('Groups');
 FGroupsList.Clear;
 FNumGroups := 0;  {assume there are no groups! }
 if GroupData = nil then
   exit
 else
   begin
     i := 0;
     TmpStr := '';
     While GroupData[i] <> #0 do
       begin
         if GroupData[i] = #13 then
           begin
             FGroupsList.Add(TmpStr);
             TmpStr := '';
             i := i + 1;  {skip the #10 char}
           end
         else
           TmpStr := TmpStr + GroupData[i];
           i := i + 1;
       end;
   end;
 StrDispose(GroupData);
end;
------------code ends-----------------

Here, FGroupsList is of type TStringlist.... therefore, you may
substitute it with Listbox.Items as easily.

[Sajan Thomas, THOMAS@warp.msoe.edu]

A:
....
var
  Groups: PChar;
begin
   if not DDEClient.SetLink('ProgMan','PROGMAN') then
      MessageDlg('Link with ProgMan NOT established.', mtInformation, [mbOK], 0);

      DDEClient.OpenLink;

      Groups:=DDEClient.RequestData('GROUPS');

      ComboBox1.Items.SetText(Groups);

      strDispose(groups);
      DDEClient.CloseLink;


end;
.....

this code will get existing progMan groups names. and put it into a comboBox ( to let the
user select...)
Note, you don't have to allocate memory for the Groups variable, the API function
RequestData is doing this. but you have to dispose of it when through.

Of course you have to put a DDEClient component on your form.

[nitsanko@netvision.net.il]

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

55. Mouse cursor position
Q:
I'd like to be able to get the actual
mouse co-ordinates though and not have rely on a mousedown event to get
them.  Anybody know how to do this?  Example,  I'd like to be able to have
a timer running and check the co-ordinates on a regular basis.  I assume
some Windows API calls are required here.

A:
You can use a MouseMove event. For example:

Var
  MyX, MyY : Integer;

Procedure Form1.Image1MouseMove(Sender : TObject...);
Var
  MyPoint : TPoint;
Begin
  If Timer1.Enabled Then 
  Begin
    MyPoint.X:=X;
    MyPoint.Y:=Y;
    MyPoint:=ClientToScreen(MyPoint);
    MyX:=MyPoint.X;
    MyY:=MyPoint.Y
  End 
End;

May not be elegant, but should be workable.

[Ray Cramer, rnc@unixa.nerc-bidston.ac.uk]

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

56. Master-Detail form
Q:
I am using a master-detail from (created with the Expert) to
display a list of people from one table and items from
another table for each individual. The problem is that I
can't seem to edit any of the fields on my form from either
of the tables. Also, the add, delete, and post buttons on
the VCR DB control are greyed out.

A:
This is normal for two reasons:

1) The Database expert by default creates query with RequestLive set to
False; if you want to modify something, you have to set RequestLive to True.

2) In a one-many relationship, due to referential integrity rules, you are
allowed to make changes only in the "many" form, not in the "one" form. BTW,
this is right, if you think about it. Suppose you have a one-many
relationship where the one are your customers, and the many are their
invoices: of course there could be more than one invoice per customer. If
the system would allow you to modify the customer by, for example, deleting
records, you'd find some invoice not related to anybody.

[Andrea Mennini, jake@blues.dsnet.it]

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

57. Change Grid Cell Color
Q:
I have a dbeGird wich displays a lot of numbers. How can I change the color
of the cells which has a value below zero.

A:
Attach the following code to the DBGrid's OnDrawCell event handler:

procedure TForm1.DBGridDrawDataCell(....);
begin
  if Table1.FieldByName( 'SomeField').AsFloat < 0 then
      DBGrid1.Canvas.Font.Color := clRed
      else DBGrid1.Canvas.Font.Color := clBlack;
  DBGrid1.DefaultDrawDataCell( Rect, Field, State );
end;

[Patrick Allen, patrick@blueridge.com]

A:
In the dbgOrdRegDrawDataCell put the next lines.

if ((Field.FieldName = 'CalcAmout') and (tbOrdCalcAmount.AsFloat < 0)) then
   dbgOrdReg.Canvas.Font.Color := clRed
dbgOrdReg.DefaultDrawDataCell(Rect,Field,State);

This is working at my place and if you make a if .. else if ... else if..
you can test every grid cell and change the colors.

[Rene Groothuis, steelcover@dataweb.nl]

A:
Well I've found the solution. It's like this:

In the dbgOrdRegDrawDataCell put the next lines.

if ((Field.FieldName = 'CalcAmout') and (tbOrdCalcAmount.AsFloat < 0)) then
   dbgOrdReg.Canvas.Font.Color := clRed
dbgOrdReg.DefaultDrawDataCell(Rect,Field,State);

This is working at my place and if you make a if .. else if ... else if..
you can test every grid cell and change the colors.

[Rene Groothuis, steelcover@dataweb.nl]

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

58. Shift Tab don't activate onexit event
Q:
I've the following problem: When an user presses Shift-Tab, in a edit-box,
I don't want the OnExit event to be activated, but how ?? Does anybody has a
solution for this problem.

A:
I think the problem occures because when the Tab key is pressed the
WM_KEYDOWN event is not sent to the control window. Is so it is easy 
to change the behaviour writing the subclass class of the component 
responding to WM_GETDLGCODE message:

TSubclass = class ( TSuperClass )
protected
  procedure WMGetDlgCode(var Msg : TMessage); message WM_GETDLGCODE;
end;

procedure TSubclass.WMGetDlgCode(var Msg : TMessage); 
begin
   inherited;
   Msg.Result := Msg.Result or DLGC_WANTTAB;
end;

Now Tab key is passed to the control. Do with it whatever you want on 
OnKeyDown event (or KeyDown method!).

[Krzysztof Hryniewiecki, kh@lodz.pdi.net]

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

59. StretchDraw example
Q:
I just need to load a small bitmap and do a Stretch draw to stretch the
graphic to the same size as the form.

A:
Perhaps you could use code something like this:
Form1.Canvas.StretchDraw(Rect(0,0, 50,50), Image1.Picture.Graphic);

The function Rect() will take two points (x,y, x,y) and turn them into
a var of type TRect. The method Form1.ClientRect, simply returns a
TRect structure that can be used by StretchDraw. (Check out the help 
file for more details on these)

Here's a modified version, to stretch the bitmap from Image2 onto 
Image1. (Try the code out, a blank form with 2 TImages and a TButton, 
on which you can attach the following code, should work well ... :)

Image1.Picture.Bitmap.Canvas.StretchDraw(Rect(0,0, Image2.Width, 
                                                Image2.Height), Image2.Picture.Graphic);

To get the effect, a neat idea would be to make a scale factor, in 
say, an edit box, ranging from 0.01 to 1. The code would then look 
something along these lines:

procdure Button1.Click(Sender etc, can't remember the rest ... :(
var
  x,y: Integer;
  s: Double;  {Scale / Magnification factor}
  r: TRect;
begin
  s := StrToFloat(Edit1.Text);
  x := Trunc(Image2.Width * s + 0.5);   {Scale and round off with 0.5}
  y := Trunc(Image2.Height * s + 0.5);
  r := Rect(0,0, x,y);
  Image1.Picture.Bitmap.Canvas.StretchDraw(r, Image2.Picture.Graphic);
end;

[Carl Mes, carl.mes@pixie.co.za]

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

60. Graph to clipboard
Q:
I'm having a problem trying to copy a graph to the clipboard.  I have the
following code:

      TGraph1.refresh;
      TGraph1.CopyBitmap := 0;

The problem is that the graph is distorted with a portion of another window
from a different program.  Is there a way to verify when the bitmap is
copied.  It appears not to do the actual copy until there's some kind of
screen change.

A:
According to the online help, the CopyBitMap is a method of Chart control. I've
never used it. I have a routine to copy a bitmap to clipboard though. Actually
it copies the whole contents of a Window to the clipboard, memos and all ! 

Var
  Image : TImage;
  BitMap : TBitmap;
Begin
  Image:=TImage.Create(Self);
  BitMap:=TBitMap.Create;
  BitMap.Width:=ClientWidth;
  BitMap.Height:=ClientHeight;
  BitBlt(BitMap.Canvas.Handle, 0, 0, ClientWidth, ClientHeight, GetDC(Handle),
         0, 0, SRCCOPY);
  Image.Picture.Graphic:=BitMap;

  Clipboard.Assign(Image.Picture);
  BitMap.Free;
  Image.Free
End;

No guarantees about efficiency, pictures, graphics, & bitmaps still confuse me,
but it works ! If you only want a bitmap from a window, you change the
GetDC(Handle) to Bitmap.Handle.

[Ray Cramer, RNC@Pol.ac.uk]

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

61. Icons loaded and converted for TBitBtn no white
Q:
I don't think this is the problem - as I said if I load it into an image, it
works - the transparent parts are - and the white parts are white - when I
put them on a button though, all white becomes transparent!

A:
If you load an icon into a TImage, it "understands" icons and displays
transparency info accordingly. Icons actually contain two bitmaps, one
containing the normal color info, with white areas to represent where
transparency should go; then a second "mask" bitmap that also has white for
the transparent areas, and black for the colored areas.  When the two are
"xored" together with the background, the white on white areas show the
background through, the color (including white) on black areas show the color
of the icon; (you can also get "inverted background" areas in an icon by
having areas that are black in the colored bitmap but are white on the mask).

When you convert the icon to a bitmap, the transparency info is lost
because bitmaps don't have the built-in capacity for storing this extra
mask bitmap that is used to make transparent parts. I'd guess the icons
you're using are generally "free standing" objects with transparent
backgrounds, that means that the color bitmap is surrounded in white.
So, when you load one into a TBitbutton or TSpeedbutton's glyph
property, the lowerleft corner color (which will be white in these
cases) now gets interpreted as transparent, giving the effect you
describe.

The solution is to convert the icon to a bitmap and save it as a .BMP
file, then edit it with ImageEdit, Resource Workshop, or even PaintBrush
and use an otherwise unused color as the lower left pixel and anywhere
else you WANT to be transparent.  Otherwise, I have some code that
extracts the color and mask bitmaps from an icon, you could then use
these to write out a bitmap that has the appropritate pixels set to an
unused color, though finding that unused color might be a bit tedious
programatically; hmmm, I'm smelling a utility here, I may have to get to
work on this.

[Stephen Posey, SLP@uno.edu]

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

62. TEdit and OnEnter event
Q:
I would like to write a TEdit component, where on the OnEnter event,
it will display a hint in a specified label.  My question is how do
override the OnEnter event in my component to do the extra bit that I
want.

A:
interface

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

type
  TNewComponent = class(TEdit)
  private
    { Private declarations }
  protected
    { Protected declarations }
    procedure DoEnter; OverRide;
  public
    { Public declarations }
    constructor Create(AOwner:  TComponent); OverRide;
    destructor Destroy; Override;
  published
    { Published declarations }
  end;

procedure Register;

implementation

procedure TNewComponent.DoEnter;
begin
  inherited DoEnter;
  <special code you want to do.>
end;


destructor TNewComponent.Destroy;
begin
  inherited destroy;
end;


constructor TNewComponent.Create(AOwner:  TComponent);
begin
  inherited Create(AOwner);

end;

procedure Register;
begin
  RegisterComponents('Samples', [TNewComponent]);
end;

end.

[David Naumowicz, denaumow@mail.coast.net]

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

63. How do I create a component like TField
Q:
I'd like to create component (subclass of TComponent) which will be
not visible on a form at design time just like subclasses of TField.
More precisely I want to have to non-window component types: first
called TListComponent and the second TListElement. Now I can write
the component editor for TListComponent and the editor can create
a plenty of TListElement components. The problem is:

  1. If I do not RegisterComponent for TListElement then creating
     it's instance at design time raises GPF.

  2. If I do RegisterComponent for TListElement then creating the
       instance of TListElement creates an icon placed on the form and
      I do not want it. I prefer to keep TListElement invisible at design time
      just like TTable does with TField.

A:
Have you considered not making it a component, but a class?  A class is
programmatic, not part of a form.  If you put the class in a unit (say
myclass.pas) and then in your program put "uses myclass;", then you can just
use it as a class, eg.

 type
    aninstance: tMyclass;
 begin
    new (aninstance);
        {equivalent to aninstance := tMyclass.create; }
    ...
      { use aninstance here }
    ...
    dispose (aninstance);
       { equivalent to aninstance.free; }
 end;

[Sid Gudes, cougar@roadrunner.com]

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

64. Create a Paradox table
Q:
How can i do to create a Paradox table with the following structure:

       TableName = Empresa

        Field        Type
        CODEMP       AUTOINCREMENT (Key)
        NomeEmp      CHARACTER(50)

A:
unit Autoinc;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, DBTables, DB, ExtCtrls, DBCtrls, Grids, DBGrids, StdCtrls,
  DbiTypes, DbiErrs, DBIProcs;
const
    szTblName = 'CR8PXTBL'; { Name of table to be created. }
    szTblType = szPARADOX;  { Table type to use. }

{ Field Descriptor used in creating a table }
const
    fldDes: array[0..1] of FLDDesc = (
              ( { Field 1 - AUTOINC }
               iFldNum:      1;            { Field Number }
               szName:       'AUTOINC';    { Field Name }
               iFldType:     fldINT32;     { Field Type }
               iSubType:     fldstAUTOINC; { Field Subtype }
               iUnits1:      0;            { Field Size }
               iUnits2:      0;            { Decimal places ( 0 ) }
               iOffset:      0;            { Offset in record ( 0 ) }
               iLen:         0;            { Length in Bytes  ( 0 ) }
               iNullOffset:  0;            { For Null Bits    ( 0 ) }
               efldvVchk:    fldvNOCHECKS; { Validiy checks   ( 0 ) }
               efldrRights:  fldrREADWRITE { Rights }
              ),
              ( { Field 2 - ALPHA }
               iFldNum:      2; szName:       'ALPHA';
               iFldType:     fldZSTRING; iSubType:     fldUNKNOWN;
               iUnits1:      10; iUnits2:      0;
               iOffset:      0; iLen:         0;
               iNullOffset:  0; efldvVchk:    fldvNOCHECKS;
               efldrRights:  fldrREADWRITE
              )    );

type
  TForm1 = class(TForm)
    Button1: TButton;
    Database1: TDatabase;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
Var
  TblDesc: CRTblDesc;
  uNumFields: Integer;
  Rslt : DbiResult;
  ErrorString : Array[0..dbiMaxMsgLen] of Char;
begin
   FillChar(TblDesc, sizeof(CRTblDesc), #0);
   lStrCpy(TblDesc.szTblName, szTblName);
   lStrCpy(TblDesc.szTblType, szTblType);
   uNumFields := trunc(sizeof(fldDes) / sizeof (fldDes[0]));
   TblDesc.iFldCount := uNumFields;
   TblDesc.pfldDesc := @fldDes;

   Rslt := DbiCreateTable(Database1.Handle, TRUE, TblDesc);
   If Rslt <> dbiErr_None then
   begin
     DbiGetErrorString(Rslt, ErrorString);
     MessageDlg(StrPas(ErrorString),mtWarning,[mbOk],0);
   end;

end;

end.

***************** EXAMPLE 2 **************************************
Here's a bit of code for creating paradox tables:

with TTable.create(self) do begin
     DatabaseName := 'C:\temp';
     TableName := 'FOO';
     TableType := ttParadox;
     with FieldDefs do Begin
          Add('Age', ftInteger, 0, True);
          Add('Name', ftString, 25, False);
          Add('Weight', ftFloat, 0, False);
     End;
     IndexDefs.Add('MainIndex','IntField', [ixPrimary, ixUnique]);
     CreateTable;
End;

[Monte Saager, kidvolt@teleport.com]

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

65. Difference between two dates
Q:
Anybody know where I can get hold of some functions for date manipulation?
I need something like the DateDiff() function in VB that returns the
number of months between dates.
Also, how about a function to calculate Present Value for a string of cash
flows?

A:
For DateDiff:

Have you looked at the DecodeDate function? It's not exactly the same, but
with it you may be able to get the result by creating your own custom function.

For Present Value

function PresentValue(const cashflows : array of double;    { the cash flows
in consecutive order nearest is at cashflows[0] }
          n : integer; { number of cash flows in array }
          rate : double; { the percent rate per period }
          atbegin : boolean) : double; { true if cash flow at beginning of
          period, false if at end }

var
     i         : integer;
     factor    : double;
begin
     factor := (1 + rate / 100.0);
     result := 0;
     for i := n - 1 downto 0 do
          result := (result + cashflows[n]) / factor;
     if atbegin then
          result := result * factor;
     end;

[George Blat, georgeb@brd.com]

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

66. How do you keep the user from resizing a form
Q:
We want to prevent the user from resizing a form vertically, but let them
resize it horizontally (like the Delphi main control form with all the buttons
and the menu on it). How do you do this?

A:
Well, one way to do this is to trap the WM_NCHITTEST and if it returns
HTBOTTOM, HTBOTTOMLEFT,  HTBOTTOM, HTTOP, HTOPLEFT or HTTOPRIGHT right then
eat it.  This will make windows completely ignore the Window resize if the
mouse is anywhere on the lower or upper border of the Window.  In fact,
windows will not even show the resizing border. Look up this message in the
Windows API.

[Jumpstile Turner, fortunat@interpath.com]

A:
You can intercept the message (See Message keyword in help) WM_GETMINMAXINFO. It
comes with a structure TMINMAXINFO. You can insert your own values in that
structure
and so limit the position or size that the user is changing.

Check the online help for more information.

[George Blat, georgeb@brd.com]

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

67. String handling
Q:
I am writing a couple of string functions to mimic those found in Basic,
specifically:Space$ and String$ functions. These functions fill a string with
a given number of Spaces or other character. Do these functions already exist
in Pascal? If not, what is a good, fast way to implement these functions.

A:
You can try


Function String(number : integer; ch : char) : string;
begin
     if number > 255 then
          number := 255;
     result[0] := chr(number);
     fillchar(result[1], number, ch);
     end;



Function Space(number : integer) : string;
begin
     if number > 255 then
          number := 255;
     result[0] := chr(number);
     fillchar(result[1], number, ' ');
     end;

[George Blat, georgeb@brd.com]

A:
FUNCTION String$(C : Char; Len : Byte) : String;
VAR Temp : String;
BEGIN
  FillChar(Temp[1], Len, C);
  Temp[0] := Char(Len);
  Result := Temp;
END;

FUNCTION AString$(C : Char; Len : Byte) : String; Assembler;
ASM
  LES DI, @Result
  CLD
  XOR CH, CH
  MOV CL, Len
  MOV AX, CX
  STOSB
  MOV AL, C
  REP STOSB
END;

[Cosimo Laddomada, mimmoladd@mail.clio.it]

A:
Probably the fastest thing to do without resorting to assembler is to
use FillChar(), but you have to be careful that you don't overwrite
anything that doesn't belong to you (lest you wish to incur the wrath of
the GPF gods), and to make sure that you keep track of all the "string
stuff"; with those caveats, something like the following ought to work:

function StringS( n : byte ; ch : char ) : string ;
var
  S : string ;
begin
  FillChar( S[1], n, ch ) ;
  (* having passed n as a byte protects us here from overwriting *)
  (* memory that doesn't belong to us                            *)

  S[0] := Chr( n ) ;         (* keep track of the string length *)

  StringS := S ;
end ;

function SpaceS( n : byte ) : string ;
begin
  SpaceS := StringS( n, ' ' ) ;
  (* of course you could simply recapitulate the StringS() code here *)
  (* replacing ch with ' ', which would save a function call         *)
end ;

[Stephen Posey, SLP@uno.edu]

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

68. Transparent Forms and Bitmaps
Q:
Anyone know how to make a form transparent? Also make a bitmap
background transparent in Delphi?

A:
Here's a nice routine to draw a transparent bitmap onto another.

{This procedure will draw a source bitmap onto a target bitmap,
 leaving information from the taget to shine through where the
 pixels in the source are of the specified transparent color.
 t   = The target canvas to be drawn onto
 x,y = The position on the target where source is drawn
 s   = The source bitmap
 TrCol = The color that will become transparent in the source bmp
 NOTE: Don't forget to repaint the target, eg Image1.Invalidate}

procedure DrawTransparent(t: TCanvas; x,y: Integer; s: TBitmap; TrCol: TColor);
var
  bmpXOR, bmpAND, bmpINVAND, bmpTarget: TBitmap;
  oldcol: Longint;
begin
  try
   bmpAND := TBitmap.Create; bmpAND.Width := s.Width; bmpAND.Height := s.Height; bmpAND.Monochrome := True;
   oldcol := SetBkColor(s.Canvas.Handle, ColorToRGB(TrCol));
   BitBlt(bmpAND.Canvas.Handle, 0,0,s.Width,s.Height, s.Canvas.Handle, 0,0, SRCCOPY);
   SetBkColor(s.Canvas.Handle, oldcol);

   bmpINVAND := TBitmap.Create; bmpINVAND.Width := s.Width; bmpINVAND.Height := s.Height; bmpINVAND.Monochrome := True;
   BitBlt(bmpINVAND.Canvas.Handle, 0,0,s.Width,s.Height, bmpAND.Canvas.Handle, 0,0, NOTSRCCOPY);

   bmpXOR := TBitmap.Create; bmpXOR.Width := s.Width; bmpXOR.Height := s.Height;
   BitBlt(bmpXOR.Canvas.Handle, 0,0,s.Width,s.Height, s.Canvas.Handle, 0,0, SRCCOPY);
   BitBlt(bmpXOR.Canvas.Handle, 0,0,s.Width,s.Height, bmpINVAND.Canvas.Handle, 0,0, SRCAND);

   bmpTarget := TBitmap.Create; bmpTarget.Width := s.Width; bmpTarget.Height := s.Height;
   BitBlt(bmpTarget.Canvas.Handle, 0,0,s.Width,s.Height, t.Handle, x,y, SRCCOPY);
   BitBlt(bmpTarget.Canvas.Handle, 0,0,s.Width,s.Height, bmpAND.Canvas.Handle, 0,0, SRCAND);
   BitBlt(bmpTarget.Canvas.Handle, 0,0,s.Width,s.Height, bmpXOR.Canvas.Handle, 0,0, SRCINVERT);
   BitBlt(t.Handle, x,y,s.Width,s.Height, bmpTarget.Canvas.Handle, 0,0, SRCCOPY);
  finally
   bmpXOR.Free;
   bmpAND.Free;
   bmpINVAND.Free;
   bmpTarget.Free;
  end;{End of TRY section}
end;

[Carl Mes, carl.mes@pixie.co.za]

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

69. Task ID
Q:
I start a program with winexec and it gives me an instance. I need the task
handle (at least I think so) so I can reactivate it later if it still
exists(rather than starting another instance) I looked at
taskfirst/tasknext, but they are not working - I must not be initing the
structure properly but I can't figure out what i'm doing wrong. HELP!!!
basically, I want to start a program... later I may need it again... if it
is still running  (ie the user has not closed it) then I would like to make
it active - if I have a task handle then I could:
PostMessage(HWND_BROADCAST,WM_ACTIVATEAPP,01,taskid);
right?

A:
First I did some tests in my machine. I arrived to the following litle program
which works perfectly well:

program CrtApp;

uses WinCrt, winprocs, wintypes, toolhelp, sysutils;

var
	rslt			: bool;
	taskentry	: ttaskentry;

begin
	taskentry.dwsize := sizeof(taskentry);
	rslt := taskfirst(@taskentry);
	while rslt do begin
		with taskentry do
			writeln(format('Task: %6x  Hinstance: %6x', [htask, hinst]));
		if taskentry.hnext = 0 then
			break;
		rslt := TaskNext(@taskentry);
		end;
end.


Based on that, I suggest you try the following.

uses winprocs, wintypes, toolhelp, sysutils;

...
{ for a given instance return the task. Return 0 if failure }

Function instancetotask(myinstance : thandle) : thandle;
var
     temp      : thandle;
     rslt	: bool;
     taskentry	: ttaskentry;

begin
        result := 0
	taskentry.dwsize := sizeof(taskentry);
	rslt := taskfirst(@taskentry);
        repeat
          if not rslt then
               exit;
          if taskentry.hinst = myinstance then begin
               result := taskentry.htask;
               exit;
               end;
          if taskentry.hnext = 0 then
               exit;
          rslt := TaskNext(@taskentry);
          until false;
     end;

[George Blat, georgeb@brd.com]

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

70. Accessing notebook pages
Q:
I want to add components at runtime to a page of a notebook.
How do I do it so that when I change the page the components disapear and
reapear.

A:
When you add the components at runtime, you need to set each component's
parent to the desired _page_ of the notebook, not to the notebook itself.

You can do this the following way (this example is for a Button):

    MyButton := TButton.Create( Form1 );  {as usual...}
    ...
    ...
    MyButton.Parent := TTabPage( TabbedNotebook1.Pages.Objects[n] );
      { <== where 'n' is an index into the desired page ==> }

The notebook's 'Pages' property is a StringList containing a list of
captions and 'TTabPage' objects.

I used this technique a few months back myself.  I can't remember now where
I picked this information up.  I can't find documentation for it at the
moment.  Maybe someone else knows where this is documented?

[Bernie Mondschein, mondschein.bernie@ehccgate.sandoz.com]

A:
To add a component to a TabbedNotebook page at run-time a pointer to the
desired page must be assigned to the new component's Parent property before it
can be shown. The way to access all the pages of a TTabbedNotebook at run-time
is with the Objects array property of the TabbedNotebook's Pages property.
In other words, the page components are stored as objects attached to the page
names in the Pages string list property. The follow demonstrates the creation
of a button on the second page of TabbedNotebook1:

   var
     NewButton : TButton;
   begin
     NewButton := TButton.Create(Self);
     NewButton.Parent := TWinControl(TabbedNotebook1.Pages.Objects[1])
     ...

   This is how a TNotebook page would be used as a parent to a newly
   created component on that page:

   NewButton.Parent := TWinControl(Notebook1.Pages.Objects[1])

   This is how a TTabSet tab page would be used as a parent to a
   newly created component on that tab page:

   NewButton.Parent := TWinControl(TabSet1.Tabs.Objects[1])

[Borland's Delphi FAQ]

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

71. Resizing (Dynamic) Arrays
Q:
Coming from a background of higher level programming languages. I have
not had a lot of experience with array's in C++ or Pascal. I was
wondering if someone could clue me in on dynamic array's in delphi.

A:
There are several techniques for doing this.  The techniques depend on
whether you have an array of strings or an array of numbers (integer, real,
etc.).

1) If you just want a dynamic one-dimensional array of strings, I suggest
you look at the tStringList component, it takes care of all the management
overhead and is easy to use.

2) If you want a dynamic multi-dimensional array of strings, you can use
tStringList also (as long as the total number of elements is less than the
maximum for tStringList, I believe 16,000).  To do this, you write a
linearizing map function as follows:

Assume you have a three-dimensional array of strings, and the current
dimensions are [12,80,7], and you want to find element [n,m,x].  Then you
can resolve this to an element in a one-dimensional array by using
((n-1)*80*7 + (m-1)*80 + x).  You would then use this as an index into a
tStringList.  To dynamically alter one of the array bounds, use the move
method of tStringList to shuffle things around.  (This involves some
embedded loops but should be pretty quick to execute because tStringList
doesn't move the strings, only pointers to the strings.)

3) If you want a dynamic one-dimensional array of numbers, here's a general
technique, there are many other details.  Declare a pointer to an array type
that has the maximum number of elements for that type (remembering that
Delphi-16 only allows a type to occupy up to 64K), eg.
    type
       bigArray: array[1..32000] of integer;  {or ^double, etc.}
       pMyArray: ^bigArray;

then allocate the array using
   getMem (pMyArray, sizeof(integer) * n);

where n is the number of elements.  Then you can refer to an array element
using, eg.
   pMyArray^[51]

Don't forget to free the array after you're done with it using FreeMem.

To resize the array, define a new pointer, reallocate, and swap, eg.
   pTemp: ^bigArray;

   getMem (pTemp, sizeof(integer) * newnumelements);
   memcopy (pTemp, pMyArray, sizeof(integer)*n);
      {n is number of elements in pMyArray}
   freeMem (pMyArray, sizeof(integer)*n);
   pMyArray := pTemp;

4) To use a multi-dimensional array of numbers, combine the technique in (3)
above with the mapping function in (2) above.

5) If you need more than 64K in your array, you'll need to develop a
cascading list of pointers to chunks of memory, which is beyond what I can
explain here.

[Sid Gudes, cougar@roadrunner.com]

A:
I would encapsualte in an object. I use what I call my "Basic String
Object" (BSO), which does dynamic allocation and deallocation for 
strings of any size. Internally it is a PChar pointing to allocated
memory. Externally I have two properties: AsString and AsPChar. I have
various properties and methods allowing various methods of accessing
and manipulating the string.

I wrote my own malloc() calloc() and realloc() using a static private
TString object to track the allocated pieces.  This has worked wonderfully
for any time I need to grab a chunk of memory.

With the two I can allocate memory as needed (in chunks so as not to
waste too much CPU time), and is disposed of (when a certain amount of
slack exists -- again, so as to not waste too much CPU time).

Another idea that I like has already been presented (the open-ended
array). If you need bounds checking and/or dynamic resizing, you may 
be forced to use a method similar to what I've done with the string object
above, and use a default array property to allow for easy access. This 
allows you to use indices and types of any kind.

TMyDynamicObject =
...
 PROPERTY Array[ idx :LONGINT ]:TMyType READ GetArray WRITE PutArray DEFAULT;
...

VAR Mine :TMyDynamicObject;
...
Mine := TMyDynamicObject.Create;
FOR i := 10 TO 20 DO Mine[i] := {whatever}

{MONSTER MEMORY WASTER - unless you get really crazy and use hash tables }
Mine[-100000] := {whatever} 
Mine[+100000] := {whatever}

If you have a sparsely-populated array, using a hash table might
be profitable. I convert index values to strings and let TStrings do
the work when I'm really lazy and don't particularly care about the
overhead to build the conversion to strings.

[James Knowles, jamesk@spillman.com]

A:
You can use TList (or TStringList.Objects) to store virtually whatever you
want!  TList.Items stores pointers to objects or records, but it doesn't do
anything with that pointer, so if you want you can typecast that to a longint, 
and not bother with the objects or records at all!  Here is an example of 
storing a list of integers in a TList:

var
  aList: TList;
  I : Integer;
  L : Longint;
begin
  aList := TList.Create;
  L := 93823;
  aList.Add(Pointer(L));
  aList.Add(Pointer(83293));
  for I := 1 to aList.Count do
    L := L + Longint(aList.Items[I-1]);
  aList.Free;
end;

You can have up to 16380 elements in a TList or TStringList.  Now here's an 
example of how to use a TList to store a record (or object):

type
  PMyRec = TMyRec;
  TMyRec = record
    Name: string[40];
    Addr : string[25];
    Comments: string;
    salary: Double;
  end;
var
  aList: TList;
  aRecPtr: PMyRec;
  I : Integer;
begin
  aList := TList.Create;
  New(aRecPtr);
  with aRecPtr^ do
  begin
    Name := 'Danno';
    Addr := 'unknown';
    Comments := 'What a guy!';
    Salary := 999000.00;
  end;
  aList.Add(aRecPtr);
  aList.Add(... );
  ...
  for I := 1 to aList.Count do
  begin
    aRecPtr := PMyRec(aList.Items[I-1]);
    {do something with the record}
  end;

{now dispose of all records, and the list object itself}
  for I := 1 to aList.Count do
    Dispose(PMyRec(aList.Items[I-1]));
  aList.Free;
end;

[Dan Butler, Dan_Butler@msn.com]

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

72. From Sizes on different platforms
Q:
I am trying to make my app work on screen sizes eg.  640x480 &
1024x768 I have tried changing the pixels per inch property but
it doesn't seem to work.

A:
The following should help make your forms look correct at different
resolutions:
a.) Set Autoscroll to' FALSE'. True means don't change the form's frame size
at run time.
b.) Set the font to a TrueType like Arial, don't leave it at the default
'SYSTEM''
c.) Set Position to something other than poDesigned, this will leave it where
you left it on your 1280x780 screen which maybe barely visible at 640x480.
d.) Change Pitch property of the font from DEFAULT to VARIABLE
e.) DON'T CHANGE THE PixelsPerInch PROPERTY.
f.) Set the Scaled Property to TRUE. This scales the forms to the value of
the PixelsPerInch property.

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

73. Which driver a TDatabase is connected to
Q:
I need to know if I'm connected to e.g. Oracle or Interbase so as to use
different sql syntaxes. Is there a way I can tell to which driver a
tdatabase is connected?
I show my user(s) a list of alias they can choose from and then assign the
alias name to the tdatabase...

A:
You could use the IDAPI dbiGetDatabaseDesc call. Here is a quick
snipit (be sure to add DB to your uses clause)

var
  pDatabase: DBDrsc:
begin

 { pAlias is a PChar holding the Alias Name}
  dbiGetDatabaseDesc ( pAlias, @pDatabase )  ;

Then just check pDatabase.szDbType to see what it is

[Mark Lussier, mlussier@best.com]

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

74. PChar from TMemoField
Q:
How can I get the text from a TMemoField as a PChar?

A:
Get size of data for PChar:

function TBWF.TMemoFieldSize(Memo: TMemoField): Word;
var
  BS: TBlobStreeam;
begin
  BS := TBlobStream.Create(TMemoField(Memo), bmRead);
  Result := BS.Size;
  BS.Free;
end;

Create PChar with Size + 1:

Assign Data from Field to PChar:

procedure TBWF.TMemoFieldToPChar(Memo: TMemoField; var Buffer: PChar; Size:
Word);
var
  BS: TBlobStream;
begin
  try begin
    BS := TBlobStream.Create(TMemoField(Memo), bmRead);
    FillChar(Buffer^, Size, #0);
    BS.Read(Buffer^, Size);
    end;
  finally
    BS.Free;
  end;
end;

[Brett Fleming, bfleming@vt.edu]

A:
function GrabMemoFieldAsPChar(TheField : TMemoField): PChar;
begin
GetMem(Result, TheField.Size + 1);
FillChar(Result^, TheField.Size + 1, #0);
with TBlobStream.Create(TheField, bmRead) do
  begin
    Read(Result^, TheField.Size);
    Free;
  end;
end;

[Ryan Peterson, rpetersn@usit.net]

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

75. Time problems
Q:
Just tried to use the 'typed constant Time24Hour' as shown in the
EncodeTime function. Can't seem to find this constant in SysUtils or
anywhere else.

A:
I found Time24Hour in the Help system, as you indicated. But...

here is the code for EncodeTime in SysUtils.Pas file:

function DoEncodeTime(Hour, Min, Sec, MSec: Word; var Time: TDateTime): Boolean;
begin
  Result := False;
  if (Hour < 24) and (Min < 60) and (Sec < 60) and (MSec < 1000) then
  begin
    Time := (LongMul(Hour * 60 + Min, 60000) + Sec * 1000 + MSec) / MSecsPerDay;
    Result := True;
  end;
end;

function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
begin
  if not DoEncodeTime(Hour, Min, Sec, MSec, Result) then
    ConvertError(LoadStr(STimeEncodeError));
end;

As you can see, any Time24Hour check is present. I looked it in the Browser
too. Nothing!

So, I thing is better conclude that Time24Hour is an old intention of
Borland-staff, aborted in Code but not in Help files. Does't you think so?

[Cosimo Laddomada, mimmoladd@mail.clio.it]

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

76. System colors
Q:
I need to emulate the color selection options of control panel... I looked
up the setsyscolor procedure, but though it seems to cause a global repaint,
it seems to have no effect... is it no longer supported? should I instead
change the win.ini (or is it system.ini - sorry - can't remember right now.)
If I change these values will programs update - or do they have to be
notified in some way?

A:
procedure TMainForm.Button4Click(Sender: TObject);
var
nColorIndex: array [1..2] of integer;
nColorValue: array [1..2] of longint;
begin
    nColorIndex[1]:=COLOR_ACTIVECAPTION;
    nColorIndex[2]:=COLOR_BTNFACE;
    nColorValue[1]:=clBlue;
    nColorValue[2]:=clRed;
    SetSysColors(2,nColorIndex,nColorValue);
    PostMessage(HWND_BROADCAST,WM_SYSCOLORCHANGE,0,0);
end;

[Nitsan Kovalsky, nitsanko@netvision.net.il]

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

77. Use Free with records
Q:
If I'm maintaining a TList full of pointers to records (NOT pointers to
objects), do I need to free the records it holds before freeing the TList
itself?  For example:

type
  PMyRecord: ^TMyRecord;
  TMyRecord = record
    MyString: string;
  end;

var
  MyRecord: PMyRecord;
  List: TList;
begin
  List := TList.Create;
  New(MyRecord);
  MyRecord^.MyString := 'Hi There';
  List.Add(MyRecord);
  MyList.Items[0].Free;  { <-- Do I Need This??? }
  List.Free;
end;

A:
You need to typecast the call to free with the right type, as follows:

var
     i    : integer;

begin
...
     for i := 0 to MyList.Count - 1 do
          dispose(PMyRecord(MyList[i]));
     MyList.Free;
end;

or

begin
     for i := 0 to MyList.Count - 1 do
          dispose(PMyRecord(MyList.items[i]));
     MyList.Free;

end;

Items is the default property, so you don't need to specify it, altough it's
ok if you do.

[George Blat, georgeb@brd.com]

A:
Don't think of it as a function so much as a reserved word.  In the form:

        var
                p : ^mystruct;
        begin
                new(p);
                ...
                dispose(p);
        end;

the new() and dispose() operate exactly like the getmem() and freemem()
procedures except that the compiler supplies the number of bytes as the size
of the structure pointed to by the pointer variable.  The pointer must be a
typed pointer for this reason, so the following isn't valid:

        var
                p : pointer;
        begin
                new(p);
        end;

because there is no set size for the memory the pointer will point to.  On
the other hand, if you use getmem() and freemem(), you can allocate bytes to
an untyped pointer, as in:

        var
                p : pointer;
        begin
                getmem( p, 32767 );
                ...
                freemem( p, 32767 );
        end;

[SDream@tgrigsby.com]

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

78. Change delete behavior in Memo
Q:
I need to change the behavior of the delete key in a memo
box.  The new behavior will stop the CR/LF from being deleted if delete is
pressed at the end of a line, or, backspace is pressed from the beginning of a
line. I want each character pressed to remain on the line it was intitially
input. There will always be 6 lines in the memo, and line 3 cannot be moved to
line 2. I still would like to be able to delete characters, just not the
CR/LF.

A:
Just change the Memo's OnKeyDown event handler to look like:-
  if Key = VK_DELETE then
  begin
    do whatever you want in here
  end;
  if Key = VK_BACK then
  begin
    do whatever
  end;
Probably better to use a CASE here but I'm not sure if CASE allows VK_?? in it.
May also need to include the Inherited for the keys you don't handle. Anyone
want to
clarify this ?
Also look up the SelStart to determine where you are in the line like so:-
  var
    Lpos, Cpos : Integer;
  Lpos := SendMessage(memo1.Handle,EM_LINEFROMCHAR,Memo1.SelStart,0);
  Cpos := SendMessage(memo1.Handle,EM_LINEINDEX,Lpos,0);
  CPos := Memo1.SelStart-CPos;

[Grant Cause, gcause@world.net]

A:
since VK_? stuff are integers this will work :

case Key of
  VK_DELETE :
    begin
      Key := 0;  {this stops the keydown message from going any
                         farther, for ex. the form and its components}
      stuff to do;
    end;
  VK_BACK:
    begin
      Key := 0;  {this stops the keydown message from going any
                         farther, for ex. the form and its components}
      stuff to do;
    end;
  end;

[Ryan Peterson, rpetersn@use.usit.net]

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

79. Fast way to Clear a TCanvas
Q:
Is there a TCanvas.Clear, or, what is the best way to clear a TCanvas?

A:
InValidateRect(Canvas.handle,NIL,True);

[caracena@henge.com]

A:
If you are using a Form's canvas try
InValidateRect(form1.handle,NIL,True);
instead.
(or the components handle)

[caracena@henge.com]

A:
This will clear a canvas:
 canvas.fillrect(canvas.cliprect) ;

[Chuck Baggett, unforget@blue.misnet.com]

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