Monday, September 22, 2008

Install ModelMaker Code Explorer on non-privilege account

If you run Delphi IDE on administrator account account, you probably won't encounter any problem launching ModelMaker Code Explorer (MMX).

We mostly work on non-privilege (limited user) account for our daily task since Windows Vista.  Installing MMX on administrator account and run Delphi IDE on limited user account will not load the MMX expert.

We have to manually install the MMX for limited user account:

  1. In Limited user account, run RegEdit.exe
  2. Navigate to:
    1. "HKEY_CURRENT_USER\Software\Borland\BDS\5.0\Experts" for Delphi 2007
    2. "HKEY_CURRENT_USER\Software\CodeGear\BDS\6.0\Experts" for Delphi 2009
  3. Add a string values "MideXExpert" (omit quotes):
    1. "C:\Program Files\ModelMakerTools\Midex\7.00\mmx_bds5.dll" for Delphi 2007
    2. "C:\Program Files\ModelMakerTools\Midex\7.00\mmx_bds6.dll" for Delphi 2009

Migrate MMX 5.0 setting to MMX 7.0

You may follow the steps to migrate old MMX setting to new version:

  1. Run RegEdit.exe in your user account
  2. Navigate to "HKEY_CURRENT_USER\Software\ModelMaker\MideX\5.0" and export the setting to a registry file (*.reg).
  3. Open the exported registry file with Notepad
  4. Replace all string of "\5.0" to "\7.0" and save the file
  5. Double click the registry file to merge the setting to registry
  6. Your MMX 5.0 setting is now migrated to MMX 7.0.

Wednesday, September 17, 2008

Using GNU Privacy Guard (GPG)

GPG exist in both Windows and Linux system. It is a tool to perform public key encryption for files or messages. GPG may download from http://www.gnupg.org/

Some useful commands

  1. Creating keys: gpg --gen-key
  2. Exporting public keys: gpg --export -a [UID]
  3. Exporting private keys: gpg --export-secret-key -a [UID]
  4. Importing keys: gpg --import [FileName]
  5. List keys: gpg --list-keys
  6. Delete public keys: gpg --delete-keys UID
  7. Delete private keys: gpg --delete-secret-keys UID
  8. Encrypt a file: gpg -e -r [UID] <filename>
  9. Decrypt a file: gpg -d -o <output-file> <myfile.gpg>

Wednesday, August 20, 2008

Delphi 2007: Out of Sync problem in .DPROJ

Background

Delphi project file consist of only one file (.dpk) before Delphi 2006.  Beginning of Delphi 2006, MSBUILD (similar to ANT) is used as project build tool.  Since then, Delphi 2006 project file consist of 2 files: .DPROJ and .DPK.  The out of sync issue between .DPK and .DPROJ cause a lot of problems when working in version control environment.

Motivation

The .DPROJ is not perfect due to the XML wasn't tidy up nicely.  There are some problems with .dproj design:

  1. We always confuse when compare local and remote copy of .DPROJ if only none or minor changes has done.
  2. RC file wasn't handle well in .DPROJ
  3. The folders and compilation parameters stored in .DPROJ will change each time a .DPROJ file was saved even no changes has made.
  4. If the changes made on .DPK file, it is hardly get reflected in .DPROJ file and thus cause confusion when comparing.

Consequences

A .DPROJ that has problems may cause:

  1. Compilation errors
  2. .RC file will not compile if it is missing in .DPROJ file
  3. Delphi IDE Project Manager show unwanted entries
  4. .pas unit that has removed but still show in .DPROJ

Solution

You may always follow the steps to re-tidy a .DPROJ that you think it has problems:

  1. Close project in Delphi IDE editor that open the .DPROJ file
  2. Use any text editor (Notepad or Notepad++) to the .DPROJ file:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      ...
      <ItemGroup>
        <DelphiCompile Include="SQL.Office.GUI.dpk">
          <MainSource>MainSource</MainSource>
        </DelphiCompile>
        <DCCReference Include="..\..\core\source\FormClass.Catalog.pas" />
        <DCCReference Include="..\..\core\source\FormClass.Export.pas" />
        <DCCReference Include="..\source\Common.GUI.Reg.pas" />
        <DCCReference Include="..\source\Profile.EF.pas">
          <Form>Profile_EF</Form>
        </DCCReference>
        <DCCReference Include="cxEditorsD11.dcp" />
        <DCCReference Include="cxExtEditorsD11.dcp" />
        <DCCReference Include="cxLibraryD11.dcp" />
        <DCCReference Include="cxPageControlD11.dcp" />
        <DCCReference Include="oobase.dcp" />
        <DCCReference Include="vcl.dcp" />
      </ItemGroup>
    </Project>

  3. The above .DPROJ file has some missing entries
  4. Remove all DCCReference item:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      ...
      <ItemGroup>
        <DelphiCompile Include="SQL.Office.GUI.dpk">
          <MainSource>MainSource</MainSource>
        </DelphiCompile>
        <DCCReference Include="..\..\core\source\FormClass.Catalog.pas" />
        <DCCReference Include="..\..\core\source\FormClass.Export.pas" />
        <DCCReference Include="..\source\Common.GUI.Reg.pas" />
        <DCCReference Include="..\source\Profile.EF.pas">
          <Form>Profile_EF</Form>
        </DCCReference>
        <DCCReference Include="cxEditorsD11.dcp" />
        <DCCReference Include="cxExtEditorsD11.dcp" />
        <DCCReference Include="cxLibraryD11.dcp" />
        <DCCReference Include="cxPageControlD11.dcp" />
        <DCCReference Include="oobase.dcp" />
        <DCCReference Include="vcl.dcp" />
      </ItemGroup>
    </Project>

  5. Open the .DPROJ project in Delphi IDE
  6. Open the corresponding .DPK file in Delphi IDE editor (Project | View Source)
  7. Make the .DPK file become modified (e.g.: add a space character and remove the space character)
  8. Save the project
  9. If you use compare your local .DPROJ copy with CVS copy, you will see all missing or weird entries has recovered:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      ...
      <ItemGroup>
        <DelphiCompile Include="SQL.Office.GUI.dpk">
          <MainSource>MainSource</MainSource>
        </DelphiCompile>
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\cxEditorsD11.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\cxExtEditorsD11.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\cxLibraryD11.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\cxPageControlD11.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\oobase.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\oobasevcl.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\oodb.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\vcl.dcp" />
        <DCCReference Include="..\..\core\source\FormID.Catalog.pas" />
        <DCCReference Include="..\..\core\source\FormID.Export.pas" />
        <DCCReference Include="..\source\Common.GUI.Reg.pas" />
        <DCCReference Include="..\source\Profile.EF.pas">
          <Form>Profile_EF</Form>
        </DCCReference>
      </ItemGroup>
    </Project>

  10. If the project contain any .RC entry, you may now drag the .RC files into the Project Manager and save it
  11. Now the content of .DPROJ should be fine for check in:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      ...
      <ItemGroup>
        <DelphiCompile Include="SQL.Office.GUI.dpk">
          <MainSource>MainSource</MainSource>
        </DelphiCompile>
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\cxEditorsD11.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\cxExtEditorsD11.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\cxLibraryD11.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\cxPageControlD11.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\oobase.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\oobasevcl.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\oodb.dcp" />
        <DCCReference Include="..\..\..\venus\account\source\stock.1\project\vcl.dcp" />
        <DCCReference Include="..\..\core\source\FormID.Catalog.pas" />
        <DCCReference Include="..\..\core\source\FormID.Export.pas" />
        <DCCReference Include="..\source\Common.GUI.Reg.pas" />
        <DCCReference Include="..\source\Profile.EF.pas">
          <Form>Profile_EF</Form>
        </DCCReference>
        <RcCompile Include="rc\library.rc">
          <Form>library.res</Form>
        </RcCompile>
      </ItemGroup>
    </Project>
  12. You may now check in .DPROJ file into Version Control service.
  13. You may encounter a situation that no changes has made to the project but remote copy shows lot of changes.  In most situation, it is only the changes to folders in .DCP entries.  You may ignore this changes and revert your local copy of the .DPROJ file from Version Control service.
    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      ...
      <ItemGroup>
        <DelphiCompile Include="SQL.Office.GUI.dpk">
          <MainSource>MainSource</MainSource>
        </DelphiCompile>
        <DCCReference Include="$(SystemRoot)\system32\cxEditorsD11.dcp" />
        <DCCReference Include="$(SystemRoot)\system32\cxExtEditorsD11.dcp" />
        <DCCReference Include="$(SystemRoot)\system32\cxLibraryD11.dcp" />
        <DCCReference Include="$(SystemRoot)\system32\cxPageControlD11.dcp" />
        <DCCReference Include="$(SystemRoot)\system32\oobase.dcp" />
        <DCCReference Include="$(SystemRoot)\system32\oobasevcl.dcp" />
        <DCCReference Include="$(SystemRoot)\system32\oodb.dcp" />
        <DCCReference Include="$(SystemRoot)\system32\vcl.dcp" />
        <DCCReference Include="..\..\core\source\FormID.Catalog.pas" />
        <DCCReference Include="..\..\core\source\FormID.Export.pas" />
        <DCCReference Include="..\source\Common.GUI.Reg.pas" />
        <DCCReference Include="..\source\Profile.EF.pas">
          <Form>Profile_EF</Form>
        </DCCReference>
        <RcCompile Include="rc\library.rc">
          <Form>library.res</Form>
        </RcCompile>
      </ItemGroup>
    </Project>

Monday, May 26, 2008

Install Windows Vista from USB Flash Drive

Most machine nowadays has built-in USB 2.0 hi speed port and able to boot from USB flash drive . But not all machine has CD/DVD-ROM. The following method prepares a USB flash drive for Windows Vista installation: Requirements:
  1. A USB flash drive supports USB 2.0 with size of 4G (able to hold a Windows Vista ISO)
  2. A machine support USB 2.0 and bootable from USB flash drive
Steps to prepares a USB flash drive from Windows Vista machine:
  1. Insert USB Flash drive
  2. Run CMD from administrator account (or run as administrator)
  3. The following commands are run within diskpart:
    1. list disk (to list all disk in system including USB flash drive, identify your USB flash drive disk number here)
    2. select disk 1 (assume USB flash drive is disk number 1)
    3. clean (clean all partition in USB flash drive)
    4. create partition primary (create a primary partition in USB flash drive)
    5. select partition 1 (select the newly created partition)
    6. active (mark the partition active)
    7. format fs=fat32 (format the partition with FAT32 file system)
    8. assign (assign a drive letter to the partition, assume is drive E:)
    9. exit (quit diskpart utility)
  4. Insert Windows Vista DVD-ROM or mount the Windows Vista ISO file to drive D:
  5. Copy all file and folders from Windows Vista DVD-ROM to USB Flash drive:
    1. xcopy d:\*.* /s/e/f e:\
  6. Safely remove the USB flash drive from your machine
  7. Plug the USB flash drive to new machine for Windows Vista Installation
  8. Make sure you try to reboot the machine with USB Booting
  9. Enjoy and start Windows Vista installation from USB flash drive without DVD-ROM
Reference:
  1. HOWTO: Install Windows Vista from a high speed USB 2.0 Flash Drive

Wednesday, May 07, 2008

Some useful RPM command

Redhat or Fedora using RPM to manage package installation. RPM will report package dependencies if the installation failed. However, I found YUM is much more easy than RPM in install and uninstall operation. However, there are some useful aspect of using RPM.
# To list all installed packages
rpm -qa

# To query a package information and dependencies packages
rpm -qilR 

# To query which package require a package
rpm -q --whatrequires 

# To query which package provide the file
rpm -q --whatprovides 

# Test package installation
rpm -ivv --test 

Configure a masqueraded router

A router need at least 2 network interfaces. The following example using 2 network interfaces, eth0 and eth1 as example. eth0 is external network interface. All packets passed thru' this interface will be masqueraded before sending out. In real world situation, this interface is usually refer to internet gateway. eth1 is internal network interface. This is usually gateway for internal private network. There are 2 steps configure a masquerade router. Step 1: Enable IP packet forwarding
sysctl -w net.ipv4.ip_forward=1
Step 2: Enable IP packet masquerading Firewall (IPTABLES)
# Clearing any existing rules and setting default policy..

iptables -P INPUT ACCEPT
iptables -F INPUT
iptables -P OUTPUT ACCEPT
iptables -F OUTPUT
iptables -P FORWARD DROP
iptables -F FORWARD
iptables -t nat -F

# FWD: Allow all connections OUT and only existing and related ones IN

iptables -A FORWARD -i eth0 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT

# Enabling SNAT (MASQUERADE) functionality on $EXTIF
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
The masquerading router should have up once the above script is executed. You may try to ping both eth0 and eth1 to confirm that. Also try to ping public network to make sure it works. You may persist the iptables rules using
service iptables save

Friday, April 11, 2008

Draw a translucent effect image

We may use windows GDI function AlphaBlend to draw translucent graphic. The following example extract a portion of desktop image and draw to TImage.canvas with translucent effect. The resulting graphic will looks like there is a green color transparent sheet cover on the image.
var hDesktop: HWND;
 DC: HDC;
 T: BLENDFUNCTION;
begin
hDesktop := GetDesktopWindow;
DC := GetDC(hDesktop);
try
 T.BlendOp := AC_SRC_OVER;
 T.BlendFlags := 0;
 T.SourceConstantAlpha := $80;
 T.AlphaFormat := 0;

 Image1.Canvas.Brush.Color := clGreen;
 Image1.Canvas.FillRect(Rect(0, 0, Image1.Width, Image1.Height));
 Windows.AlphaBlend(Image1.Canvas.Handle, 0, 0, Image1.Width, Image1.Height, DC, 0, 0, Image1.Width, Image1.Height, T);
finally
 ReleaseDC(hDesktop, DC);
end;
end;

Wednesday, March 19, 2008

Compile DPK files using DCC32

Using Delphi command lines compiler DCC32 to compile programs allow us to set some switches to get various kind of output. The most common usage is compile a debugged or non-debugged release. For example, we may use dcc32 -B -$O-,D+,L+,YD to compile dcu/dcp with debug information and dcc32 -B -$O-,D-,L-,Y- to compile dcu/dcp without debug information. However, you may notice that using such switches has no effect on DPK (Delphi package) files. The reason is simple but yet difficult to discover. If you study DPK files, you may see the following:
package MyPackage;

{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION OFF}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DESCRIPTION 'E Stream Software - Runtime Library'}
{$LIBPREFIX 'SQL.'}
{$RUNONLY}
{$IMPLICITBUILD OFF}
{$DEFINE DEBUG}
The DPK files already has built in compiler directives. It has priority over the switches you specified in command lines. As stated by Allen Bauer in the post:
The IDE supports a very little know feature where you can continue to control these options while in the IDE, yet allow the command-line be able to also control the options. In a package file, all the options listed there are propagated to all the contained units. This is different than a normal .dpr program/library file. In order to do this, all you need to do is replace the '$' with a ' ' (that's a ). Then when you open the dpk in the IDE, you can toggle those options as much as you like, but when you compile on the command-line, you can still control them. This is how we build all our packages for the product itself. All of the Borland built packages have DEBUGINFO, STACKFRAMES, ASSERTIONS, OPTIMIZATION, LOCALSYMBOLS setup this way because like you, we wan't to be able to control those options from the command-line. The developers will typically do a complete "debug" build, whereas the integration team will not. $DEFINES will sort of work the same way, in that if you replace the '$' with a ' ', the IDE will still recognize the defines and apply them, but any other modifications to the dpk source will re-insert the '$' character.
The solution is replace $ with a space character (' ') for all compiler directives only:
package MyPackage;

{$R *.res}
{$ALIGN 8}
{ ASSERTIONS ON}
{ BOOLEVAL OFF}
{ DEBUGINFO ON}
{ EXTENDEDSYNTAX ON}
{ IMPORTEDDATA ON}
{ IOCHECKS ON}
{ LOCALSYMBOLS ON}
{ LONGSTRINGS ON}
{ OPENSTRINGS ON}
{ OPTIMIZATION OFF}
{ OVERFLOWCHECKS OFF}
{ RANGECHECKS OFF}
{ REFERENCEINFO ON}
{ SAFEDIVIDE OFF}
{ STACKFRAMES OFF}
{ TYPEDADDRESS OFF}
{ VARSTRINGCHECKS ON}
{ WRITEABLECONST OFF}
{ MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DESCRIPTION 'E Stream Software - Runtime Library'}
{$LIBPREFIX 'SQL.'}
{$RUNONLY}
{$IMPLICITBUILD OFF}
{$DEFINE DEBUG}
By doing so, both the IDE and DCC32 will respect the switches and yield expected results and. The output files (DLL, BPL or EXE) do not contain any debugging information even if you turn on the debugging switches. Those debugging information are stored in DCU or DCP files. To keep the debug information in DLL, BPL or EXE, turn on the Include TD32 debug info or use -V switch with DCC32.

Saturday, March 08, 2008

Retrieve Shell Folders

Windows has some special folders like My Documents, SendTo, Desktop or Favorites. The location of this folders are keep in the following registry key: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders However, it is not encourage to retrieve the location of special folders using registry. The recommended way to retrieve is via windows API function SHGetFolderPath:
uses SHFolder;

var sDir: string;
begin
  SetLength(sDir, MAX_PATH);
  ZeroMemory(@sDir[1], MAX_PATH);
  if Succeeded(SHGetFolderPath(0, CSIDL_PERSONAL, 0, 0, PAnsiChar(sDir))) then
    ShowMessage(sDir);
end;

Tuesday, March 04, 2008

Beware of XML Content contain CRLF always cause problem with Google API

We may consume the Google API to post entry of event kind. Those entry are mostly encoded in XML content. It is a common habit for windows user to create the XML content in windows notepad. If we pass the XML content from notepad directly to the Google API, we will mostly encounter error such as HTTP 400 Bad Request. The reason is simple but yet difficult to discover. CRLF (0x0D 0x0A) is default line separator in windows operating system. The Google API or perhaps the content type we specify in the HTTP request header is application/atom+xml may not recognize the CRLF and cause the HTTP 400 Bad request error. Always remember to well form the XML content before consume the API services. For example, replace all CRLF to a space character:
NewXMLText := StringReplace(XMLText, #13#10, ' ', [rfReplaceAll, rfIgnoreCase]);

Using Indy HTTPS client to consume Google API service

Indy VCL 10 distribution supports SSL via OpenSSL but it didn't bundle the DLL library. We have to download the Win32 OpenDDL from here. After install the Win32 OpenSSL library, there are 2 .DLL files we are interested may be found in %windir%\system32: libssl32.dll and libeay32.dll. However, the Indy Open SSL unit IdSSLOpenSSLHeaders.pas are coding as: SSL_DLL_name = 'ssleay32.dll'; {Do not localize} SSLCLIB_DLL_name = 'libeay32.dll'; {Do not localize} To make the Indy works with OpenSSL, we have to rename one of the DLL file libssl32.dll to ssleay32.dll. Here is a sample code using Indy VCL to authenticate with Google Calendar service:
var S: TStringList;
   M: TStream;
begin
 S := TStringList.Create;
 M := TMemoryStream.Create;
 try
   S.Values['Email'] := 'your google account';
   S.Values['Passwd'] := 'your password';
   S.Values['source'] := 'estream-sqloffice-1.1.1.1';
   S.Values['service'] := 'cl';

   IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
   IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
   IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', S, M);
   Memo1.Lines.Add(Format('Response Code: %d', [IdHTTP1.ResponseCode]));
   Memo1.Lines.Add(Format('Response Text: %s', [IdHTTP1.ResponseText]));

   M.Position := 0;
   S.LoadFromStream(M);
   Memo1.Lines.AddStrings(S);
 finally
   S.Free;
   M.Free;
 end;
end;

Sunday, February 24, 2008

Resize Form even if it is borderless

We may make a form without borders to show some additional information using some popup windows. However, a borderless form will no longer resizable. Use the following code to make a borderless form resizable:
type
 TForm1 = class(TForm)
 protected
   procedure CreateParams(var Params: TCreateParams); override;
 end;

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
 BorderStyle := bsNone;
 inherited;
 Params.ExStyle := Params.ExStyle or WS_EX_STATICEDGE;
 Params.Style := Params.Style or WS_SIZEBOX;
end;

Move a form without dragging on the title bar

We usually use mouse to drag the windows form title bar to move the window on our desktop. There is a new way to move the form by just dragging on any point in the form itself. This is ideally suitable for those form that don't have title bar. For example, FormStyle := bsNone; Use the following code to drag on window content and you able to move the form just as you drag on title bar:
type
  TForm1 = class(TForm)
  private
    procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHitTest;
  end;

procedure TForm1.WMNCHitTest(var Msg: TWMNCHitTest);
begin
  inherited;
  if Msg.Result = htClient then
    Msg.Result := htCaption;
end;
The above code attempt to hijack the mouse event to tell the system to treat the mouse click action on windows client area as mouse click on windows title bar. There is a drawback using the message handler. All form's customize mouse events will no longer function as the mouse event has been hijacked by WM_NCHitTest handler. What if there are controls on the forma and you still wish to drag on the window to move the form around? The above above will only function if you drag on the empty area in the form. To make the dragging more sensible, write a MouseMove event for the control:
procedure TForm12.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y:
  Integer);
begin
  ReleaseCapture;
  SendMessage(Self.Handle, WM_SYSCOMMAND, 61458, 0);
end;

Thursday, February 21, 2008

TListBox review

TListBox is a simple visual component that always use for display a list of items but it has some nice features that we always neglected. Using TListBox to show column-like data
procedure TForm1.FormCreate(Sender: TObject);
var L: TListBox;
begin
 L := TListBox.Create(Self);
 L.Parent := Self;
 L.Align := alClient;
 L.TabWidth := 20;
 with L.Items do begin
   Add('Item 01'^I'This is Item 01'^I'3.00');
   Add('Item 02'^I'This is Item 02'^I'5.00');
   Add('Item 03'^I'This is Item 03'^I'23.00');
   Add('Item 04'^I'This is Item 04'^I'33.00');
   Add('Item 05'^I'This is Item 05'^I'43.00');
 end;
end;
Custom Draw items in TListBox #1
procedure TForm1.FormCreate(Sender: TObject);
var L: TListBox;
begin
 L := TListBox.Create(Self);
 L.Parent := Self;
 L.Align := alClient;
 L.Style := lbOwnerDrawFixed;
 L.OnDrawItem := OnDrawItem;
 with L.Items do begin
   Add('Item 01');
   Add('Item 02');
   Add('Item 03');
   Add('Item 04');
   Add('Item 05');
 end;
end;

procedure TForm1.OnDrawItem(Control: TWinControl; Index: Integer;
 Rect: TRect; State: TOwnerDrawState);
var C: TColor;
begin
 with Control as TListBox do begin
   Canvas.FillRect(Rect);

   if odSelected in State then
     C := Canvas.Font.Color
   else if Index mod 2 = 0 then
     C := clGreen
   else
     C := clBlue;
   Canvas.Font.Color := C;

   Canvas.TextOut(Rect.Left, Rect.Top, Items[Index]);
 end;
end;
Custom Draw items in TListBox #2
procedure TForm1.FormCreate(Sender: TObject);
var L: TListBox;
begin
 L := TListBox.Create(Self);
 L.Parent := Self;
 L.Align := alClient;
 L.Style := lbOwnerDrawVariable;
 L.OnDrawItem := OnDrawItem;
 L.OnMeasureItem := OnMeasureItem;
 with L.Items do begin
   Add('Item 01');
   Add('Item 02');
   Add('Item 03');
   Add('Item 04');
   Add('Item 05');
 end;
end;

procedure TForm11.OnDrawItem(Control: TWinControl; Index: Integer;
 Rect: TRect; State: TOwnerDrawState);
var iSize: integer;
begin
 with Control as TListBox do begin
   Canvas.FillRect(Rect);

   if Index mod 2 = 0 then
     iSize := 12
   else
     iSize := 20;
   Canvas.Font.Size := iSize;

   Canvas.TextOut(Rect.Left, Rect.Top, Items[Index]);
 end;
end;

procedure TForm11.OnMeasureItem(Control: TWinControl; Index: Integer; var
 Height: Integer);
var iSize: integer;
   L: TListBox;
begin
 L := Control as TListBox;
 if Index mod 2 = 0 then
   iSize := 12
 else
   iSize := 20;
 L.Canvas.Font.Size := iSize;
 Height := L.Canvas.TextHeight(L.Items[Index]);
end;


Custom Draw items in TListBox #3 Due to the design of windows list box control, the OnMeasure event is trigger once only if an item is added. The following code shows example of how to draw item with custom item height.
procedure TForm1.BeforeDestruction;
begin
 inherited;
 Font1.Free;
 Font2.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
var L: TListBox;
begin
 L := TListBox.Create(Self);
 L.Parent := Self;
 L.Align := alClient;
 L.Style := lbOwnerDrawVariable;
 L.OnDrawItem := OnDrawItem;

 Font1 := TFont.Create;
 Font1.Name := 'Tahoma';
 Font1.Size := 25;
 Font1.Color := clGreen;

 Font2 := TFont.Create;
 Font2.Name := 'Arial';
 Font2.Size := 15;
 Font2.Color := clNavy;
 Font2.Style := [fsBold];

 with L.Items do begin
   AddObject('Item 01', Font1);
   AddObject('Item 02', Font2);
   AddObject('Item 03', Font1);
   AddObject('Item 04', Font2);
   AddObject('Item 05', Font1);
 end;
end;

procedure TForm1.OnDrawItem(Control: TWinControl; Index: Integer;
 Rect: TRect; State: TOwnerDrawState);
var C: TColor;
 L: TListBox;
begin
 L := Control as TListBox;
 L.Canvas.FillRect(Rect);

 C := L.Canvas.Font.Color;
 L.Canvas.Font.Assign(L.Items.Objects[Index] as TFont);
 if odSelected in State then
   L.Canvas.Font.Color := C;

 SendMessage(L.Handle, LB_SETITEMHEIGHT, Index, L.Canvas.TextHeight(L.Items[Index]));
 L.Canvas.TextOut(Rect.Left, Rect.Top, L.Items[Index]);
end;

Saturday, February 09, 2008

Create home directory manually

When we use /usr/sbin/useradd to create a user, it's home directory may contain some default files or folders (it is hidden usually). However, we may want to create a the home directory for user manually in certain situation, for example, use LDAP to manage user account: # mkdir /home/someuser # cp /etc/skel/.* /home/someuser # chown -R somebody.users /home/someuser # chmod 700 -R /home/someuser

Thursday, January 31, 2008

Color MDI Form

If we want to change background color of a form: we usually do like this:
 Form1.Color := clYellow;
However, if a Form's FormStyle is fsMDIForm, change the color at runtime will not update immediately not until the form has been fully repaint or resize. Invoke TForm.Refresh, TForm.Update, TForm.Repaint will not update the color as well. To make the a MDI form update the background color immediately, we may do this:
 Form1.Color := clYellow;
 Windows.InvalidateRect(Form1.ClientHandle, nil, True);

Thursday, January 24, 2008

How to avoid creation of ~BPL files in Delphi 2007

There is only one way to prevent creation of ~BPL in Delphi 2007 IDE: patch delphicoreide100.bpl in C:\Program Files\CodeGear\RAD Studio\5.0\bin. A good news is someone from http://www.liteapplications.com/ has wrote Delphi Distiller to patch the delphicoreide100.bpl. Hope CodeGear will provide an option in Delphi IDE to turn on and off the creation of ~BPL soon.

Thursday, January 17, 2008

Setup Visual Studio 2005 to build Firebird source

Due to I/O redirector problem in GBAK.EXE (http://tech.groups.yahoo.com/group/firebird-support/message/90995), I download Firebird source to attempt compile it under Visual Studio 2005. Here are the steps:
  1. Install Visual Studio 2005
  2. Read the instruction in firebird source folder: doc/README.build.msvc
  3. Install Firebird server
  4. Setup these environment variables:
    1. FIREBIRD=C:\Program Files\Firebird\Firebird_1_5
    2. ISC_USER=SYSDBA
    3. ISC_PASSWORD=masterkey
    4. LIB=C:\Program Files\Microsoft Visual Studio 8\VC\lib
    5. PATH=C:\Program Files\Microsoft Visual Studio 8\Common7\IDE;C:\Program Files\Microsoft Visual Studio 8\VC\bin
  5. Open file "builds\win32\blrtable.BAT", add an include path: -I"C:\Program Files\Microsoft Visual Studio 8\VC\include" to the cl.exe clause
  6. Open folder "builds\win32\msvc7", convert 2 solution files: Firebird2.sln and Firebird2Boot.sln to Visual Studio 2005 solution format.
  7. Execute cmd.exe, and run Prepare.BAT, make_boot.BAT and make_all.BAT

Wednesday, January 16, 2008

Embed out of process windows into Delphi Control

We may easily embed a Delphi GUI control into another container control like TForm or TPanel:
var F: TForm;
  B: TButton;
begin
F := TForm.Create(nil);
F.Show;
B := TButton.Create(nil);
B.Parent := F;
end;
As TForm or TPanel is a Window GUI control too, we may embed a non-delphi out of process windows into TForm or TPanel control too:
var F: TForm;
   H: THandle;
   T: Cardinal;
begin
 F := TForm.Create(nil);
 F.Show;

 ShellExecute(0, 'open', 'calc', nil, nil, 0);

 // New window may not be created yet, find the window in a loop until it show out
 H := 0;
 T := GetTickCount;
 while H = 0 do begin
   H := FindWindow(nil, PChar('Calculator'));
   if GetTickCount - T > 5000 then
     Break;
 end;

 if H <> 0 then
   Windows.SetParent(H, F.Handle);
end;