                        - November '95 Issue -

Delphi-Talk: delphi-talk@bridge.net the very best of delphi-talk.
Summary November 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!




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



                                    - NEWS -

                                    DELPHI 32

Here are some information about Delphi32:

---> From Tris Hopkins:
This is not all-inclusive, but this is what I remember from the Borland
Developer's Conference back in August:

- Yes, the compiler now reports all errors instead of stopping on the first one.
- The compiler also has greatly improved code generation - meaning: your
  applications when recompiled in Delhi32 will be faster and smaller.
  As a matter of fact, the Delphi team said that they went back and rewrote
  some of the library routines in pascal as opposed to assembler because the
  compiler actually generated more efficient code than they could write!
- WARNING: The standard pascal type 'String' will change in Delhi32:
           - The statement SomeString[0] will no longer be valid!!
             nor will the famous coding trick:
             function Trim(s : String) : String;
             var
               slen : byte absolute s;   { <-- not valid in Delphi32! }
             begin
                  ... ...
             end;
           work.
- There is now a (theoretical) limit of 4 billion characters per string.
  This is because the length is now determined by a LongInt at the
  front of the string, not a single byte.  As a matter of fact, there is a
  whole structure stored before the first byte of the string, but I can't
  remember all the details.
- The compiler also now has 'hints', for example:
  Hint: variable tmpStr is declared but never used in procedure Trim.
  The demo at the BDC was pretty amazing as far as how smart the compiler
  was when it came to showing hints.  You will love it!
- Blazing speed (As if Delphi 16 was all that slow anyway!)
- And (obviously) it generates 32-bit native app's for Win '95 and Win NT.
There are many other features and improvements related to OLE 2.0, the IDE,
the VCL and the compiler itself, but I can't remember everything right off hand.

---> From Dan Butler:
There is a good article in the Delphi Informant, October issue, which is a first
look at Delphi32.  Here is a short list of new features (not all-inclusive), as
found in the article:

* 32-bit optimizing compiler
* Database engine improvements
* OCX support
* Multithreading support
* New data types (long string, variant, and WideChar
* Ease in porting 16-bit code to 32-bit environment
* OLE Automation
* Win95 common controls
* Closer integration with C++
* Enhanced IDE (debugger)

In addition, some highlights of the article:

* No more 64k barrier (as a result of using 32-bit flat memory model, as all
  32-bit apps do)
* strings no longer limited to 255 characters, new compiler directive $H used to
  determine whether to use 'huge strings' or the old max-255-char-strings
* variant type included for OLE support
* faster performance:  code executes 300 to 400 percent faster
* ability to create and use .OBJ files--integration with C++
* BDE 3.0, which is 32-bit


                         DELPHI KNOWLEDGE BASE

Here is another information about best of questions and answers:
You can also get all questions and answers as a paradox table. The archive
named DDBQA.ZIP is avalailable at my homepage. Included in this package is
a simple database reader and a Paradox 5.0 for Windows form. The database
will be updated once a month after the final update of dtxxxx files.

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

 Contents

  1. Getting large text from TClipboard
  2. TQuery and TDBGrid
  3. Testing for the existence of a Component Property
  4. Is a Bit set?
  5. Get line from TMemo
  6. Floating point trouble in DLLs
  7. Set event-handler at run-time
  8. Previous Instance
  9. Descanding indexes
 10. Cancel on related tables
 11. How to detect if running in Delphi IDE
 12. EnumChildWindows - ExampleCode
 13. Difference between Smart Callbacks and Dumb Callbacks
 14. Is menu open
 15. FindWindow
 16. Heaping objects
 17. Application Events
 18. Sizing MDI child windows
 19. Inherited free
 20. Long file names from Win95 DirListbox
 21. ReleaseDC and TCanvas
 22. How to use the ItemAtPos to get DirListBox item
 23. From database to variables
 24. Minimize a non-delphi window from a delphi program
 25. Number of current week
 26. Application.Title font color
 27. WinAPI functions
 28. TMessage vs TMsg
 29. Button does not respond to enter key
 30. DBEdit and real values
 31. Form width
 32. Duplicating a set of records
 33. DBGrid MoveToNextField
 34. Creating a table in a unit
 35. Bmp picture printing with Printer.Canvas
 36. Using TStringList in a visual component
 37. Make index files in delphi
 38. Fixed field data input
 39. Positioning self-created forms
 40. #0 KeyPress
 41. Exceptions
 42. Simulating key presses
 43. Key presses / edit position
 44. Bmp printing problem on a Hp Laserjet 4+
 45. OLE Server
 46. Number of maximal Components in .DCR
 47. VER.DLL functions
 48. StringGrid Masks
 49. StringGrid right alignment
 50. Avoiding close
 51. Looping AVIs
 52. TOutLine drag and drop
 53. Changing the dir from an Alias at run time
 54. Speaker Bleep
 55. Finding total global variable size
 56. Opening file for read only
 57. Application to tile on windows desktop
 58. Assigning a SQL count to a variable
 59. Procedure to wrap dialog box with an object
 60. TGraphicComponent child
 61. Attach procedures to components created
 62. AppendStr and ConCat
 63. File of type TList
 64. Passing strings in the lParam part of a message
 65. Linking functions
 66. FileName property in non-visual component
 67. Running without a form
 68. Calling functions from different MDIChilds
 69. Changing a forms icon at runtime
 70. Need help on overriding Sys Err
 71. Transparent splash screen
 72. Query gauge bar
 73. Screen resolution
 74. Difference of TMemoFields
 75. Drag and drop
 76. Strings and messages
 77. DDE- passing text
 78. Load a BMP in a EXE file
 79. DLL's and Memory Management
 80. How to call a function with a TFarProc
 81. DLL: killing utility
 82. Pre-setting TDBLookupCombo boxes
 83. Variable length record and phrase search
 84. Commenting out large sections of code
 85. Tables in memory
 86. Put a variable in a memo field
 87. Printing bitmaps
 88. Simulating a pause in a loop
 89. Duplicate resource identifier error
 90. Table locking
 91. Callback functions
 92. Change wallpaper bitmap
 93. Using integer pointers
 94. Loading a custom cursor
 95. Type missmatch (PChar <> String)
 96. Trapping database exceptions
 97. Copying from a TEdit to an integer field
 98. Vexing printing problems
 99. Changing the HP Laserjet display
100. Trapping a MDI Child Event
101. SendMessage and TLabel
102. Error creating cursor handle
103. WinHelp macros
104. 2% resources, in design mode
105. Call the Hint method directly
106. SQL date field
107. Closing MDI childs
108. Refreshing database data
109. Using menu options to cut, copy, and paste
110. Reading long strings from a file
111. Appending in TMemo, don't start a new line
112. Changing in TOpenDialog
113. Disk full error

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

1. Getting large text from TClipboard
Q:
How get a large text from TClipboard?

A:
var
  Buffer: PChar;
  MyHandle : THandle;
 TextLength : Integer;
begin
MyHandle := Clipboard.GetAsHandle(CF_TEXT);
Buffer := GlobalLock(MyHandle);
If Buffer = Nil then
 begin
  GlobalUnlock(MyHandle);
  exit;
 end;

TextLength := StrLen(buffer);  {since buffer is of type PChar,
   all the functions applicable to PChar can be applied}

[Sajan Thomas, THOMAS@MSOE.EDU]

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

2. TQuery and TDBGrid
Q:
1. How do I index the list generated by TQuery ?
2. How do I create an OnClick Event in a TDBGrid ?

A:
1) After your where clause place an order by clause
   Select fname, lname, title
   from T_EMPLOYEE
   where title = 'MGR'
   order by lname, fname


2) Try the ColEnter event.

[Daniel Adeniji, 0006513351@mcimail.com]

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

3. Testing for the existence of a Component Property
Q:
I would like to iterate through all my components on a form, at form
activation time, and if a particular component has a Font property, I would
like to set the Font.Pitch value at that time. In general, how do I test to
see if a component has a particular property?

A:
{you'll probably want to replace the "is TButton.." with some kind of set
that you define on your own.. then you just ask.. if Components[i] in myset
then}

{gee i dont know ... i just looked up TFont and it applies to 40 some
different objects, and everything that has a TFont also has a TPitch ...
there must be an easier way}

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to ComponentCount -1 do
     if Components[i] is TButton then
       TButton(Components[I]).Font.Pitch :=fpFixed ;

end;

[Geeky0010@aol.com]

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

4. Is a Bit set?
Q:
Is a Bit set?
What is the most efficient way to see if a bit is set in an integer?
My main problem is finding out if the File Dialog box Readonly flag is set.

A:
AND is probably the most efficient.

   IF (filelistbox1.filetype AND ftreadonly)<>0 THEN read_only_flag_is_set;

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

A:
The expression after the 'IF' will always resolve to a boolean.
'A AND B' yields a boolean result (any non-zero value is 'TRUE') - so does '(A
AND B) <> 0'.  However in certain instances with range checking on I have had
to use a boolean() typecast.

[Nigel Campbell, ncampbel@ccmail.dsccc.com]

A:
  IF (ftReadonly in filelistbox1.filetype) THEN read_only_flag_is_set;

According to the Object Pascal Language Guide, "Small set operations...are
generated inline using AND, OR, NOT, and TEST machine code instructions", so
chances are the code generated will be as efficient as the AND.

[Eric Nielsen, htrsoft@midwest.net]

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

5. Get line from TMemo
Q:
How do I know which line the cursor is on in a TMemo?

A:
You can use the Windows API messages EM_LINEFROMCHAR and
EM_LINEINDEX to determine the current line and offset within that
line (starting from SelStart).

   var
     LineNum: longint;
     CharsBeforeLine: longint;
   begin
     LineNum := SendMessage(Memo1.Handle, EM_LINEFROMCHAR, Memo1.SelStart,0);
     CharsBeforeLine := SendMessage(Memo1.Handle, EM_LINEINDEX, LineNum, 0);
     Label1.Caption := 'Line ' + IntToStr(LineNum +1)
     Lebel2.Caption := 'Position ' + IntToStr((Memo1.SelStart -
CharsBeforeLine) + 1);
   end;

[DELPHI.FAQ]

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

6. Floating point trouble in DLLs
Q:
Got problem using Floating point numbers in DLLs.
I wrote a DLL using C++. The function in C++ needed a float variable.
I send the Single variable in Delphi to the DLL Function.
I got a GPF error stating Invalid Opcode. Anyone can help?

A:
If you wrote both the DLL and the Delphi side, do not return floating point
values as the function return value. Instead, use a var parameter (a pointer or
reference parameter in C++)
to return values.

I am assuming that your DLL is compiled by M$ VC++. The reason
is that Borland and M$ use different ways of returning a floating point value.
Borland's C++ and Delphi might use the same way (in the Math Coprocessor stack)
but I'm not sure.

So, if you work with procedures instead of functions you should be OK.

BTW, do not use single or float precision. They may be changed by the
compiler. Use doubles.

[George Blat, georgeb@brd.com]

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

7. Set event-handler at run-time
Q:
I need to set the onTimer property of a TTimer-component at run-time.
Lets say I have a procedure called ThisOne and I , want it to be the
eventhandler - how is that done?

A:
It's pretty straightforward: you define the procedure (say ThisOne) that
takes the same parameter list as onTimer, then when you want it to be
attached to the timer you say:

   Timer1.onTimer := ThisOne;

To turn off the event, use:

   Timer1.onTimer := nil;

The procedure (ThisOne) does not need to be in the same class as the timer,
but it _must_ take the same parameters in the same order or you risk things
going off into la-la land.

[Sid Gudes, cougar@roadrunner.com]

A:
The way I'd do it is to put the component on the form and create the
appropriate event. (this gets better - honest). Then copy the procedure line
of the event, rename it and put it in the public section. This ensures that
the event has the right parameters - with some of the longer event lines,
typing the correct definition can be a real hassle.

Remove the component from the form if you don't want it. Put the code you
want called in the new event handler you've defined. When you create the
object put the address of the code into the ONevent handler of the object.

I think the appropriate line of code is:

Timer1.ontimerevent:=formdynamictimerevent;

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

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

8. Previous Instance
Q:
I have a program that is minimized. When I execute it again, it does not
run again, but, the first copy remains minimized.

A:
In your main form:
procedure TForm1.AppOnActivate(Sender: TObject);
begin

 Self.WindowState := wsNormal;
 {Self.Minimize1.Caption := 'Mi&nimize';
 }
 end;
procedure TForm1.FormActivate(Sender: TObject);
begin
    Application.OnActivate := AppOnActivate;

end;

In Your main unit:
begin
  { If there's another instance already running, activate that one and
    terminate this one }
  if HPrevInst <> 0 then begin
    LoadPreviousInstance;
    Exit;
  End;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Then here is where the work is done - I know it is alot of code but 
it will do the trick (the problem is that you have to activate the 
application and then restore the form)

unit PrevInst;

interface

uses WinProcs, WinTypes, SysUtils;

type
  PHWnd = ^HWnd;

{EnumFunc must be  exported. It  is used as the callback function for
the API call to EnumWindows}

function EnumFunc(Wnd : HWnd; TargetWindow : PHWnd): Bool; export;
procedure LoadPreviousInstance;

implementation

function EnumFunc(Wnd : HWnd; TargetWindow : PHWnd): Bool;
{Callback function for EnumWindows. EnumWindows passes the handle 
(Wnd) of each open parent window (not children). }
var
  ClassName : array [0..30] of char;
begin
  Result := True;
  if GetWindowWord(Wnd,GWW_HINSTANCE) = HPrevInst then begin
    GetClassName(Wnd,ClassName,30);
    if StrIComp(ClassName,'TApplication') = 0 then begin

      {It is the window we want - assign it to TargetWindow}

      TargetWindow^ := Wnd;
      Result := False;     {Stop enumeration - see EnumWindowsProc
                            in the API help}
    end;
  end;
end;

procedure LoadPreviousInstance;
var
  PrevInstWnd : HWnd;
begin
  PrevInstWnd := 0;
  {Pass each open parent window's handle in turn to EnumFunc}
  EnumWindows(@EnumFunc,Longint(@PrevInstWnd));
  if PrevInstWnd <> 0 then        {We have a handle to the previous
                                   instance}
      BringWindowToTop(PrevInstWnd); End;

end.

[Sean Gates, sgates@goofy.iafrica.com]

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

9. Descanding indexes
Q:
I can't figure out how to create a descending secondary index in Paradox!

A:
I just found a way of getting a descending index. It's pretty easy as
anything else
in Delphi.


     Table1.AddIndex('NewIndex', 'CustNo;CustName', [ixDescending]);

[George Blat, georgeb@brd.com]

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

10. Cancel on related tables
Q:
I have a data base app. in it there is a cancel Btn. I try to do this:
when the user press this button, the changes on related table will be
canceled. The code is somehow like this :

procedure TForm1.CancelSpdBtnClick(Sender: TObject);
begin
  ExTable.Cancel;
  ExTable.Refresh;
  PostSpdBtn.Enabled := false;
  CancelSpdBtn.Enabled := false;
  EditSpdBtn.Enabled := true;
end;

But to my surprising, after some modification has been done, I can not
cancel the changes, even I press this button. All of the changes have been
written into database no matter cancel or not.

A:
In 'Delphi unleashed' book pp 520, the author wrote:

   '...,you can still undo your work at any time, as long as you have
not either directly or indirectly posted. '
                       ^^^^^^^^^^

My problem was caused by assigning ExTable.Edit twince in diffrent
procedures. The codes somehow like this...

 Procedure1 ....
 begin

    ExTable.Edit ;
    ExTable.FieldByName('...').AsString := ...;
    ...

 end;


 procedure2 .....
 begin

    ExTable.Edit;
    .....
 end;


After these 2 procedures were called,  the CancelSpdBtnClick procedure
was called.  Actually, before  cancelling was done, posting was
indirectly called between 2 ExTable.Edit statements were executed.
^^^^^^^^^^        ^^^^^^^
Now I made the modification, the program works fine.

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

11. How to detect if running in Delphi IDE
Q:
How to detect if your program running in Delphi IDE or running as a standalone
EXE?

A:
 function DelphiLoaded : boolean;
{----------------------------------------------------------------}
{ Checks whether Delphi is loaded.  Gives correct result whether }
{ or not Delphi is minimized and whether or not Delphi has a     }
{ project open.  Also gives correct result whether the calling   }
{ application is launched free-standing or from within the IDE.  }
{ From ideas in a posting to the Delphi-Talk List by Wade Tatman }
{ (wtatman@onramp.net).       Mike O'Hanlon, The Pascal Factory. }
{----------------------------------------------------------------}

   function WindowExists(ClassName, WindowName: string): boolean;
   {------------------------------------------------------}
   { Checks for the existence of the specified Window,    }
   { conveniently using Pascal strings instead of PChars. }
   {------------------------------------------------------}
    var
     PClassName, PWindowName: PChar;
     AClassName, AWindowName: array[0..63] of char;
   begin
    if ClassName = ''
     then PClassName := nil
     else PClassName := StrPCopy(@AClassName[0], ClassName);
    if WindowName = ''
     then PWindowName := nil
     else PWindowName := StrPCopy(@AWindowName[0], WindowName);
    if FindWindow(PClassName, PWindowName) <> 0
     then WindowExists := true
     else WindowExists := false;
   end;  {WindowExists}

 begin {DelphiLoaded}
  DelphiLoaded := false;
  if WindowExists('TPropertyInspector', 'Object Inspector') then
    if WindowExists('TMenuBuilder',     'Menu Designer') then
      if WindowExists('TApplication',   'Delphi')    then
        if WindowExists('TAlignPalette','Align') then
          if WindowExists('TAppBuilder','')  then
            DelphiLoaded := true;
 end; {DelphiLoaded}

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

A:
 The following routine will return TRUE when running in the Delphi IDE
(NOTE:  this does _not_ work if this routine is in a DLL).

function InIDE: Boolean;
begin
  Result := Bool(PrefixSeg) and
            Bool(PWordArray(MemL[DSeg:36])^[8]));
end;  { InIDE }

[Ed Salgado, easalgad@ICSI.Net]

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

12. EnumChildWindows - ExampleCode
Q:
EnumChildWindows - ExampleCode?

A:
I suggest you start with a hWnd of 0, meaning the screen. You can recurse from
there.

{$K+,F+}     { IMPORTANT use smartcallbacks }

function EnumChilProc(hwnd: thandle; lparam: longint) : wordbool; export;
{ IMPORTANT use export }
begin
{ you are called here with hwnd = the child window
     do what you want, store it in a tlist or array or whatever
     lparam is an arbitrary parameter that you pass from EnumChildWindows
     you may cast it to a tlist if you want
}
     result := true;  { return false if you want to abort the enumeration }
     end;

{$F-}

Function myenum(parentwnd : thandle);
begin
     enumchildwindows(parentwnd, @EnumChildProc, 0);   {you may replace the
last 0 with any value that you want to send to lparam in the EnumChildProc}
end;

[George Blat, georgeb@brd.com]

A:
This example sets the font of all child windows, but you can adapt this a lot of
different ways. I'm pretty sure your callback function cannot be a method in an
object (except possibly by making it a class method). The lParam argument is
user-definable.  Notice that I used MakeLong to make a longint out of two words.
It is not mentioned in the Help, but is part of WinProcs. The following must
be compiled with $K+ (Smart Callbacks), which is global to each UNIT (at least
it was in BPW). If you don't want to use smart callbacks, you must declare a
variable of type TFarProc, and use MakeProcInstance and FreeProcInstance. See
second example. There are certain cases in which Smart callbacks are not
allowed, but EnumChildWindows is NOT one of them.

function SetChildWinFont(aWnd: hWnd; lParam: longint): Bool; export;
begin
  SendMessage(aWnd, wm_SetFont, LoWord(lParam), HiWord(lParam));
  SetChildWinFont := true;
end;

{$K+}

begin
  EnumChildWindows(HWindow, TFarProc(@SetChildWinFont), MakeLong(aFont, 1));
end;

{$K-} or {$K+}

var
  MyProc: TFarProc;

begin
  MyProc := MakeProcInstance(@EnumChildWindows, hInstance);
  EnumChildWindows(HWindow, MyProc, MakeLong(aFont, 1));
  FreeProcInstance(MyProc);
end;

[Dan Butler, dbutler@pscorp.com]

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

13. Difference between Smart Callbacks and Dumb Callbacks
A:
A Smart Callback is one in which you do not need to use MakeProcInstance.
MicroSoft docs generally all say that you have to use
MakeProcInstance/FreeProcInstance whenever you use a callback function of any
kind.  A dumb callback (first time I've heard the term used), is the opposite,
i.e. you have to use MakeProcInstance and FreeProcInstance.

Borland tools are 'smart' and can get around that in many instances through the
use of Smart Callbacks.  That is all you really need to know to use them.  So
far as what's really going on, as I understand it, MakeProcInstance does a type
of thunking.  "What's thunking?", you may ask.  Kind of hard to explain, but
basically it places a layer of code between two modules in which one needs to
call the other, to 'translate' things.  In this case, I think it mostly has to
do with properly setting the DS and SS registers upon entering and leaving the
callback function.

[David Ashamalla, David_Ashamalla@OasysInc.Com]

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

14. Is menu open
Q:
Is there a (preferably non-Delphi specific) way to tell if the menu as a
whole is currently open/selected?

A:
{....}
type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    item01: TMenuItem;
    item11: TMenuItem;
    Item21: TMenuItem;
  private
    { Private declarations }
  public
	{----- HERE ------}
        PROCEDURE WMMENUSELECT(VAR M: TWMMENUSELECT);MESSAGE WM_MENUSELECT;
  end;

{...}

PROCEDURE Tform1.WMMENUSELECT(VAR M: TWMMENUSELECT);
begin
     inherited;
	{This beeps even if it is the sysmenu (control menu) and/or on any 	
selected item: }
     {   messagebeep(MB_ICONASTERISk);  }
     
     {This beeps when mainmenu1 is opened, but only beeps on item[0]}
          if M.menu=Mainmenu1.handle then
                  messagebeep(MB_ICONASTERISk);

end;

end.

[caracena@henge.com]

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

15. FindWindow
Q:
I need to pass 0 Long as one of the parameters in FindWindow.

A:
In Delphi, 0 can be a byte, integer, word, longint, real, double.  But in this
case FindWindow is wanting a PChar type, which is essentially a pointer to an
array of char.  Nil should work just fine; it works for me.  For the second
parameter, I noticed that you're using double-quotes ["].  That is incorrect
syntax.  In Delphi, strings are denoted by single-quotes ['].  For instance,
try:

function FindaWin: hWnd;
begin
  Result := FindWindow(nil, 'Title');
end;

By the way, a PChar is not compatible with a pascal string, but in the above
example, the compiler accepts the literal 'Title' as a PChar just fine.  If you
wanted to write a function that would accept a pascal string and use it to call
FindWindow, you could do this:

function FindaWin(aName: string): hWnd;
var
  aStr: array[0..255] of char;
begin
  Result := FindWindow(nil, StrPCopy(aStr, aName));
end;

[Dan Butler, dbutler@pscorp.com]

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

16. Heaping objects
Q:
I have allocated objects on the heap, created pointer to them, but I cannot
call any of their methods.
My Object is TAnimal, here is my syntax:
ptrAnimal^.Run;
This does not seem to work, although the same syntax works for setting
properties. What am I doing wrong?

A:
If you created this class your self and inherited it from someone else  you
should try the following:
If you want to access the method, say Click, you'll have to assign the
procedure to yours object's event click, that is:
ptrAnimal^.OnClick:= OnMyClick;
This is the procedure wich gets the event, looking something like this:
procedure [FILE].OnMyClick(SENDER: TObject);
BEGIN
  (SENDER as TAnimal).YourProperty:= Value;
END;

[Jn Ptursson, kolbeinn@ismennt.is]

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

17. Application events
Q:
I am trying to allow my program to accept file manager drops...I have it
working OK with The Main window opened, but not minimised. The Accept files
works OK, but I am not sure how to get the WM_Drop message. I reallize it must
be somewhere in the OnMessage handler (I know how to create event Handlers for
Application events), but, in the OnMessage handler, how do I get the message?
Msg is NOT one of the parameters.

A:
The problem here is that when your Delphi application is minimized, it is a
different window handle than at other times. The Application object actually
has its own window handle! Application.Handle is the window that is active when
your app is minimized. When you minimize the app, all the forms are actually
just hidden. Notice the Application methods Minimize and Restore. Also notice
that there are two undocumented events in TApplication, OnMinimize and
OnRestore. These are there because there is no event handler in TForm that will
fire when you minimize the main window. Kind of bizarre. I think it is done
this way to support SDI applications.

[Dan Butler, dbutler@pscorp.com]

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

18. Sizing MDI child windows
Q:
We are using MDI for what is really a single document application. In this
application we simply present the user with different views of the data.
My question is once I create my MDI child windows, I would like to have them be
a certain default size. I can achieve this by sending a form resize message and
trapping that in each window and forcing it to the desired size.
Is this the best way to achieve this?

A:
If you want to set the size at design time, then just set the Position property
of the MDI form to poDefaultPosOnly, and it will always be created with the size
you used at design time.  If you want to let the user set the default, my
suggestion would be to add an option to the system menu (I use a component
called acMenus for this) to let the user save the current size to an INI file
setting (or two, I use one variable that is comma-delimited).  Then in the
FormCreate method, I read the INI file and set Height and Width appropriately.
No need for sending messages after the form is created.

[Dan Butler, dbutler@pscorp.com]

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

19. Inherited free
Q:
If I derive a class from TObject and I define my own constructor and destructor
IE:

    MyClass = class(TObject)
      public
         constructor Create;
         destructor  Free;
    end;

Should the code in either my constructor or destructor call the inherited
create and free.  IE:

   destructor MyClass.Free;
   begin
      ... free my stuff ...

     { Call low-level free ???? }
      inherited Free;
   end;

A:
When deriving from TObject, you should have a Create and Destroy, not Create and
Free. Free is NOT a virtual method, and therefore cannot be overridden. Free
simply calls Destroy if the object is actually in existance. And yes, you
should *always* call the inherited Create and Destroy methods in your descendant
classes. So to clarify, use:

MyClass = class(TObject)
  public
    constructor Create; override;
    destructor  Destroy; override;
end;

constructor MyClass.Create;
begin
  inherited Create;
{do initialization}
end;

destructor MyClass.Destroy;
begin
{free up resources}
  inherited Destroy;
end;

[Dan Butler, dbutler@pscorp.com]

A:
Yes, you should call the inherited Create method.
Free is a different story. You should not Override the free method, you should
override the destroy method, and also call the inherited destroy method. The
reson for this is the Free method automatically calls the Destroy method, IF
AND ONLY IF THE OBJECT IS NOT NIL.  Someone told me the Free method is some
kind of black-magic assembler function.

[David Ashamalla, David_Ashamalla@OasysInc.Com]

A:
You might want to note that you can't use the 'override' keyword with the
'constructor Create' or the 'destructor Free' as indicated below - or so
the compiler says when I attempt to do so. You can however use the
'virtual' keyword.  In fact you can use the 'virtual' keyword with both
'Create' and 'Free'.
The only hokey thing about using 'destructor Free' is that when your
procedure executes it automatically calls 'Destroy' on exit - so don't
explicitly call 'Destroy' when using 'destructor Free'. Maybe that is a
function of the 'destructor' keyword?

[Nigel Campbell, ncampbel@ccmail.dsccc.com]

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

20. Long file names from Win95 DirListbox
Q:
How do I get the long path string from a TDirectoryListbox in Win95 instead
of the 'hybrid' string that includes a tilde (~) character and a number?

A:
Delphi 16 in principle doesn't deal with longfilenames. But you can do it
yourself.
If you are familiar with assembly language get the M$ book "Programmer's
Guide to Microsoft Windows 95". Look in page 520 for the new DOS function
Find First File (with long filenames). It returns a  WIN32_Find_Data record with
the long and short filenames.
From what I see you can pass either one and you get back both. You have to
code it as a simple
assembler routine into your code.

It's too much to give you an example here, but if you get the book you should be
on your way. Please keep in mind that you send and receive ASCIIZ strings,
not pascal strings.

[George Blat, georgeb@brd.com]

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

21. ReleaseDC and TCanvas
Q:
I have a canvas that I create, and set the Handle using GetWIndowDC.
I do explicitly destroy the Canvas, is is automatically calling RealeseDC?
Is this a safe method of doing this?

A:
TCanvas will not automatically call ReleaseDC. To create a canvas with
a WindowDC as its handle, probably the best idea would be to create a
descendant of TCanvas (modeled on TControlCanvas):

  type
    TWindowCanvas =3D class(TCanvas)
    private
      FWinControl: TWinControl;
      FDeviceContext: HDC;
      procedure SetWinControl(AControl: TWinControl);
    protected
      procedure CreateHandle; override;
    public
      destructor Destroy; override;
      procedure FreeHandle;
      property WinControl: TWinControl read FWinControl write =
SetWinControl;
    end;

  implementation

  destructor TWindowCanvas.Destroy;
  begin
    FreeHandle;
    inherited Destroy;
  end;

  procedure TWindowCanvas.CreateHandle;
  begin
    if FControl =3D nil then inherited CreateHandle else
    begin
      if FDeviceContext =3D 0 then
        FDeviceContext :=3D GetWindowDC(WinControl.Handle);
      Handle :=3D FDeviceContext;
    end;
  end;

  procedure TControlCanvas.FreeHandle;
  begin
    if FDeviceContext <> 0 then
    begin
      Handle :=3D 0;
      ReleaseDC(WinControl.Handle, FDeviceContext);
      FDeviceContext :=3D 0;
    end;
  end;

  procedure TControlCanvas.SetWinControl(AControl: TWinControl);
  begin
    if FWinControl <> AControl then
    begin
      FreeHandle;
      FWinControl :=3D AControl;
    end;
  end;

Obviously, you should be careful to destroy the TWindowCanvas (or free
its handle) before destroying the control associated with it. Also,
note that the DeviceContext handle will not be released automatically
after completing the processing of each message (as happens with the
handles of TControlCanvas); you must explicitly call FreeHandle (or
destroy the Canvas) to release the handle.  Finally, note that
"WindowCanvas.Handle:=3D 0" will not release the handle, you must call
FreeHandle to release it.

[Greg Chapman, glc@well.com]

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

22. How to use the ItemAtPos to get DirListBox item
Q:
I want to use DnD to move directory names from a TDirectoryListbox to a
TListbox where the list of directories that the user has chosen can be listed.
So far, all of the code is in place to allow OnDblClick to change the
contents of the label poiinted to in the DirLabel property and then add that
text ( the label's caption property ) to the awaiting TListbox.
Now, I've put the code in to allow the user to also drag an item from the
directory listbox to the regular listbox.  And....here's my problem:
The drag begins in the MouseDown event handler for the DirectoryListbox by
checking to see if the user has pressed the mouse button down over the top
of a valid item...

           if ItemAtPos( Point( X, Y ), True ) >= 0 then
               BeginDrag( False );

Well, once the user has dragged the mouse over the TListbox and released the
mouse button, I want to go back to the TDirectoryListbox and get the
'string' and add it to the TListbox.
NOTE: if listbox this, and listbox that is beginning to confuse you ( I'm
getting dizzy just typing this in ) then what I want to do is drag a
directory name from a TDirectoryListbox and have it appear in the TListbox
when the user successfully completes the dragdrop operation.

A:
Just save the result of the ItematPos function in a variable in the form, then use that variable in the ListBoxDragDrop event. E.g.:

   FDragItem:= ItematPos(X, Y, True);
   if FDragItem >= 0 then
     BeginDrag(false);

...

  procedure TForm1.ListBoxDragDrop(Sender, Source: TObject; X, Y: Integer);
  begin
    if Source is TDirectoryListBox then
      ListBox.Items.Add(TDirectoryListBox(Source).GetItemPath(FDragItem));
  end;

[Greg Chapman, glc@well.com]

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

23. From database to variables
Q:
I want to retrieve data from DB (or dbf) to variables or arrays.
For example, assign a variable with a data in a selected FIELD and RECORD,
use assignment (:=) to do it? or something?  How?

A:
You can retrieve field values programmatically like so:

aValue := TMyTable.FieldByName('SomeField').AsText;

or

aValue := TMyTable.FieldByName('SomeField').AsInteger;

or

aValue := TMyTable.Fields[1].AsFloat;

What you're actually doing here is getting a TField object from the TTable (or
TQuery) object, then calling a method of the TField object to get at the data
itself. You can also assign values to the field in the same way, if the TTable
object is in Insert or Edit mode. The AsFloat, AsInteger, AsDateTime, AsString
members are actually properties, and as such can accept assignment. You can
also create field objects using the Fields Editor by double-clicking on the
TTable or TQuery object. These field objects can be used instead of getting it
from the TTable or TQuery object every time.

[Dan Butler, dbutler@pscorp.com]

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

24. Minimize a non-delphi window from a delphi program
Q:
I need to minimize a window from another program (non delphi) when
my program executes. Is there a way to send this window a message
to minimize itself?

A:
First you need to get the HWND (Window Handle see the WinAPI help) of the
window you want to close (The subject of another thread here.) Then try
this Windows API routine. SendMessage( hWnd, WM_SIZE, SIZE_MINIMIZED, 0);
This sends the message to the window in hWnd, that the user pressed
the minmize box or selected it from the system menu.

[Brad Siemssen]

A:
A better solution is calling the Windows API function: CloseWindow(hWnd) -- or
ShowWindow(hWnd, SW_MINIMIZE);

[Greg Chapman, glc@well.com]

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

25. Number of current week
Q:
I need to calculate the week number - does anyone know of a debugged formula
to do this?

A:
There are 2 other functions included which are required for our
function. One checks for a Leap year, the other returns the # of days
in a month (checking the leap year) and the 3rd is the one you want,
the week of the year.

{***************************************************************************}
function kcIsLeapYear( nYear: Integer ): Boolean;
begin
  Result := (nYear mod 4 = 0) and ((nYear mod 100 <> 0) or (nYear mod
400 = 0));
end;

{***************************************************************************}
function kcMonthDays( nMonth, nYear: Integer ): Integer;
const
  DaysPerMonth: array[1..12] of Integer = (31, 28, 31, 30, 31, 30, 31,
31, 30, 31, 30, 31);
begin
  Result := DaysPerMonth[nMonth];
  if (nMonth = 2) and kcIsLeapYear(nYear) then Inc(Result);
end;

{***************************************************************************}
function kcWeekOfYear( dDate: TDateTime ): Integer;
var
 X, nDayCount: Integer;
 nMonth, nDay, nYear: Word;
begin

  nDayCount := 0;

  deCodeDate( dDate, nYear, nMonth, nDay );

  For X := 1 to ( nMonth - 1 ) do
    nDayCount := nDayCount + kcMonthDays( X, nYear );

  nDayCount := nDayCount + nDay;

  Result := ( ( nDayCount div 7 ) + 1 );

end;

[Mark Lussier, mlussier@best.com]

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

26. Application.Title font color
Q:
Is there a way to change the color of the application's title font?

A:
You can change all windows title font colors, but not your windows title font
color. This is a windows limitation.

It is possible to create a hot spot caption and add a label to it.
If you would like to create a custom captionless and borderless form that is
movable and resizable at runtime.

[caracena@henge.com]

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

27. WinAPI functions
Q:
I'm having trouble figuring out how to use the functions that are listed in the
WinAPI Help file, and would appreciate any help that anyone has to offer ...
The specific function I am looking at is WinExec.

A:
    To call Windows API functions like WinExec:

1.  You need WinProcs (and often WinTypes) in the uses clause
    of the unit from which you want to make the call.

2.  If you want to supply a string constant as the actual
    parameter where the formal parameter is PChar, you need to
    check Extended syntax in the Options | Project [Compiler]
    dialog (or alternatively use the {$X+} compiler directive at
    the top of the unit concerned.

3.  Then just code something like:
	     WinExec('Notepad', SW_ShowMaximized)

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

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

28. TMessage vs TMsg
Q:
Code taht I got from the 'net uses TMessage, and it worked OK. Docs use TMsg,
and do not mention TMessage. What's the deal?

A:
Use TMessage for cracking messages passed to a window.  TMsg is for certain API
calls such as PeekMessage, TranslateMessage, GetMessage, etc., in other words,
stuff that you probably won't be doing in a Delphi app.  TMsg also includes info
about where the mouse cursor was at the time the message was generated, and a
time stamp.

[Dan Butler, dbutler@pscorp.com]

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

29. Button does not respond to enter key
Q:
I want to make a Button respond to pressing the Enter/Return key. Note the
following snippet.

buttonKeyPress(.....)
begin
if key = #13 then do SOMETHING;
else do SOMETHING ELSE;
end;

Using this programming, if the return key is pressed NOTHING happens.
The trace-over just skips it.
IF a key OTHER than the return key is pressed, then SOMETHING ELSE does
get done!?

A:
The reason for this is that TButton or TBitBtn uses the Windows standard button
control, which uses the Return key.  To overcome this, you will need to create a
descendant of TButton and add a message handler for wm_GetDlgCode, like this:

type
  TMyButton = class
  protected
    procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message wm_GetDlgCode;
  end;

procedure TMyButton.WMGetDlgCode;
begin
  inherited;
  Msg.Result := Msg.Result or dlgc_WantReturn;
end;

See the WinAPI help file for further information on this message.

[Dan Butler, dbutler@pscorp.com]

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

30. DBEdit and real values
Q:
I have a Paradox database that needs to use numbers with values of real and a
picture of ###.# in the table. When I use DBEdit, I have created a parser of
sort with the following code:

procedure TForm.SpinButton1DownClick(Sender: TObject);
begin
value := value - 0.1;
Str (value:6:2,s);
Edit1.Text := s;
end;

The problem is that the table is storing very large and incorrect values for
the numbers. Example: I want to add 1.1 to a value of 4.3 and I get something
like 5.4000000000182 as the value set in the table. Basically, I put real
values in and get garbage out.

A:
When working with real numbers stored in a Paradox table, are you using type
Real as your data type? If so, try using Double. Double is an 8-byte (64-bit)
real number, which is what the BDE normally works with, while Real is a 6-byte
version that pretty much only Delphi and BP use. Or you could try using
Extended, which is 10 bytes.

[Dan Butler, dbutler@pscorp.com]

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

31. Form width
Q:
I want to create a Form with the width set to 130, however when I do
this either by trying to set the property at design time or by
setting the width in FormCreate ir always returns to 142.

A:
Could it because of some components you have on your forms?
Try creating a _new_ form with no components and set it to 130 and see if
the same thing happens.

[delphi@mailhost.net]

A:
You may be running up against a Windows behavior in which it sets a default
minimum width. If so, you can try creating a message handler for
wm_GetMinMaxInfo to set the minimum width of a window. The way this message
works is by passing a pointer to a structure (in lParam). You can also set
the maximum size of a window, and the maximized size of a window, and the
position of the window when maximized. Here is an example of how to use it
to only change the minimum width:

Add this to the private declaration of your TForm descendant:

    procedure WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo); message
wm_GetMinMaxInfo;

and this would be the implementation:

procedure TForm1.WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo);
begin
  Msg.MinMaxInfo^.ptMinTrackSize.x := 130;
end;

Note that Windows 95 is a little different than Windows 3.1 on minimum window
widths.  Since Win95 adds new buttons in the caption bar of windows in the
case that you specified using either a Minimize or Maximize button to be in
the caption, the minimum width in that case is a bit more than in Windows
3.1.  Also, it might be a bit different depending on the size of the caption
font.

[Dan Butler, dbutler@pscorp.com]

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

32. Duplicating a set of records
Q:
Does anyone know it there is a smart way of doing this:
given a set of records in a table, modify some field(s) and create new
records within the _same_ table. (Yes, this is a detail set of a master-
detail pair, the purpose being to create a new master record inheriting
all detail records.)
With "smart" I am aiming at something that does not look like jumping
here and there ("repeat read; modify; write until done").

A:
You could either use a second TTable object working on the same table, or you
could call the DisableControls method in your TTable object before making
your changes, followed by EnableControls.  If you want to keep your same
place you might try using a bookmark.  Sort of like this:

procedure TMyForm.MakeChanges;
var
  aBookmark: TBookmark;
begin
  Table1.DisableControls;
  aBookmark := Table.GetBookmark;
  try
    {do stuff}
  finally
    Table1.GotoBookmark(aBookmark);
    Table1.FreeBookmark(aBookmark);
    Table1.EnableControls;
  end;
end;

[Dan Butler, Dan_Butler@msn.com]

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

33. DBGrid MoveToNextField
Q:
Does anyone know if there is a TDBGrid method (or similar) to perform the same
as entering a TAB, moving to next field in the grid?

A:
To go to the next field
   MyDBGrid.SelectedIndex := MyDBGrid.SelectedIndex + 1;

The grid columns are 0 indexed so SelectedIndex := 0 goes to the first
column.  The FieldCount property gives you the number of columns so you can
handle wrapping at the end of the row.

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

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

34. Creating a table in a unit
Q:
How can I create a Table in a unit (without a form) at runtime? The following
code doesn't work:

procedure CreateATableInAUnit;
var
   myTable : TTable;
begin
     myTable := TTable.Create(Self);
end;

A:
The TTable object can be created with an owner, or without one. Since you
are declaring it as local to a procedure you should not give it an owner.
Whenever you don't give it an owner, that means that its your job to free it
when you're done. Otherwise, the owner will free it whenever the owner is
itself freed. Make sense?  To create it without an owner, do the following:

procedure CreateATableInAUnit;
var
   myTable : TTable;
begin
  myTable := TTable.Create(nil);
  try
    myTable.DatabaseName := 'MyDB';
    myTable.TableName := 'MyTable.db';
    mytable.IndexName := 'MyIndex';
    myTable.Open;
    {do stuff}
  finally
    myTable.Free;
  end;
end;

[Dan Butler, Dan_Butler@msn.com]

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

35. Bmp picture printing with Printer.Canvas
Q:
Which way is the best for printing a bmp picture with printer.canvas?

A:
var GRect : TRect;
      IRatioDif : real;

{First calculate the ration of the height to width of bitmap}

  IRatioDif := Image1.Picture.Height / Image1.Picture.Width;

{Then get the various sizes of the printer canvas}

  GRect.Top := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
  GRect.Left := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
  GRect.Right := Printer.PageWidth - GRect.Left;
  GRect.Bottom := trunc(Printer.PageHeight * IRatioDif) - GRect.Top;

{Then you can start printing}

  with Printer do
  begin
    BeginDoc;

    {Use StretchDraw to strech the image to the size specified by
GRect}

    Canvas.StretchDraw(GRect, Image1.Picture.Graphic);
    EndDoc;
  end;

[David Hargreave, davidh@magna.com.au]

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

36. Using TStringList in a visual component
Q:
I need to build a buffer for queuing output requests to be handled by a
component I am trying to build. I thought I would just use a TStringList,
add to it's end and using and deleting the first entry when possible... It
doesn't seem to be this simple... does anyone know the difference between
TStrings and TStringList and which I should be using.... any tips????

A:
TStrings is an abstract base class used by many of the visual controls such
as TListBox. What you want is TStringList, or if all you need to keep track
of is objects, use TList instead. To add to the end of either, use the Add
method. To insert at a specific place in the list, use Insert. To get a
string from the list, use the Items property. Keep in mind that it is
zero-based, so the last item in the list is referenced by Count-1 (Count is
another property). To delete a string, use the Delete method. To find a
string in the list, use IndexOf. TStringList can be made to keep the list in
alphabetical order. To do that, set the Sorted property to true before you
add anything to it. It can also be used to keep an object for every string,
using AddObject and the Objects property. TList does most of the above, just
without the strings. To create a TStringlist, do this:

procedure MakeList;
var
  aList: TStringList;
begin
  aList := TStringList.Create;
  aList.Sorted := true;  {optional}
  aList.Duplicates := dupIgnore;  {or dupAccept or dupError}
  aList.Add('String 1');
  aList.Add('String 2');
  Edit1.Text := aList.Items[0];   {Edit1 now says 'String 1'}
  aList.Delete(0);
  aList.Free;
end;

[Dan Butler, Dan_Butler@msn.com]

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

37. Make index files in delphi
Q:
Does someone know how to create, maintaince, refresh a index file in
delphi, please give me a help.

A:
If you are using dBASE or Paradox tables, you can use the AddIndex to create
a new index. For example:

     Table1.AddIndex('Articles','Title', []) ;

will create an index file named ARTICLES using the field TITLE as index key.
You can also specify various index options (e.g., unique, non-maintained,
etcetera) -- check the Delphi help file for more information.  NOTE: Your
table must be opened exclusively in order to use the AddIndex method.

As to maintaining/refreshing an index file, unless you specifically create a
non-maintained index file, it's automatic.

[Greg Lief, greglief@grumpfish.com]

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

38. Fixed field data input
Q:
I am trying to read in a text data file in fixed-field format, where variable
V1 is colums 1-3, V2 is colums 4-5, etc. What is the best method to import
this data in Delphi?

A:
     Here's the easiest way to do it, it might not be the best:

     var F : TextFile;
         S : String;

     AssignFile(F, 'FILENAME.TXT');
     Reset(F);
     while Not EOF(F)
     do    begin
              Readln(F, S);
              V1:= Copy(S,1,3);
              V2:= Copy(S,4,6);
              ...
           end;
     CloseFile(F);

     This is what I would normally do, but if the file is very large I
     would read it using blockread and scan the block afterwards. If you're
     thinking of using ODBC, I don't think it's worth it.

[Palle Due Larsen, pdl@vki.dk]

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

39. Positioning self-created forms
Q:
In my MainForm (Form1) I create another form, the position of which I want
to control. Form1 is a standard blank form, Form2 too but with DialogBox
style (but I have for sure tested all combinations of borderstyles etc).

Now, with a simple "child" form (no MDI!) code like

procedure Form1.Button1Click(Sender: TObject);
begin
  Form2 := TForm2.Create(self); { from within Form1 }
  with Form2 do begin
    Top  := Form1.Top + somevalue;
    Left := Form1.Left + someothervalue;
    ShowModal;
    Free;
  end;
end;

which works as expected.

A:
Delphi How-To book published by the Waite Group:
unit Runtime1;  { How-To 1.6 }

interface

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

type

  TNewForm = class(TForm)
    NewEdit: TEdit;
    NewButton: TButton;
    procedure NewButtonClick(Sender: TObject);
  end;

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

var
  Form1: TForm1;

implementation

{$R *.DFM}

{ OnClick handler for generated forms }
procedure TNewForm.NewButtonClick(Sender: TObject);
begin
  NewEdit.Text := 'Hi!';
end;

{ Generates and shows form from scratch }
procedure TForm1.Button1Click(Sender: TObject);
var
  NewForm: TNewForm;
begin
  { Create new form }
  NewForm := TNewForm.CreateNew(Application);
  { Set its properties }
  with NewForm do
  begin
    Top := 140;
    Left := 220;
    Width := 435;
    Height := 300;
    Caption := 'Runtime!';
    { Create new edit component }
    NewEdit := TEdit.Create(NewForm);
    { Set its properties }
    with NewEdit do
    begin
      Parent := NewForm;
      Left := 153;
      Top := 40;
      Width := 121;
      Height := 29;
      TabOrder := 1;
      Text := 'Edit1';
    end;
    { Create new button component }
    NewButton := TButton.Create(NewForm);
    { Set its properties }
    with NewButton do
    begin
      Parent := NewForm;
      Left := 153;
      Top := 176;
      Width := 121;
      Height := 33;
      TabOrder := 0;
      Caption := 'Change Edit';
      { Wire the component to OnClick handler }
      OnClick := NewButtonClick;
    end;
    Show;
  end;
end;

end.
In fact, they set the coordinates but they don't try to give some new
values. Moreover, couldn't you try the poScreenCenter property, only to see
if it could work?

Another opportunity would be to store in an Ini file values for your
positions, but you probably already tought about it.

[Delphi How-To published by the Waite Group]
[castors@worldnet.net]

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

40. #0 KeyPress
Q:
I've seen this code in a component I've downloaded:

   if Key <> #0 then inherited KeyPress(Key);

Since Windows virtual keycodes range from 1 to 145 (decimal),
what does 'if Key <> #0' check for?

A:
It checks for whether a key was really pressed; by convention, #0 means a
key was not pressed. In some cases an event may get activated by other than
a keypress (eg. by direct call), or an ancestor may have already processed
the keypress, in which case Key will have been set to #0.

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

41. Exceptions
Q:
I wanted to write some fairly simple code...

procedure part_of_starting_up(n:string....)
var f: typed file;
begin
 try
  assign/reset(f,n);
  while not eof(f) do
     read_and_process_each_record(f);
  close(f);
 except
  on exxxxx do messagedlg('couldn't find/open file');
  on eyyyyy do
   begin
    messagedlg('error reading file');
    close(f);
   end
(etc)

where exxxx = the exception created when reset failed,
and  eyyyy = any exception occuring in my (dumb) file reading code.
The key point, of course, is making sure that the file is closed if
it was opened. (note: "fileExists" may not mean "file can be opened")

I RTFM'd and used the browser, but am still confused as to what e____'s
relate to this kind of file management. Is what I'm trying to do possible?
Or should I do

  try
   assign/reset/readthefile
  except blahblahblah
  try
   close;
  except (*nothing*)

A:
Try this scheme. It worked for me.


procedure part_of_starting_up(n:string....)
var f: typed file;
begin 
     try
          try
               assign/reset(f,n);
               while not eof(f) do
               read_and_process_each_record(f);
          finally
               {$i-}     { No need to complain if close fails. }
               close(f);
               {$i+}
          end;
     except
          on E:EInOutError do 
               case e.ErrorCode of
               nn1: messagedlg('couldn't find/open file');
               nn2: messagedlt('error reading file');
               end;
     end
(etc)

Keep in mind that for the type of functions you are using you will
get the same exception, called EInOutError. You have to use the errorcode 
field to know what message to show. 

[George Blat, georgeb@brd.com]

A:
You cannot combine except & finally in a try clause, unfortunately or wisely.

[Tommy Ericson, teit@pi.se]

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

42. Simulating Key presses
Q:
When my users enter a certain text box, I want conditional text to fill in
automatically. Let's use the following fictitious scenario.
In Edit1.Text the user fills in "CT" the state code for CT. Edit2 collects
phone number information. I know that "(203) " should be the first six
characters entered. So, using the OnEnter method of Edit2 I would like the
following to happen; the text value set to "(203) " and the cursor to
position itself at the end of those characters.
I imagine that there is an easy way to reposition a cursor in an edit box
that I do not know. If so, I'd appreciate hearing about it.
I figured I could use the following code (don't ask me why I figured this
would work ... it seems to be some vestigal memory from a something I
read or something.). In any case, it doesn't work.  Any insights?

begin
  Edit2.Text := '(203 )';
  SendMessage(WM_CHAR,VK_END,0,0);
end;

A:
Try this:

begin
  Edit2.Text := '(203 )';
  Edit2.selStart:=Edit2.GetTextLen;
end;

Also, you might want to set Edit.AutoSelect to False, otherwise when a user tab
to this editBox, the text you inserted is selected, and if the user starts
typing it will be overwrited.

[Nitsan, nitsanko@NetVision.net.il]

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

43. Key presses / edit position
Q:
I imagine that there is an easy way to reposition a cursor in an edit box
that I do not know.

A:
Try this 2 Edit box sample below.

unit Cursor;

interface

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

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    procedure Edit1Change(Sender: TObject);
    procedure Edit1KeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
  private
    { Private declarations }
  public
    { Public declarations }
  CurPos : integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Edit1Change(Sender: TObject);
begin
CurPos := Edit1.SelStart;
 edit2.Text := IntToStr(CurPos);
end;

procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
If Key = VK_LEFT then dec(CurPos);
if Key = VK_RIGHT then inc(CurPos);  {RIGHT Arrow}
edit2.text := inttostr(CurPos);
end;

end.

[John O'Sullivan, jos@iol.ie]

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

44. Bmp printing problem on a Hp Laserjet 4+
Q:
  Printer.BeginDoc;
  Printer.Canvas.Draw(100,100,Bitmap1);
  Printer.EndDoc;

Does anybody has another suggestion.

A:
Try something different to check the canvas, such as
Printer.Canvas.Rectangle(10,10, 500,500); to make sure that your printer setup
is OK. (If this fails then try printing a bitmap from something like windows
paintbrush)

The 100,100 in your print statement might also be placing your
bitmap way off the page, to start with it might be a good idea
to print at 0,0. (You can use the Printer.PageWidth and Printer.
PageHeight properties to determine what size your page is)

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

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

45. OLE Server
Q:
I want to make an OLE-server with Delphi. In OLE you pass 'Interfaces'
around, which are basically structures with function pointers. I need
to be able to generate these structures and to call functions defined
by those structures. The problem is that the functions use the 'c'
calling convention, which Delphi supports (sort of) with the 'cdecl'
keyword. I say 'sort of' because the following wont compile:

  ...
  interface
    function OLEfunction(x, y, z: integer): integer; cdecl; export;
  implementation
    function OLEfunction(x, y, z: integer): integer;
    begin
    end;

    procedure buildOLEstructure;
    var
      F: function (x, y, z: integer): integer; { need 'cdecl' here }
    begin
      F := OLEfunction; { Type mismatch here... }
    end;

This will not compile, because of a type mismatch caused by the 'cdecl'
on the function (which I really need) and the missing 'cdecl' on the
definition of the variable 'F'. In this example I could live with a
simple pointer-definition for F, because I don't need to call the
function myself, but OLE sometimes gives me a pointer to a 'cdecl'
function, which I need to call myself. As I see it, there is no way
that this is possible. Am I right in this assumption?

A:
The following code compiled OK. It's not so clean but it may help you:

  unit Unit1;

  interface
	 function OLEfunction(x, y, z: integer): integer; cdecl; export;
  implementation


	 function OLEfunction(x, y, z: integer): integer;
	 begin
	 end;

	 procedure buildOLEstructure;
	 var
		F: pointer;
	 begin
		F := @OLEfunction; { Compiles OK ... }
	 end;


   end.

[George Blat, georgeb@brd.com]

A:
Use the approach that follows. You have to declare one calling function for
each combination of parameters that you are going to pass. Then you call the
calling function passing as a pointer the function you want to call.

library Pcdecl;

function olefunction(a1 : pchar; a2 : longint; x : integer )  : integer;
		cdecl; export;
begin
	end;

function callolefunction(func : pointer; a1 : pchar; a2 : longint; x :
integer)  : integer;
	assembler;
asm
	push	x	            { push parameters in reverse order }
	push	word ptr a2 + 2     { if a 32 bit value push it in two steps, starting
with the high part }
	push	word ptr a2
	push	word ptr a1 + 2
	push	word ptr a1
	call	func
	add	sp, 10		     { restore stack by adding back the bytes pushed. Note that
func is not pushed }
	end;

procedure buildolefunction;
var
	f	: pointer;
	reslt	: integer;
begin
	f := @olefunction;
	{ --- }
	reslt := callolefunction(f, 'hello world', 1000000, 25);
	{ --- }
	end;

begin
	{ --- }
end.


This compiled perfectly well in my computer. It should do the job.
Warning. Methods should be treated slightly different than functions.

[George Blat, georgeb@brd.com]

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

46. Number of maximal Components in .DCR
Q:
What is the maximum number of components one can have in one .DCR file?
The reason I ask is that at present I have 277 components, and when I
want to install a new component I have created, it compiles correctly but
gives me a Disk Full error. I have 9MB free on my disk. What could the
problem be?

A:
Perhaps the problem is caused by the fact that the process uses HDD space to
build the file. If there is too much data to fit on your drive temporarily,
then this error. Will it correctly build if you remove only 1 component?
You may be right on the edge...

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

47. VER.DLL functions
Q:
Does anybody have any experience using the version-checking functions in
VER.DLL? The online help for this is clear as mud (to me at least). I'm
writing a simple setup routine (I have to write my own, because it does few
things I can't find in any of the available shareware installers) and need
to do version checking on a few shared DLLs.

A:
This isn't exactly what you are looking for, but it might be useful. I use
the version information in my "About" boxes, code is below. You may not have
to use the StringFileInfo block at all for simple version checking, but
instead get the info out of the root block (see the TVS_FIXEDFILEINFO
structure in the API help).

procedure TAboutBox.FormCreate(Sender: TObject);
var
  VIHandle : LongInt;
  VSize : LongInt;
  VData : Pointer;
  VVers : Pointer;
  Len : Word;
  FileName : String;

const
  Prefix = '\StringFileInfo\040904E4\'; { Prejudiced to U.S. character set, if
                                          I remember right }   

  function GetVerValue(Value : String) : String;
    var
      ItemName : String;

    begin
      ItemName := Prefix + Value + chr(0);
      Result := '';
      If VerQueryValue(VData,@ItemName[1],VVers,Len)
        Then
          If Len > 0
            Then
              Begin
                If Len > 255
                  Then
                    Len := 255; { "Truncate" any long strings }
                Move(VVers^, Result[1], Len);
                Result[0] := Chr(Len);
              End;
    end;

begin
  FileName := Application.EXEName + chr(0);
  VSize := GetFileVersionInfoSize(@FileName[1], VIHandle);
  If VIHandle <> 0
    Then
      Begin
        GetMem(VData, VSize);
        Try
          If GetFileVersionInfo(@FileName[1], VIHandle, VSize, VData)
            Then
              Begin
                { At this point, I grab values from the StringFileInfo block,
                  but you could just as easily get values from the root 
                  block using VerQueryValue }

                ProductName.Caption := GetVerValue('ProductName');
                Version.Caption := GetVerValue('ProductVersion');
                Copyright.Caption := GetVerValue('LegalCopyright');
                Comments.Caption := GetVerValue('FileDescription');
              End;
        Finally
          FreeMem(VData, VSize);
        End;
      End;
end;

[Eric Nielsen, htrsoft@midwest.net]

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

48. StringGrid Masks
Q:
I have a column in a string grid that I need to be displayed like the
"comma" format of any Spreadsheet:
User types: 123456.7
Grid displays: 123,456.70

A:
What you want is tricky, but doable cause I've got a right aligned, floating
point, editable grid working right now (albeit a DrawGrid, but the concepts
should be the same).

You'll need a couple of things:
- An OnDrawCell handler to display the formatted, right-justified data.
- The cells of your stringgrid loaded with unformatted string representations of
  your data.

In your OnDrawCell handler you will need to do something like this.  This
code displays right justified comma-tized values:

  begin
  if (Row > 0) and (Col > 0) and (grdDivBudget.Cells[Col, Row] <> '') then
    begin
      {Format floating point string}
      strText := FloatToStrF(StrToFloat(grdDivBudget.Cells[Col, Row]),
ffNumber,         13,2);

      {Set font}
      grdDivBudget.Canvas.Font.Name := 'Courier';
      if StrToFloat(grdDivBudget.Cells[Col, Row]) < 0 then
        grdDivBudget.Canvas.Font.Color := clRed;
      grdDivBudget.Canvas.Font.Style := grdDivBudget.Canvas.Font.Style -
[fsBold];

      {Center the text in the cell from top to bottom, right justify it and
       move the right margin to the left two pixels.}
      X := Rect.Right - grdDivBudget.Canvas.TextWidth(strText);
      Y := Rect.Top + ((Rect.Bottom - Rect.Top -
        grdDivBudget.Canvas.TextHeight(strText)) div 2);
      Dec(Rect.Right, 2);

      grdDivBudget.Canvas.TextRect(Rect, X, Y, strText);
    end;

   Make sure you have DefaultDrawing := True so you only have to write code to
draw the cells which need to be specially formatted.

  That should do it.  When the user tries to edit the number in a cell it
should be displayed in its non-formatted state.  If not the you'll need an
OnGetEditText handler.

  Also, you'll find that validation of the data can be problematic (i.e.
what if the user enters 'TX' in a numeric column).  It's made worse by the
fact that the grid (well, the DrawGrid anyway) does not exhibit consistent
behavior for different ways of navigating the grid (i.e. it triggers
different events if you use the arrow keys, versus the mouse, versus simply
hitting enter).  If this becomes a problem post another message and I'll
give you my solution (which I'm not proud of, but it works).

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

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

49. StringGrid right alignment
Q:
To right align the cells in a string grid, and have the numeric cells
containing comma, I did the following.

- Made all the contents of the cells the same length.  Padded with spaces
to ensure all were the same.
- This will enable you to right-align the cells.  Just make sure you use
a TT font else certain letters such as the letter i will take less space
than a letter such as M. I used Courier New as the grids font.

A:
I suppose this one is better method

procedure TForm1.GridSumaDrawCell(Sender: TObject; ACol, ARow: Longint;
  ARect: TRect; State: TGridDrawState);
var
   dx : integer;
begin
   with (Sender as TStringGrid).Canvas do begin
      Font := GridSuma.Font;
      Pen.Color := clBlack;
      if (ACol = 0) or (ARow = 0) then begin
{ Draw header }
          Brush.Color := clBtnFace;
          FillRect(ARect);
          TextOut(ARect.Left, ARect.Top, GridSuma.Cells[ACol, ARow])
      end
      else begin
{ Draw right aligned cell }
          Brush.Color := clWhite;
          FillRect(ARect);
          dx := TextWidth (GridSuma.Cells[ACol, ARow]) + 2;
          TextOut(ARect.Right - dx, ARect.Top, GridSuma.Cells[ACol, ARow])
      end
     end
end;

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

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

50. Avoiding close
Q:
I am writing an application that needs to to run continously, so if the user
clicks on the close button it should minimize. I tried to use the on close
event in the main form giving the value caNone and caMinimze to the var
parameter Action. However, the application quits.

A:
     type

       skAction = ( skfNone, skfClose, skfMinimize, skfMaximize, skfReload
     );


procedure TSKDForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  case FormAction of
    skfNone :
    begin
      Action := caNone;
      ShowWindow( Handle, SW_MINIMIZE );
      Exit;
    end;
    skfClose :
    begin

{ my clodr window code }
      Exit;
    end;
    skfMinimize :
    begin
      Action := caMinimize;
      Exit;
    end;
  end;
end;

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

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

51. Looping AVIs
Q:
I have an AVI which I want to loop continuously on my form. I though of a
FOR, or WHILE or REPEAT structure, but nothing worked because of the length
of the AVI.
Nevertheless, I found a solution using the TTimer and giving it as interval
the duration of the AVI. Is there any more convenient way to do this?

A:
Use the mediaplayer's OnNotify event so it will let you know when it's finished:


procedure TForm1.BitBtn1Click(Sender: TObject);
begin
With MediaPlayer1 do
  Begin
    Open;
    Notify := True;
    Play;
  End;
end;


procedure TForm1.MediaPlayer1Notify(Sender: TObject);
begin
  With MediaPlayer1 do
    If NotifyValue = nvSuccessful
      Then
        Begin
          Notify := True;
          Play;
        End;
end;

[Eric Nielsen, htrsoft@midwest.net]

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

52. TOutLine drag and drop
Q:
How does one do Drag 'n Drop with a TOutLine. If I make the dragmode
dmAutomatic, then I cannot expand an item by clicking on it. I want both
features available. (i.e. Drag 'n Drop and to be able to click on an
item.)

A:
Set DragMode = dmManual, create an OnMouseDownHandler, inside the handler
call BeginDrag(False).  BeginDrag(False) will not actually start dragging
until the user moves the mouse 5 pixels so if the user is simply clicking
then the drag operation will not begin.

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

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

53. Changing the dir from an Alias at run time
Q:
Is it possible to change the directory from an Alias at run-time?

A:
     I do this all the time. I have an INI file that  specifies wher to
     look for the tables and the directories to use here is mi code:
     
     procedure CheckTable( var Table : TTable; var TName : string );
     var
       ChangePath  : boolean;
       Path        : string;
       ActiveState : Boolean;
     begin
       if ( TName = '' ) then
         TName := Table.TableName
       else
         with Table do
         begin
           ActiveState := Active;
           Close;
           Path := ExtractFilePath( TName );
           ChangePath := HasAttr( DatabaseName, faDirectory  ) or
                         ( CompareText( DatabaseName, Path ) <> 0  );
           if ( Length ( Path ) > 0 ) and ChangePath then
                 DatabaseName := Path;
           if ( CompareText( ExtractFileName( Tname ), TableName ) <> 0 )
     then
             TableName := ExtractFileName( Tname );
           Active := ActiveState;
         end;
     end;

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

54. Speaker Bleep
Q:
Anybody know how to make the PC speaker bleep in Delphi, like when a user
types a wrong character?

A:
Try to take a look in the Windows API on-line help and search for
MessageBeep procedure...

Anyway, something like the following may be right for you:

uses WinProcs;

...

MessageBeep( -1 );

...

[Pieramato Gramenzi, gramenzi@cli.di.unipi.it]

A:
If you use the WINDOS unit in your uses clause, you can do a standard Pascal
write of the BEL ASCII character to output:

Write(Chr(7))

I think it produces the rather prosaic BIOS type BEEP.

[Dr. Peter W. Mueller, p.mueller@ic.ac.uk]

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

55. Finding total global variable size
Q:
Is there a way to determine how much of the data segment all the global
variables/constants in my program are occupying?

A:
You can create a map file in the linker options, recompile and then take a
look at it. Look for the globals and the data segment size. Select a detailed
map file.

[George Blat, georgeb@brd.com]

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

56. Opening file for read only
Q:
I am working on an App that must allow multiple stations to open a
file a the same time.  Is there anyway to open a file for read
only,  and then should an update be needed,  lock the file so no
one else can do an update until the current operation is complete?

These files are created and read with the BlockRead and Blockwrite
commands if it's any help.

A:
Set the FileMode variable before opening or creating the file. You can use
the 'File Open Mode constants' to set this. Look at the help for 'Sysutils
unit'. About half way down the page you'll see 'File Open Mode constants'.
These work great with the FileMode variable. You should OR one of the
fmOpen... constants with one of the fmShare... constants to set the mode.

[Dan Butler, Dan_Butler@msn.com]

A:
Look-up the FileMode variable in help. If you set it to zero before opening
the file, the opening will be read-only. The default is read/write for
untyped files.

[George Blat, georgeb@brd.com]

A:
  You might like to try setting the filemode after you assign the
file.  ie:
 
  AssignFile(F, FileName);
  FileMode := 0;  ( Set file access to read only }
  Reset(F);
  .
  .
  .
  CloseFile(F);

[David Hargreave, davidh@magna.com.au]

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

57. Application to tile on windows desktop
Q:
How can I get my Application to tile with the other windows app's currently on
the Windows Desktop? (ie using the Tile button of Task Manager).  Delphi apps
don't behave the way they should).

A:
type
  TForm1 = class(TForm)
{ ..... }
     procedure AppOnMessage(var Msg: TMsg; var Handled: Boolean);
     procedure FormCreate(Sender: TObject);
  private
	 { Private declarations }
  public
	 { Public declarations }
  end;

var
  Form1: TForm1;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
	Application.OnMessage := AppOnMessage;
	ShowWindow(Handle, CmdShow);
end;

procedure TForm1.AppOnMessage(var Msg: TMsg; var Handled: Boolean);
var
     WindowPosp	: ^TWindowPos;
begin
     with Msg do
          if Message = WM_WINDOWPOSCHANGED then begin
		WindowPosP := pointer(lparam);
		with WindowPosP^ do begin
                    left := X;
                    Top := Y;
                    width := cx;
                    Height := cy;
                    end;
		end;
     end; { AppOnMessage }
end.

I tried it with Tile and Cascade in my Window 95 setup and it works great.

By the way the ShowWindow call that you mention is not to fix a bug. Delphi,
like Visual Basic has an invisible application form, distinct from the
main form.

[George Blat, georgeb@brd.com]

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

58. Assigning a SQL count to a variable
Q:
I would like to query a table to find the number of matches to a
string. The table has a field containing titles of positions.

I would like to query the table with something like:

select count(*) from table
where field = "XXX"

and then be able to copy the result into a variable.

Can I do something like:

query.Close;
query.SQL.Clear;
query.SQL.Add('select count(*) from table where field = "XXX");
variable :=query.Open;

I know that the above is incorrect, I am just using it to give you
the idea of what I want to do.  I presume that the SQL cursor returns
the result as as set of parameters, I just don't know how to get at
them.

A:
query.Close;
query.SQL.Clear;
query.SQL.Add('select count(*) from table where field = :XXX);
Query.ParamByName('XXX').AsString := value;
query.Open;
while Query.Eof <> True do
begin
        SqlCount := Query.Fields[0].AsInteger;
        Query.Next
end;


[Daniel Adeniji, 0006513351@mcimail.com]

A:
Applies to

TTable, TQuery, TStoredProc components

Declaration

property RecordCount: Longint;

Description

Run-time and read only. The RecordCount property specifies the number of
records in the dataset. The number of records reported may depend on the
server and whether a range limitation is in effect.

[Clive McAdam, cmcadam@icl.co.za]

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

59. Procedure to Wrap dialog box with an object
Q:
I would like to create an object that has contained in it a dialog box.
The effect I am looking for is similiar to the FileOpen/Save dialogs, where you
set/get properties, then call an Execute method.

A:
I would go for a new form, Dialog style, fill in the necessary components,
and use it roughly like

  with TMyForm.Create(self) do begin
    Parent := Self;
    MyExecute(inParam, outParam);
    Release;
  end;

where MyExecute() would do the Show[Modal] and other things

[Tommy Ericson, teit@pi.se]

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

60. TGraphicComponent child
Q:
But, as you may already know, my question is: How can i manage to  get the
MouseDown event occur *only* when my routine - that checks for  this (the
point being into the shape)  - returns TRUE to the points given?

A:
You can't. However, you can restrict any event handlers attached to it, and
you can make sure that click doesn't get called. I take it you want an
OnClick event that takes into account your triangular shape? Not a problem.
Oh, and just to address one of your other replies, it's not neccesary to
trap the windows messages, as TGraphicControl already does trap most (or all,
not sure) of them.
Now, I'm sure there's a couple of ways to do this, and this is the way I'd
do do it, and it will work, but keep in mind I have no VCL source so this is
probably a bit awkward.

procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer)
; override;

procedure TThing.MouseDown...
begin
	{Not the mouse coordinates in some private property}
	inherited MouseDown...
end;

procedure TThing.Click
begin
	if YourHitTest(X,Y) then
	   inherited Click;
end;
I'm a bit dubious about that last procedure, but it will work. I'm sure
there's some better documented people here who can come up with a more
elegant solution.

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

61. Attach procedures to components created
Q:
I've created some components dynamically.  The problem is that I don't have
any idea on how to attach some common events such as "OnClick", "OnEnter"
to these components.

A:
I would recommend that you consider overriding the default KeyPress, Click,
DblClick (etc) events.
Here's a brief example of an override Click procedure:

(Declaration in protected section of class type definition)

     procedure Click ; override ;

(Code below, in implementation section)

procedure GOKButton.Click ;
begin
   inherited Click ;
   (Owner as TForm).Close ;
end ;

[Greg Lief, mrgrump@teleport.com]

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

62. AppendStr and ConCat
Q:
Can anyone please explain to me why there is such a redundancy of procedures
and functions in Delphi? Why are, for example, AppendStr and Concat almost
identical in their operation and, more, when you consult the help for the one,
there is no cross reference (see also) for the other? Is there some essential
difference between AppendStr and Concat that is not obvious from the Help?

A:
There are usually subtle differences, and the help does a reasonable job of
highlighting these. For example, Concat can concatenate 2 OR MORE strings, and
does it with similar efficiency to using the + operator. On the other hand,
AppendStr only adds one string to another, but does it a bit more quickly.

Of course much of the proliferation of string handling routines is due to the
difference in philosophy between Pascal and Windows i.e. byte-counted vs. null-
terminated, and the need to provide conversion back and forth.

I guess that some of the duplication also arises because of the need to ensure
backwards compatibility when adding improved routines.

I agree about the "See Also" popup windows. Quite a few useful cross-references
have been omitted. But on the whole I think Borland did a brilliant job of the
on-line help.

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

A:
AppendStr combines one string with another.  Concat allows you to
combine many string into one. i.e. Concat(s,s1,s2,s3,etc)

[Mel Meltzer, melm@cris.com]

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

63. File of type TList
Q:
I have a TList object which itself contains a TList object. I want to be able
to save the entire contents to a disk file.

A:
Ok, this is not as simple as it looks. However, some time ago with much
help from people on this list, I did this. Some source code for Toverheadmap
follows..

Note the ReadData and WriteData methods on the objects being written to
disk, and the SaveToFile and LoadFromFile methods on the TList itself. This
really should be a bit more generic, but I haven't had the time/inclination
to make it so as of yet. (Ie, the TList should be able to save/restore any
object with a readdata/writedata method.)


------------------------------------
unit Charactr;

interface

uses
    Graphics, StdCtrls, Classes, Sysutils, Winprocs, Ohmap, ohmstuff;

type
    TMapCharacterList = class(TList)
    private
    FMap:TOverHeadMap;
    public
    procedure RenderVisibleCharacters; virtual;
    procedure Savetofile(const filename:String);
    procedure Loadfromfile(const filename:String);
    procedure Clear;
    destructor Destroy; override;
    property MapDisp:TOverHeadMap read FMap write FMap;
    end;

    TFrameStore = class(TList)
    procedure WriteData(Writer:Twriter); virtual;
    procedure ReadData(Reader:TReader); virtual;
    procedure Clear;
    end;

    TMapCharacter = class(TPersistent)
    private
    FName:string;
    FMap:TOverHeadMap;
    FFrame:Integer;
    FFramebm,FFrameMask,FWorkBuf:TBitmap;
    FFrameStore,FMaskStore:TFrameStore;
    FXpos,FYpos,FZpos:Integer;
    FTransColor:TColor;
    FVisible,FFastMode,FIsClone,FRedrawBackground:Boolean;
    procedure SetFrame(num:Integer);
    function GetOnScreen:Boolean;
    procedure SetVisible(vis:Boolean);
    procedure MakeFrameMask(trColor: TColor);
    procedure MakeFrameMasks; {For switching to fast mode...}
    procedure ReplaceTransColor(trColor: TColor);
    procedure SetXPos(x:Integer);
    procedure SetYPos(y:Integer);
    procedure SetZPos(z:Integer);
    procedure SetFastMode(fast:Boolean);
    public
    constructor Create(ParentMap:TOverheadmap); virtual;
    destructor Destroy; override;
    property Name:string read FName write FName;
    property Fastmode:Boolean read FFastMode write SetFastMode;
    property FrameStore:TFrameStore read FFrameStore write FFramestore;
    property MaskStore:TFrameStore read FMaskStore write FMaskStore;
    property Frame:integer read FFrame write SetFrame;
    property Framebm:TBitmap read FFramebm;
    property FrameMask:TBitmap read FFrameMask;
    property TransColor:TColor read FTransColor write FTransColor;
    property Xpos:Integer read FXpos write SetXpos;
    property YPos:Integer read FYpos write SetYpos;
    property ZPos:Integer read FZpos write SetZpos;
    property Map:TOverHeadMap read FMap write FMap;
    property OnScreen:Boolean read GetOnScreen;
    property Visible:Boolean read FVisible write SetVisible;
    property IsClone:Boolean read FIsClone write FIsClone;
    property RedrawBackground:Boolean read FRedrawBackground write
FRedrawBackground;

    procedure Render; virtual;
    procedure RenderCharacter(mapcoords:Boolean;cxpos,cypos:Integer;mask,bm,
wb:TBitmap); virtual;

    procedure Clone(Source:TMapCharacter); virtual;

    procedure SetCharacterCoords(x,y,z:Integer); virtual;
    procedure WriteData(Writer:Twriter); virtual;
    procedure ReadData(Reader:TReader); virtual;
    end;

implementation

constructor TMapCharacter.Create(ParentMap:TOverheadmap);
begin
     inherited Create;
     FIsClone:=False;
     FFramebm:=TBitMap.create;
     FFrameMask:=TBitmap.Create;
     FWorkbuf:=TBitMap.Create;
     if Not(FIsClone) then
        FFrameStore:=TFrameStore.Create;

     FTransColor:=clBlack;
     FFastMode:=False;
     FMap:=ParentMap; end;

destructor TMapCharacter.Destroy;
var
a,b:Integer;
begin
     FFramemask.free;
     FFramebm.free;
     FWorkBuf.Free;
     if Not(FIsClone) then begin
        FFrameStore.Clear;
        FFrameStore.free;
     end;

     if (MaskStore<>nil) and Not(FIsClone) then begin
        MaskStore.Clear;
        MaskStore.Free;
     end;
     inherited Destroy; end;

{
 This procedure copies the relevant information from a character into itself
...
      Clones start out invisible, with zeroed map coordinates.
}

procedure TMapCharacter.Clone(Source:TMapCharacter);
begin
     FName:=Source.Name;
     FFastMode:=Source.FastMode;
     FFrameStore:=Source.FrameStore;
     FMaskStore:=Source.MaskStore;
     FTransColor:=Source.TransColor;
     FMap:=Source.Map;
     FVisible:=False;

     Frame:=Source.Frame; {Trigger frame retrieval.}

     FIsClone:=True; end;

procedure TMapCharacter.SetXPos(x:Integer);
begin
     Map.Redraw(xpos,ypos,zpos,-1);
     FXpos:=x;
     Render;
end;

procedure TMapCharacter.SetYPos(y:Integer);
begin
     Map.Redraw(xpos,ypos,zpos,-1);
     FYPos:=y;
     Render;
end;

procedure TMapCharacter.SetZPos(z:Integer);
begin
     Map.Redraw(xpos,ypos,zpos,-1);
     FZpos:=z;
     Render;
end;

procedure TMapCharacter.SetCharacterCoords(x,y,z:Integer);
begin
     Map.Redraw(xpos,ypos,zpos,-1);
     Fxpos:=x; Fypos:=y; Fzpos:=z;
     Render;
end;

procedure TMapCharacter.SetFrame(num:Integer);
begin
     if (num<=FFrameStore.count-1) and (num>-1) then begin
        FFrame:=num;
        FFramebm.Assign(TBitmap(FFrameStore.items[num]));
        if Ffastmode=false then begin
           FFrameMask.Width:=FFramebm.width;
           FFrameMask.Height:=FFramebm.height;
           FWorkBuf.Height:=FFramebm.height;
           FWorkBuf.Width:=FFramebm.width;
           makeframemask(TransColor);
           replacetranscolor(TransColor);
        end
        else begin
             FWorkBuf.Height:=FFramebm.height;
             FWorkBuf.Width:=FFramebm.width;
             FFrameMask.Assign(TBitmap(FMaskStore.items[num]));
        end;
     end;
end;

procedure TMapCharacter.MakeFrameMask(trColor: TColor);
var
testbm1,testbm2: TBitmap;
trColorInv: TColor;
begin
  testbm1 := TBitmap.Create;
  testbm1.width := 1;
  testbm1.height:=1;
  testbm2 := TBitmap.Create;
  testbm2.width := 1;
  testbm2.height:=1;
  testbm1.Canvas.Pixels[0,0]:=trColor;
  testbm2.Canvas.CopyMode:=cmSrcInvert;
  testbm2.Canvas.Draw(0,0,testbm1);
  trColorInv:=testbm2.Canvas.Pixels[0,0];
  testbm1.free;
  testbm2.free;
  with FFrameMask.Canvas do
    begin
    Brush.Color:= trColorInv;
    BrushCopy( Rect(0,0,FFrameMask.Width,FFrameMask.Height),FFramebm,
               Rect(0,0,FFramebm.Width,FFramebm.Height),trColor);
    CopyMode:=cmSrcInvert;
    Draw(0,0,FFramebm);
    end;
end;
procedure TMapCharacter.ReplaceTransColor(trColor: TColor);
begin
  with FFramebm.Canvas do
    begin
    CopyMode:=cmSrcCopy;
    Brush.Color:= clBlack;
    BrushCopy( Rect(0,0,FFramebm.Width,FFramebm.Height),FFramebm,
               Rect(0,0,FFramebm.Width,FFramebm.Height),trColor);
    end;
end;

function TMapCharacter.GetOnScreen:Boolean;
var
dispx,dispy:Integer;
begin
     dispx:=Map.width div map.tilexdim;
     dispy:=Map.height div map.tileydim;
     if (xpos>=Map.xpos) and (xpos<=map.xpos+dispx) and (ypos>=map.ypos) and
(ypos>=map.ypos+dispy) then
        result:=true; end;

procedure TMapCharacter.SetVisible(vis:Boolean);
begin
     if vis and OnScreen then Render;
     FVisible:=vis;
end;

procedure TMapCharacter.SetFastMode(fast:Boolean);
begin
     if fast<>FFastMode then begin
        if fast=true then begin
           FMaskStore:=TFrameStore.Create;
           MakeFrameMasks;
           FFastMode:=True;
           frame:=0;
        end
        else begin
            FMaskStore.Free;
            FFastMode:=False;
        end;
     end;
end;

procedure TMapCharacter.MakeFrameMasks;
var
a:Integer;
bm:TBitMap;
begin
     if FFrameStore.count>0 then begin
        for a:=0 to FFrameStore.Count-1 do begin
            Frame:=a;
            bm:=TBitMap.create;
            bm.Assign(FFrameMask);
            FMaskStore.add(bm);
        end;
     end;
end;

procedure TMapCharacter.Render;
var
x,y:Integer;
begin
     if visible and onscreen then
        RenderCharacter(true,xpos,ypos,FFramemask,FFramebm,FWorkbuf);
end;

procedure TMapCharacter.RenderCharacter(mapcoords:Boolean;cxpos,cypos:
Integer;mask,bm,wb:TBitmap);
var
x,y:Integer;
begin
     if map.ready then begin
        {
        If the user specifies it in mapcoords, we handle redrawing the tile
(s) first.
        if not, he does.
        }
        if mapcoords then begin
           if FRedrawBackground then
              Map.redraw(cxpos,cypos,FMap.zpos,-1);
           wb.Canvas.Draw(0,0,TMapIcon(FMap.Iconset[map.zoomlevel].items
[FMap.Map.Iconat(cxpos,cypos,Map.zpos)]).image);
           x:=(cxpos-Map.xpos)*FMap.tilexdim;
           y:=(cypos-Map.ypos)*FMap.tileydim;
        end
        else
            wb.Canvas.Copyrect(rect(0,0,FMap.tilexdim,FMap.tileydim),FMap.
Screenbuffer.canvas,rect(x,y,x+FMap.tilexdim,
            y+FMap.tileydim));

        with wb do begin
             Map.Canvas.CopyMode := cmSrcAnd;
             Map.Canvas.Draw(0,0,Mask);
             Map.Canvas.CopyMode := cmSrcPaint;
             Map.Canvas.Draw(0,0,bm);
             Map.Canvas.Copymode:=cmSrcCopy;
        end;
        Map.Canvas.CopyRect(Rect(x,y,x+FMap.tilexdim,y+FMap.tileydim),wb.
canvas,
        Rect(0,0,FMap.tilexdim,FMap.tileydim));
     end;
end;


procedure TMapCharacter.WriteData(Writer:TWriter);
begin
     with Writer do begin
          WriteListBegin;
          WriteString(FName);
          WriteBoolean(FFastMode);
          WriteInteger(TransColor);
          FFrameStore.WriteData(Writer);
          if FFastMode then
             FMaskStore.WriteData(Writer);
          WriteListEnd;
     end;
end;

procedure TMapCharacter.ReadData(Reader:TReader);
begin
     with Reader do begin
          ReadListBegin;
          Fname:=ReadString;
          FFastMode:=ReadBoolean;
          TransColor:=ReadInteger;
          FFrameStore.ReadData(Reader);
          if FFastMode then begin
             FMaskStore:=TFrameStore.Create;
             FMaskStore.ReadData(Reader);
          end;
          ReadListEnd;
     end;
end;

procedure TMapCharacterList.RenderVisibleCharacters;
var
a:Integer;
begin
     for a:=0 to count-1 do
         TMapCharacter(items[a]).render;
end;

procedure TMapCharacterList.clear;
var
obj:TObject;
begin
     {This routine deallocates all resources inside this here list}
     if self.count>0 then
     begin
          repeat
                obj:=self.items[0];
                obj.free;
                self.remove(self.items[0]);
          until self.count=0;
     end;
end;

destructor TMapCharacterList.Destroy;
var
a:Integer;
begin
     if count>0 then
        for a:=0 to count-1 do
            TObject(items[a]).free;
     inherited destroy; end;

procedure TMapCharacterList.loadfromfile(const filename:string);
var
   i:Integer;
   Reader:Treader;
   Stream:TFileStream;
   obj:TMapCharacter; begin
     stream:=TFileStream.create(filename,fmOpenRead);
     try
        reader:=TReader.create(stream,$ff);
        try
           with reader do begin
                try
                   ReadSignature;
                   if ReadInteger<>$6667 then
                      Raise EReadError.Create('Not a character list.');
                except
                      Raise EReadError.Create('Not a valid file.');
                end;
                ReadListBegin;
                while not EndofList do begin
                    obj:=TMapCharacter.create(FMap);
                    try
                       obj.ReadData(reader);
                    except
                          obj.free;
                          raise EReadError.Create('Error in character list
file.');
                    end;
                    self.add(obj);
                end;
                ReadListEnd;
           end;
        finally
               reader.free;
        end;
     finally
            stream.free;
     end;
end;

procedure TMapCharacterList.savetofile(const filename:String);
var
   Stream:TFileStream;
   Writer:TWriter;
   i:Integer;
   obj:TMapCharacter; begin
     stream:=TFileStream.create(filename,fmCreate or fmOpenWrite);
     try
        writer:=TWriter.create(stream,$ff);
        try
           with writer do begin
                WriteSignature;
                WriteInteger($6667);
                WriteListBegin;
                for i:=0 to self.count-1 do
                    TMapCharacter(self.items[i]).writedata(writer);
                WriteListEnd;
           end;
        finally
               writer.free;
        end;
     finally
            stream.free;
     end;
end;

procedure TFrameStore.WriteData(Writer:TWriter);
var
mstream:TMemoryStream;
a,size:Longint;
begin
     mstream:=TMemoryStream.Create;
     try
        with writer do begin
             WriteListBegin;
             WriteInteger(count);
             for a:=0 to count-1 do begin
                 TBitmap(items[a]).savetostream(mstream);
                 size:=mstream.size;
                 WriteInteger(size);
                 Write(mstream.memory^,size);
                 mstream.position:=0;
             end;
             WriteListEnd;
        end;
     finally
            Mstream.free;
     end;
end;

procedure TFrameStore.ReadData(Reader:TReader);
var
mstream:TMemoryStream;
a,listcount,size:Longint;
newframe:TBitMap;
begin
     mstream:=TMemoryStream.create;
     try
        with reader do begin
             ReadListBegin;
             Listcount:=ReadInteger;
             for a:=1 to listcount do begin
                 size:=ReadInteger;
                 mstream.setsize(size);
                 read(mstream.Memory^,size);
                 newframe:=TBitmap.create;
                 newframe.loadfromstream(mstream);
                 add(newframe);
             end;
             ReadListEnd;
        end;
     finally
            Mstream.free;
     end;
end;

procedure TFrameStore.clear;
var
Obj:TObject;
begin
     {This routine deallocates all resources inside this here list}
     if self.count>0 then
     begin
          repeat
                obj:=self.items[0];
                obj.free;
                self.remove(self.items[0]);
          until self.count=0;
     end;
end;

end.

[Jason Maskell, maskell@quadrant.net]

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

64. Passing strings in the lParam part of a message
Q:
My problem is how to pass a string in a message. If I remember correctly,
this can only be done in the lParam part, but there is something missing
(Because it's not working!).

A:
The lParam part can be the address of a PChar (you shouldn't be
using Pascal strings). This can be done like this:
    var
      myStr : array[0..20] of char;
    begin
      StrPCopy(myStr,'This is an example');
      SendMessage(Handle,WM_setText,0,LongInt(@myStr));
    end;

[Bruno, Sonnino, bruno.sonnino@mandic.com.br]

A:
In the Win16 API, lParam is a longint (32-bit value), and wParam is a word
(16-bit value). So the only way to pass a string using lParam is by passing
a pointer to it. The Windows-defined messages (such as WM_SetText, for
example) are going to use a null-terminated string, which translates to a
PChar in Delphi. So if you're going to pass a string using one of the
standard windows messages you'll have to translate from a pascal string to a
PChar, like this:

procedure SetText(aWnd: hWnd; Value: string);
var
  p: array[0..255] of char;
begin
  StrPCopy(p, Value);
  SendMessage(aWnd, wm_SetText, 0, Longint(p));
end;

Note that in the above example it is necessary to 'type-cast' the PChar 
(which is essentially a pointer) to a longint, so the compiler won't reject 
it. Also, its rather interesting that the compiler understands an 'array[]
of char' to be the same thing as a PChar. In the above example, the StrPCopy
function (in SysUtils) specifies that the first parameter needs to be of type
PChar, but the type I passed to it looks like an array of char. So why
didn't I declare p as a PChar? Because declaring it as a PChar would only be
declaring a *pointer* to a string, whereas I needed to actually allocate the
space to store the string. I could also have done something like:

procedure SetText(aWnd: hWnd; Value: string);
var
  p: PChar;
begin
  GetMem(p, 256);
  try
    StrPCopy(p, Value);
    SendMessage(aWnd, wm_SetText, 0, Longint(p));
  finally
    FreeMem(p, 256);
  end;
end;

This would accomplish the same thing, but instead of allocating the storage
for the null-terminated string on the stack, this stores it on the heap.  And
this is fine, but I like the first example better because I don't have to
worry about cleaning up afterward, and it should be faster, too. Now, what
if I had used the PostMessage instead of SendMessage in the above example?
If I did that, I would probably get a GPF! Why? Because PostMessage only
places the message in a queue, while SendMessage delivers it before
continuing on to the next line. So by placing it in a queue, that means I
would continue on and deallocate the storage for the string before it
actually got delivered! So, you should almost always use SendMessage when
communicating with Windows controls (if you're going to do it the
old-fashioned way, that is!).

If you're talking about responding to a Windows message that uses a PChar in
lParam, you could break that out like this:

type
  TMyComponent = class(TWinControl)
  private
    procedure wmMyMessage(var Msg: Message); message wm_MyMessage;
  end;

procedure TMyComponent.wmMyMessage;
var
  st: string;
begin
  st := StrPas(PChar(Msg.lParam));
end;

Anyway, if you want to define your own user-defined messages, you could 
decide that you want to pass a pointer to a pascal string (PString) rather 
than a pointer to a null-terminate string (PChar). In this case you could
send the message like this:

SendMessage(aWnd, wm_MyMessage, 0, Longint(@MyString));

where MyString is a pascal string. Then in the component that receives the
message, you could typecast the lParam to PString, and dereference using the
'^'.  Like this:

procedure MyMessage(var Msg: Message);
begin
  Label1.Caption := PString(Msg.lParam)^;
end;

Whew!  This turned into a long message! As you can see, there a lot of
different things you can do with this. In fact, you can even pass whole
records, or a pointer to an object which then would allow you to access any
of its properties or methods! (But not from a DLL, sorry.) If you have any
more questions on this, feel free to ask. I've had quite a bit of experience
with this.

[Dan Butler, Dan_Butler@msn.com]

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

65. Linking functions
Q:
If I create a Delphi unit file (.PAS) with various functions and procedures
and use this unit in another unit but only use say one out of maybe five
functions will the other four be linked into the final .exe?

A:
Delphi has a very smart linker, which does not add unreferenced functions and
procedures to your final exe file.

This is actually better than almost all linkers used with C, which tend to
link in all functions in a given module (code file) if any one function is
referenced.

[Steven Dubnoff, sdubnoff@circlesys.com]

A:
I thought this to. But one of my forms had a "uses dll_link" where
dll_link was a component that used a dll. Although the component had
been removed from the form, the program would still crash if run
on a machine without the dll. Surely the linker should have stripped
all that code out, since I no longer called it, either explicitly,
nor, so far as I could follow, implicitly? Removing the "uses
dll_link" fixed the problem and reduced the exe size by 100k. So
obviously the linker hadn't stripped it out.

My conclusion is that the smart linker isn't quite smart enough to
strip all the unnecessary code out with a structure so convoluted
as delphi programs can get, what with my dumb coding, etc.

[Grahame Grieve, g.grieve@pgrad.unimelb.edu.au]

A:
If this is the case, why do you see mention of optimizing your compile size
by removing unneeded units (which delphi always includes by default) I have
seen size differences by doing this - why???

[Mitch Cant, pokada@Direct.ca]

A:
I just ran a quick test on "Smart Linking" with Delphi. I created a "nothing"
app: one window, nothing else.  In the FormCreate event, I put in two
variables and their initialization: one was a string, which I initialized to
'Hi there!' and the other was an Hwnd, which I initialized to "handle".

I created a second unit. In this unit, I included SysUtils, WinTypes, and
WinProcs. I created a function called "This". "This" takes two parameters:
an Hwnd and a String. It converts the string to a C-string, and calls
MessageBox. I did this because I wanted "This" do be a non-trivial function
(okay, it is trivial).

Anyway, the important thing is that there was no point where I called
"This". I did a "uses" in the form's unit for the second unit (where "This"
was sitting), but I never called "This".

I did a build, and noted the size of the executable.

Then, I went into the FormCreate procedure.  I called "This" with the
variables I initialized earlier (the string and the window handle).

I did a build, and noted the size.

The second executable, where "This" was being called, was about 300 bytes
larger than the first build. So, it looks like, in that case, it stripped
out the unused fcn.

[Animesh Karna, karna@omni.voicenet.com]

A:
The option "Optimize for size and load time" is a very different thing from
"smart-linking". Apparently most linkers waste a lot of space. It is a very
complicated thing to explain, and I don't understand all of it myself, but if
you want to read up on it, there's an article in MicroSoft Systems Journal,
the July 1993 issue, called "Liposuction your Corpulent Executables and
Remove Excess Fat". It is on the MSDN CD, if you have that. Anyway, to kind
of summarize the article, there is something known as "alignment", which is
similar to picking a good cluster size when creating a disk partition, that
often gets set poorly, wasting space. There are a number of other things as
well. Anyway, "Optimize for size and load time" is the same thing as running
the W8LOSS.EXE program (found in \Delphi\Bin) on your compiled app.

[Dan Butler, Dan_Butler@msn.com]

A:
One thing to remember is that when the linker does it's smart linking, it
doesn't actually run through the execution path of your code to see if a
particular function/procedure will be used.  It only checks to see if the
function/procedure "could" be used.  I haven't verified this, but there are
probably many such routines in the VCL that your program may never actually
use, but the code is linked in because the "possibility" exists that it
might be used.
In addition, I have noticed that a simple one-screen database application
quickly exceeds 500k, but when additional screens are added, the App does
not increase in size at the same rate.

[Tris Hopkins, hopman@ns.sal-junction.com]

A:
Types are not linked.  They are only used by the compiler itself.  Variables
would not be removed by the smart linker.  And I think initialization code
will ALWAYS be called.  When Delphi compiles a program, it is a two-step
process:  first, all the units of the program are compiled to create the .DCU
files.  Second, they are all linked together to get the .EXE file.  It is
during this second step that any un-referenced functions/procedures are
removed.  So, so far as you're concerned, when you're writing the program, it
really doesn't matter, because EVERYTHING is exported by the .DCU file.
Things will work as you would expect; there are no nasty surprises,
otherwise, it wouldn't be 'smart', and there would be an option to turn it
off.

[Dan Butler, Dan_Butler@msn.com]

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

66. FileName property in non-visual component
Q:
I'm trying to get a new non-visual component created, and one of the properties
will be a FileName property. How can I set it up so that at design time the
user will get the ellipses button on the property manager and get the file
open box if they click on it and then be able to select a file name that way?

A:
The following code is taken out of dsgnintf.pas (a file
worth exploring!) for the TMPLayer.filename
property, with help for C.Calvert..

In the component unit file header...

   TFileNameProperty = class (TStringProperty)
     public
      function getattributes: TPropertyattributes; override;
      procedure Edit; override;
    end;

add to the register function...
     RegisterPropertyEditor(Typeinfo(String),
       TMyComponent, 'Filename', TFileNameProperty);

 and the code...

function TFileNameProperty.GetAttributes;
begin
  Result := [paDialog];
end;

Procedure TFilenameProperty.edit;
var
  MFileOpen: TOpenDialog;
begin
  MFileOpen := TOpenDialog.Create(Application);
  MFileOpen.Filename := GetValue;
  MFileOpen.Filter := 'The Right Kind Of Files|*.*'; (* Put your own filter
here...*)
  MFileOpen.Options := MFileOpen.Options + [ofPathMustExist,ofFileMustExist];
  try
    if MFileOpen.Execute then SetValue(MFileOpen.Filename);
  finally
    MFileOpen.Free;
  end;
end;

[Grahame Grieve, g.grieve@pgrad.unimelb.edu.au]

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

67. Running without a form
Q:
Is it possible to run a Delphi application without creating a(ny) form?

A:
Yes, using the project manager window, you can Remove the Form which is created
automatically when you start a new project.

Additionally (to get the .exe down to a reasonable size) you can remove the
"Application.Run" statement and the "uses Forms" from the project source.

Obviously, you will then have to add _some_ functionality to the application
in the Project Source (or provide it in a separate unit, and call one or more
of the routines in that unit from the Project Source).

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

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

68. Calling functions from different MDIChilds
Q:
I'm working on a MDI-project. All MDIChilds have two functions called
'DefaultSize' and 'MinimalSize'. I would like to call this functions
from the MDIParent-form like this:

ActiveMDIChild.DefaultSize;

This isn't working. When the function is called directly, e.g.:

MyMDIChild1.DefaultSize;

it works. So here's my question:

Is it possible to access functions of the active MDIChild without
knowing it's name?

A:
Try this:

var
  MyMDIForm: TForm;

begin
 
  MyMDIForm:=ActiveMDIChild;
  MyMDIForm.DefaultSize;

end;

I have not tried it myself, but I'll hold my thumbs... :-)

[Rainier Lamers, QUESTEK@pixie.co.za]

A:
Try casting the reference to ActiveMDIChild:

TChild(ActiveMDIChild).SomeMethod;

[Donald A. Daugherty, bigd@raintown.com]

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

69. Changing a forms icon at runtime
Q:
I have an app that checks for new incoming files, which is basically a form
with a timer on it. When the timed event happens, if new files are found I want
to change the forms icons to something else.
I've tried the LoadFromFile routine, even assigning it from another form that's
hidden. Nothing changes the icon at runtime.
Also, what's the best way of storing multiple icons in the exe?  If it's a
resource file, how can I read the particular Icon I want?

A:
Set the Application.Icon property.
Store them in a Resource file then get them as needed using the WinAPI
LoadIcon function:  IE:

  if FileExists( 'Some Darn File Name Here' ) then  begin
    Application.Icon.Handle := LoadIcon( hInstance, 'ICON_NAM' );
 end;

 etc..

Note: The 'ICONNAME' is a PChar rerfereing to the Icon's Resource ID.

[Mark Lussier, mlussier@best.com]

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

70. Need help on overriding Sys Err
Q:
Want the app. to read and recognize a drive not ready (i.e., user selects a
floppy drive with no disk in drive) -  then want the app to display the error
box rather than Windows System Error display box.

A:
Here's a routine that will do the trick for diskette drives:

 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;
   PrevInt24, DiscardReturnValue: word;
 begin
   DisketteDriveReady := false;    {until proven otherwise}
   case DisketteDrive of
     'A', 'a':  Drive := 1;
     'B', 'b':  Drive := 2;
     else Exit;
   end; {case}
   PrevInt24 := SetErrorMode(SEM_FAILCRITICALERRORS);
   if DiskFree(Drive) <> -1 then
     DisketteDriveReady := true;
   DiscardReturnValue := SetErrorMode(PrevInt24);
 end; {DisketteDriveReady}

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

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

71. Transparent splash screen
Q:
It's splash screen is a circle, instead of the normal square. Everything behind
the circle (ie. what would normally be covered by the square) was visible.

Does anyone have any idea how they did this, or how I could do something
similar?

A:
Get the DC of the Screen and link it to a Canvas. Use the Canvas as you
would a forms canvas. Free the DC.

[caracena@henge.com]

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

72. Query gauge bar
Q:
How can i draw a gauge bar during a tquery.open session? I know the record
count of the table (=100%), but how can i get the actual record, which tquery
is processing? how can i operate during running query?

A:
Not possible.
The idea behind the TQuery object is to have a SQL server process the
request in the background and take the local machine completely out of
the loop. The app is never supposed to know what the TQuery is doing,
so a gauge that measures progress would have to get its data from a
SQL server. Since most SQL servers dont publish this info, you are
probably out of luck on this one...

[Khari Villela, squid@rt66.com]

A:
If you're using Paradox or DBase I think you can use the

DBIRegisterCallback function

>From the DBE users manual:

Usage:
Callbacks are used when a client application needs clarification about a given
engine function before completing an operation or to return information to the
client. DBIRegisterCallback allows the client to instruct the database engine
about what further actions should be taken by the engine upon occurence of an
event.

I've never used it before, so I can't give you the details.

[rcram@knoware.nl]

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

73. Screen resolution
Q:
Has anyone else noticed that your Delphi app looks wonderful on YOUR
windows setup, but gets screwed up on others?

A:
Here is an exerpt from Loyd's Help File

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

Screen Resolution

When designing forms, it is sometimes helpful to write the code  so
that the screen and all of its objects are displayed at the  same size
no matter  what the screen resolution is.  Here is  some code to show
how that is done:

implementation
const
  ScreenWidth: LongInt = 800; {I designed my form in 800x600 mode.}
  ScreenHeight: LongInt = 600;

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  scaled := true;
  if (screen.width <> ScreenWidth) then

  begin
    height := longint(height) * longint(screen.height) DIV
ScreenHeight;
    width := longint(width) * longint(screen.width) DIV ScreenWidth;
    scaleBy(screen.width, ScreenWidth);
  end;
end;


Then, you will want to have something that checks to see that  the
font sizes are OK.  Before you change the font's size, you  would need
to ensure the object actually has a font property by checking the
RTTI.   This can be done as follows:


USES typinfo;  {Add this to your USES statement.}

var
  i: integer;
begin
  for i := componentCount - 1 downto 0 do
    with components[i] do
    begin
      if GetPropInfo(ClassInfo, 'font') <> nil  then
        font.size := (NewFormWidth DIV OldFormWidth) * font.size;
    end;
end;

{This is the long way to do the same thing.}
var
  i: integer;
  p: PPropInfo;
begin
  for i := componentCount - 1 downto 0 do
    with components[i] do

    begin
      p := GetPropInfo(ClassInfo, 'font');
      if assigned(p) then
        font.size := (NewFormWidth DIV OldFormWidth) * font.size;
    end;
end;


Note:  not all objects have a FONT property.  This should be  enough
to get you started.

Note:  The following are issue to bear in mind when scaling  Delphi
applications (forms) on different screen resolutions:

* Decide early on in the form design stage whether you're  going to
allow the form to be scaled or not.  The advantage of  not scaling is
that nothing changes at runtime.  The  disadvantage of not scaling is
that nothing changes at runtime  (your form may be far too small or
too large to read on some  systems if it is not scaled).

* If you're NOT going to scale the form, set Scaled to False.

* Otherwise, set the Form's Scaled property to True.

* Set AutoScroll to False.  AutoScroll = True means 'don't  change the
form's frame size at runtime' which doesn't look  good when the
form's contents do change size.

* Set the form's font to a scaleable TrueType font, like  Arial.   MS
San Serif is an ok alternate, but remember that it  is still a
bitmapped font.  Only Arial will give you a font  within a pixel of
the desired height.  NOTE: If the font used  in an application is not
installed on the target computer, then  Windows will select an
alternative font within the same font  family to use instead.  This
font may not match the same size  of the original font any may cause
problems.


* Set the form's Position property to something other than
poDesigned.  poDesigned leaves the form where you left it at  design
time, which for me always winds up way off to the left  on my
1280x1024 screen -  and completely off the 640x480 screen.

* Don't crowd controls on the form - leave at least 4 pixels  between
controls, so that a one pixel change in border  locations (due to
scaling) won't show up as ugly overlapping  controls.

* For single line labels that are alLeft or alRight aligned,  set
AutoSize to True.  Otherwise, set AutoSize to False.


* Make sure there is enough blank space in a label component  to allow
for font width changes - a blank space that is 25% of  the length of
the current string display length is a little too  much, but safe.
(You'll need at least 30% expansion space for  string labels if you
plan to translate your app into other  languages) If AutoSize is
False, make sure you actually set  the label width appropriately.  If
AutoSize is True, make sure there is enough room for the label  to
grow on its own.

* In multi-line, word-wrapped labels, leave at least one line  of
blank space at the bottom.  You'll need this to catch the  overflow
when the text wraps differently when the font width  changes with
scaling. Don't assume that because you're using  large fonts, you
don't have to allow for text overflow -  somebody else's large  fonts
may be larger than yours!

* Be careful about opening a project in the IDE at different
resolutions.  The form's PixelsPerInch property will be  modified as
soon as the form is opened, and will be saved to  the DFM if you save
the project. It's best to test the app by  running it standalone, and
edit the form at only one  resolution. Editing at varying resolutions
and font sizes  invites component drift  and sizing problems.


* Speaking of component drift, don't rescale a form multiple  times,
at design time or a runtime.  Each rescaling introduces  roundoff
errors which accumulate very quickly since coordinates  are  strictly
integral.  As fractional amounts are truncated  off control's origins
and sizes with each successive  rescaling,  the controls will appear
to creep northwest and get  smaller. If you want to allow your users
to rescale the form  any number  of times, start with a freshly
loaded/created form  before each  scaling, so that scaling errors do
not accumulate.

 * Don't change the PixelsPerInch property of the form, period.

* In general, it is not necessary to design forms at any  particular
resolution, but it is crucial that you review their  appearance at
640x480 with small fonts and large, and at a  high-resolution with
small fonts and large before releasing  your app.  This should be
part of your regular system  
compatibility testing checklist.

* Pay close attention to any components that are essentially
single-line TMemos - things like TDBLookupCombo.  The Windows
multi-line edit control always shows only whole lines of text  -  if
the control is too short for its font, a TMemo will show   nothing at
all (a TEdit will show clipped text). For such   components, it's
better to make them a few pixels too large  than to be one pixel too
small and show not text at all.


* Keep in mind that all scaling is proportional to the  difference  in
the font height between runtime and design time,  NOT the pixel
resolution or screen size.  Remember also that  the origins of your
controls will be changed when the form is  scaled - you can't very
well make components bigger without  also moving them over a bit.

{This code came from Lloyd's help file!}

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

74. Difference of TMemoFields
Q:
While I'm on the subject can someone please explain to me why this doesn't
work! I'm afraid I just don't understand pointers all the time.

procedure TForm1.Button1Click(Sender: TObject);
var
  Buffer: Pointer;
  MyStrings: TStringList;

begin
  MyStrings := TStringList.Create;
  GetMem(Buffer, Table1Notes.DataSize);
     {After this call Pointer's Addr becomes nil?}

  Table1Notes.GetData(Buffer); {Of course this doesn't work, Pointer is nil }

  FreeMem(Buffer, Table1Notes.DataSize);
  MyStrings.Free;
end;

A:
First, if the size argument to GetMem is zero, GetMem will set the pointer
to nil (not leave it the way it was, but actually assign nil to it).  Might
want to check the value of DataSize (or getTextLen) in your debugger before
the call.

(Ignore this paragraph if Table1Notes is not a memo.)
Second, if Table1Notes is a memo field, you probably want to use
Table1Notes.getTextLen, not DataSize, since DataSize gives you the length of
the record buffer segment (0-254) while getTextLen gives you the actual size
of the memo.  (For a string field, DataSize will work, but then it's curious
as to why it's zero.)  Also, might want to consider getTextBuf rather than
getData, don't exactly know why, but I played around with them awhile ago
and getTextBuf seemed to work while getData didn't.

[Sid Gudes, cougar@roadrunner.com]

A:
Since wordwrapping is your application, you can just replace the #10 (line
feed) and #13 (carriage return) characters with spaces, eg.

   cursor: pchar;

   cursor := your buffer;
   while cursor^ <> #0 do
      if (cursor^ = #13) or (cursor^ = #10) then cursor^ := ' ';

This is easy because we don't have to move any text around, although it does
mean there'll be two blanks at the end of each line, which shouldn't matter
since you're word wrapping anyway.  Alternatively, you can change them to
some special character that your word wrapper recognizes as a word break but
otherwise discards (eg. #8).  If you need to get rid of them, use two
cursors as follows (OTTOMH, not tested):

   out, in: pchar;

   out := your buffer;
   in := out;
   while in^ <> #0 do begin
      if (in^ <> #10) and (in^ <> #13) then begin
          out^ := in^;
          inc (out);
      end;
      inc (in);
   end;
   out^ := #0;

[Sid Gudes, cougar@roadrunner.com]

A:
If you instead want to replace every CR-LF pair or a single CR or LF with a
single space you could use this:

  out, inn: PChar;

  out := your byffer;
  inn := out;
  while in^ <> #0 do begin
    if (in^ = #10) then begin
    end
    else if (in^ = #13) then begin
      if (in+1)^

[Tommy Ericson, teit@pi.se]

A:
If you instead want to replace every CR-LF pair or a single CR or LF with a
single space you could use this:

  out, inn: PChar;

  out := buf;
  inn := out;
  while inn^ <> #0 do begin
    if (inn^ = #10) or ((inn^ = #13) and ((inn+1)^ <> #10)) then begin
      out^ := ' ';
      Inc(out);
    end
    else if (inn^ = #13) then
      { CR alone - ignore }
    else begin
      out^ := inn^;
      Inc(out);
    end;
    Inc(inn);
  end;
  out^ := #0;
  { buf is now massaged }

Untested is: the effect of shortening (by way of setting the #0 terminator)
this PChar - safest to use compile time arrays or GetMem'ed buffers, what
would happen using StrAlloc/StrDispose?

[Tommy Ericson, teit@pi.se]

A:
Here is
the final code after some small changes! For example, at the end, we needed
to tell the pointer to go back to the beginning of its new string.

procedure TForm1.RemoveSpaces(var InBuf: PChar; Size: Word);
var
  Input,
  OutPut,
  Orig: PChar;
begin
  GetMem(Output, Size);
  input := Inbuf;
  Orig := Output;
  while input^ <> #0 do
  begin
    if (input^ <> #10) and (input^ <> #13) then
    begin
      output^ := input^;
      inc (output);
    end;
    inc (input);
  end;
  Output^ := #0;
  Output := Orig;
  InBuf := Output;
end;

I still wonder about that darn GetData thing! I would still like to not have
to use a TMemo! If Anyone out there can solve this problem I'd be very
greatfull! I'd give you a complimetary first hand look at my new printing
rountines! That's what this whole mess is for anyhow! So far I have implemented
text output in any font at any position in inches, and all that basic stuff!
But what I think is really cool, Is my dynamic grid! You can create a grid of
any number of rows and column. Assign text to and cell, setting its Horizontal
AND Vertical Justification, Select border style for each cell and many ways to
manipulate and print these!

[Brett Fleming, bfleming@vt.edu]

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

75. Drag and drop
Q:
1st problem:
I'm using the DragnDrop from Filemanager with the API "DragAcceptFiles(Handle,
True)" which works fine. But I want to Drop the File(s) / Dir(s) into an
Outline. So I need to (somehow) detect when the Mouse is moved over my
Application so that I can Select on which Outline Node to drop it. As soon as
my App looses the Focus I can't track any mouse movements!  Must be an API
that lets me do this, right?

2nd problem:
How do you do the reverse? (dragnDrop to Filemanager)

A:
What you need to do is trap the wm_DropFiles message in the TOutline
component. This will require creating a descendant of it. You should also
make sure that TOutline's Handle is the one you're passing to
DragAcceptFiles. You can tell where the mouse is at the time of the drop by
using DragQueryPoint. If you read the help in WINAPI.HLP on DragAcceptFiles,
wm_DropFiles, DragQueryFile, DragQueryPoint, and DragFinish, you should be
able to figure out how that all works.

[Dan Butler, Dan_Butler@msn.com]

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

76. Strings and messages
Q:
I have an app which sends a message:

var
  strSend: String;
begin
  strSend:=dfEdit.Text;
  PostMessage (hWndDest, PM_Update, 0, longint(@strSend));
end;

My Receiving app has the following:

with msg do
  dfReceive.Text := strPas (PChar(lParam));

The problem is as follows:
  The string received/displayed is always prefixed with an unprintable 
character, which gets displayed as a square box. The other problem is that 
if the first string sent is, for example 'HELLO', and the second string
sent is 'IAN', the output will be 'IANLO' (With the square box as well).

A:
I see two problems with the example you give.  The first one is you're mixing
Pascal strings and PChars.  You should stick with one or the other.  Also, if
you're allocating storage for the string on the stack local to a procedure or 
function), and you use PostMessage, you will likely have problems because it 
will be deallocated before the message is received.  Try this:

procedure TForm2.Button1Click(Sender: TObject);
var
  st: PString;
begin
  st := NewStr(Edit1.Text);
  PostMessage(MainForm.Handle, am_MyMessage, 0, Longint(st));
end;

procedure TForm1.amMyMessage(var Msg: TMessage);
var
  st: PString;
begin
  st := PString(Msg.lParam);
  Label1.Caption := st^;
  DisposeStr(st);
end;

The above should work as long as the message is sent and received in the same 
app.  And this should work in Delphi32 as well.  The problem with sending it 
between applications is that Delphi has a memory suballocator which will get 
all confused if you try to free the memory from another app.  If you must
send it between apps, you should either make sure the string is allocated
statically (by placing it outside of any object declaration and outside of 
any procedure or function, and don't use NewStr and DisposeStr), or allocate 
and deallocate it using GlobalAlloc and GlobalFree, which are the Windows API 
functions for that purpose.  To declare it statically, do the following:

var
  TempStorage: string;

procedure TForm2.Button1Click(Sender: TObject);
begin
  TempStorage := Edit1.Text;
  PostMessage(MainForm.Handle, am_MyMessage, 0, Longint(@TempStorage));
end;

In second app, 

procedure TForm1.amMyMessage(var Msg: TMessage);
begin
  Label1.Caption := PString(Msg.lParam)^;
end;

Of course, you won't have the problem of the memory being deallocated before 
the message arrives if you can use SendMessage instead.  In that case, you 
could do the following for the first method:

procedure TForm2.Button1Click(Sender: TObject);
var
  TempStorage: string;
begin
  TempStorage := Edit1.Text;
  PostMessage(MainForm.Handle, am_MyMessage, 0, Longint(@TempStorage));
end;

and leave the second one alone.

[Dan Butler, Dan_Butler@msn.com]

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

77. DDE- passing text
Q:
I just want to pass a text value from a ddeclient to a ddeserver using
pokedata.
I have , however, found it strangely difficult. I've put all relevant
values as watches to see what was happening and found that the ddeItem
property was a nil string!! It should be something like ddeServerItem1.
The following is some example code. See if you can spot any deliberate
mistakes.


procedure TForm2.Button2Click(Sender: TObject);
Var
  DDeCli : TDdeClientConv;
  SPtr : array[0..200] of char;
begin
 {DdeClientItem1.DdeItem := 'DdeServerItem1'; what it should be?}
  DdeCli := DdeClientItem1.DdeConv;
  if DdeCli  <> nil then
    DdeCli.PokeData(DdeClientItem1.DdeItem, StrPCopy(SPtr, Edit1.Text));
end;

As you can see when button2 is hit it should poke the data from an
editbox upto the ddeServer. It doesn't work. {even when i included the
bracketed code}
What am i missing. I bet it's pretty obvious.

A:
     This works for me when poking to Excel:

        DDEClientConv1.SetLink('Excel','Sheet1');
        try
          DDEClientConv1.OpenLink;
          DDEClientItem1.DDEItem:= 'R1C1';
          DDEClientConv1.PokeData(DDEClientItem1.DDEItem,
                    StrPCopy(P, SomeString)));
        finally
          DDEClientConv1.CloseLink;
        end;

     As you see the DDEItem property is determined by the server. If your
     server is a Delphi application, the DDEItem is the name of the
     DDEServerItem. I wouldn't trust debugging DDE programs too much, if I
     were you. There is a lot of synchronization involved, that goes either
     right or wrong when debugging.

[Palle Due Larsen, pdl@vki.dk]

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

78. Load a BMP in a EXE file
Q:
How i can load in an TImage a BMP stored as bytes at the end of a file EXE?

A:
Use Resource Workshop (workshop.exe) to put your bitmap into a
Resource file (example: BITMAP.RES) and give it a name (example:
Bitmap1) and save the resource file in your source directory.

Add this line to the unit that will load the bitmap:

{$R BITMAP.RES}

This line will tell Delphi to link the resouce file into you program
for you.

In this example I use MyImage as the name of the TImage component
that was placed on the form. The following lines show an example of
how to load the bitmap at runtime:

var
  ImageName : STRING[10];
...
MyImage.AutoSize := TRUE;
ImageName := 'Bitmap1' + #0;
MyImage.Picture.Bitmap.Handle := LoadBitmap(hInstance, @ImageName[1]);

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

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

79. DLL's and Memory Management
Q:
Does anyone know how Delphi manages it's memory?
I have an app written in Delphi, which calls a DLL, also written in Delphi. I
have done a lot of work on both, and now whenever I try to run the two, I get
a GPF (I now suspect it is a memory problem).
First, how can I guestimate how much memory is used by my DLL and how much is
used by the EXE?
Where does the DLL get its memory from?

A:
I doubt if the GPF is being caused by a shortage of memory. If it was stack
shortage you would get Error 202. In the case of heap memory, Windows supports
virtual global memory so the normal symptom of a shortage of real memory is a
drastic slow-down rather than a GPF.

    From the "Writing DLLs" on-line help:

Global Variables: A DLL has its own data segment and any variables declared
in a DLL are private to that DLL. A DLL cannot access variables declared by
modules that call the DLL, and it is not possible for a DLL to export its
variables for use by other modules. Such access must take place through a
procedural interface.

DLLs and Stack Segments: A DLL does not have its own stack segment, so it uses
the stack segment of the application that called the DLL.

Also note the following advice from "Run-time errors in DLLs"

If a run-time error does occur in a DLL, the safest thing to do is to exit
Windows entirely. If you simply try to modify and rebuild the faulty DLL code,
when you run your program again, Windows will not load the new version of the
DLL if the faulty one is still in memory. Exiting Windows and then restarting
Windows and Delphi ensures that your corrected version of the DLL is loaded.

I would try and re-create the problem with the DLL code in a normal unit so
that the debugger can help you.

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

A:
If you've ever developed a serious DLL, you'd know that exiting Windows every
time you get a GPF is totally impractical.  You can generally just kill it
from memory and recompile and everything is fine.  I've done this many, many
times.  Also, once you have your DLL working, it is often impractical to move
the offending code to a 'normal' unit within an EXE to debug it.  So, the
answer to this problem is Turbo Debugger for Windows, which comes in the RAD
pack.  To debug a DLL with it, you can simply include a 'hard' breakpoint by
adding 'inline($cc);' to your code near the offending area, or immediately
after the entry point to the DLL.  This will wake up the debugger and you can
step through your code.

[Dan Butler, Dan_Butler@msn.com]

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

80. How to call a function with a TFarProc
Q:
I gather I can use GetProcAddress to return a pointer to a procedure in a
.drv file. For example, ExtDeviceMode to get or change some printer settings.

Does anyone know how to use the pointer to call the function? I get a
type mismatch however I try.

A:
In the WinProcs unit, ExtDeviceMode is declared as a 'type' of procedure,
like this:

type
  TExtDeviceMode = function(Wnd: HWnd; Driver: THandle;
    var DevModeOutput: TDevMode; DeciveName, Port: PChar;
    var DevModeInput: TDevMode; Profile: PChar; Mode: Word): Integer;

So, what you want is to declare a variable of that type, which will in
reality be a pointer, load the printer driver, and get the address to the
function, like this:

var
  aModule: THandle;
  ExtDeviceMode: TExtDeviceMode;
begin
  aModule := LoadLibrary('HPPCL.DRV');
  if DeviceModule < 32 then
    raise Exception.Create('Can''t load printer driver');
  try
    @ExtDeviceMode := GetProcAddress(aModule, 'ExtDeviceMode');
    if (@ExtDeviceMode = nil) then
      raise Exception.Create('Can''t find ExtDeviceMode function');
    ExtDeviceMode(   ...  );

  finally
    FreeLibrary(aModule);
  end;
end;

[Dan Butler, Dan_Butler@msn.com]

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

81. DLL: killing utility
Q:
DLL: killing utility?

A:
Here's a simple method, taken from a small project that I built for the
purpose of removing a DLL from memory.  There is one TEdit box called
EditDLLName, an Ok button and a Close button.  The following is the code that 
executes when you click on the Ok button:

procedure TForm1.OkBtnClick(Sender: TObject);
var
  hDLL        : THandle;
  aName       : array[0..10] of char;
  FoundDLL    : Boolean;
begin
  if EditDLLName.Text = '' then
  begin
    MessageDlg('You must first enter the name of a DLL to 
unload!',mtInformation,[mbOk],0);
    exit;
  end;
  StrPCopy(aName, EditDLLName.Text);
  FoundDLL := false;
  repeat
    hDLL := GetModuleHandle(aName);
    if hDLL = 0 then
      break;
    FoundDLL := true;
    FreeLibrary(hDLL);
  until false;
  if FoundDLL then
    MessageDlg('Success!',mtInformation,[mbOk],0)
  else
    MessageDlg('DLL not found!',mtInformation,[mbOk],0);
  EditDLLName.Text := '';
end;

[Dan Butler, Dan_Butler@msn.com]

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

82. Pre-setting TDBLookupCombo boxes
Q:
How do you "pre-set" the value of a TDBLookupCombo box?
Example:  when the user brings up a form and goes into edit mode.. there are
several TBDBLookupCombo boxes on it... (about 17-20 per form).
Aall of the combo's have their lookupsource, lookupdisplay, and lookupfield
properties set the same... but this still forces the user to select each
combo box and pick a value.
I'd like to have all of combo's pre-set when the user goes into edit mode.

A:
You can edit your datasource. Say, you want to save your data lookuping
from customer table into sales table - 'Cust No'. You can simply preset
(default value) by editing sales table "Cust No"

   with tbSales do
   begin
      Edit;
      FieldByName('Cust No').AsInteger := 1;
      Post;
   end;

[Stephen Tse, stephent@asiaonline.net]

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

83. Variable length record and phrase search
Q:
I wonder if any of you have done this. I am trying to redo a Windows
application for a Helpdesk application.
The database must allow variable length text. And it must allow search based
on synonyms or fuzzy match. Example "PC kaput" will still retrieve "PC hang"
and "hanged PC" will get "PC hang" too.
I did this before but it was rather slow even when I have my operators up to
30 Meg RAM and CPU 100 Mhz Overdrive processors.

A:
For variable length text you can use a DBmemo.  Most people will do this by
scanning "on the fly" (when an operator poses a query), but to really speed
up  processing, try pre-scanning the way the "big boys" do it (ie. operators
of large databases):

1) when a new entry is placed into the database, it is scanned for keywords
(this can be either a pre-defined list of keywords or any word not in an
exclusion list [eg. "the", "of", "and"])

2) the keywords are added to a keyword list referencing the record number,
eg. "hang",46 or "PC",22.

3) when a user does a query, you retrieve all records meeting each keyword,
eg. "hang" might return record numbers 11, 46, and 22, while "PC" might
return record numbers 91, 22, and 15.

4) you merge the numbers on each list, which in the above example results in
record 22 (if it is an AND match) or records 11, 15, 22, 46, and 91 (if it
is an OR match).  You then retrieve and display those records.

5) for synonyms, you define a synonym table (eg. "hang","kaput") and look up
the synonyms too, adding them to the same list as the original word.

6) for common endings (eg. "hang" and "hanged"), you can either make them
synonynms, have your program analyze common word endings, or what most
systems do is allow a partial match (eg. "hang" will match any word whose
first 4 letters are "hang").

Of course, there are a lot of technical details to be determined, most
especially how to organize the lists so they are most efficiently merged and
managed.  This can give you very fast retrieval times (witness search
engines such as Nexus, Lycos, or WebCrawler, searching hundreds of thousands
of records in less than a second).

[Sid Gudes, cougar@roadrunner.com]

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

84. Commenting out large sections of code
Q:
While we're on this kind of thing, has anyone got a quick way of commenting
out a highlighted block of code lines??
It must work correctly with pre-existing embedded comments in {braces}.
Otherwise it's back to the Windows recorder!

A:
There are 2 sets of comment characters in pascal, {} and (* *). You can
embed one sort in the other.
Hence putting a (* at the start of your block and a *) at then end would
still work with embedded { } comments.

[Daryl Maunder, dmaunder@vitgcom1.telecom.com.au]

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

85. Tables in memory
Q:
How do I make a virtual table (in memory)?

A:
{
This is an InMemoryTable example. Free for anyone to use, modify and do
whatever else you wish.
Just like all things free it comes with no guarantees. I cannot be
responsible for any damage this code may cause. Let me repeat this:

 WARNING! THIS CODE IS PROVIDED AS IS WITH NO GUARANTEES OF ANY KIND!

 USE THIS AT YOUR OWN RISK - YOU ARE THE ONLY PERSON RESPONSIBLE FOR

 ANY DAMAGE THIS CODE MAY CAUSE - YOU HAVE BEEN WARNED!

THANKS to Steve Garland <72700.2407@compuserve.com> for his help. He
created his own variation of an in-memory table component and I used it
to get started.

InMemory tables are a feature of the Borland Database Engine (BDE).
InMemory tables are created in RAM and deleted when you close them. They
are much faster and are very useful when you need fast operations on
small tables. This example uses the DbiCreateInMemoryTable DBE function call.
This object should work just like a regular table, except InMemory
tables do not support certain features (like referntial integrity, secondary
indexes and BLOBs) and currently this code doesn't do anything to
prevent you from trying to use them. You will probably get some error if
you try to create a memo field.
If you have comments - please contact me at INTERNET:grisha@mira.com
}

unit Inmem;

interface

uses DBTables, WinTypes, WinProcs, DBITypes, DBIProcs, DB, SysUtils;

type TInMemoryTable = class(TTable)
     private
       hCursor: hDBICur;
       procedure EncodeFieldDesc(var FieldDesc: FLDDesc;
       const Name: string; DataType: TFieldType; Size: Word);
       function CreateHandle: HDBICur; override;
    public
      procedure CreateTable;
    end;

implementation

{ luckely this function is virtual - so I could override it. In the
original VCL code for TTable this function actually opens the table - but since
we already have the handle to the table - we just return it }

function TInMemoryTable.CreateHandle;
begin
  Result := hCursor;
end;

{ This function is cut-and-pasted from the VCL source code. I had to do
this because it is declared private in the TTable component so I had no
access to it from here. }

procedure TInMemoryTable.EncodeFieldDesc(var FieldDesc: FLDDesc;
  const Name: string; DataType: TFieldType; Size: Word);
const
  TypeMap: array[TFieldType] of Byte = (
    fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, fldBOOL,
    fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, fldBYTES,
    fldVARBYTES, fldBLOB, fldBLOB, fldBLOB);
begin
  with FieldDesc do
  begin
    AnsiToNative(Locale, Name, szName, SizeOf(szName) - 1);
    iFldType := TypeMap[DataType];
    case DataType of
      ftString, ftBytes, ftVarBytes, ftBlob, ftMemo, ftGraphic:
        iUnits1 := Size;
      ftBCD:
        begin
          iUnits1 := 32;
          iUnits2 := Size;
        end;
    end;
    case DataType of
      ftCurrency:
        iSubType := fldstMONEY;
      ftBlob:
        iSubType := fldstBINARY;
      ftMemo:
        iSubType := fldstMEMO;
      ftGraphic:
        iSubType := fldstGRAPHIC;
    end;
  end;
end;

{ This is where all the fun happens. I copied this function from the VCL
source and then changed it to use DbiCreateInMemoryTable instead of
DbiCreateTable.
Since InMemory tables do not support Indexes - I took all of the
index-related things out }

procedure TInMemoryTable.CreateTable;
var
  I: Integer;
  pFieldDesc: pFLDDesc;
  szTblName: DBITBLNAME;
  iFields: Word;
  Dogs: pfldDesc;
begin
  CheckInactive;
  if FieldDefs.Count = 0 then
    for I := 0 to FieldCount - 1 do
      with Fields[I] do
        if not Calculated then
          FieldDefs.Add(FieldName, DataType, Size, Required);
  pFieldDesc := nil;
  SetDBFlag(dbfTable, True);
  try
    AnsiToNative(Locale, TableName, szTblName, SizeOf(szTblName) - 1);
    iFields := FieldDefs.Count;
    pFieldDesc := AllocMem(iFields * SizeOf(FLDDesc));
    for I := 0 to FieldDefs.Count - 1 do
      with FieldDefs[I] do
      begin
        EncodeFieldDesc(PFieldDescList(pFieldDesc)^[I], Name,
          DataType, Size);
      end;
      { the driver type is nil = logical fields }
      Check(DbiTranslateRecordStructure(nil, iFields, pFieldDesc,
        nil, nil, pFieldDesc));
      { here we go - this is where hCursor gets its value }
    Check(DbiCreateInMemTable(DBHandle, szTblName, iFields, pFieldDesc,
hCursor));
  finally
    if pFieldDesc <> nil then FreeMem(pFieldDesc, iFields *
SizeOf(FLDDesc));
    SetDBFlag(dbfTable, False);
  end;
end;

end.

{This code came from Lloyd's help file!}

[Cliff Nel, Nelc@Telkom15.telkom.co.za]

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

86. Put a variable in a memo field
Q:
How do I get a variable into a Memo Field. I want to ask the user for
his / her name and display that name, plus more, in a memo.

A:
If I realized your question, I thing you need something like this (to GET data)

  Memos := TStringList.Create;
  Memos.Assign(Table1Memo);
  yourvariable_0 := Memos[0];
  yourvariable_1 := Memos[1];
  ..........................
  yourvariable_n := Memos[n];
  Memos.Free;

and like this (to SET data)

  Memos := TStringList.Create;
  Memos.Add(yourvariable_0);
  Memos.Add(yourvariable_1);
  ..........................
  Memos.Add(yourvariable_n);
  Table1Memo.Assign(Memos);
  Memos.Free;

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

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

87. Printing bitmaps
Q:
What is the best way to print a bitmap so that the width= Pagewidth?
(with the size ratio mantained)

A:
Var
  GRect : TRcet;
Begin
  GRect.Left:=0;
  GRect.Top:=0;
  With Printer Do
  Begin
    BeginDoc;
    With Image1.Picture Do
      If BitMap.Height/BitMap.Width < PageHeight/PageWidth Then
      Begin
        GRect.Bottom := Trunc(BitMap.Height/BitMap.Width * PageWidth);
        GRect.Right := PageWidth
      End
      Else
      Begin
        GRect.Bottom := PageHeight;
        GRect.Right := Trunc(BitMap.Width/BitMap.Height * PageHeight)
      End;
    Canvas.StretchDraw(GRect, Image1.Picture.BitMap);
    EndDoc
  End
End;

No Guarantees

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

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

88. Simulating a pause in a loop
Q:
I'm looking for some help in simulating a pause within a loop. This
is my scenario :
Form1.Procedure initiates a while..do loop which displays data on
Form2. When the Form2 is filled (part way through the Form1 loop) I
want to Show Form2. On Form2 are two buttons <Continue> and <Cancel>,
which if the user selects <Continue> passes control back to the Form1
loop for continuation of processing.. but I DO NOT want to Close
Form2.. hence I cannot use ShowModal.
My attempts to date have been to pass control to a function in Form2
which will return true/false depending on the button selected, but I
still can't work out how, on a button click, to return info back to
the original function called in Form2 so it can return the
appropriate result ie I still need a 'wait until a button is pressed
on this form' loop (or equivalent) within the function.

A:
in response to the following:
what about this:
pass control to you funtion, and in you function,

buttonpressed:=0;
repeat
  Application.processmessages;
until buttonpressed<>0;
result:=buttonpressed;

in your on click methods, you could set the buttonpressed variable to a value
other than 0 to indicate which was pressed.

[Paul Okada, pokada@Direct.ca]

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

89. Duplicate resource identifier error
Q:
Duplicate Resource identifier (c:/delphi/lib/controls.res)  ERROR!
I keep getting this... I don't know what's wrong... my project .RES
called (project1.res) doesn't have a single thing that's in common
with controls.res!
This is driving me absolutely nuts... the only thing I ever did was
to delete {$R .res} in the project file but since the, I've replaced
that line...

A:
Do you have the VCL source?  If so, you may want to recompile the whole thing
by adding that directory to your Library path in Environment Options | Library.
I think I had to do this once to get rid of such an error.  Another thing to
do is to figure out which $R directive is causing the problem, by removing it
temporarily and recompiling.  You can temporarily disable the $R directive by
adding a '.' before the $ (that's one of many ways to do it).

[Dan Butler, Dan_Butler@msn.com]

A:
You probably have subclassed one of the built in VCL's.  You need to be sure
that the resource identifier for your icon is unique.  Just pull it up in any
resource editor and change its number. Then you should be able to rebuild your
library.

[Dave Fuller, dpfuller@icon.net]

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

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

90. Table locking
Q:
If one people is editing the record, the other people cannot view
the record. Can I prompt the user that the message " The record is currently
edited by other user"?

A:
When you get this or similars error, you can intercept these using the try
construct in this way (supposing you are trying to post a record):

  try
     Table1.Post;
  except
     MessageDlg ('Error posting record', etc...
     Table1.Cancel;
  end;

Otherwise, you -shouldn't- get an error if an looks to a record currently
viewed by another user (if you are using the Paradox database provided with
Delphi) if you had correctly set it. Paradox self-creates a file called
pdxusers.lck viewed by every users in the net dir, so every BDE on every
local machine can be able to lock a record forbiding other users to post it
until he had relased. I can't imagine what kind of things you are doing to
get this error, if I don't know some other specs.

[Marco Ermini, marko@softeam.it]

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

91. Callback functions
Q:
What is a callback function and is it supported by Delphi?

A:
A Callback function is a windows function that gets called every time
something happens.  You could sorta say that message events are callback
functions, but they're not.  You can do callback functions in delphi but i
wouldn't say it "supports" them. I.e. you have to declare callback
functions(the dec's are in winapi.hlp) the declarations and constants are
not in wintypes or winprocs etc. You also have to put callback functions
into a .dll i believe there a ways getting around that, but in the help it
says to put callback function code into a dll.  Callback functions can do
lots of advanced windows things like hooks,subclassing other apps etc.

To subclass another app use setwindowlong(). You'll see help in winapi.hlp

To install hooks on certain things look up setwindowshookex() the VB
sendkeys function is an example of the wh_journalplayback hook.
Alot of these functions in my eyes are best used to make add on programs for
other programs.  or to tell what's happening in another program.  By
subclassing another program you can find out everything that is sent to it,
and handle its messages etc. An example could be you subclass aol or prodigy
chat and allow alias's and actions and such in the chatroom. (there'd be no
need to subclass mirc as it already supports 'em,just in case anybody didn't
like the example)
You can also do many other powerful things with hook/callback functions such as

Creating a win95 style taskbar for win 3.1
Finding out whats heading thru the comport
Make programs like interactive tutors that came with delphi

I can't really think of anything else offhand, if your trying to figure out
whether you need to or should use a callback function, let us know what
you're trying to do and somebody(probably not me) will know what the best
call is.

[Michael Donohue, di38559@goodnet.com]

A:
Callback functions is procedures/functions that you use to notify (for
instance) something in your code. For example, imagine a procedure that
install a software in your hard disk. In the Install procedure, you may want
to update a gauge in your screen to tell the user how much of the installation
already has passed, right? So, you can pass a procedure in a parameter of the
Install procedure that points to another function that update this gauge. Too
confusing? An example:

Type
    TGaugeUpdateFunc = procedure( pos : integer );


( ... )

procedure Install( GaugeFunc : TGaugeUpdateFunc; drive : byte );
var
        Percent : integer;
begin
   { do some code here }
   
   { Update the gauge }
   GaugeFunc( Percent );
end;

So, what this example tell us? It tell us that there is a private attribute
called FOnError that is a "callback" function. So, in your test function, if
a situation causes an error, you may call the OnError routine to signal that
an error happenned. The Assigned is nothing more than FOnError <> Nil.
Well, i think this may clear something in this topic. If not, or if i am
mistaken in something, tell me and we can debate on this more.

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

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

92. Change wallpaper bitmap
Q:
I want to change wallpaper (win95 desktop) bitmaps run-time.
How can i do it?

A:
The same way as in Win31 - using the SystemParametersInfo API call.

SystemParametersInfo(SPI_SETDESKWALLPAPER,0,pBitMap,SPIF_UPDATEINIFILE +
                        SPIF_SENDWININICHANGE);

This I do in one of my apps, and the app has quite successfully run
under Win31 and Win95

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

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

93. Using integer pointers
Q:
var
  myptr: PInteger;
begin
  GetMem(myptr, 10);
   (myptr + 1)^ := 1;       { How can I do this? }
  FreeMem(myptr, 10);
end;

A:
You would have to create the type first.

Type
	Pinteger : ^Integer;

Var
	MyPtr : Pinteger;

Possibly you have used a bad example but it does not seem to make sense to
use a (32 bit) pointer for a 16 bit value or to allocate 10 bytes for the
variable.

Pascal allows you to use NEW and DISPOSE which automatically allocates and
de-allocates the correct size block.

ie NEW(MyPtr) = GetMem(MyPtr, Sizeof(MyPtr)).

Presumably you wish to do a calculation on a variable number of integers.
In that case have a look at TList in the help. Better yet use a linear
array (or a pointer to one if there could be enough elements to make memory
an issue).

[Sean Gates, sgates@goofy.iafrica.com]

A:
For completeness, this should be

  NEW(MyPtr) = GetMem(MyPtr, SizeOf(MyPtr^));

SizeOf(MyPtr) will always be 4 bytes, as 16 bit pointer.

If I understand what you want, a dynamic array of integers, since you know
how many you want at runtime, you could also do

Type
  pIntArr = ^IntArr;
  IntArr  = Array[1..1000] of Integer;
Var
  MyPtr : pIntArr;
Begin

  GetMem(MyPtr, 10); { 10 = SizeOf(Integer) * 5  !!)
{  MyPtr[2]:=1; }
<<<< My turn to be incomplete !! >>>>
  MyPtr[2]^:=1;

  FreeMem(MyPtr,10);
End;

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

A:
It looks like Delphi makes a special case of pchar.  Best I could figure out
that would let you use the syntax you want is:

type
   intarray = array[0..20000] of integer;

procedure TForm1.Button1Click(Sender: TObject);
var
   xptr:  ^IntArray;
begin
   GetMem(xptr, 10);
   xptr^[idx] := 1;  { where idx is 0 to 4 since we have 10 bytes = 5 integers }
   FreeMem(xptr, 10);
end;

Note that you don't really need to allocate an array of 20,000, but Delphi
range checking won't work unless it is 20,000.  (Pointer users caveat
emptor!)

[Sid Gudes, cougar@roadrunner.com]

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

94. Loading a custom cursor
Q:
When I define a cursor with the image editor I cannot get my program
to use it. Even though I exactly the code used in the help-file. It is
probably very simple.

Here is the code I use to load the cursor.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Screen.Cursors [crMikesCursor] := LoadCursor (form1.handle, 'MIKE');
  Cursor := crMikesCursor;
end;

A:
Screen.Cursors [crMikesCursor] := LoadCursor (form1.handle, 'MIKE');

	use hInstance instead of form1.handle

[Patrick Allen, patrick@blueridge.com]

A:
hInstance is defined in the System unit for you and is assigned by the
Windows environment when your application or DLL is created. When
you created your own variable by the same name, Delphi used the one
you declared locally to the FormCreate method which was probably not
assigned a valid value. For more details look in the on-line help
under hInstance.

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

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

95. Type missmatch (PChar <> String)
Q:
I need to pass a string variable to a procedure which is expecting a String
constant (Type PChar to be exact).

A:
You have a couple of options, the cannonical (and presumably future
portable) method is to use the StrPCopy() function to convert the string
to a PChar (just be sure to allocate space for the PChar!).

A quick hack that will also work relies on the fact that both Pascal and
C style strings are, at base, arrays of characters.  Most of the Windows
API calls that want PChars expect the passed string to be null (ASCII
zero) terminated, though some want a length, you have to check on that;
anyhoo, some code like the following should work.

var
  S : string ;

begin
  S := {whatever} ;
...

  S[Length(S) + 1 ] := #0
  { add the null terminator, be careful not to write past }
  { the end of the full allocated length of S (255 if a   }
  { full string) lest you wish to incur the wrath of the  }
  { GPF gods :-)   }

  WinAPICall( ..., @S[1], SizeOf( S ), ...) ;
  { index past the length byte at S[0] }

[Stephen Posey, SLP@uno.edu]

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

96. Trapping database exceptions
Q:
Can anyone tell me how I can abort a change-of-focus from within an
OnExit event?

procedure DBEdit1OnExit(Sender : TObject);
begin
  try
    Tabl.Post;
  except
     On EDatabaseError do ShowMessage('Cannot Post');
  end;
end;

Also, can anyone explain how I can "isolate" specific EDatabaseErrors?

Finally, has anyone else noticed that EDatabase exceptions ALWAYS get
handled by the default handler (which stops the app) when running
under the Delphi IDE?  The problem (feature?) goes away when the
application is not running within Delphi.

A:
Try
  Tabl.Post;
Except
  Begin
    On EDatabaseError do ShowMessage('Cannot Post');
    (Sender AS TDBEdit).SetFocus;
  End;	{Begin}
End,	{Try}

I am parsing the Error and reraising the exception if I am not going to deal
with it. If you use

On E : EDatabaseError do...

you can get the value of E.Error. This is OTTOMH as I am not lookin at Delphi,
its probably something like E.Message (it is in the help).

[Sean Gates, sgates@goofy.iafrica.com]

A:
On EDatabaseError do begin
       ShowMessage('Cannot Post');
       Edit1.setFocus;
    end;

[Sid Gudes, cougar@roadrunner.com]

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

97. Copying from a TEdit to an integer field
Q:
Can someone help me. I am trying to copy a Tedit (stringfield) to an
integerfield.
I am getting an error " Type mismatch"

A:
Try the follwing code :

     function IVal( Str : string ) : LongInt;
     var ErrCode : integer;
     begin
     
       result := 0;
       if ( Str = '' ) then
         exit;
       Val( Str, result, ErrCode );
     end;


     ....

     YourIntegwrField := IVal( YourStringField );


     if you are reading from a table or query component, You can also use:


        YourIntegwrField := YourStringField.AsInteger;

[Paul Abiola, paul.abiola@tri-c.cc.oh.us]

A:
MyIntField.Value := StrToInt(MyEditField.Text);

[Brett Fleming, bfleming@vt.edu]

A:
That's because Tedit contains a string, not an integer, so you have to do a
conversion. Try something like:

    val(Tedit1.text, myinteger, code);

You need to trap an error if Tedit1.text is non-numeric, "code" will be set
if this is so, look it up in Help.

[Sid Gudes, cougar@roadrunner.com]

A:
integerfield.value := StrToInt(stringfield.value);
or
integerfield.AsString := stringfield.value;
or
integerfield.Value := stringfield.AsInteger;

[Tris Hopkins, hopman@ns.sal-junction.com]

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

98. Vexing printing problems
Q:
Some Questions:
Is the pagewidth the physical display or the size of the page?
Is the printers canvas the actual physical display?

Vexing Printing Problems
If I try stretchdraw and use rect(0,0,Printer.PageWidth,Printer.PageHeight),
The right side of the image gets clipped. Has anybody else experienced this
behaviour?

If I print a bitmap and it prints well, then start Paint (in win 95) and
print a bitmap, then print another bitmap from my program ...
The second image sometimes spools(prints nothing) or prints black areas
where there where various colors.  What's going on here?

Worst Vexing Problem:
After printing a random amount of bitmaps (could be 2 or could be 1000), when
I go to print another bitmap windows says my program has performed an
'Illegal Operation and will be terminated'. After closing my program windows
does a HARD crash. (doesn't responed to  anything). However, my program
contains no code (I've double check and traced) that would crash windows.
What's wrong?

A:
It's the actual with, in specified units (could be twips, pixels) of what-
ever device you're writing to.

The canvas is a virtual representation of the page to be printed. When the
"page" is completed, it gets sent to the printer via GDI.

Depending on your printer either the right, left, or both sides would get
clipped with that code. Try specifying 1/4 inch margins (75 pixels @ 300 dpi)
on each side and add the value as an offset to your base coordinates.

Sounds like either the canvas or the GDI stack are getting clobbered. Does your
program explicitly free the graphic resources it uses? (it should).

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

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

99. Changing the HP Laserjet display
Q:
I know it's possible to change the text in the display of the HP Laserjet. I
want to display my own text when I print something with delphi.

A:
What you're looking for are the PJL commands:

        RDYMSG, which specifies a "ready message" that replaces the "00 READY"
                message on the printer control panel. RDYMSG does not affect
                the online state.

        OPMSG,  this command displays a specified "operator message" on the
                printer control panel and takes the printer offline.

        STMSG,  this command displays the specified "status message" on the
                printer control panel and takes the printer offline. It re-
                turns the name of the key the operator presse to put the
                printer back online.

An example of RDYMSG would be to send the following as a byte stream:

<esc>%-12345X@PJL <cr><lf>
@PJL JOB NAME = "Delphi Job" <cr><lf>
@PJL RDYMSG DISPLAY = "DELPHI JOB" <cr><lf>

        <esc>%-12345X@PJL <cr><lf>
        @PJL ENTER LANGUAGE = PCL <cr><lf>
        <esc>E  ...your PCL job, sent directly...<esc>E<esc>%-12345X(cont'd)
            (cont'd)<esc>%-12345X@PJL <cr><lf>

@PJL COMMENT Restore READY message <cr><lf>
@PJL RDYMSG DISPLAY = "" <cr><lf>
@PJL EOJ NAME = "End of Delphi Job" <cr><lf>
<esc>%-12345X

This would emulate a spooler. The spooler would send the non-indented lines.
A normal application would send the indented lines.

This information came from "Printer Job Language Technical Reference
Manual". 1992. Edition 1. Hewlett Packard Co.

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

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

100. Trapping a MDI Child Event
Q:
I have written a procedure to enable/disable menu and toolbar buttons and
it works fine calling it from the MCIFrame window menus. How can I trap
when the user closes a MDIChild Window using the MDIChild System Menu |
Close.
Example:
This works -> Select the application's file menu and choose close window.
If it is the last window then the appropriate menu items are greyed out.
This does not work -> User clicks on the system menu of the child window
and chooses close. Here is the problem, there is no way to tell if this
was the last window closed.

A:
procedure TMainForm.FormCreate(Sender: TObject);
begin
  {  place FormCreate code here  }
  Screen.OnActiveFormChange := UpdateObjectss;
  {  and here, too, if you like  }
end;

procedure TMainForm.UpdateObjects(Sender: TObject);
begin
  <objectname>.Enabled := MDIChildCount > 0;
end;

The (MDIChildCount > 0) part returns true if _any_ child windows are
open, and false otherwise.  So, you don't have to worry about
counting the chidren that are open.

[Dean Tessman, deant@CancerBoard.ab.ca]

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

101. SendMessage and TLabel
Q:
How does one send a message to TLabel particularly since it's a non-windowed
control. I need to send a label WM_LButtonUp, but there's no handle.

A:
You can't send a message to a control that doesn't have an hWnd (i.e., a
Handle property in Delphi). One solution would be to send the message to
an invisible control that *does* have a handle, and do your Tlabel work
in a handler  for that control.

[Ian Land, iml@dircon.co.uk]

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

102. Error creating cursor handle
Q:
I'm writing a very simple query that updates a field called CARD1 with
a string "Gold" based on a second field called GPA, both of which are
in a table named STUDENTS.DB. The SQL is

    update students
        set card1 = "Gold"
        where gpa > 3.0;

It generates an error message ERROR CREATING CURSOR HANDLE.

A:
You've to use ExecSql instead of Open. For example, if your query name is
UpdateStudent, when you want to update the STUDENT.DB you've to write this
code:
Begin
 .....
  UpdateStudent.ExecSql;
 .....
End;

Your query is a Passtrough query that can't return a result set so it can't
be opened but must be 'EXECUTED'.

[Simone Mori, simone@softeam.it]

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

103. WinHelp macros
Q:
Is it possible to call the WinHelp macro 'Search()' from  Delphi?

A:
Yes, you can do this by a direct call to the Windows API WinHelp function, or
(more easily) by calling the Application.HelpCommand method.
Or (more easily still) you could avoid having to fiddle with pointers to null
terminated strings every time, by going a stage further and encapsulating the
call into a simple procedure like this:

unit Help;

{$X+      {Allow Application.HelpCommand's result to be discarded}

interface

 procedure HelpSearch (const Key: string);


implementation
 uses
   Forms,         {for Application}
   SysUtils,      {for StrPCopy}
   WinTypes;      {for HELP_PARTIALKEY}

 procedure HelpSearch (const Key: string);
{----------------------------------------------------------------}
{ Calls Windows Help engine for a Search on the supplied Key.    }
{ If more than one match or no match, brings up Search dialog.   }
{----------------------------------------------------------------}
 var
   AStr255: array[0..255] of char;
   PStr255: pointer;
 begin
   PStr255 := StrPCopy(@AStr255, Key);
   Application.HelpCommand(HELP_PARTIALKEY, longint(PStr255));
 end;


end.

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

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

104. 2% resources, in design mode
Q:
I have a problem with Delphi. When I load Delphi my resources drop from
around the 65% mark to around the 45% mark. No real problem there
but..... When I load my project it suddenly drops to around the 2%
mark!! :(
The project is not that complex, about 5 screens one Tabbed notebook on
each with about 20 controls on each page. No database stuff yet. A few
speed buttons with glyphs on each screen. Nothing exciting.
The question is, where are all my resources going and what can be done
about it? When you run the exe of the app it uses only the customary
15%.

A:
If you have all of the forms open (either displayed or minimized) and
all of the units open in the editor, then resources very quickly
disappear. Try closing all forms and units, and only have open those
ones that you will be using. It is when the resources are used like
this that compiles start to hang Delphi and the machine.

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

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

105. Call the Hint method directly
Q:
Is it possible to call the  Hint method directly?
I've a case where I want to click on a Button, and the Hint for
another component (eg, an edit box) will appear for 1 second or
so, and then it will disappear after the button is released. I
saw something like "ActivateHint" but it seemed that I
couldn't make a direct call.

A:
 function RevealHint (Control: TControl): THintWindow;
{----------------------------------------------------------------}
{ Pops up Hint window for the specified Control, and returns a   }
{ reference to the hint object so it may subsequently be removed }
{ with RemoveHint (see below).                                   }
{----------------------------------------------------------------}
 var
   ShortHint: string;
   AShortHint: array[0..255] of Char;
   HintPos: TPoint;
   HintBox: TRect;
 begin
   { Create the window: }
   Result := THintWindow.Create(Control);

   { Get first half of hint up to '|': }
   ShortHint := GetShortHint(Control.Hint);

   { Calculate Hint Window position & size: }
   HintPos := Control.ClientOrigin;
   Inc(HintPos.Y, Control.Height + 6);    <<<< See note below
   HintBox := Bounds(0, 0, Screen.Width, 0);
   DrawText(Result.Canvas.Handle,
       StrPCopy(AShortHint, ShortHint), -1, HintBox,
       DT_CALCRECT or DT_LEFT or DT_WORDBREAK or DT_NOPREFIX);
   OffsetRect(HintBox, HintPos.X, HintPos.Y);
   Inc(HintBox.Right, 6);
   Inc(HintBox.Bottom, 2);

   { Now show the window: }
   Result.ActivateHint(HintBox, ShortHint);
 end; {RevealHint}

 procedure RemoveHint (var Hint: THintWindow);
{----------------------------------------------------------------}
{ Releases the window handle of a Hint previously popped up with }
{ RevealHint.                                                    }
{----------------------------------------------------------------}
 begin
   Hint.ReleaseHandle;
   Hint.Free;
   Hint := nil;
 end; {RemoveHint}

The line marked <<<< above is the one that positions the hint
window below the control.  This could obviously be altered if
you want a different position for some reason.

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

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

106. SQL date field
Q:
I'm trying this again.  I got absolutely no response last time.
Is it possible to do this in SQL, have a field with the name Date?
select * from PFMANUAL where Date = 31/11/95

A:
There are a lot of ways to do this:

1) If the date is costant, use:

        WHERE Date = #31/11/95#

Depending upon your country settings, this could be #11/31/95#. Try both:
one of them works.

2) If the date is variable, you must use a parameter, for instance in this way:

        WHERE Date = :MyDate

Then, after clicking ok, select the Query Params in the Object inspector,
click on the ellipsis button, and set MyDate as Date type.

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

A:
SELECT * from PFMANUAL WHERE PRMANUAL."DATE" = "31/11/95"

I found it after having had a similar problem and using DataBase Desktop to
buil a QBE statement which it then "translated" to SQL

[schulden@mercury.co.il]

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

107. Closing MDI childs
Q:
What must make so that a child form of a MDI application could be destroyed to
if same?
Upon executing the code associated with a button of the child form (this
action only it can be carried when is pressed this button!) I need that is
destroyed to if same and that create the following child form. I have
>treated of several things, but up until now what is only that it does not
provoke an errorGPF is when I establish the focus to a control of the main
form (MDIPARENT) and the user presses a key or accomplishes other action that
could be detected and in which put the code that accomplishes the destruction.

A:
To destroy a MDI Child, you should take it off the autocreate forms
(Project options), create it at runtime and, on the OnClose event of the
child, assign caFree to the Action Variable, like this
    Action := caFree;

[Bruno Sonnino, bruno.sonnino@mandic.com.br]

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

108. Refreshing database data
Q:
I have a form that  reads and updates from a paradox table. Its also calls
another form that will add records into the same table and this works fine. It
also displays another modal form that has a DBGRID in it pointing to the same
table.
Everything works to start with, however when I add or change data and then
call this modal form with the DBGRID in it, the DBGRID is not showing any
updated information eg it still shows deleted records and wont show any new
records that have been added. It is almost like it is pointing to the
'original' version of the table.
After I terminate my application and start it again the DBGRID does reflect
the true information, so it does prove that it is pointing to the correct
table. (I have checked this all out in the sample MASTAPP application
supplied with DELPHI and it all seems to work fine in a similar circumstance
- seemingly with no 'extra' code to handle this)

A:
Perhaps it will help to use the same table and datasource in your
modal form as in your main form. Try to modify the code of your modal
form like this:

unit myModalF;

interface
{...}

implementation
{...}

uses
     MainForm; {The file-name of the parent form of your modal form)

MyModalForm.OnCreate(Sender: TObject);
begin
        DBGrid1.DataSource := MyMainForm.DataSource1;
end;

[Thomas Scheffczyk, scheffczyk@verwaltung.uni-mainz.de]

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

109. Using menu options to cut, copy, and paste
Q:
It's easy in Delphi to use the standard Windows keystrokes to cut, copy, and
paste in any component that holds text.  You don't even have to write code
to do it!
But if I want to add Cut, Copy, and Paste to my app's menu and have it work
no matter what component I'm in, is there an easy one-liner way to do this?

A:
If the component "knows" how to copy, cut and paste it's contents
to clipboard you can just send a message to it.

For example WM_COPY message:

if GetFocus <> 0 then  { if any window has a focus }
SendMessage( GetFocus, WM_COPY, 0, 0);   { send WM_COPY to it }

Note:
I prefer to use API's GetFocus insteed of ActiveControl property,
because ActiveControl not always points to a windows having a focus.

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

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

110. Reading long strings from a file
Q:
What's the easiest way to read in a long ASCII record directly into a PChar
or character array?  Can I use ReadLn for this?  All the examples in the
Delphi help use strings, but could I do something like this instead?

A:
You might want to consider using a stream (TFileStream,
TMemoryStream) to help you do the job. You'll have to find the CR/LF
pair yourself, but it would probably work fairly well -- something like this
(too lazy tonight for real code)

Start := Stream.Position;
End := Start;
Repeat
  Stream.Read(Buffer^, 1024);
  CRPos := FindCR(Buffer^);   { Where FindCR returns 0..1023 for CR,
                                                      1024 if not found}
  Inc(End, CRPos);
Until CRPos < 1024;
GetMem(MyPChar, End - Start);  { May be +-1 here -- again, lazy me! }
Stream.Seek(Start);
Stream.Read(MyPChar^, End - Start)

Then set the CR at the end of MyPChar to 0, and Seek to End + 1 or so
(to skip the LF).

[Eric Nielsen, htrsoft@midwest.net]

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

111. Appending in TMemo, don't start a new line
Q:
I've got 2 strings, each 210 bytes in length. I want to fill a memo box with
this stuff. Unfortunately when I do:

         Memo1.Lines.Add(var1);
         Memo1.Lines.Add(var2);

A:
The TMemo class has a property called WordWrap.  You need to set it's
value to FALSE.  This should fix the problem.

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

A:
The help text for TMemo says:

    "You can...access the text line by line using the Lines
    property. If you want to work with individual lines of text,
    the Lines property will suit your needs better.  If you want
    to work with the text as one chunk, use the Text property."

But, as you've probably discovered, the Text property seems to
be a string and is therefore limited to 255 bytes. So you may
_have_ to work with Lines. In this case you'll sometimes need to
concatenate the strings then add them e.g.

    Memo1.Lines.Add(var1+var2);

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

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

112. Changing in TOpenDialog
Q:
I need to change caption in the check-box "Read Only" of a
TSaveDialog/TOpenDialog. I wish it becomes "Only selected records". Is it
possible? Or I have to create my own component?

A:
Try Searching for Open Dialog Box in the Windows API help file.  Look down
at the bottom of the topic at lpTemplateName.  Basically, you can create a
new dialog box for the Open Dialog Box and replace the standard one with
your own.

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

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

113. Disk full error
Q:
I keep on getting disk full errors when I try to recompile my component
library. I have over 150MB of disk space free and 32MB of ram. The
problem only started after I created a few .DCR files (the bitmaps in
the component palette) for some components.

A:
Try deleting everything in your project except the dpr, pas and dfm files
and recompile. It sounds like one of your project files has been corrupted.
I had a similar occurence once and fixed it that way.

[Monte Saager, kidvolt@teleport.com]

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