home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 2000 March / pcp161b.iso / full / delphi / DELPHI16 / TECHINFO / DELPHI / FAQS / DELSEC07.FAQ < prev    next >
Encoding:
Text File  |  1995-08-24  |  24.0 KB  |  762 lines

  1. SECTION 7 - Object Pascal
  2.                                     
  3. This document contains information that is most often provided
  4. to users of this section.  There is a listing of common
  5. Technical Information Documents that can be downloaded from the
  6. libraries, and a listing of the most frequently asked
  7. questions and their answers.
  8.  
  9. Technical Information Documents related to Delphi Object Pascal:
  10.  
  11. TI1171  Bug Report Form
  12. TI2841  Delphi Consultants and Training Centers
  13.  
  14. Index Of Questions
  15. ------------------
  16.  
  17. 1) I am using a form from the gallery and I am getting an 'unknown identifier' 
  18.    error when I compile.  What is wrong?
  19. 2) How can I do standard output with writeln?
  20. 3) How to I cast a floating point number (e.g. real) to an integer?
  21. 4) How do I make it so that when the user hits <enter>, it goes to the next
  22.    object as though he had hit the tab key? (Note: This will not work within 
  23.    a DBGrid, since the next field is not a separate object.)
  24. 5) In a DBGrid, how do I Tab to the next field when the Enter Key is pressed?
  25. 6) How do I process command line parameters in a Delphi app?
  26. 7) How do you select a specific field on a TDBGrid to get focus?
  27. 8) I have a paradox table that uses a password.  How do I make it so that the 
  28.    form that uses the table comes up without prompting the user for the 
  29.    password?
  30. 9) How do I design a form so that the display is the same no matter what the 
  31.    resolution is?
  32. 10) How do I do date math on calculated fields?
  33. 11) Where is the exponent function?
  34. 12) How do I keep the form in icon form when I run it?  
  35. 13) I have a form that is a sort of template.  I want to be able to call the
  36.     same form several times (with different data displayed).  How do I use 
  37.     one form several times?
  38. 14) How can the component that was right clicked be determined while in an 
  39.     event handler of a popup MenuItem?
  40. 15) How can I capture windows messages and process them before my 
  41.     application.run line is executed?
  42. 16) How do I use a case statement to determine which object calls the procedure?
  43. 17) How can you do scrolling functions in a TForm component using keyboard 
  44.     commands?  For example, scrolling up and down when a  PgUp or PgDown is 
  45.     pressed.  Is there some simple way to do this or does it have to be 
  46.     programmed by capturing the keystrokes and manually responding to them?
  47. 18) How do I transfer the text in a TMemo component on a form to a TMemofield 
  48.     in a Paradox table?
  49. 19) How do I make an Fixed Length ASCII text table from a table?
  50. 20) How do I set a format for a data field?
  51. 21) How do I format a number to place commas as a thousanth's separator?
  52. 22) I have a CPU intensive function.  I want to be able to check to see if the 
  53.     user wants to cancel.  How do I do that?
  54. 23) How do you extract the high or low order byte from a word?  How do you 
  55.     insert it?  
  56. 24) I have an OBJ file that has several assembler routines compiled into it.
  57.     I wouldn't want to have to rewrite them.  Is there a way to use them in
  58.     a Delphi app?
  59. 26) If a component doesn't handle the windows messages that I need it to 
  60.     handle, do I have to write my own version that passes the messages that 
  61.     I need, or is there way to tap (from a TForm or else where) into the 
  62.     message loop, and grap what I need?
  63. 27) Borland decided that accessing environment variables from Windows 
  64.     Programs is a Bad Thing.  Why do they force you to use the "obsolete" 
  65.     WinDos unit?
  66. 28) How would I write a basic screen saver (like, blank the screen) using Delphi?
  67. 29) How can I determine the Length in pixels of a string after a specific font has 
  68.     been aplied to it?
  69. 30) Where is the best place to open a splash screen on program start up?
  70. 31) How do I call a function from a DLL?
  71. 32) What is a Callback function, and how do I create one?
  72.  
  73.  
  74. Questions and Answers
  75. ---------------------
  76.  
  77. 1) Q:  I am using a form from the gallery and I am getting an 'unknown 
  78.        identifier' error when I compile.  What is wrong?
  79.  
  80.    A:  If you change the name of some item that has code associated with it, that 
  81.        code will not be changed by Delphi.  Delphi does not go through your code 
  82.        and second guess your intent.  The code that you may assume is generated  
  83.        by Delphi is actually programmed into the form by the designer of that    
  84.        form. Just go through the code and make the modifications.
  85.  
  86.  
  87. 2) Q:  How can I do standard output with writeln?
  88.  
  89.    A:  Include WinCRT in your uses statement.
  90.  
  91.  
  92. 3) Q:  How to I cast a floating point number (e.g. real) to an integer?
  93.  
  94.    A:  There are a couple of methods.
  95.  
  96.            var
  97.       i: integer;
  98.       r: real;
  99.  
  100.     begin
  101.       r := 34.56;
  102.       i := trunc(r);  {i = 34}  {First Method}
  103.       i := round(r);  {i = 35}  {Second Method}
  104.     end;
  105.  
  106.  
  107. 4) Q:  How do I make it so that when the user hits <enter>, it goes to the next
  108.        object as though he had hit the tab key? (Note: This will not work within 
  109.        a DBGrid, since the next field is not a separate object.)
  110.  
  111.    A:  You need to trap the keystroke and set up your own response to it.  Try
  112.        this:
  113.  
  114.     procedure TMainForm.FormCreate(Sender: TObject);
  115.     begin
  116.       keyPreview := true; {To turn the event "ON".}
  117.     end;
  118.  
  119.     procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);
  120.     begin
  121.       if Key = #13 then
  122.       begin
  123.         Key := #0;
  124.         PostMessage(Handle, WM_NEXTDLGCTL, 0, 0);
  125.       end;
  126.     end;
  127.  
  128. 5) Q: In a DBGrid, how do I Tab to the next field when the Enter Key is pressed?
  129.  
  130.    A: The action of the tab key is acted upon after the OnKeyDown event but the
  131.       Enter key is acted on in the OnKeyPress.  Therefore, the Enter key has to  
  132.       be accounted for in both areas for the desired effect to occur. Try this:
  133.  
  134.     procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift:
  135.      TShiftState);
  136.     begin
  137.         if Key = VK_RETURN then Key = VK_TAB;
  138.     end;
  139.  
  140.     procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
  141.     begin
  142.         if Key = #13 then Key := #9;
  143.     end;
  144.  
  145.  
  146. 6) Q:  How do I process command line parameters in a Delphi app?
  147.  
  148.    A:  Use the ParamCount and ParamString methods.
  149.  
  150.  
  151. 7) Q:  How do you select a specific field on a TDBGrid to get focus?
  152.  
  153.    A:  Using this code:
  154.  
  155.     DBGrid1.SelectedField := Table1SomeField;
  156.     DBGrid1.SetFocus;
  157.  
  158.  
  159. 8) Q:  I have a paradox table that uses a password.  How do I make it so that the 
  160.        form that uses the table comes up without prompting the user for the 
  161.        password?  
  162.  
  163.    A:  The table component's ACTIVE property must be set to FALSE.  Then, put 
  164.        this code on the form's create event:
  165.  
  166.     session.AddPassword('My secret password');
  167.     table1.active := true; 
  168.  
  169.  
  170. 9) Q:  How do I design a form so that the display is the same no matter what the
  171.        resolution is?  
  172.  
  173.    A:  Here is some code to show how it is done:
  174.  
  175.     const
  176.       ScreenHeight: real = 800; {I designed my form in 800x600 mode.}
  177.       ScreenWidth: real = 600;
  178.  
  179.     procedure TForm1.FormCreate(Sender: TObject);
  180.     var
  181.       x, y: LongInt; {Integers will not not a large enough value.}
  182.  
  183.     begin
  184.       form1.scaled := true;
  185.       x := getSystemMetrics(SM_CXSCREEN);
  186.       y := getSystemMetrics(SM_CYSCREEN);
  187.       if (x <> ScreenHeight) or (y <> ScreenWidth) then
  188.         begin
  189.           form1.height := form1.height * x DIV ScreenHeight;
  190.           form1.width := form1.width * y DIV ScreenWidth;
  191.           scaleBy(x, ScreenHeight);
  192.         end;
  193.     end;
  194.  
  195.        Note: you may want to add code that will change the font sizes 
  196.        proportionally.
  197.  
  198.  
  199. 10) Q:  How do I do date math on calculated fields?
  200.  
  201.     A:  When doing date math on calculated fields, it is important to ensure that 
  202.         all values being used are properly matched as to type.  The double method 
  203.         (not in the docs) casts the value to a useable type.
  204.  
  205.  
  206.         In the following method, d1, and d2 (part of table1) can be of either    
  207.         date or dateTime type and d3 is an integer field.
  208.  
  209.     procedure TForm1.Table1CalcFields(DataSet: TDataset);
  210.     var
  211.       t1, t2: tDateTime;
  212.     begin
  213.       table1d1.asDateTime := Date + 2; {or table1d1.value := date + 2;}
  214.       table1d2.asDateTime := Date - 2; 
  215.       t1 := table1d1.asDateTime;
  216.       t2 := table1d2.asDateTime;
  217.       table1d3.asInteger := trunc(double(t1) - double(t2));
  218.     end;
  219.  
  220.  
  221. 11) Q:  Where is the exponent function?
  222.  
  223.     A:  For ExpXY(X, Y) (i.e. Y^X), try:
  224.  
  225.         ExpXY := Exp(Y * Ln(X))
  226.  
  227.         To use a generic function, declare the formal parameters and function
  228.         result as Extended, and the conversions from the actual parameters and
  229.         back to your result variable will be done automatically.  (Note:  Ln is
  230.         the natural logarithm of a number).
  231.  
  232.  
  233. 12) Q:  How do I keep the form in icon form when I run it?  
  234.  
  235.     A:  In the private section of the form object's declaration, put:
  236.  
  237.     PROCEDURE WMQueryOpen(VAR Msg : TWMQueryOpen); message WM_QUERYOPEN;
  238.  
  239.         In the implementation section, put this method:
  240.  
  241.     PROCEDURE TForm1.WMQueryOpen(VAR Msg : TWMQueryOpen);
  242.     begin
  243.       Msg.Result := 0;
  244.     end;
  245.  
  246.     That's it! The form will always remain iconic. OBTW, of course you must 
  247.     set WindowState to wsMinimized in the form's properties initially.
  248.  
  249.  
  250. 13) Q:  I have a form that is a sort of template.  I want to be able to call the 
  251.     same form several times (with different data displayed).  How do I use 
  252.     one form several times?
  253.  
  254.     A:  You need to make modeless window like this:
  255.  
  256.     with TMyForm.create(self) do show;
  257.  
  258.     Now we want to control it.  Here is a way to change the caption of each 
  259.     form that is created.  You have access to it through the form's component 
  260.     array.  This example uses an about box (named "box") as the other form.
  261.  
  262.     procedure TForm1.Button1Click(Sender: TObject);
  263.  
  264.     begin
  265.       with tBox.create(self) do show;
  266.       {ComponentCount-1 is needed because it is zero based.}
  267.       (form1.Components[form1.ComponentCount-1] as tForm).caption :=
  268.         'About Box # ' + intToStr(form1.ComponentCount-2);
  269.     end;
  270.  
  271.  
  272. 14) Q:  How can the component that was right clicked be determined while in an 
  273.     event handler of a popup MenuItem?
  274.  
  275.     A:  Use the PopupComponent property of the PopupMenu component to determine 
  276.     what control was right clicked.
  277.  
  278.     procedure TForm1.PopupItem1Click(Sender: TObject);
  279.     begin
  280.       Label1.Caption := PopupMenu1.PopupComponent.ClassName;
  281.     end;
  282.  
  283.     The form's ActiveControl property can also be used, however,  the active 
  284.     control may not necessarily be the control that caused the popup menu to 
  285.     appear. 
  286.  
  287.  
  288. 15) Q:  How can I capture windows messages and process them before my 
  289.     application.run line is executed?
  290.  
  291.     A:  The following project source demonstrates how to get Window messages 
  292.     before the application's window procedure is called. It is rare, if ever, 
  293.     that this needs to be done.  In most cases assigning a procedure to the 
  294.     Application.OnMessage will accomplish the same thing.
  295.  
  296.     program Project1;
  297.  
  298.     uses
  299.         Forms, messages, wintypes, winprocs,
  300.       Unit1 in 'UNIT1.PAS' {Form1};
  301.  
  302.  
  303.     {$R *.RES}
  304.  
  305.     var
  306.         OldWndProc: TFarProc;
  307.  
  308.     function NewWndProc(hWndAppl: HWnd; Msg, wParam: Word;
  309.                      lParam: Longint): Longint; export;
  310.       begin
  311.         result := 0; { Default WndProc return value }
  312.         {*** Handle messages here; The message number is in Msg ***} 
  313.         result := CallWindowProc(OldWndProc, hWndAppl, Msg,                             wParam, lParam);
  314.       end;
  315.  
  316.     begin
  317.         Application.CreateForm(TForm1, Form1);
  318.         OldWndProc := TFarProc(GetWindowLong(Application.Handle,
  319.                   GWL_WNDPROC));
  320.         SetWindowLong(Application.Handle, GWL_WNDPROC,
  321.                   longint(@NewWndProc));
  322.         Application.Run;
  323.     end.
  324.  
  325.  
  326. 16) Q:  How do I use a case statement to determine which object calls the 
  327.     procedure?
  328.  
  329.     A:  Use the object's TAG property.  You set the value of the tag property of 
  330.     each object so that you know what it is.  (Using constants that describe 
  331.     the object is ideal.)
  332.  
  333.     Note:  This code assumes that only a tButton will call the procedure.
  334.  
  335.     case (sender as tButton).tag of
  336.       0:  blah;
  337.       1:  blah_blah;
  338.     end;
  339.  
  340.  
  341. 17) Q:  How can you do scrolling functions in a TForm component using keyboard 
  342.     commands?  For example, scrolling up and down when a  PgUp or PgDown is 
  343.     pressed.  Is there some simple way to do this or does it have to be 
  344.     programmed by capturing the keystrokes and manually responding to them.
  345.  
  346.     A:  Form scrolling is accomplished by modifying the VertScrollbar or 
  347.     HorzScrollbar Postion properties of the form.  The following code 
  348.     demonstrates how to do this:
  349.  
  350.       procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  351.       Shift: TShiftState);
  352.       const
  353.         PageDelta = 10;
  354.       begin
  355.         With VertScrollbar do
  356.           if Key = vk_Next then  Position := Position+PageDelta
  357.           else if Key = vk_Prior then Position := Position-PageDelta;
  358.       end;
  359.  
  360.  
  361. 18) Q:  How do I transfer the text in a TMemo component on a form to a TMemofield 
  362.     in a Paradox table?
  363.  
  364.     A:  Here is an example.
  365.  
  366.     procedure TForm1.Button1Click(Sender: TObject);
  367.     var
  368.       t: TTable;
  369.     begin
  370.     t := TTable.create(self);
  371.     with t do
  372.       begin
  373.         DatabaseName := 'MyAlias'; {personal alias}
  374.           TableName := 'MyTbl.db';
  375.         open;
  376.         edit;
  377.         insert;
  378.         fieldByName('TheField').assign(memo1.lines); {This is it!}
  379.         post; {required!!!}
  380.         close;
  381.       end;  { End of the with statement. }
  382.     end;
  383.  
  384.  
  385. 19) Q:  How do I make an Fixed Length ASCII text table from a table?
  386.  
  387.     A:  Like this:
  388.  
  389.     procedure TForm1.Button1Click(Sender: TObject);
  390.     var
  391.       t1, t2: tTable;  {t1 = PW table; t2 = ASCII version}
  392.     begin
  393.       t1 := tTable.create(self);
  394.       with t1 do
  395.       begin
  396.         DataBaseName := 'pw'; { Personal Alias for Paradox Directory }
  397.         tableName := 'customer.db'; { Source Table }
  398.         open;
  399.       end;    
  400.       t2 := tTable.create(self);
  401.       with t2 do
  402.       begin
  403.         DataBaseName := 'pw'; { Personal Alias for Paradox Directory }
  404.         tableName := 'asdf.txt';
  405.         TableType := ttASCII;
  406.  
  407.         createTable; 
  408.         open;
  409.         edit;
  410.         BatchMove(t1, batCopy);
  411.         close;
  412.       end;
  413.       t1.close;
  414.     end;
  415.  
  416.  
  417. 20) Q:  How do I set a format for a data field?
  418.  
  419.     A:  Select the table object, and double click.  Select the field that you 
  420.     want to format.  Use the DisplayFormat and the EditFormat properties to 
  421.     do what you want.  DisplayFormat works for when the field does not have 
  422.     focus.  EditFormat works for when the field has focus.  Use the commands 
  423.     as you would for the first parameter of the FormatFloat function, but 
  424.     without the quotes.
  425.  
  426.  
  427. 21) Q:  How do I format a number to place commas as a thousanth's separator?
  428.  
  429.     A:  If you are not using Delphi, try this code:
  430.  
  431.     function FormatNumber(l: longint): string;
  432.     var
  433.       len, count: integer;
  434.       s: string;
  435.     begin
  436.       str(l, s);
  437.       len := length(s);
  438.       for count := ((len - 1) div 3) downto 1 do
  439.         begin
  440.           insert(',', s, len - (count * 3) + 1);
  441.           len := len + 1;
  442.         end;
  443.       FormatNumber := s;
  444.     end;
  445.  
  446.         If you are using Delphi, there is, of course, the easy way:
  447.  
  448.     function FormatNumber(l: longint): string;
  449.  
  450.     begin
  451.       FormatNumber := FormatFloat('#,##0', StrToFloat(IntToStr(l)));
  452.     end;
  453.  
  454.  
  455. 22) Q:  I have a CPU intensive function.  I want to be able to check to see if 
  456.     the user wants to cancel.  How do I do that?
  457.  
  458.     A:  Call Application.ProcessMessages from time to time in your code.
  459.  
  460.  
  461. 23) Q:  How do you extract the high or low order byte from a word?  How do you 
  462.     insert it?  
  463.  
  464.     A:  There are built in methods hi() and lo() for extracting, but for those 
  465.     that want to know how to do it on their own, here it is in its most 
  466.     efficient form (if I do say so myself <G>).  The functions for inserting
  467.     bytes are not in Delphi.  
  468.  
  469.     Note:  Assembler functions return the contents of the AX register.
  470.  
  471.     function GetHiByte(w: word): byte; assembler;
  472.     asm
  473.        mov ax, w
  474.        shr ax, 8
  475.     end;
  476.  
  477.     function GetLoByte(w: word): byte; assembler;
  478.     asm
  479.        mov ax, w
  480.     end;
  481.  
  482.     function SetHiByte(b: byte; w: word): word; assembler;
  483.     asm
  484.        xor ax, ax
  485.        mov ax, w
  486.        mov ah, b
  487.     end;
  488.  
  489.     function SetLoByte(b: byte; w: word): word; assembler;
  490.     asm
  491.        xor ax, ax
  492.        mov ax, w
  493.        mov al, b
  494.     end;
  495.    
  496.     Another way of doing it (with an example of its use):  
  497.  
  498.     Type
  499.        TWord2Byte = record
  500.                   Lo,Hi: Byte;
  501.            end;
  502.  
  503.     var W: Word;
  504.          B: Byte;
  505.     begin
  506.        W := $1234;
  507.        B := TWord2Byte(W).Hi;
  508.        writeln(TWord2Byte(W).Hi);
  509.        { going back }
  510.        TWord2Byte(W).Lo := $67;
  511.        TWord2Byte(W).Hi := $98; { no shl needed! }
  512.     end.
  513.  
  514.  
  515. 24) Q:  I have an OBJ file that has several assembler routines compiled into it.
  516.     I wouldn't want to have to rewrite them.  Is there a way to use them in
  517.     a Delphi app?
  518.  
  519.     A:  You don't indicate if these return values or not (in Pascal this 
  520.         matters.)
  521.  
  522.     If they don't, include the following near the front of your code:
  523.  
  524.       Procedure TurnOn;  External;
  525.       Procedure TurnOff; External;
  526.  
  527.     If they return values, use:
  528.  
  529.       Function TurnOn: Integer; External;
  530.       Function TurnOff: Integer; External;
  531.  
  532.     Replace Integer with whatever datatype they return.
  533.     In either case follow that with:
  534.  
  535.       {$L filename.obj}
  536.  
  537.     to link in the obj file.  See also the "linking external assembler code"
  538.     in the on-line help.
  539.  
  540.  
  541. 25) Q:  Which memory model does Delphi use?
  542.  
  543.     A:  Delphi uses a mixed memory model, but it is very close to the "C" large model.  The defaults are:
  544.  
  545.       - Methods are far
  546.       - Procedures in an interface section are far
  547.       - Procedures only used in an implementation section are near
  548.       - Heap data and all pointers in general (including class instances)
  549.         are far
  550.       - Global variables are near (DS based)
  551.       - Procedure parameters and local variables are near (SS based)
  552.       - Procedures declared FAR or EXPORT are far
  553.       - Virtual memory tables are far for the new class model and near for
  554.         the old
  555.  
  556.     This scheme has been used by Borland Pascal for a very long time. I find
  557.     it flexible and efficient.
  558.  
  559.     Since all public procedures, methods and pointers are 32bit already, 
  560.     Delphi32 won't have to change any of that.  It's likely that Delphi32 
  561.     will switch to 32bit addressing for the data and stack segments too, 
  562.     but that shouldn't affect any of your code either.  What will affect it
  563.     is the change of Integer from 16 to 32 bit.
  564.  
  565.  
  566. 26) Q:  If a component doesn't handle the windows messages that I need it to 
  567.     handle, do I have to write my own version that passes the messages that 
  568.     I need, or is there way to tap (from a TForm or else where) into the 
  569.     message loop, and grap what I need?
  570.  
  571.     A:  To respond to, say, the wm_size message, you would add:
  572.  
  573.      procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  574.  
  575.     to your component.  Then you could have:
  576.  
  577.      procedure TWhateverComponent.WMPaint(var Message: TWMPaint);
  578.      begin
  579.         {have your way with the component}
  580.      end;
  581.  
  582.     This will work for any windows message.  Most components respond to the 
  583.     more popular messages already, and you can override their event handlers.
  584.  
  585.  
  586. 27) Q:  Borland decided that accessing environment variables from Windows 
  587.     Programs is a Bad Thing.  Why do they force you to use the "obsolete" 
  588.     WinDos unit?
  589.  
  590.     A:  Use the GetDOSEnvironment() call from the API.
  591.  
  592.  
  593. 28) Q:  How would I write a basic screen saver (like, blank the screen) 
  594.     using Delphi?
  595.  
  596.     A:  An easy implementation would be something like this:
  597.  
  598.     var
  599.       f: tForm;
  600.  
  601.     begin
  602.       f := tForm.create(self);
  603.       f.WindowState := wsMaximized;
  604.       f.color := black;
  605.       f.borderStyle := bsNone;
  606.       f.show;
  607.     end;
  608.  
  609.     There is more to do, of course.  You must have a way back from there.  
  610.     Perhaps an actual form that has a mouse click event programmed to 
  611.     f.close it.  Also, you want to hide the mouse.  Etc, etc.  But, this 
  612.     answers the question.
  613.  
  614.  
  615. 29) Q:  How can I determine the Length in pixels of a string after a specific 
  616.     font has been aplied to it?
  617.  
  618.     A:  The two methods, TextHeigh and TextWidth, can be used to determine 
  619.     both the text height and width of a string in pixels.  These methods 
  620.     can only be accessed through components that have a Canvas property 
  621.     such as TForm.  The TPanel component does not have access to its Canvas 
  622.     property by default because it is protected.
  623.  
  624.     If a component doesn't have a Canvas property then The following 
  625.     function will return the text width based on the font passed.
  626.  
  627.       function GetTextWidth(CanvasOWner: TForm; Text : String;
  628.                                  TextFont :  TFont): Integer;
  629.       var
  630.          OldFont : TFont;
  631.       begin    
  632.         OldFont := TFont.Create;
  633.         try
  634.           OldFont.Assign( CanvasOWner.Font );
  635.           CanvasOWner.Font.Assign( TextFont );
  636.           Result := CanvasOWner.Canvas.TextWidth(Text);
  637.           CanvasOWner.Font.Assign( OldFont );
  638.         finally
  639.           OldFont.Free;
  640.         end;
  641.       end;
  642.  
  643.  
  644. 30) Q:  Where is the best place to open a splash screen on program start up?
  645.  
  646.     A:  The best place to open a splash screen is in the project source file 
  647.     after the first FormCreate and before the Run  This is  accomplished 
  648.     by creating a form on the fly and then displaying it before the app is 
  649.     actual opened.
  650.  
  651.      program Project1;
  652.  
  653.      uses Forms,  Unit1 in 'UNIT1.PAS' {Form1}, Splash;
  654.  
  655.      {$R *.RES}
  656.      var
  657.        SplashScreen : TSplashScreen;  {in the Splash unit}
  658.  
  659.      begin
  660.        Application.CreateForm(TForm1, Form1);
  661.        SplashScreen := TSplashScreen.Create(Application);
  662.        try
  663.          SplashScreen.Show;
  664.          {
  665.           do other CreatForms or any other processing 
  666.           before the app is to be opened
  667.          }
  668.          SplashScreen.Close;
  669.        finally  {Make sure the splash screen gets released}
  670.          SplashScreen.Free; 
  671.        end;   
  672.        Application.Run;
  673.      end.
  674.  
  675.  
  676. 31) Q:  How do I call a function from a DLL?
  677.  
  678.     A:  In order to call a function you must know it's exact syntax. and setup
  679.     a function type.  For example to call the CallMe function, which accepts
  680.     two integers and returns a string, from MyTest.Dll when a button is 
  681.     pushed, you could use the following code:
  682.  
  683.     procedure TForm1.Button1Click(Sender: TObject);
  684.  
  685.     type
  686.       TCallMeDll = function(a,b: Integer): string;
  687.  
  688.     var
  689.       CallMeDll: TCallMeDll;
  690.       FuncPtr: TFarProc;
  691.       hDll: THandle;
  692.       result: string;
  693.  
  694.     begin
  695.       hDll:=LoadLibrary('Mytestdll.dll');
  696.       FuncPtr:=GetProcAddress(hDLL,'CallMe');
  697.       @CallMeDll:=FuncPtr;
  698.       if @CallMeDll <> nil then
  699.         result:=CallMeDll(4,5);
  700.       FuncPtr:=nil;
  701.       FreeLibrary(hDll);
  702.     end;
  703.  
  704.     Note that w must first load the dll into memory, then we can obtain a 
  705.     pointer to the function, and assign that to the contents of CallMeDll.
  706.     At this point I am checking to see if the ProcAdderss is Nil, this would 
  707.     indicate that the call to GetProcAddress failed.
  708.  
  709.     It is important to note that sometimes you might want to load the 
  710.     library, and get the procedure address once at the begenning of your 
  711.     program, and free the library once at the end.
  712.       
  713.  
  714. 32) Q:  What is a Callback function, and how do I create one?
  715.  
  716.     A:  A call back function is a function which you write, but is called by 
  717.     some other program or module, such as windows.  To create a callback function, you
  718.     must first declare a function type, the funciton itself, and implement 
  719.     the function.  In the interface section:
  720.  
  721.     { In main program interface }
  722.  
  723.     type
  724.       TCallBackFunction = function(s: string): integer;
  725.       CallMe(s: string): integer;
  726.  
  727.     And in the Implementation section:
  728.  
  729.     { In main program implementation }
  730.  
  731.     procedure TestCallBack(CallBackFunction: TCallBackFunction); far; external 'Other';
  732.         { Note that 'other' is a Dll containing the procedure TestCallBack }
  733.  
  734.     function CallMe(s: PChar): integer;
  735.     begin
  736.       { what ever you need to do }
  737.       CallMe := 1; { What ever you need to return }
  738.     end;
  739.  
  740.     procedure TForm1.Button1Click(Sender: TObject);
  741.     begin
  742.       TestCallBack(CallMe);
  743.     end;
  744.  
  745.     Note that in 'Other' you would also declare a function type, and use it
  746.     like this:
  747.  
  748.     { in library Other interface }
  749.  
  750.     type
  751.       TMainFunction = function(s: string): integer;
  752.       TestCallBack(MainFunc: TMainFunction);
  753.  
  754.     { in library Other implementation }
  755.  
  756.     TestCallBack(MainFunc: TMainFunction);
  757.     var
  758.       result: integer;
  759.     begin
  760.       result:=MainFunc('test');
  761.     end;
  762.