Wednesday, January 28, 2009

Delphi: Scale your form to work in multi-resolution environment

14" and 15" monitor has became a standard desktop environment for years.  The CRT monitor became obsolete after LCD become a standard equipment now.  End user requires bigger and bigger monitor to show more from desktop application.  15" display no longer fulfill today's desktop application.  17", 19", 20", 22" and 24" and even bigger display show it's value.

The screen resolution ranges from 800x600, 1024x768, 1280x1024, 1680x1050, 1920x1080 and etc are great for users who want to show more and more but it come with a price.  You have more space on screen, but the font image may become smaller.  You get a bigger display but the resolution also increase, your application that was designed for lower resolution occupy only a small rectangle area on your nice big display.

While some application looks nicer on higher resolution display but some not.  Such small font doesn't make your priceless eyes comfort especially for aged end users.  They may think bigger display will show bigger display image for desktop application won't make their eyes pain.  This perception is not right as most desktop application design against resolution.  However, there are end users who has brilliant eye power that like to have more working space to show more on bigger display.  It would be great if we can design our application that able to adjust the form scaling for different requirements.

In Delphi, we can use Anchor and Alignment of GUI controls to make our form-based application work nicer with multi-resolution environment but the font size usually stay same for various resolutions.  This type of design doesn't change font size but the work space get increase for higher resolution display.  End users must have good eye power using this application in higher resolution display.  Although we may change font size but it is hard to align the controls properly for multi-resolution environment.

Delphi introduces other mechanism like ScaleBy, ScaleControls, PixelsPerInch for us make the form's controls scale properly in multi-resolution environment.  However, it has a drawback. It doesn't work well with anchored controls.

Scaling is an acceptable solution to solve all the above problems.  I have learned a straight and easy way to perform the scaling that even work well with anchored controls.  Changing Screen.PixelsPerInch will make all later instantiated forms display better scaled view.  The VCL source reveals that:

  1. TCustomForm.InitializeNewForm set
    FPixelsPerInch := Screen.PixelsPerInch;
  2. TScreen.PixelsPerInch is a readonly property and TScreen.FPixelsPerInch is a private field

If we can change Screen.PixelsPerInch at runtime then all subsequent form instances will get the new Screen.PixelsPerInch value.

The following code shows a possibility:

type
  TScreenHelper = class helper for TScreen
  public
    procedure SetPixelsPerInch(Value: integer);
  end;

  TScreenEx = class(TScreen)
  published
    property PixelsPerInch;
  end;

procedure TScreenHelper.SetPixelsPerInch(Value: integer);
begin
  PInteger(Integer(Self) + (Integer(GetPropInfo(TScreenEx, 'PixelsPerInch').GetProc) and $00FFFFFF))^ := Value;
end;

begin
  Screen.SetPixelsPerInch(120);

  // Create your form here ...
end.

Reference:

  1. Multi-Resolution Delphi Applications. By Zarko Gajic, About.com
  2. Hack #1: Write access to a read-only property. By Hallvard Vassbotn; Oslo, Norway

Sunday, January 18, 2009

How to create a debug version of VCL Package

Delphi ship the following VCL libraries:

  1. DCU: Both debug and non-debug version
  2. DCP: non-debug version only
  3. BPL: non-debug version only

The only reason we need to build our application with debug version of libraries is we want to debug application in the powerful Delphi IDE.  We can trace from the "Call Stack Window" to know our current program location was derived from which lines or code in which unit:

2

These libraries are enough if we only build standalone application (non-package and only a single .EXE file).  However, if we compile and deploy our application with package, we will have problem perform the debugging activities as how the standalone .EXE does.  Delphi didn't ship debug version of DCP/BPL files.  We are unable to compile our packaged application that will show the debug information we want.  This cause the debugging operation particularly hard and difficult.

1

To overcome this problem, we can create our own debug version of VCL libraries manually.  We may use the following code to extract the files in VCL packages (.BPL files) that can be found in "%windir\system32":

procedure ShowInfoProc(const Name: String; NameType: TNameType; Flags: Byte;
  Param: Pointer);
begin
  if (Name = 'System') or (Name = 'SysInit') then Exit;
  if Flags and ufMainUnit <> 0 then Exit;

  case NameType of
    ntRequiresPackage: 
      ShowMessage(Name);
    ntContainsUnit:
      ShowMessage(Name);
    ntDcpBpiName:
      ShowMessage(Name);
  end;
end;

procedure TPackageInfo2.ExtractInfo(const aPackage: string);
var NeedFreeLibrary: boolean;
    M: HModule;
    Flags: Integer;
begin
  // Extract description
  FDescription := GetPackageDescription(PChar(aPackage));

  // Extract DcpBpiName, requires and contains clause
  NeedFreeLibrary := True;
  M := LoadResourceModule(PChar(aPackage));
  if M = 0 then begin
    M := GetModuleHandle(PChar(aPackage));
    if M = 0 then
      M := LoadLibraryEx(PChar(aPackage), 0, LOAD_LIBRARY_AS_DATAFILE + DONT_RESOLVE_DLL_REFERENCES)
    else
      NeedFreeLibrary := False;

    if M = 0 then raise Exception.Create('Package Error !');
  end;

  GetPackageInfo(M, Self, Flags, ShowInfoProc);

  if NeedFreeLibrary then FreeLibrary(M);
end;

Once you know the contains clause and requires clause for the package file, you may assemble your package file as follow:

  1. Start Delphi IDE
  2. Create a new package
  3. Edit the contains and requires clauses to add the files your found in the package using above code.
  4. Click Project |Option... to bring out project option window
  5. In "Description" page:
    1. make the package as "Runtime Only"
    2. Set Build Control to "Explicit rebuild"
    3. Enter the Lib Suffix value (e.g.: 100 for Delphi 2007 package, 120 for Delphi 2009 package)
  6. In Compiler page, Set "Use Debug DCU" and turn off "Optimization"
  7. In Directories/Conditional page, set the Output, Unit output and DCP output directory of your choice
  8. Save the package file as the name of the VCL package.  For example, VCL100.bpl will save as VCL.dpk
  9. Build the package
  10. It will produce 2 important files: Debug version of DCP and BPL files.

The above steps finish the creation of debug version of DCP and BPL files.

The next steps is how to make use of both the DCP/BPL files:

  1. Open your application that you build with VCL packages
  2. Click Project | Options...
  3. In Directories/Conditional page, add the path of debug version of DCP files to "Search Path"
  4. Compile all packages of your application
  5. Copy the debug version of BPL files to the output folder of your application
  6. Try to run your application in debug mode and you may start enjoying the powerful Delphi IDE debugging for your package built application.

Reference:

  1. Creating a debug version of the vcl package (CodeGear TeamB Blogs: Dave Nottage)

Installing Microsoft SQL Server Express 2008

It requires some efforts to get Microsoft SQL Server Express 2008 to get running on a Vista SP1 machine with Microsoft SQL Server Express 2005 installed.

First, my machine wasn't always synchronize with latest windows updates and hot fixes.  I have Vista SP1 installed.  My machine has SQL Server Express 2005 installed.  I still need it in for my daily development.  I need both 2005 and 2008 services running at same time.

After download the SQL Server Express 2008, it will perform a pre-requisite validation to check if you have extra tools installed.  Here are a list of software that I have to install first before I can pass the SQL Server Express 2008 pre-requisite validation:

  1. .NET Framework 3.5 SP1
  2. Windows PowerShell 1.0 Installation Package for Windows Vista (KB928439)
  3. Windows Installer 4.5 Redistributable
  4. Uninstall SQL Server Management Studio for SQL Server 2005

After all the above tools installed, the SQL Server 2008 Express installation process is smooth.  However, there is one more step to get it right before both 2005 and 2008 services can co-exist in same machine.

By default, the default service instance name for both SQL Server 2005 and 2008  is same: "SQLEXPRESS".  You have to give distinct name for both service name.  For example, I name SQL Server 2005 and 2008 instance name as "SQLEXPRESS2005" AND "SQLEXPRESS2008" respectively.  After that, you may start both services in same machine at the same time.

SQL Server communicate on few network protocols. We may use "SQL Server Configuration Manager" to configure it.  Among them are:

  1. Shared Memory
  2. Named Pipes
  3. TCP/IP
  4. VIA

The TCP/IP protocol is using dynamic port by default.  We may continue using that provided we enable "SQL Server Browser".  We may use a connection string like "<ip-address>\<instance-name>" to make a connection to a SQL Server service bind to dynamic port.  For example: "192.168.0.1\SQLEXPRESS2008".

One excellent feature with SQL Server 2008 I learned is the PowerShell.  It allows us to perform task in batch on the database entities.  For example, to delete all tables at once, we may issue "rm *".  A very good entry point for Unix/Linux users.

There is also SQL Server Compact that target on standalone or embedded desktop application.  It seems that SQL Server is getting more scalable for different application.

Thursday, January 08, 2009

Firebird Installation

Firebird support running as service or application mode.  It always confuse me and I don't know what is the best working mode in my daily development and deployment environment.

Running Firebird as application

Running Firebird as application provide developer greater control during development process.  It allow user to start and stop the application anytime.  It also allow user to run various version of Firebird easily without performing any installation in privilege account.

The following example show deployment strategy of running few instances of Firebird as application.  First, obtain the following zip archive files from Firebird installation repositories:

  1. Firebird-1.5.6.5026-0_win32.zip
  2. Firebird-2.1.3.18185-0_Win32.zip / Firebird-2.1.3.18185-0_x64.zip
  3. Firebird-2.5.0.26074-0_Win32.zip / Firebird-2.5.0.26074-0_x64.zip

Extract the zip archive files in your account profile: e.g: %USERPROFILE% (c:\Users\my-account)

Open Windows Registry Editor, and locate the key:[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run]

Enter the following entries:

"FB156"="C:\Users\my-account\Firebird-1.5.6.5026-0_win32\bin\fbserver -a"
"FB213"="C:\Users\my-account\Firebird-2.1.3.18185-0_x64\bin\fbserver -a -p 3213"
"FB250"="C:\Users\my-account\Firebird-2.5.0.26074-0_x64\bin\fbserver -a -p 3250"

Parameter –a indicates the application should run as application.  Parameter –p specify the port number the application should listen to.  Please note that port number is should be used by application exclusively.

You should logoff from your account and logon again to activate the changes.

The above example deploy 3 instance of Firebird:

  1. Firebird-1.5.6.5026-0_win32 listen on default port 3050
  2. Firebird-2.1.3.18185-0_x64 listen on port 3213
  3. Firebird-2.5.0.26074-0_x64 listen on port 3250

Running Firebird as service

If you run Firebird 1.5 or Firebird 2.0/2.1 binary setup file, you may specify if you want to run Firebird as application or service.  For Windows 2000, XP, Vista and above, you should choose to run Firebird as service.  You may also configure the running mode after installation in Firebird Control Panel Applet.

Capture

It is not necessary to run Firebird Guardian for Firebird running in service mode.

Install Firebird service with command line tool

Apart from the common GUI configuration we familiar for Firebird startup configuration, we may also use the command line tool to perform the configuration.  The tool is call "instsvc.exe" that is usually located in "%ProgramFiles\Firebird\Firebird_X_X\bin".  We may use the tool to configure the running mode, and start or stop the Firebird Service:

C:\Program Files\Firebird\Firebird_1_5\bin>instsvc

Usage:
  instsvc i[nstall] [ -s[uperserver]* | -c[lassic] ]
                    [ -a[uto]* | -d[emand] ]
                    [ -g[uardian] ]
                    [ -l[ogin] username [password] ]

          sta[rt]   [ -b[oostpriority] ]
          sto[p]
          q[uery]
          r[emove]

  This utility should be located and run from the 'bin' directory
  of your Firebird installation.

  '*' denotes the default values
  '-z' can be used with any other option, prints version
  'username' refers by default to a local account on this machine.
  Use the format 'domain\username' or 'server\username' if appropriate.

For example:

  1. Start cmd In Administrator Account
  2. Run
      "C:\Program Files\Firebird\Firebird_1_5\bin\instsvc" -i -s
    to configure Firebird running as service
  3. Run
      "C:\Program Files\Firebird\Firebird_1_5\bin\instsvc" start
    to start the service

Running both Firebird 1.5 and 2.1 service in same machine

It is possible to run more than one instance or one version of Firebird service in single operating system.  This is particularly useful in development environment where we want to make sure our application works fine for various versions of Firebird service.

Port Conflict

Firebird service listen to TCP port 3050.  All Firebird versions listen to same port 3050 by default.  One TCP port may listened by one instance of Firebird service only.

To get the job done, we have to choose different port for another instance of Firebird service.  For example, we may configure Firebird 1.5 listen to port 3050 and Firebird 2.1 listen to port 3052.

Windows Service Name

Another obstacle is both Firebird 1.5 and Firebird 2.1's instsvc tool will use the same service name ("DefaultInstance").  Window service name must be unique at all time.  Fortunately, Firebird 2.1's instsvc supports installing service with custom service name.

Here is an example to show you how to install both Firebird 1.5 and 2.1 in single machine:

  1. Run the following in privilege account.
  2. Install Firebird 1.5 running in service mode.  Start the service after installation
  3. Install Firebird 2.1, the setup should prompt you that another instance of Firebird is running and you have to configure Firebird 2.1 service manually.
  4. Start cmd in privilege account and run "notepad %ProgramFiles%\Firebird\Firebird_2_1\firebird.conf" to configure the Firebird Service listen to port 3052:
        RemoteServicePort = 3052
  5. Configure the Firebird 2.1 service to run as difference instance name ("Firebird 2.1"):
        "%ProgramFiles%\Firebird\Firebird_2_1\bin\instsvc" i -name "Firebird 2.1"
  6. Start Firebird 2.1:     "%ProgramFiles%\Firebird\Firebird_2_1\bin\instsvc" start
  7. Remember to configure your Firewall for port 3052
  8. The hostname setting for client connection is something like "host-name/3052"

To check both service is installed properly, you may open Windows services (Start | Control Panel | Administrative Tools | Services):

Capture1

You may now enjoy both Firebird 1.5 and 2.1 in same machine.  The same steps may apply to future version of Firebird and you may install as many version of Firebird you want provided you choose the port number and service name carefully.

Running more than one instance of Firebird

Using the same technique as "Running both Firebird 1.5 and 2.1 in one machine", you may also running more than one instance of Firebird service.  For example, running 3 instances of Firebird 1.5 in same machine.  To get the job done, you may obtain the Firebird Zip installation file and extract the zip files into 3 difference folders.  Just follow the above steps to configure the Firebird services.

Maximize GBAK performance

GBAK is a command line tool that allow us to perform Firebird database backup and restore operations.  We may use the tool with degraded performance if we didn't use it properly.  GBAK allow us to perform backup or restore remotely.

Restore database from Host A to Host B

In this scenario, Host A has the backup file and Host B is the machine we want the database to restore to.  We may issue the command from Host A:

gbak -c -v backup.fbk HostB:c:\...\db.fdb

This operation will degrade GBAK as it has to travel through a TCP/IP layer to transport the data packet.

To maximize performance of the operation, we copy the backup file to Host B and execute GBAK with service_mgr.  Both backup and database file are located in same Host B machine.  You will be amazed by the performance of the GBAK operations:

gbak -c -v -se HostB:service_mgr c:\..\backup.fbk c:\...\db.fdb

Reference:

  1. http://www.destructor.de/firebird/instsvc.htm