Tuesday, February 14, 2006

Detecting when the mouse is over a component


    When the mouse enters the area of a component, Delphi sends the CM_MouseEnter message. When it leaves, Delphi sends a CM_MouseLeave message. Treating this two messages, you can detect if the mouse is over the component. The Fly-Over label component shows this technique.

Executing default action of an Ole Container

To execute the default action of an Ole Container (open a Word or Excel document or run a Powerpoint presentation), you can use this code:


    procedure TForm1.Button1Click(Sender: TObject);

    begin

      OleContainer1.DoVerb(ovprimary);

    end;

Paradox Password in code

To give the password of a Paradox table in code, you can use this code:


    procedure TForm1.FormCreate(Sender: TObject);

    begin

      Session.AddPassWord('MyPass');

      Table1.Acive := True;

    end;

Hiding and showing the Windows Taskbar

To hide the Windows Taskbar you must use a code like this:


    procedure TForm1.Button1Click(Sender: TObject);

    var

      hTaskBar :Thandle;

    begin

      hTaskBar := FindWindow('Shell_TrayWnd',Nil);

      ShowWindow(hTaskBar,Sw_Hide);

    end;


To Show the Windows Taskbar, you must use this code:



    procedure TForm1.Button2Click(Sender: TObject);

    var

      hTaskBar :Thandle;

    begin

      hTaskBar := FindWindow('Shell_TrayWnd',Nil);

      ShowWindow(hTaskBar,Sw_Normal);

    end;

Hiding Minimized MDI Child Windows

To hide minimized MDI child windows, you must trap its WM_Size message, like this:


    type

      TForm1 = class(TForm)

      public

        procedure WMSize(var M : TWMSIZE);Message WM_Size;

      end;



    implementation



    procedure TForm1.WMSize(var M:TWMSIZE);

    begin

      if M.SizeType=Size_Minimized then

        showwindow(Handle,Sw_Hide);

    end;

Acessing Protected Members of an Object

Usually, you can't access protected members of an object. But Delphi let you access protected members if the object is defined in the same unit. So you can define a derived object and typecast where you want to use the protected member. It's something like this:


    THackControl = class(TCustomEdit)

    end;

After defining this class, you can access all protected members of TCustomEdit, with objects of derived classes with a code like this:


    THackControl(MyEdit).Color := clBlack;

Calling Windows DialUp Connection Dialog

To call Windows DialUp Connection Dialog, you can use WinExec, like this:


    procedure TForm1.Button1Click(Sender: TObject);

    begin

      winexec(PChar('rundll32.exe rnaui.dll,RnaDial '+Edit1.Text),sw_show);

    end;

Drawing with different line types


    Windows allows to draw lines where each pixel is another shape or drawing with LineDDa function. It needs a callback function that is called when each pixel must be drawn. There you can put the drawing routines. This routine draws a rectangle every 4 pixels:


      TForm1 = class(TForm)

        procedure FormCreate(Sender: TObject);

        procedure FormPaint(Sender: TObject);

      public

        DrawNow : Integer;

      end;



      var

        Form1: TForm1;



      procedure DrawPoint(x,y : Integer;lpData : LParam); stdcall;



      implementation



      {$R *.DFM}



      procedure DrawPoint(x,y : Integer;lpData : LParam);

      begin

        with TObject(lpData) as TForm1 do begin

          if DrawNow mod 4 = 0 then

            Canvas.Rectangle(x-2,y-2,x+3,y+3);

          Inc(DrawNow);

        end;

      end;



      procedure TForm1.FormCreate(Sender: TObject);

      begin

        DrawNow := 0;

      end;



      procedure TForm1.FormPaint(Sender: TObject);

      begin

        LineDDA(0,0,Width,Height,@DrawPoint,Integer(Self));

      end;

Detecting Windows shutdown

When Windows is shutting down, it sends a WM_QueryEndSession to all open applications. To detect (and prevent shutdown), you must define a message handler to this message. Put this definition on the private section of the main form:




    procedure WMQueryEndSession(var Msg : TWMQueryEndSession); message WM_QueryEndSession;

And put this method in the implementation section of the unit:


    procedure TForm1.WMQueryEndSession(var Msg : TWMQueryEndSession);

    begin

      if MessageDlg('Close Windows ?', mtConfirmation, [mbYes,mbNo], 0) = mrNo then

        Msg.Result := 0

      else

        Msg.Result := 1;

    end;

Display and Change video resolutions

To display available video modes, you must use the API function EnumDisplaySettings: it gets all display setting modes available.

To change modes, you must use ChangeDisplaySettings, that changes the video resolution and color depth.
procedure TForm1.FormCreate(Sender: TObject);
var
i : Integer;
DevMode : TDevMode;
begin
i:=0;
while EnumDisplaySettings(nil,i,DevMode) do begin
with Devmode do
ListBox1.Items.Add
(Format('%dx%d %d Colors',
[dmPelsWidth,dmPelsHeight,Int64(1) shl dmBitsperPel]));
Inc(i);
end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
DevMode : TDeviceMode;
liRetValue : Longint;
begin
if EnumDisplaySettings
(nil,Listbox1.ItemIndex,Devmode) then
liRetValue := ChangeDisplaySettings
(DevMode, CDS_UPDATEREGISTRY);

SendMessage(HWND_BROADCAST,
WM_DISPLAYCHANGE,
SPI_SETNONCLIENTMETRICS,
0);
end;

Right justifying menu items

To justify menu items on the right side of the main menu bar, you must do like this:





{It will right justify all menu items to the right of the one selected}



procedure SetJustify(Menu: TMenu; MenuItem: TMenuItem; Justify: Byte);

{$IFDEF WIN32}

var

  ItemInfo: TMenuItemInfo;

  Buffer: array[0..80] of Char;

{$ENDIF}

begin

{$IFDEF VER80}

  MenuItem.Caption := Chr(8) + MenuItem.Caption;

{$ELSE}

  ItemInfo.cbSize := SizeOf(TMenuItemInfo);

  ItemInfo.fMask := MIIM_TYPE;

  ItemInfo.dwTypeData := Buffer;

  ItemInfo.cch := SizeOf(Buffer);

  GetMenuItemInfo(Menu.Handle, MenuItem.Command, False, ItemInfo);

  if Justify = 1 then

    ItemInfo.fType := ItemInfo.fType or MFT_RIGHTJUSTIFY;

  SetMenuItemInfo(Menu.Handle, MenuItem.Command, False, ItemInfo);

{$ENDIF}

end;

Moving forms with no caption

To move a form with no caption, you must override WM_NCHITTEST message, like this:




type

  TForm1 = class(TForm)

  public

    procedure WMNCHitTest(var M: TWMNCHitTest); message WM_NCHitTest;

  end;



var

  Form1: TForm1;



implementation

{$R *.DFM}



procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);

begin

  inherited;

  if M.Result = htClient then {if the mouse clicked on the form}

    M.Result := htCaption; {make windows think that mouse has been clicked on caption}

end;

Positioning the caret in a line in a memo or RichEdit


    To position the caret in a line in a Memo or RichEdit you must use this:




      With Memo1 do

        SelStart := Perform(EM_LINEINDEX, Line, 0);



Moving the mouse pointer


To move the Mouse pointer with no user's action, you can use a timer and put the following code in it's OnTimer event:



    procedure TForm1.Timer1Timer(Sender: TObject);

    var

       pt:tpoint;

    begin

      getcursorpos(pt);

      pt.x := pt.x + 1;

      pt.y := pt.y + 1;

      if pt.x>=screen.width-1 then setcursorpos(0,pt.y);

      if pt.y>=screen.height-1 then setcursorpos(pt.x,0);

    end;

Detecting Windows Shutdown


To detect Windows Shutdown, you must trap WM_EndSession message. These steps should be taken:

Declare a message handling procedure in your Form's Private section:



    procedure WMEndSession(var Msg : TWMEndSession); message WM_ENDSESSION;


Add the procedure to the implementation section of your Unit:



    procedure TForm1.WMEndSession(var Msg : TWMEndSession);

    begin

      if Msg.EndSession = TRUE then

        ShowMessage('Windows is shutting down ' + #13 + 'at ' +

          FormatDateTime('c', Now));

      inherited;

    end;

Creating non rectangular windows (D2/D3)


To create a non rectangular window, you must create a Windows Region and use the API function SetWindowRgn, like this (this works only in D2/D3):




    var

      hR : THandle;

    begin

      {creates an Elliptic Region}

      hR := CreateEllipticRgn(0,0,100,200);

      SetWindowRgn(Handle,hR,True);

    end;

Doing incremental search in a Query


To do incremental search in a Query, using a TEdit, you must put this code in the TEdit's OnChange Event (this works only in D2/D3):




    procedure TForm1.Edit1Change(Sender: TObject);

    begin

      With Edit1 do

        if Text <> '' then begin

          Query1.Filter := 'code = '''+Edit1.Text+'''';

          Query1.FindFirst;

        end;

    end;


Another way could be




    procedure TForm1.Edit1Change(Sender: TObject);

    begin

      With Edit1 do

        if Text <> '' then

          Query1.Locate('code',Edit1.Text,[loPartialKey]);

    end;

Doing incremental search in a Table


To do incremental search on a table, using a TEdit, you must put this code in the TEdit's OnChange Event:




    procedure TForm1.Edit1Change(Sender: TObject);

    begin

      With Edit1 do

        if Text <> '' then

          Table1.FindNearest([Text]);

    end;

Converting the first letter of an EditBox to uppercase

To convert the first letter of an EditBox to uppercase this code can be used:




    procedure TForm1.Edit1Change(Sender: TObject);

    var

      OldStart : Integer;

    begin

      With Edit1 do

        if Text <> '' then begin

          OnChange := NIL;

          OldStart := SelStart;

          Text := UpperCase(Copy(Text,1,1))+LowerCase(Copy(Text,2,Length(Text)));

          SelStart := OldStart;

          OnChange := Edit1Change;

        end;

    end;

Listing all open windows

To list (get) all open windows, you must use EnumWindows API function. It uses a callback function, that receives 2 parameters: a Window handle and a Pointer.
You can use it with a code like this (this code lists all open windows, even invisible ones in a listbox):



    function EnumWindowsProc(Wnd : HWnd;Form : TForm1) : Boolean; Export;
    {$ifdef Win32} StdCall; {$endif}

    var

      Buffer : Array[0..99] of char;

    begin

      GetWindowText(Wnd,Buffer,100);

      if StrLen(Buffer) <> 0 then

        Form.ListBox1.Items.Add(StrPas(Buffer));

      Result := True;

    end;



    procedure TForm1.Button1Click(Sender: TObject);

    begin

      EnumWindows(@EnumWindowsProc,LongInt(Self));

    end;

Working with multiselect grids

Delphi 2.0 multiselect grids have an undocumented SelectedRows property, a TBookmark list.
You can use it with a code like this:



    With DbGrid1 do begin

      for i := 0 to Pred(SelectedRows.Count) do begin

        DataSource.DataSet.Bookmark := SelectedRows[i];

        {the dataset is positioned on the selection. Do your stuff}

      end;

    end;

Packing tables


    To pack (remove phisically all deleted records) from a Paradox table you must use this code




      procedure ParadoxPack(Table : TTable);

      var

        TBDesc : CRTblDesc;

        hDb: hDbiDb;

        TablePath: array[0..dbiMaxPathLen] of char;

      begin

        FillChar(TBDesc,Sizeof(TBDesc),0);

        with TBDesc do begin

          StrPCopy(szTblName,Table.TableName);

          StrPCopy(szTblType,szParadox);

          bPack := True;

        end;

        Table.Open;

        hDb := nil;

        Check(DbiGetDirectory(Table.DBHandle, True, TablePath));

        Table.Close;

        Check(DbiOpenDatabase(nil, 'STANDARD', dbiReadWrite,

          dbiOpenExcl,nil,0, nil, nil, hDb));

        Check(DbiSetDirectory(hDb, TablePath));

        Check(DBIDoRestructure(hDb,1,@TBDesc,nil,nil,nil,False));

        Table.Open;

      end;

    To pack Dbase tables use this command



        DBIPackTable(Table1.DBHandle,Table1.Handle,nil,nil,True);

Making Enter key act as Tab


    To make the Enter key act as Tab key is a 3 step procedure:



    1) Set the form's KeyPreview property to True

    2) Set all form's buttons property Default to False

    3) Create an OnKeyPress event for the form like this:




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

      begin

        if Key = #13 then begin

          Key := #0;

          if (Sender is TDBGrid) then

            TDBGrid(Sender).Perform(WM_KeyDown,VK_Tab,0)

          else

            Perform(Wm_NextDlgCtl,0,0);

        end;

      end;



    Tip collaboration by: Antonio Carlos Maduro

Accessing DBNavigator buttons

The DBNavigator has a protected Buttons array that acesses its buttons. An inherited Navigator can acess them and change their properties. TBSNavigator does this to change their Enabled state or Glyph.

Filtering and sorting tables in Delphi 1.0


    To filter and sort a table in Delphi 1.0 you can use a QBE (Query by Example) file as the TableName TTable's property. This is useful to filter, sort or join tables, while still using the TTable component. QBE files can be created in Database Desktop.


Extracting icons from an executable


    To extract icons from an executable, you must use ExtractIcon API function. It gets 3 parameters:


      Instance - Instance of the application

      FileName - Name of the executable. Must be a PChar

      NumIcon - Number of the icon to be retreaved. If it's Word(-1), the function returns the number of icons in the executable.


    Download an example.




How to Get Dos Environment




    To get DOS Environment, you must use GetDosEnvironment API Function.It returns a PChar that can be parsed. Download an example.


International settings


    By default, Delphi gets its date/time, currency and numeric format from Control Panel's International settings.
    This can lead to errors, when parsing dates, numbers or lists. To avoid these errors, you can set the constants defined in Delphi,
    like DecimalSeparator, ShortDateFormat and others like this:




      DecimalSeparator := '.';

      ShortDateFormat := 'mm/dd/yy';


    This will override the default settings assuring the correct values. To a complete list of these variables, look at Currency Formatting Variables in Delphi Help

Changing a menu font


    To change a menu font, you must forget using Delphi's menu designer.
    You must create the menu items using Api function AppendMenu, with flag
    MF_OWNERDRAW, in the Form's OnCreate Event. Then you must use WM_MEASUREITEM
    and WM_DRAWITEM to draw your menu options. Download
    an example.


Getting current line and column from a memo


    To get the current line and column from a memo you must use this:




      With Memo1 do begin

        Line := Perform(EM_LINEFROMCHAR,SelStart, 0);

        Column := SelStart - Perform(EM_LINEINDEX, Line, 0);

      end;



Shrinking the executable


    In Delphi 1.0, sometimes checking the Optimize for size and load
    time
    checkbox, in Options/Project/Linker doesn't work (you get
    a Disk full message, even with plenty of space). Delphi 1.0 comes with
    a Dos program, W8LOSS, that does the same. To use, it you must type:




      W8LOSS program.exe




    It will shrink your executable in about 20%, and will speed up loading
    time.


Changing a TEdit text in its OnChange event


    If you want to change a TEdit text in its OnChange event, it will fire
    the event recursively until stack exhausts. To do this, you must set it
    to NIL before changing its text and reassigning it after, like this:






      procedure Edit1Change(Sender : TObject);

      begin

        Edit1.OnChange := NIL;

        if Edit1.Text = 'Some Text' then

          Edit1.Text := 'New Text';

        Edit1.OnChange := Edit1Change;

      end;




    This tip works also with OnValidate events.


Running a DOS program and closing its window after running it


    When you run a DOS program in Windows95, its window remains open until
    closed by the user. To run a Dos program that closes its windows after
    running, you must specify "command.com /c program" in the
    command line. Using WinExec API function to run a program named progdos.exe,
    the call should be:




      WinExec("command.com /c progdos.exe",sw_ShowNormal);




    If you wish that the program is run hidden from the user, the second
    parameter must be sw_Hide. You must specify the .com extension, or the program will not run.