Delphi for .NET Compact Framework Preview Quickstart Guide - By David Clegg

By: David Clegg

Abstract: Provides information to assist developers in getting up and running with the Delphi for .NET Compact Framework preview compiler.


Introduction

Borland have recently released a preview version of their latest Delphi for .NET compiler, which allows Delphi developers to target the .NET Compact Framework for the first time. This article is designed to assist developers in exploring the world of Compact Framework development with the preview compiler.

Setting up the Development Environment

The first step is to download the preview compiler from the Registered User Updates page, and to install it onto your development system. For this article, the compiler and all support files have been extracted to the 'C:\Program Files\Borland\3.0\CF Preview' directory.

As the preview compiler can not be invoked directly from the Delphi 2005 IDE, we will set up a batch file to assist us in compiling Compact Framework applications. The batch file we will use looks like this:-

 
@echo off
rem **************************************************************
rem ** Batch file to be used from the Delphi 2005 IDE Tools menu
rem ** to invoke the Compact Framework Preview compiler. It takes
rem ** two parameters as follows:
rem **  %1 - Name of project .dpr file
rem **  %2 - Path to project directory
rem **************************************************************

set _DCCILPATH_="C:\Program Files\Borland\BDS\3.0\CFPreview\Bin\dccil.exe"
set _CFUNITS_="C:\Program Files\Borland\BDS\3.0\CFPreview\Lib"
set _PROJECTNAME_=%1
set _PROJECTDIR_=%2

rem *************************************************************
rem ** Ensure the Delphi 2005 Bin directory is in the path. It
rem ** may have been removed by a User path override.
rem *************************************************************
set _BDSDIR_=C:\Program Files\Borland\BDS\3.0\Bin
path %_BDSDIR_%;%PATH%

rem *************************************************************
rem ** Cater for the fact that paths with spaces can't be passed
rem ** in from the Tools menu
rem *************************************************************
SHIFT
SHIFT
:LOOP
IF "%1" == "" GOTO END
set _PROJECTDIR_=%_PROJECTDIR_% %1
SHIFT
GOTO LOOP
:END

cd %_PROJECTDIR_%
del *.dc?il

%_DCCILPATH_% "%_PROJECTDIR_%%_PROJECTNAME_%" -u%_CFUNITS_% -luSystem.Windows.Forms -luSystem.Data
pause

    

The batch file takes two command line parameters, with the first one being the file name of the project to compile, and the second one being the path where the project resides. For the purpose of this article, only the System.Windows.Forms.dll assembly needs to be referenced, but additional assemblies can be referenced by adding extra -lu parameters.

It caters for the fact that the Delphi 2005 directory may not be in the PATH variable accessible to the batch file when run within the context of the IDE (this can be overridden). It also handles project paths that may contain spaces, as this information cannot currently be passed in as one parameter from the built-in macros available when configuring an item on the IDE tools menu.

Once this batch file has been created, we can configure the IDE so it can be invoked for the current project from the Tools menu. To do this, select the Configure Tools... option from the Tools menu, click the Add... button, and enter the following information:

Title: Delphi.NET CF Preview Compiler
Program: <Fully qualified path to batch file>
Parameters: $SAVEALL $NAMEONLY($PROJECT).dpr $PATH($PROJECT)

Below is an example of this:

Click the OK button, and you should now have a Delphi.NET CF Preview Compiler option at the end of the Delphi 2005 Tools menu. When invoked, it will prompt for all changed files to be saved, and will call our batch file, passing in the Project dpr file name and full path information.

Configuring the Emulator

For those developers not lucky enough to own a Compact Framework compatible device, an emulator will be required in order to test the programs created with the compiler. This article will use the Microsoft Windows CE 5.0 Device emulator, which is freely downloadable from the Microsoft web site.

Marc Rohloff has already given detailed instructions on how to configure the Microsoft Windows CE 5.0 Device emulator, in his Getting Started with C# and the Compact Framework. With his kind permission, this information has been reproduced below.

Before running the installation, make sure that you have an active internet connection. If you have a slow connection and do not yet have MSXML 4.0 sp 2, installed it also helps to download and install this first.

Once you have the emulator installed, make a copy of the menu shortcut and modify it. You will want to add some command line options. Below is an example of a valid command line for the emulator.:

Emulator_500.exe nk.cem /vmid {6F5AE65B-98FF-46A9-A0E1-29B82C9779D4} /vmname "CE 5.0 (CF 1.0)" /sharedfolder c:\dev\net\cf /video 240x320x16

The emulator uses the GUID specified by the vmid option to save the state of the device between sessions, while the sharedfolder option makes your c:devnetcf folder appear as a storage card on the emulated device. This makes it much easier to share and copy files from your desktop.

Emulator Tips and Tricks

     If you change the command line parameters then you will need to delete the saved files from your My Documents\My Virtual Machines folder for the new parameters to take effect.

     Occasionally the emulator does not find the storage card when you start up. You can fix this by performing a soft reset.

 

 

 

 

 

 

Emulating other Mobile Devices

Additional emulator images can be downloaded from the Microsoft website, and the CE 5.0 emulator can be configured to use these so other devices can be emulated. For this article, we will create an application that targets PocketPC devices running the Windows Mobile 2003 operating system.

The Windows Mobile 2003 emulator images can be downloaded from the Microsoft site. Once we have installed the images, we need to change the shortcut for the CE 5.0 emulator to reference one of the new emulator images. The nk.cem parameter in the emulator command line specified earlier refers to the binary image that the emulator loads. We will change this to reference one of the Windows Mobile 2003 emulator images we've installed. We will also use the /video command line parameter to ensure the emulator is displayed using the correct screen dimensions, and use the /sharedfolder parameter to specify a local directory which will be available on the emulator. Our new command line looks like this:

"C:\DevTools\Windows CE 5.0 Emulator\Emulator_500.exe" "C:\DevTools\Windows CE Tools\wce420\POCKET PC 2003\Emulation\PPC_2003_WWE.bin" /sharedfolder c:\source\d2005\delphi\delphi.net\cf\shared /video 240x320x16

We can now use the shortcut to run a PocketPC Windows Mobile 2003 emulator, as shown below.

Installing the Compact Framework

In order to link against the Compact Framework assemblies, it is necessary to have them installed on your development machine, or to have a .dcpil file which the compiler can link against instead. Borland have provided .dcpil files for the more common Compact Framework assemblies, but you may need to use a class in an assembly which doesn't have an existing .dcpil file. In this case you will need the original assembly. If you don't already have the Compact Framework SDK installed, the assemblies will have to be obtained from a device that already has them installed. Once again, this information was published in Marc's article, and with his permission has been reproduced below.

Many devices, including the CE Emulator, come with version 1.0 of the Compact Framework installed. You can check the version on your device by opening the Run dialog and typing cgacutil. If you have latest service pack (sp 2) installed the version should be 1.0.3316.0.

If you do not have the framework installed then you can download the latest version from Microsoft's mobility website. The end-user version comes with an automatic installer but requires an ActiveSync connection.

If you do not have ActiveSync then you will need to download the developer version of the CF Framework. Unpack it, copy the appropriate CAB file to your device and open it from there to install it.

Copying the CF Assemblies

To be able to compile for the compact framework you need to have the assemblies available on your desktop. You can copy them from your compact device or emulator. Open the My DeviceWindows folder, and make sure that all the hidden files are visible. Find all the dlls that that have names starting with "GAC_", there should be 11 of them, and then copy them onto your PC via the Storage Card. I copied mine to a folder called c:devnetcfassemblies.

Lastly, rename the files as shown in the table below.

Filename on Compact Device

Required Filename

GAC_mscorlib_v1_0_5000_0_cneutral_1

mscorlib

GAC_System_v1_0_5000_0_cneutral_1

System

GAC_System.Data_v1_0_5000_0_cneutral_1

System.Data

GAC_System.Drawing_v1_0_5000_0_cneutral_1

System.Drawing

GAC_System.Net.IrDA_v1_0_5000_0_cneutral_1

System.Net.IrDA

GAC_System.Web.Services_v1_0_5000_0_cneutral_1

System.Web.Services

GAC_System.Windows.Forms.DataGrid_v1_0_5000_0_cneutral_1

System.Windows.Forms.DataGrid

GAC_System.Windows.Forms_v1_0_5000_0_cneutral_1

System.Windows.Forms

GAC_System.Xml_v1_0_5000_0_cneutral_1

System.Xml

GAC_Microsoft.WindowsCE.Forms_v1_0_5000_0_cneutral_1

Microsoft.WindowsCE.Forms

GAC_Microsoft.VisualBasic_v7_0_5000_0_cneutral_1

Microsoft.VisualBasic

Developing and running a Delphi for .NET Application

We are now ready to create our first Delphi for .NET Compact Framework application. Start up Delphi 2005, select File|New|Windows Forms Application - Delphi for .NET. Save the form unit as MainForm.pas, and the project file as HelloWorld.dpr.

Switch to the form designer for MainForm and change the Name property to TMainForm. Drop on a Label from the Windows Forms category of the Tool Palette. Set the Label.Text property to "Hello World!", and save the form. The code for MainForm looks like this:-


unit MainForm; 

interface

uses 
  System.Drawing, System.Collections, System.ComponentModel, System.Windows.Forms, 
  System.Data; 

type
  TMainForm = class(System.Windows.Forms.Form) 
  {$REGION 'Designer Managed Code'}
  strict private
    /// <summary>
    /// Required designer variable.
    /// </summary>
    Components: System.ComponentModel.Container; 
    Label1: System.Windows.Forms.Label; 
    ///<summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    procedure InitializeComponent; 
  {$ENDREGION}
  strict protected
    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    procedure Dispose(Disposing: Boolean); override; 
  private
  {Private Declarations }
  public
    constructor Create; 
  end;
      
[assembly: RuntimeRequiredAttribute(TypeOf(TMainForm))] 

implementation

{$AUTOBOX ON}

{$REGION 'Windows Form Designer generated code'}
/// <summary>
/// Required method for Designer support -- do not modify
/// the contents of this method with the code editor.
/// </summary>
procedure TMainForm.InitializeComponent; 
begin
  Self.Label1 := System.Windows.Forms.Label.Create;
  Self.SuspendLayout;
  //
  // Label1
  //  
  Self.Label1.Location := System.Drawing.Point.Create(16, 16); 
  Self.Label1.Name := 'Label1';
  Self.Label1.TabIndex := 0; 
  Self.Label1.Text := 'Hello World!'; 
  //         
  // TMainForm
  //  
  Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13); 
  Self.ClientSize := System.Drawing.Size.Create(292, 266);
  Self.Controls.Add(Self.Label1); Self.Name := 'TMainForm';
  Self.Text := 'WinForm8';
  Self.ResumeLayout(False); 
end; 
{$ENDREGION}

procedure TMainForm.Dispose(Disposing: Boolean); 
begin
  if Disposing then
  begin
    if Components <> nil then 
      Components.Dispose(); 
  end; 
  inherited Dispose(Disposing); 
end; 

constructor TMainForm.Create; 
begin
  inherited Create; 
  //
  // Required for Windows Form Designer support
  // 
  InitializeComponent; 
  //
  // TODO: Add any constructor code after InitializeComponent call
  //
end; 
end. 

We will now configure our project so it will compile to the directory shared with the emulator. We will also set the output directories for our .dcuil and .dcpil files so they will be placed in the app Select Project|Options..., and click on the Directories/Conditionals entry. Enter the path to the shared directory in the Output Directory: section, click OK, and save the project.

We are now ready to compile our first Compact Framework application. Select the Delphi CF Preview Compiler option from the Tools menu:

This will invoke the batch file we created earlier, after prompting for any changes to be saved. The output is shown below:

As you can see, the compilation failed with many Undeclared identifier errors. Not all the methods and classes available on the full .NET Framework have been implemented on the Compact Framework, and because we used a WinForms designer targetting the full .NET Framework, some properties specific only to that were generated in our InitializeComponent section. Thankfully, the .NET Framework SDK documentation explicitly states which methods and classes are available on the Compact Framework, so dilligent referral to this is recommended, especially considering not all instances will be picked up at compile time.

We will change the code to fix these errors and attempt to compile the application again. Our new code for MainForm.pas looks like this (I have also stripped out all the auto-generated comments for brevity):


unit MainForm;

interface

uses 
  System.Drawing, System.Collections, System.ComponentModel, 
  System.Windows.Forms, System.Data; 

type
  TMainForm = class(System.Windows.Forms.Form) 
  {$REGION 'Designer Managed Code'} 
  strict private 
    Components: System.ComponentModel.Container; 
    Label1: System.Windows.Forms.Label; 
    procedure InitializeComponent; 
  {$ENDREGION} 
  strict protected
    procedure Dispose(Disposing: Boolean); override; 
  private
  {Private Declarations }
  public
    constructor Create; 
  end; 

implementation

{$AUTOBOX ON}

{$REGION 'Windows Form Designer generated code'}
procedure TMainForm.InitializeComponent; 
begin
  Self.Label1 := System.Windows.Forms.Label.Create; 
  //
  // Label1
  // 
  Self.Label1.Location := System.Drawing.Point.Create(16, 16); 
  Self.Label1.Text := 'Hello World!'; 
  //
  // TMainForm
  // 
  Self.ClientSize := System.Drawing.Size.Create(292, 266);
  Self.Controls.Add(Self.Label1); 
end; 
{$ENDREGION}

procedure TMainForm.Dispose(Disposing: Boolean); 
begin
  if Disposing then
  begin
    if Components <> nil then 
      Components.Dispose(); 
  end; 
  inherited Dispose(Disposing); 
end; 

constructor TMainForm.Create; 
begin
  inherited Create; 
  InitializeComponent; 
end; 
end. 

To resolve the File not found: 'MainForm.TMainForm.resources' error, we need to remove the MainForm.resx file from the project. To do that, select Project|Remove from Project..., click on the MainForm.resx line in the listview, and click the OK button. And although the error wasn't reported in our initial compile attempt, we will also need to remove the [STAThread] attribute from the project source. Once these changes are made, save all files and invoke the batch file again.

This time the compilation was successful, so we should now be able to see our results on the emulator. It should have been compiled to, and be visible from, a storage card on the emulator. To navigate to this, click the Start button on the emulator, select Programs, and click on the File Explorer icon. By default, this will place us in the My Documents folder. In order to get to the storage card, click the drop down arrow next to the My Documents text at the top of the screen, and select My Device

Click on the Storage Card folder, and you should see two entries called HelloWorld. The top one will be HelloWorld.exe, and the bottom one will be HelloWorld.pdb (a debug file generated by the compiler). Click on the HelloWorld.exe entry, and the application should run.

Take a moment to marvel at our handywork, then click the X in the titlebar to close the application (or did it? More on that in a minute)

We will now change the application to add a Button which will change the caption of a label when clicked. Our new code is as follows:


unit MainForm;

interface

uses
  System.Drawing, System.Collections, System.ComponentModel,
  System.Windows.Forms, System.Data;

type
  TMainForm = class(System.Windows.Forms.Form)
  {$REGION 'Designer Managed Code'}
  strict private
    Components: System.ComponentModel.Container;
    Label1: System.Windows.Forms.Label;
    Button1: System.Windows.Forms.Button;
    Label2: System.Windows.Forms.Label;
    procedure InitializeComponent;
    procedure Button1_Click(sender: System.Object; e: System.EventArgs);
  {$ENDREGION}
  strict protected
    procedure Dispose(Disposing: Boolean); override;
  private
    { Private Declarations }
  public
    constructor Create;
  end;

implementation

{$AUTOBOX ON}

{$REGION 'Windows Form Designer generated code'}
procedure TMainForm.InitializeComponent;
begin
  Self.Label1 := System.Windows.Forms.Label.Create;
  Self.Button1 := System.Windows.Forms.Button.Create;
  Self.Label2 := System.Windows.Forms.Label.Create;
  //
  // Label1
  //
  Self.Label1.Location := System.Drawing.Point.Create(16, 16);
  Self.Label1.Text := 'Hello World!';
  //
  // Button1
  //
  Self.Button1.Location := System.Drawing.Point.Create(16, 40);
  Self.Button1.Text := 'Click Me!';
  Include(Self.Button1.Click, Self.Button1_Click);
  //
  // Label2
  //
  Self.Label2.Location := System.Drawing.Point.Create(16, 72);
  Self.Label2.Size := System.Drawing.Size.Create(152, 23);
  //
  // TMainForm
  //
  Self.ClientSize := System.Drawing.Size.Create(292, 266);
  Self.Controls.Add(Self.Label2);
  Self.Controls.Add(Self.Button1);
  Self.Controls.Add(Self.Label1);
end;
{$ENDREGION}

procedure TMainForm.Dispose(Disposing: Boolean);
begin
  if Disposing then
  begin
    if Components <> nil then
      Components.Dispose();
  end;
  inherited Dispose(Disposing);
end;

constructor TMainForm.Create;
begin
  inherited Create;
  InitializeComponent;
end;

procedure TMainForm.Button1_Click(sender: System.Object; e: System.EventArgs);
begin
  Label2.Text := 'Delphi is in the building!';
end;

end.

Invoke the batch file from the Tools menu to compile the code

As you can see from the above output, the exe couldn't be overwritten because it is currently in use. This is because of a feature introduced with Pocket PC 2002 called Smart Minimize. By default on devices running Pocket PC 2002 and above, when the user clicks the X in an applications main form titlebar it doesn't close the application, but instead minimizes to the background. If the application is invoked again (via a shortcut or clicking on it in the File Explorer), it is brought back to the foreground.

Our HelloWorld application can be unloaded from memory by accessing the Memory section of the PocketPC system settings, but for simplicity now we will simply shutdown and restart the emulator. This can be achieved by selecting Shut Down... from the Emulator menu, selecting Turn off, and clicking the OK button. Restart the emulator, and our application can once again be compiled to the Storage Card directory.

We will now also change our code to add a Close button, so the application can be terminated normally. Another approach would be to disable the Smart Minimize behaviour by setting the WinForm.MinimizeBox property to False. This would change the X button to an OK button, which when clicked would close the application. Below is our updated code:-


unit MainForm;

interface

uses
  System.Drawing, System.Collections, System.ComponentModel,
  System.Windows.Forms, System.Data;

type
  TMainForm = class(System.Windows.Forms.Form)
  {$REGION 'Designer Managed Code'}
  strict private
    Components: System.ComponentModel.Container;
    Label1: System.Windows.Forms.Label;
    Button1: System.Windows.Forms.Button;
    Button2: System.Windows.Forms.Button;
    Label2: System.Windows.Forms.Label;
    procedure InitializeComponent;
    procedure Button1_Click(sender: System.Object; e: System.EventArgs);
    procedure Button2_Click(sender: System.Object; e: System.EventArgs);
  {$ENDREGION}
  strict protected
    procedure Dispose(Disposing: Boolean); override;
  private
    { Private Declarations }
  public
    constructor Create;
  end;

implementation

{$AUTOBOX ON}

{$REGION 'Windows Form Designer generated code'}
procedure TMainForm.InitializeComponent;
begin
  Self.Label1 := System.Windows.Forms.Label.Create;
  Self.Button1 := System.Windows.Forms.Button.Create;
  Self.Button2 := System.Windows.Forms.Button.Create;
  Self.Label2 := System.Windows.Forms.Label.Create;
  //
  // Label1
  //
  Self.Label1.Location := System.Drawing.Point.Create(16, 16);
  Self.Label1.Text := 'Hello World!';
  //
  // Button1
  //
  Self.Button1.Location := System.Drawing.Point.Create(16, 40);
  Self.Button1.Text := 'Click Me!';
  Include(Self.Button1.Click, Self.Button1_Click);
  //
  // Button2
  //
  Self.Button2.Location := System.Drawing.Point.Create(152, 224);
  Self.Button2.Text := 'Close';
  Include(Self.Button2.Click, Self.Button2_Click);
  //
  // Label2
  //
  Self.Label2.Location := System.Drawing.Point.Create(16, 72);
  Self.Label2.Size := System.Drawing.Size.Create(152, 23);
  //
  // TMainForm
  //
  Self.ClientSize := System.Drawing.Size.Create(292, 266);
  Self.Controls.Add(Self.Label2);
  Self.Controls.Add(Self.Button2);
  Self.Controls.Add(Self.Button1);
  Self.Controls.Add(Self.Label1);
end;
{$ENDREGION}

procedure TMainForm.Dispose(Disposing: Boolean);
begin
  if Disposing then
  begin
    if Components <> nil then
      Components.Dispose();
  end;
  inherited Dispose(Disposing);
end;

constructor TMainForm.Create;
begin
  inherited Create;
  InitializeComponent;
end;

procedure TMainForm.Button1_Click(sender: System.Object; e: System.EventArgs);
begin
  Label2.Text := 'Delphi is in the building!';
end;
          
procedure TMainForm.Button2_Click(sender: System.Object; e: System.EventArgs);
begin
  Close;
end;

end.

Compiling and running the application, and clicking on the Click Me! button yields the following output:

And below is an image of the same application running on an actual Compaq iPaq PocketPC device:

 


  Summary
    

With the delivery of the Delphi for .NET preview compiler, Borland has given Delphi developers the ability to target the Compact Framework for the first time. This article has hopefully provided some guidance to allow developers to start exploring what will be for many a new and exciting world. 

Acknowledgements

Thanks to Marc Rohloff for the permission to use excerpts from his Getting Started with C# and the Compact Framework article. Also thanks to Marc and John Kaster for assisting with some of the technical aspects of this article. And of course, thanks to Seppy Bloom, Danny Thorpe, Tagawa-san, and all the other R & D engineers who made this article possible in the first place by creating the preview compiler.



Server Response from: ETNASC03