HomePage Delphi Library

{
To programmatically close another application,
send to the application a WM_QUIT message.
"Window caption" is the caption of the
window that you are sending the message to.
}

function KillApp(const sCapt: PChar) : boolean;
 var AppHandle:THandle;
begin
 AppHandle:=FindWindow(Nil, sCapt);
 Result:=PostMessage(AppHandle, WM_QUIT, 0, 0);
end;

{Usage:}
if not KillApp('Window caption') then
 ShowMessage('App not closed');

03/2000. IsTrueTypeFont

{
Given a font's name (such as Arial,
Verdana, Times New Roman, etc), function
returns a boolean value meaning
if the font is or isn't a True Type font. 
}

function IsTrueTypeFont(FontName : string):boolean;
const
 PITCH_MASK: byte = $0F;
var
 TxMet: TTextMetric;
 TempCanvas : TCanvas;
 PitchTest : byte;
begin
 TempCanvas:=TCanvas.Create;
 TempCanvas.Handle:=CreateCompatibleDC(0);
 TempCanvas.Font.Name:=FontName;
 GetTextMetrics(TempCanvas.Handle, TxMet);
 PitchTest:=TxMet.tmPitchAndFamily and PITCH_MASK;
 Result:=(PitchTest and TMPF_TRUETYPE) <> 0;
 TempCanvas.free;
end;

02/2000. ALT+TAB, CTRL+ESC, CTRL+ALT+DEL

{
If you wish to disable those keys while your
application is running call SystemKeys:
When you call SystemKeys, if Disable is True,
the keys will be disabled, False otherwise.
}
	
procedure SystemKeys(Disable: Boolean);
 var OldVal : LongInt;
begin
 SystemParametersInfo(SPI_SCREENSAVERRUNNING, 
                      Word(Disable), @OldVal, 0);
end;

01/2000. Hide Title Bar

{A quick way to hide your program's title bar:}
	
procedure TForm1.FormCreate(Sender: TObject);
begin
  SetWindowLong( Handle,
    GWL_STYLE,
    GetWindowLong( Handle, GWL_STYLE )
    and not WS_CAPTION );
  ClientHeight := Height;
end;

Calculate a week-of-the-year index (0-51) for a given date. Week 0 is the week containing the first Sunday of the year. } function WeekNum(const TDT:TDateTime) : Word; var Y,M,D:Word; dtTmp:TDateTime; begin DecodeDate(TDT,Y,M,D); dtTmp := EnCodeDate(Y,1,1); Result := (Trunc(TDT-dtTmp)+(DayOfWeek(dtTmp)-1)) DIV 7; if Result <> 0 then Result := Result - 1; end;

Change the font in Tool Tip (Hint box)}

Type
 TMyHintWindow = Class (THintWindow)
  Constructor Create (AOwner: TComponent);override;
 end;

Constructor TMyHintWindow.Create(AOwner:TComponent); 
begin
 Inherited Create (AOwner);
 Canvas.Font.Name := 'Courier New';
 Canvas.Font.Size := 72;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 Application.ShowHint := false;
 HintWindowClass := TMyHintWindow;
 Application.ShowHint := True;
end;

37. Date/Time and other International Settings

{
By default, Delphi gets its date/time, currency
and numeric format from Control Panel's
International settings.

We can set the constants defined in Delphi,
like DecimalSeparator, ShortDateFormat
and others like:
}

procedure SetLocale;
begin
 DecimalSeparator := '.';
 ThousandSeparator : = ','; 
 ShortDateFormat := 'mm/dd/yy';
end;

{
To a complete list of these variables, look at
Currency and date/time formatting variables
in Delphi Help 
}

36. Window Flashing

{
Flashing a window means changing the appearance
of its caption bar as if the window were changing
from inactive to active status, or vice versa.

Typically, a window is flashed to inform the user
that the window requires attention but that it
does not currently have the keyboard focus.

The FlashWindow API function flashes
the window only once.

To create a flashing window add a TTimer component
on a form, and use the following code in the
OnTimer event handler:
}

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  FlashWindow (Handle, True);
end;

35. Open & Close CD-drive

{To OPEN the CD-ROM:}
mciSendString
  ('Set cdaudio door open wait', nil, 0, handle); 

{To CLOSE the CD-ROM:}
mciSendString
  ('Set cdaudio door closed wait', nil, 0, handle); 

{Remember to include the MMSystem
unit in your uses clause.}
uses ShellAPI;
Function DelTree(DirName : string): Boolean;
{
Completely deletes a directory regardless
of whether the directory is filled or has
subdirectories.  No confirmation is requested
so be careful. If the operation is successful
then True is returned, False otherwise
}
var
 SHFileOpStruct : TSHFileOpStruct;
 DirBuf         : array [0..255] of char;
begin
 try
  Fillchar(SHFileOpStruct,Sizeof(SHFileOpStruct),0);
  FillChar(DirBuf, Sizeof(DirBuf), 0 );
  StrPCopy(DirBuf, DirName);
  with SHFileOpStruct do begin
   Wnd    := 0;
   pFrom  := @DirBuf;
   wFunc  := FO_DELETE;
   fFlags := FOF_ALLOWUNDO;
   fFlags := fFlags or FOF_NOCONFIRMATION;
   fFlags := fFlags or FOF_SILENT;
  end; 
   Result := (SHFileOperation(SHFileOpStruct) = 0);
  except
   Result := False;
 end;
end;

{
Usage

if DelTree('c:\TempDir') then
  ShowMessage('Directory deleted!')
else
  ShowMessage('Errors occured!');
}

33. From/To the 8.3 Format To/From the Long Format

unit LFN_ALT;
interface
{
This unit provides two functions that convert
filenames from the long format to the 8.3
format, and from the 8.3 format to the long
format.
}
function AlternateToLFN(AltName:String):String;
function LFNToAlternate(LongName:String):String;

implementation

uses Windows;

function AlternateToLFN(AltName:String):String;
var 
 temp: TWIN32FindData;
 searchHandle: THandle;
begin
 searchHandle:=FindFirstFile(PChar(AltName),temp);
 if searchHandle <> ERROR_INVALID_HANDLE then
   result := String(temp.cFileName)
 else
   result := '';
 Windows.FindClose(searchHandle);
end;

function LFNToAlternate(LongName:String):String;
var 
 temp: TWIN32FindData;
 searchHandle: THandle;
begin
 searchHandle:=FindFirstFile(PChar(LongName),temp);
 if searchHandle <> ERROR_INVALID_HANDLE then
   result := String(temp.cALternateFileName)
 else
   result := '';
 Windows.FindClose(searchHandle);
end;
end.{unit}

32. Get Files "Last Modified" Attribute

function FileLastModified
  (const TheFile: string): string;
var
 FileH            : THandle;
 LocalFT          : TFileTime;
 DosFT            : DWORD;
 LastAccessedTime : TDateTime;
 FindData         : TWin32FindData;
begin
 Result := '';
 FileH := FindFirstFile(PChar(TheFile), FindData);
 if FileH <> INVALID_HANDLE_VALUE then begin
  Windows.FindClose(Handle);
  if (FindData.dwFileAttributes AND
      FILE_ATTRIBUTE_DIRECTORY) = 0 then
   begin
    FileTimeToLocalFileTime
     (FindData.ftLastWriteTime,LocalFT);
    FileTimeToDosDateTime
     (LocalFT,LongRec(DosFT).Hi,LongRec(DosFT).Lo);
    LastAccessedTime := FileDateToDateTime(DosFT);
    Result := DateTimeToStr(LastAccessedTime);
   end;
 end;
end;
{
Usage:
label1.Caption:=FileLastModified('c:\autoexec.bat');
}

31. Reading a Directory's Content

procedure FindAll (const Path: String;
                         Attr: Integer;
                         List: TStrings);
var
  Res: TSearchRec;
  EOFound: Boolean;
begin
  EOFound:= False;
  if FindFirst(Path, Attr, Res) < 0 then
    exit
  else
    while not EOFound do begin
      List.Add(Res.Name);
      EOFound:= FindNext(Res) <> 0;
    end;
  FindClose(Res);
end; 

{
The following example lists all files and
subdirectories of the C:\Windows directory
into a TListBox called ListBox1:

FindAll('C:\Windows\*.*',faAnyFile,ListBox1.Items);
}
LeftStr() takes a certain portion of
the left side of a string.
. MidStr() takes a specified number of
characters from a string.
. RightStr() takes a certain portion of
the right side of a string.
}

function RightStr
   (Const Str: String; Size: Word): String;
begin
 if Size > Length(Str) then Size := Length(Str);
 RightStr := Copy(Str, Length(Str)-Size+1, Size)
end;

function MidStr
   (Const Str: String; From, Size: Word): String;
begin
 MidStr := Copy(Str, From, Size)
end;

function LeftStr
   (Const Str: String; Size: Word): String;
begin
 LeftStr := Copy(Str, 1, Size)
end;

{
Let's say we have a string
Dstr := 'Delphi is the BEST', then

LeftStr(Dstr, 5) := 'Delph'
MidStr(Dstr, 6, 7) := 'i is th'
RightStr(Dstr, 6) := 'e BEST'
}

29. Reverse a String

}28. Search And Replace

function SearchAndReplace
  (sSrc, sLookFor, sReplaceWith : string) : string;
var
  nPos, nLenLookFor : integer;
begin
  nPos        := Pos(sLookFor, sSrc);
  nLenLookFor := Length(sLookFor);
  while (nPos > 0) do begin
    Delete(sSrc, nPos, nLenLookFor);
    Insert(sReplaceWith, sSrc, nPos);
    nPos := Pos(sLookFor, sSrc);
  end;
  Result := sSrc;
end;

{
Let's say you have a string
'testing;search;and;replace'
and you want to replace ';' with spaces.
Here's how you'd call this function

sOldString:='testing;search;and;replace';
sNewString:=SearchAndReplace(sOldString, ';', ' ')
}

27. Convert The First Letter In Any Word To A Capital

{
Tip submitted by: Andrew Burt, [email protected]
}
procedure TForm1.Button1Click(Sender: TObject);
var
 GetString : string;
 GetLength : Integer;
 I         : Integer;
 T         : String;
begin
 if edit1.SelLength > 0 then
   GetString:= Edit1.Seltext
 else GetString:= Edit1.Text;
 GetLength:= Length(Edit1.Text);
 if GetLength>0 then begin
  for I:= 0 to GetLength do begin
   if (GetString[I] = ' ') or (I=0) then begin
    if GetString[I+1] in ['a'..'z'] then begin
     T:=GetString[I+1];
     T:=UpperCase(T);
     GetString[I+1]:=T[1];
    end;
   end;
  end;
  if edit1.Sellength>0 then
    Edit1.Seltext:=GetString
  else Edit1.Text:=GetString;
 end;
end;
Here is an example of creating a
font that is rotated 45 degrees
}

procedure TForm1.Button1Click(Sender: TObject);
var
  lf : TLogFont;
  tf : TFont;
begin
  with Form1.Canvas do begin
    Font.Name := 'Arial';
    Font.Size := 24;
    tf := TFont.Create;
    try
      tf.Assign(Font);
      GetObject(tf.Handle, sizeof(lf), @lf);
      lf.lfEscapement := 450;
      lf.lfOrientation := 450;
      tf.Handle := CreateFontIndirect(lf);
      Font.Assign(tf);
    finally
      tf.Free;
    end;
    TextOut(20, Height div 2, 'Rotated Text!');
  end;
end;

25. Checking If File Is In Use

{
IsFileInUse will return true if the file is
locked for exclusive access. It would fail if the
file doesn't exist at all.
}

function IsFileInUse(fName : string) : boolean;
var
   HFileRes : HFILE;
begin
   Result := false;
   if not FileExists(fName) then exit;
   HFileRes :=
     CreateFile(pchar(fName),
                GENERIC_READ or GENERIC_WRITE,
                0, nil, OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL,
                0);
   Result := (HFileRes = INVALID_HANDLE_VALUE);
   if not Result then
   CloseHandle(HFileRes);
end;

24. Capture Maximize/Minimize Buttons

...
public
  procedure WMSysCommand
    (var Msg: TWMSysCommand);
	message WM_SYSCOMMAND;
...

implementation
...
procedure TForm1.WMSysCommand;
begin
  if (Msg.CmdType = SC_MINIMIZE) or
     (Msg.CmdType = SC_MAXIMIZE) then
  MessageBeep(0);
  
  DefaultHandler(Msg);
end;

23. Enlarge a Form Over Screen Size

{Add a button to a form and try this:}

//Due to the default Windows handling of
//the WM_GETMINMAXINFO message,
//the max size of the form is set to
//the screen size. 

...
private
  procedure WMGetMinMaxInfo
    (var msg: TWMGetMinMaxInfo);
    message WM_GETMINMAXINFO;
...

implementation
procedure TForm1.WMGetMinMaxInfo
    (var msg: TWMGetMinMaxInfo);
begin
  inherited;
  with msg.MinMaxInfo^.ptMaxTrackSize do begin
    X := GetDeviceCaps(Canvas.Handle, HORZRES)
	     + (Width - ClientWidth);
    Y := GetDeviceCaps(Canvas.Handle, VERTRES)
	     + (Height - ClientHeight);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
Const
 Rect: TRect = (Left:0; Top:0; Right:0; Bottom:0);
begin
if Left > 0 then begin
  Rect := BoundsRect;
  SetBounds(
    Left - ClientOrigin.X,
    Top - ClientOrigin.Y,
    GetDeviceCaps(Canvas.Handle, HORZRES)
              + (Width - ClientWidth),
    GetDeviceCaps(Canvas.Handle, VERTRES)
                 + (Height - ClientHeight)
  );
end
else BoundsRect := Rect;
end;
Here is how to keep the window from moving:

First, make the borderstyle something
like bsDialog, so that the window cant be resized.

Next, add the following declaration
to your form class:
}
procedure PosChange(var Msg: TWmWindowPosChanging);
  message WM_WINDOWPOSCHANGING;

//Finally, implement the procedure like:

procedure TForm1.PosChange
  (var Msg: TWmWindowPosChanging);
begin
   Msg.WindowPos.x := Left;
   Msg.WindowPos.y := Top;
   Msg.Result := 0;
end;

{
Thats it. Easy as can be. The only problem with
this is that you cant move the form if you want
your code to. To get around this, just set up a
Boolean variable called PosLocked, set it to
True when you want to lock the forms position,
and to false when you need to move the form (when
your done, remember to set it back to true). Then
to implement the proc above, just make it
}

if PosLocked then begin
   Msg.WindowPos.x := Left;
   Msg.WindowPos.y := Top;
   Msg.Result := 0;
end else inherited;

21. Execute the Windows Explorer Find File Dialog Box

{
The following example demonstrates using DDE to
execute Explorer's find file dialog. The example
opens the dialog in the Directory "C:\DelphiTips".
}

uses ddeman;
procedure TForm1.Button1Click(Sender: TObject);
begin
 with TDDEClientConv.Create(Self) do begin
  ConnectMode := ddeManual;
  ServiceApplication := 'explorer.exe';
  SetLink( 'Folders', 'AppProperties');
  OpenLink;
  ExecuteMacro
      ('[FindFolder(, C:\DelphiTips)]', False);
  CloseLink;
  Free;
 end;
end;

20. Delete files with the ability to UNDO

{
Delete a file with the ability to undo by
sending the file to the "Recycle Bin."

Function FileDeleteRB will return True
if the operation was successful. 
}

uses ShellAPI;

function FileDeleteRB(
  FileName:string): boolean;
var
  fos : TSHFileOpStruct;
begin
  FillChar(fos, SizeOf(fos), 0);
  with fos do begin
    wFunc  := FO_DELETE;
    pFrom  := PChar(FileName);
    fFlags := FOF_ALLOWUNDO
              or FOF_NOCONFIRMATION
              or FOF_SILENT;
  end;
  Result := (ShFileOperation(fos)=0);
end;

19. Add documents to the Windows Start-Documents Menu

{
If you would like to add documents to the new
"Start-Documents" menu that was added with the
creation of Windows 95/NT, use the following
function. Be sure to add ShellAPI and ShlOBJ
to your uses clause. 
}

procedure AddtoDocMenu(fName : String);
begin
  SHAddToRecentDocs(SHARD_PATH, PChar(fName));
end;

18. Making a transparent form

{Add a button to a form and try this:}

procedure TForm1.FormCreate(Sender: TObject);
var
  FullRgn, ClientRgn, ButtonRgn: THandle;
  Margin, X, Y: Integer;
begin
  Margin := (Width - ClientWidth) div 2;
  FullRgn := CreateRectRgn(0, 0, Width, Height);
  X := Margin;
  Y := Height - ClientHeight - Margin;
  ClientRgn := CreateRectRgn
    (X, Y, X + ClientWidth, Y + ClientHeight);
  CombineRgn(FullRgn, FullRgn, ClientRgn, RGN_DIFF);
  X := X + Button1.Left;
  Y := Y + Button1.Top;
  ButtonRgn := CreateRectRgn
    (X, Y, X + Button1.Width, Y + Button1.Height);
  CombineRgn(FullRgn, FullRgn, ButtonRgn, RGN_OR);
  SetWindowRgn(Handle, FullRgn, True);
end;
When users enter data, they often need to format it
in a standard way. You can't depend on the user to
do it, so the best bet is to make your program do
the formatting itself.

For example, if the user types a name in all
lowercase letters, the program could automatically
convert the first character in the first and last
names to uppercase letters.

Add the following code to the OnKeyPress event for
the Edit1 component: 
}

with Sender as TEdit do
   if (SelStart = 0) or 
      (Text[SelStart] = ' ') then
         if Key in ['a'..'z'] then 
            Key := UpCase(Key);

16. Using the RunOnce Registry Key

end;15. Select a block of code by column

//Submited by Joe Ebbeler
{
Many times you may want to select a block of code
in the Delphi editor by column rather than line.
e.g.. You just removed a loop of some kind and you
want to shift a big block of text over 2 spaces.
You can do this by pressing Ctrl + O + C then use
the arrow keys to select the number of characters
then the number of rows. Then press the delete key
or copy or cut. To go back to regular select mode
you can press Ctrl + O + L or you can click
anywhere with the mouse.
}
The table component's ACTIVE property
must be set to FALSE.
Then, put this code on the form's create event:
}
Session.AddPassword('My secret password');
TableName.Active := True;

13. Enable/Disable the Windows "START" button

//Enable:
EnableWindow(FindWindowEx(FindWindow
  ('Shell_TrayWnd', nil), 0,'Button',nil),TRUE);
//Disable:
EnableWindow(FindWindowEx(FindWindow
  ('Shell_TrayWnd', nil), 0,'Button',nil),FALSE);

12. Is IDE running?

{
If you want to check whether IDE is runing or not
try this function: (Supose you want your new
shareware component to work only when IDE is
running, or something like that)
}
Function RunningInTheIDE: boolean;
Begin
  Result:=FindWindow('TAppBuilder', nil) > 0;
End;

11. Hint and Warning messages

{
It's a good idea to turn on hint and warning
messages specially when writing and compiling
your own code. Although some of these hints and
warnings can be ignored, most of them - if
nothing else - will remind you of things you
could do to improve your code.

- Go to "Project | Options" 
- Change to the "Compiler" tab 
- Make sure "Show hints" and "Show warnings"
check boxes are checked
}

10. Show/Hide the TaskBar in Windows 95

//To hide the task bar use 
ShowWindow(FindWindow
   ('Shell_TrayWnd',nil), SW_HIDE);

//To show the task bar use 
ShowWindow(FindWindow
   ('Shell_TrayWnd',nil), SW_SHOWNA);
Procedure IcoToBmp;
var
  Icon   : TIcon;
  Bitmap : TBitmap;
begin
  Icon   := TIcon.Create;
  Bitmap := TBitmap.Create;
  Icon.LoadFromFile('c:\picture.ico');
  Bitmap.Width := Icon.Width;
  Bitmap.Height := Icon.Height;
  Bitmap.Canvas.Draw(0, 0, Icon );
  Bitmap.SaveToFile('c:\picture.bmp');
  Icon.Free;
  Bitmap.Free;
end;

8. Prevent CTRL+DELETE in DBGrid

//How to disable deleting
//records with keyboard in DBGrid

procedure Form1.DBGrid1KeyDown
  (Sender: TObject; var Key: Word; Shift:TShiftState);
begin
  if (Shift = [ssCtrl]) and (Key = VK_DELETE) then
    Key := 0; {ignore}
end;

7. Extract Icon and paint on Form

{
How can I extract the associated icon and draw 
it into a small area of the form (e.g. Notepad icon)?
}
procedure TForm1.Button1Click(Sender: TObject);
var
  IconIndex : word;
  h : hIcon;
begin
 IconIndex := 0;
 h:=ExtractAssociatedIcon
    (hInstance,'C:\WINDOWS\NOTEPAD.EXE', IconIndex);
 DrawIcon(Form1.Canvas.Handle, 10, 10, h);
end;

6. Detecting Drive Types

//Note: you will need one button and
//one memo on your form, for this tip...
procedure TForm1.Button1Click(Sender: TObject);
var
 Drive: Char;
 DriveLetter: String[4];
begin
 for Drive := 'A' to 'Z' do
 begin
  DriveLetter := Drive + ':\';
  case GetDriveType(PChar(Drive + ':\')) of
   DRIVE_REMOVABLE:
    Memo1.Lines.Add(DriveLetter + ' Floppy Drive');
   DRIVE_FIXED:
    Memo1.Lines.Add(DriveLetter + ' Fixed Drive');
   DRIVE_REMOTE:
    Memo1.Lines.Add(DriveLetter + ' Network Drive');
   DRIVE_CDROM:
    Memo1.Lines.Add(DriveLetter + ' CD-ROM Drive');
   DRIVE_RAMDISK:
    Memo1.Lines.Add(DriveLetter + ' RAM Disk');
   end;
 end;
end;
procedure ScreenShot(DestBitmap : TBitmap);
var
  DC : HDC;
begin
 DC := GetDC (GetDesktopWindow);
 try
  DestBitmap.Width  := GetDeviceCaps (DC, HORZRES);
  DestBitmap.Height := GetDeviceCaps (DC, VERTRES);
  BitBlt(DestBitmap.Canvas.Handle,
         0,
         0,
         DestBitmap.Width,
         DestBitmap.Height,
         DC,
         0,
         0,
         SRCCOPY);
 finally
  ReleaseDC (GetDesktopWindow, DC);
 end;
end;

4. Remove App from TaskBar

procedure TForm1.FormCreate(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
  SetWindowLong(Application.Handle, GWL_EXSTYLE,
    getWindowLong(Application.Handle, GWL_EXSTYLE) or
    WS_EX_TOOLWINDOW );
  ShowWindow( Application.Handle, SW_SHOW );
end;

3. Show window contents while dragging

{
If you see the Microsoft Plus! you should notice
the "Show window contents while dragging", you
can use this option from your program by calling
the API function: SystemParametersInfo
}
//To Show window contents while dragging:
SystemParametersInfo
    (SPI_SETDRAGFULLWINDOWS, 1, nil, 0);

//To disable this option call the function:
SystemParametersInfo
    (SPI_SETDRAGFULLWINDOWS, 0, nil, 0);

2. Life Cycle Of A Form

{
When determining which events of a form to define
to do what you want, it's good to know the life
cycle of a form.
}
ACTION
Create
Show
Paint
Activate
ReSize
Paint
Close query
Close
Deactivate
Hide
Destroy

EVENT
OnCreate
OnShow
OnPaint
OnActivate
OnResize
OnPaint
OnCloseQuery
OnClose
OnDeactivate
OnHide
OnDestroy

1. Updating Cursor immediately

{
For example, if I change my cursor to a waiting
clock cursor before some time-consuming operations
is executed, why isn't my waiting clock cursor
activated immediately? It's only activated after
the event! This peace of code will update cursor
immediately.
}
begin
  Try
    Screen.Cursor:=crHourGlass;
    MyDataForm := TMyDataForm.Create(Application);
    Screen.Cursor:=crDefault;
    MyDataForm.ShowModal;
    MyDataForm.Free;
  Finally
    Screen.Cursor:=crDefault;
  end;
end;

http://www.zdjournals.com/ddj/

http://www.zdjournals.com/ddj/https://secure.zdjournals.com/ddj/cbw13m.htm

https://secure.zdjournals.com/ddj/cbw13m.htm

Breaking the language barrier

by Lee Inman

Does the thought of creating applications for multiple foreign markets make you cringe? Do you worry about extreme development times and endless searching and replacing? If so, read on. This article will show you how to get your application ready for the international market without months of development time. Adhering to a few simple guidelines during the application's development and having the right tools makes a world of difference.

What does it mean to internationalize?

You internationalize your applications to prepare them for the international market. It's the process that enables your application to operate in multiple locales (the user's natural environment). This means that all of the strings displayed by the application must be translated, and that forms and dialogs need to be modified to display the translated strings properly. The ideal situation is one where you actually plan to internationalize an application early in the development process. If you fail to do so, a little more work will be required.

Preparing to internationalize

The most obvious internationalizing step is to translate, or localize, the strings that appear in your application. Your primary goal in preparing your application is to write it so that it can be translated without altering the code. At this stage, don't worry about the strings that are defined as form and component property values--we'll handle them later. You'll need to isolate strings, such as error messages and hints, that you present to the user. In fact, all literal strings in your application should be isolated. The easiest way to do this is by declaring constants for them using the resourcestring keyword:

resourcestring
	MouseError    = `Error using the 
		mouse';
	KeyboardError = `Error using the 
		keyboard';

Using the resourcestring keyword to define strings has the following benefits:

The resource file that Delphi automatically creates for the above example will contain a string table similar to this:

STRINGTABLE
{
	65368,  "Error using the mouse"
	65369,  "Error using the keyboard"
}

If possible, all strings should be isolated in a single unit. Placing the strings in a single unit will make your life easier when it's time to actually create different language versions. Once all strings have been isolated and your application's forms are defined, you can begin the process of localizing your application.

Oops, I didn't prepare

If your application wasn't designed with internationalization issues in mind, don't despair. It will just take a little work to get the job done. Start by searching for all literal strings and move them to a separate unit, where you define them using the resourcestring keyword as described in the previous section.

Creating resource DLLs

Isolating literal strings simplifies the translation process. The next step involves the creation of one or more resource DLLs. Delphi produces executables that can detect and load language-specific DLLs that contain internationalized versions of the application's resources (text strings, graphic images, forms, etc.) automatically! All you need to do is make sure that the correct information is placed in the DLLs. There's no special code to add to your application--Delphi determines the proper resource DLL to load each time the application is executed.

Delphi 4 makes this part a snap, because it generates the resource DLL, including copies of all your forms, and builds the string resource file for you. Use the Delphi 4 Resource DLL Wizard to create a resource DLL for your program. To invoke the Resource DLL Wizard, choose File | New from the Delphi main menu to display the Object Repository. On the New tab, double-click the Resource DLL Wizard Icon. The Resource DLL Wizard requires an open, saved, and compiled project. It will create an RC file that contains the string tables from used RC files and resourcestring strings of the project, and generate a project for a resource--only DLL that contains the relevant forms.

The first step you must take when using the Resource DLL wizard is to select which forms should be included in the resource DLL. Do this by selecting one or more forms and clicking the Add button. Then, click Next to select which resource file to include. If you defined string constants with the resourcestring keyword, you should see at least one resource file with an .rc extension. When you're finished selecting resource files, click Next to advance to the final wizard screen. The final wizard screen allows you to select the country, the filename, and the storage location for the generated DLL. After entering this information, click Finish and Delphi automatically creates and loads the resource DLL project. You should create a resource DLL for each locale you want to support. Each resource DLL should have a filename extension specific to the target locale. For example, the French version of your resources will be named PGMNAME.FRA, the German version PGMNAME.DEU, and so on. If you use the Resource DLL wizard, this is handled for you. Otherwise, use the code in Listing A to obtain the locale code for the target translation. The code in Listing A places the locale names and codes in a list box on the main form. You may need to modify it slightly to suit your specific needs.

Listing A: Target local code

function LocalesCallback(Locale : PChar) :Bool; stdcall;
var
	LCID : Integer;
	Size : Integer;
	S    : string;
	Ext  : string;
begin
	LCID := StrToInt(`$' + Copy(Locale, 5, 4));
	//get name for this locale id
	Size := GetLocaleInfo(LCID, LOCALE_SLANGUAGE, nil, 0);
	SetLength(S, Size); //includes null
	GetLocaleinfo(LCID, LOCALE_SLANGUAGE, PChar(S), Size);
	//don't include null
	SetLength(S, Size - 1); 
	//get locale code (file extension)
	Size := GetLocaleInfo(LCID, LOCALE_SABBREVLANGNAME,
		 nil, 0);
	SetLength(Ext, Size); //includes null
	GetLocaleinfo(LCID, LOCALE_SABBREVLANGNAME,
		 PChar(Ext), Size);
	//don't include null
	SetLength(Ext, Size - 1);
	//add to list
	if Ext > `` then
		Form1.lbLocales.Items.Add(Ext + ` - ` + S);
	Result := Bool(1);
end;

procedure TForm1.GetLocalesClick(Sender :TObject);
begin
	EnumSystemLocales(@LocalesCallback,
		LCID_SUPPORTED);
end;

PLACE THE PARAGRAPH BELOW IN A NOTE BOX Note: With versions of Delphi prior to version 4, you'll need to take a different approach to internationalization, and one that this article doesn't attempt to cover. If you're using an older version of Delphi, you'll need to create the resource DLLs manually. Unfortunately, the earlier versions of Delphi don't offer the Resource DLL Wizard.

Localizing your application

At this point, your resources are isolated into one or more resource DLLs that contain DFM and RC files. You can open individual forms in the IDE and translate the relevant parts. First, open the RC files and translate relevant strings. Notice that all of the Delphi-defined string constants are automatically placed in the string resource file. Next, open each form file and edit it as you would normally. You can change the size and layout for forms and components, but not property values. Finally, edit each string resource file using Delphi's StringTable editor (by opening the resource file from the Project Manager), using the Delphi code editor, or by using a text editor, such as NotePad.

Using resource DLLs

The executables, DLLs, and packages that make up your application contain all the necessary resources. However, to replace those resources by localized versions, you need only to ship your application with localized resource DLLs that have the same name as your EXE, DLL, or BPL files. When your application starts up, it automatically checks the locale of the local system. If it finds any resource DLLs with the same name as the EXE, DLL, or BPL files it's using, it checks the extension on those DLLs. If the extension of the resource module matches the language and country of the system locale, your application will use the resources in that resource module instead of the resources in the executable, DLL, or package. If there isn't a resource module that matches both the language and the country, your application will try to locate a resource module that matches just the language. If there's no resource module that matches the language, your application will use the resources compiled into the executable, DLL, or package.

If you need to load resources dynamically, use the global FindResourceHInstance function to obtain the handle of the current resource module, rather than using MainInstance or simply HInstance. Doing so insures that you'll use the proper module instance for the current locale (and load the resource from the appropriate resource DLL). For example, to load a bitmap resource:

Bitmap.Handle :=
	LoadBitmap(FindResourceHInstance
		(HInstance),'SOMEIMAGE');

If you want your application to use a different resource module than the one that matches the locale of the local system, you can set a locale override entry in the Windows registry. Under the HKEY_CURRENT_USER\Software\Borland\Locales key, add your application's path and filename as a string value, and set the data value to the extension of your resource DLLs. At startup, the application will look for resource DLLs with this extension before trying the system locale. Setting this registry entry allows you to test localized versions of your application without changing the locale on your system. For example, the following procedure can be used in a test program to set the registry key value that indicates the locale to use when loading your Delphi application:

procedure SetLocaleOverrides(const
	FileName, LocaleOverride : string);
var
	Reg: TRegistry;
begin
	Reg := TRegistry.Create;
	try
		if Reg.OpenKey(`Software\Borland\Locales',
	True) then Reg.WriteString(FileName,
		LocaleOverride);
	finally
		Reg.Free;
	end;
end;
...
//test resources for France
SetLocaleOverrides(ParamStr(0), `FRA');

To revert to the default resources, just set the LocalOverride parameter to an empty string or delete the added registry key (the path and filename of the project).

SetLocaleOverrides(ParamStr(0), ``);

Conclusion

Using this process, you can ship a single application that adapts itself automatically to the locale of the system it's running on, simply by providing the appropriate resource DLLs. To add support for additional locals, all you need to do is create another resource DLL and place it in the same directory as your application. Nothing could be easier.

Windows Programming
Delphi phones home

We based this article on code and techniques developed by Bill Hannan of Sparta, New Jersey.

Many people develop and use applications that manage data-bases of contact names and information. Some commercial personal information managers (PIMs) even let you dial a phone number in your database directly from your computer. In this article, we'll show you how to use a few Windows Application Programming Interface (API) calls to accomplish the same task.

Overview

This technique uses three Windows API functions. The openComm() function initializes a connection to the Com Port, the writeComm() function sends data to the Com Port, and the closeComm() function closes the connection. We'll use the openComm() function to create a handle to a Com Port, and then we'll send our dialing commands to that port using the writeComm() function. Finally, we'll close the connection using the closeComm() function.

Create the code

The code for dialing the phone via the Windows API is shown in Figure A . When you call this procedure, simply pass a phone number (as a string), and the procedure will open a connection from the Com Port to the modem and send the commands to dial the specified number. After you've finished your call, the function closes the connection to the Com Port.


Figure A - You can use the dialPhone() function to automatically dial your modem.

procedure dialPhone(const numberToDial: String; 
                    const commPort: String);
var
  theHandle: Integer;
  openErrMsg: String;
  myBuffer: String;
  dialString: array[0..79] of char;
  port: array[0..10] of char;
begin
StrPCopy(port, commPort);
theHandle := openComm(port, 50 , 50);
if theHandle < 0 then
  begin
    case theHandle of
      -1: openErrMsg := 'Bad Comm Port ID';
      -2: openErrMsg := 
           'Comm Port in use by another program';
     -10: openErrMsg := 
           'Comm Port Hardware Problem';
    else
      openErrMsg := 
           'Unexpected error Opening Comm Port';
    end;
    showMessage(openErrMsg);
  end
else
  if MessageDlg('Dial '+numberToDial+ 
                ' on port ' + commPort + ' now?',
    mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    begin
      myBuffer := 'ATDT' + numberToDial + ';\r';
      StrPCopy(dialString, myBuffer);
      writeComm(theHandle,dialString,50);
      application.processMessages;
      showMessage('Click OK To Disconnect Modem');
      writeComm(theHandle, '+++', 5);
      writeComm(theHandle, 'ATH', 5);
    end;
  closeComm(theHandle);
end;


The code first opens the Com Port using the openComm() function. In this case, it specifies 50 input and 50 output character buffers. Next, it tests to see that the port opened. If the port didn't open, the code tests to see what error occurred, and then it reports the error.

If the port opened successfully, the code displays a message box asking the user to confirm the dial. If the user confirms the dial request, the code sends the dial command to the opened Com Port via the writeComm() function. Finally, another dialog box is displayed so that you can disconnect the modem from the call. When the modem disconnects, the code uses the writeComm() function again to reset the modem. Finally, the code uses the closeComm() function to close the opened Com Port.

Conclusion

In this article, we've shown how you can use the Windows API to dial a phone number. Using a few simple calls to the API, you can open, write to, and close communications ports on your computer from within your Delphi applications.


Embedding files in applications.

Ever wanted to hide files inside your application ? Well, it is possible.

Inprise does actually ship an editor with delphi which is capable of compiling any file in to a resource. The name of this compiler (In delphi 4 anyway) is BRCC32.EXE and can be found in the \program files\borland\delphi4\bin directory.
I will now give an example of how to embed your files, and then how to use these files.

Stage 1, making the RES

  1. Open a DOS window
  2. Change to your delphi\bin directory
  3. type Edit MyRes.RC and type the following :
  4.         clouds   rcdata   c:\windows\clouds.bmp
  5. Save your file
  6. Exit back to your DOS window
  7. Type brcc32 MyRes

You now have a file called MyRes.Res. Clouds is your ID for the resource item, rcdata tells the compiler that this is a binary file, and the last paramter is the file you want included. You may have as many lines as you like in your RC file, as long as the ID is unique.

Stage 2, using the RES

  1. Copy the RES file to your application directory
  2. In the unit code for your main form you will see
  3.         {$R *.DFM}

    You need to change this to

            {$R *.DFM}
            {$R MyRes.Res}
  4. Drop a TImage on your main form
  5. In the OnCreate method of your form do the following :
  6. procedure TForm1.FormCreate(Sender: TObject);
    var
      //We need a stream to read our resource
      RS : TResourceStream;
    begin
      //Open resource item named "Clouds"
      RS := TResourceStream.Create(HInstance,'Clouds',RT_RCDATA);
      try
        //Load our Image from our ResourceStream
        Image1.Picture.Bitmap.LoadFromStream(RS);  //Load the bitmap from our stream
      finally
        //Free the memory used for our stream
        RS.Free;
        end;
    end;
  7. On the other hand, you may extract these items to disk using the following code :
procedure TForm1.FormCreate(Sender: TObject);
var
  //Obviously we need our Resource Stream
  RS : TResourceStream;
  //Also we need a File stream to write to disk with
  FS : TFileStream;
begin
  //Open our resource
  RS := TResourceStream.Create(HInstance,'Clouds',RT_RCDATA);
  //Create our file
  FS := TFileStream.Create('c:\newfile.bmp', fmCreate);
  try
    //Copy from Resource to Disk
    FS.CopyFrom(RS,RS.Size);
  finally
    FS.Free;
    RS.Free;
  end;
end;

 

ENTERING TAB

Dateline: 01/26/99

We know that, generally, pressing the Tab key moves the input focus to next control and Shift-Tab to previous in the tab order of the form. When working with Windows applications, most users intuitively expect the Enter key to behave like a Tab key.

Over the past few years, I've seen a lot of third-party code for implementing better data entry processing in Delphi. In this article, I'll try to bring you the best methods I have found (with some modifications of my own).

Examples below are written with assumption that there is no default button on the form. When your form contains a button whose Default property is set to True, pressing Enter at runtime executes any code contained in the button's OnClick event handler.

Solutions
Some of the code examples that can be found on the Web:
A component that treats [Enter] like [Tab] How to make the Enter Key work like the Tab key Using Arrow Keys Instead of TAB to Navigate Between Controls Making the Enter key work like a Tab in a TDBGrid

First modification
This causes Enter to behave like Tab, and Shift+Enter like Shift+Tab.

procedure TForm1.Edit1KeyPress 
(Sender: TObject; var Key: Char);
begin
If Key = #13 Then Begin
 If HiWord(GetKeyState(VK_SHIFT)) <> 0 then
  SelectNext(Sender as TWinControl,False,True)
 else
  SelectNext(Sender as TWinControl,True,True);
  Key := #0
end;
end;

Second modification
If you want to have similar Enter (Shift+Enter) processing in DBGrid:

procedure TForm1.DBGrid1KeyPress 
 (Sender: TObject; var Key: Char);
begin
If Key = #13 Then Begin
 If HiWord(GetKeyState(VK_SHIFT)) <> 0 then begin
  with (Sender as TDBGrid) do
  if selectedindex > 0 then
   selectedindex := selectedindex - 1
  else begin
   DataSource.DataSet.Prior;
   selectedindex := fieldcount - 1;
  end;
 end else begin
  with (Sender as TDBGrid) do
  if selectedindex < (fieldcount - 1) then
   selectedindex := selectedindex + 1
  else begin
   DataSource.DataSet.Next;
   selectedindex := 0;
  end;
end;
Key := #0
end;
end

 

Hosted by www.Geocities.ws

1