Developing Pocket PC applications with Delphi 2006

By: Stefan Cruysberghs

Abstract: This article provides information about the Delphi .NET Compact Framework compiler and some Delphi 2006 plugins for developing Pocket PC applications

    Introduction

A few months after the release of Delphi 2005, Borland made available a preview of the Delphi .NET Compact Framework compiler. Now there is full support for this compiler in Delphi 2006. There are no CF designers yet, but with some extra tools it is quite easy to produce executables which can run on Pocket PCs, Smartphones and all devices running the Windows CE operating system. This article explains many of the differences between the compact and desktop version of the .NET framework and I will show you step by step how to develop and test Pocket PC applications with Delphi 2006.

    .NET Compact Framework

Pocket PCs and other devices running Windows CE are the devices that currently support the .NET Compact Framework. The .NET CF is a rich subset of the .NET Framework, thus providing the same benefits as the .NET Framework. The .NET CF also has a few additional libraries that are specific to mobility and device development.

But not all properties, methods and classes which are available on the full .NET Framework have been implemented on the Compact Framework. In this article I will show the most important differences and some workarounds.

Delphi 2006 (and 2005) supports the .NET CF 1.0 which will run on all current Pocket PCs. Microsoft recently released version 2.0 which includes some new controls (datetimepicker, monthcalendar, linklabel, webbrowser) and some new managed API's for telephony, messaging, configuration, ...

    Windows Mobile Emulators

To test your mobile applications on your PC you have to install an emulator. On the Microsoft website you can download two kind of emulators and several images for the mobile operating systems. The Windows Mobile 2003 and 2003 Second Edition operating systems are primarily used on current Pocket PCs. The newest Pocket PCs will be installed with Windows Mobile 5.0.

    Windows CE 5.0 Emulator

The older Windows CE 5.0 Emulator (Emulator_500.exe) can be used to run images of the Pocket PC and Smartphone 2003 operating systems for x86 CPUs. There are no x86 images of Windows Mobile 5.0 available for this emulator.

    Windows Device Emulator 1.0

Those who have installed Microsoft Visual Studio 2005 will notice that there is a new emulator called Device Emulator (and Device Emulator Manager). Recently, Microsoft has released a standalone version of the Device Emulator. This new Device Emulator is significantly better than its predecessor (easier to install, more features, ActiveSync support, works in a VMWare session, better help file) but this new emulator only runs code compiled for ARM processors. So, when you try to use an old x86 OS image you will get an error "Invalid or missing ROM image".

If your PC does not have a Virtual Machine Network driver, then you first have to download and install it :

Localized images and extra tools are also available :

At this moment no Windows Mobile 2003 SE images for ARM CPUs are available on the Microsoft website. These will be made available in the next few days.

There is also a newer bèta version which is much faster and has some bugfixes :

    Paths and startup parameters

Hide image
Windows Mobile Emulators

Paths Windows CE 5.0 Emulator & PPC 2003 SE images (x86)

Emulator C:\Program Files\Windows CE 5.0 Emulator\Emulator_500.exe
Images (2003 SE) C:\Program Files\Pocket PC 2003 Second Edition Emulators\WWE\
Images (2003) from SDK C:\Program Files\Windows CE Tools\wce420\POCKET PC 2003\Emulation
Skins C:\Program Files\Pocket PC 2003 Second Edition Emulators\WWE\

Paths Device Emulator 1.0 (standalone version) & PPC 5.0 images (ARM)

Emulator C:\Program Files\Microsoft Device Emulator\DeviceEmulator.exe
Images (5.0 MSFP) C:\Program Files\Microsoft Windows Mobile 5.0 MSFP Emulator Images\
Images (5.0) from SDK C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Pocket PC SDK\Deviceemulation\0409
Localized images (5.0) C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Emulator Images for Pocket PC - NLD\Deviceemulation\0413
Skins C:\Program Files\Microsoft Windows Mobile 5.0 MSFP Emulator Images\

Paths Device Emulator 1.0 (VS2005) & PPC 2003 SE (ARM) + PPC 5.0 images (ARM)

Emulator C:\Program Files\Microsoft Device Emulator\1.0\DeviceEmulator.exe
Images (2003 SE & 5.0) C:\Program Files\Microsoft Visual Studio 8\SmartDevices\Emulators\Images\
Skins C:\Program Files\Microsoft Visual Studio 8\SmartDevices\Skins\

Startup parameters

  Link to emulator image file (Pocket PC or Smartphone)
/sharedfolder Link to shared folder with your own applications. On the Pocket PC this folder can be accessed by the Storage Card link.
/video Resolution (width x height) and bit depth. 240x320x16 for Pocket PC's, 176x220x16 for Smartphones
/ethernet Specifies the level of networking support in the Emulator (none, shared, virtualswitch)
/skin Link to a XML skin file which will show a bitmap of a Pocket PC or Smartphone around the Windows CE window. This parameter is not required.

VBScripts to start an emulator

Dim WshShell
Set WshShell = CreateObject("WScript.Shell")

q = Chr(34)
strCommandLine = q & "C:\Program Files\Windows CE 5.0 Emulator\Emulator_500.exe" & q
& " " & q & "C:\Program Files\Pocket PC 2003 Second Edition Emulators\WWE\Pocket_PC\PPC_2003_SE_WWE.bin" & q
& " /sharedfolder " & q & "C:\Program Files\NET CF\Shared" & q
& " /video 240x320x16" & " /ethernet shared"& " /skin "
& q & "C:\Program Files\Pocket PC 2003 Second Edition Emulators\WWE\Pocket_PC\PPC_2003_SE.xml" & q

WshShell.Exec(strCommandLine)
Dim WshShell
Set WshShell = CreateObject("WScript.Shell")

q = Chr(34)
strCommandLine = q & "C:\Program Files\Microsoft Device Emulator\DeviceEmulator.exe" & q
& " " & q & "C:\Program Files\Microsoft Windows Mobile 5.0 MSFP Emulator Images\pocketpc.nb0" & q
& " /sharedfolder " & q & "C:\Program Files\NET CF\Shared" & q
& " /video 240x320x16" & " /ethernet shared"& " /skin "
& q & "C:\Program Files\Microsoft Windows Mobile 5.0 MSFP Emulator Images\Pocket_PC.xml" & q

WshShell.Exec(strCommandLine)

Remove the enters in the command line rule because this should be one source line !

    .NET CF assemblies

If you want to deploy your application on your Pocket PC, you have to make sure the latest service pack of the .NET Compact Framework 1.0 is installed. These assemblies are already included in the Emulator Images but you need to install them on your Pocket PC. Service Pack 3 can be found at the Microsoft website. This end-user version comes with an automatic installer but it requires that ActiveSync is active and that your Pocket PC is connected to your PC.

    Create, compile and deploy a Delphi Pocket PC application

We will start creating our first Delphi Pocket PC application. For now we will not utilize extra plugins.

    Design a form with a button

1) Start with File|New|Windows Forms .NET application - Delphi for .NET

2) Resize the form. Size.Width=240, Size.Height=320

3) Set Text property of form : "Pocket PC"

4) Add a Button and change the Text : "Delphi"

    Compile the project with the .NET CF compiler

When you press the Run F9 button, the application will be compiled with the .NET compiler. But we are trying to create a .NET Compact Framework application for a Pocket PC, so we can not proceed the normal way.

To compile this application we have to use the Delphi .NET CF compiler (DCCIL) which is available in the Borland\BDS\4.0\Bin folder. The .dcpil and .dcuil files for the .NET CF can be found in the C:\Program Files\Borland\BDS\4.0\lib\cf folder. When .dcpil files are present, you do not need to install these .NET CF assemblies (DLL files) on your PC. This DCCIL compiler can not be invoked directly from the Delphi 2006 IDE.

There are 2 ways to invoke the DCCIL compiler :

The hard way is to to create a small script to run this compiler. I have updated my Delphi 2005 VBScript to do this. In the Tool Properties dialog (Configure Tools) you can add a menu item for this script. Specify the title (this is the caption of the new menu item), the location of the program and a list of parameters which contains some macros.

Title Compile with Delphi .NET CF compiler
Program C:\WINDOWS\system32\wscript.exe
Parameters $SAVEALL "C:\Program Files\NET CF\Scripts\CompileCFNET.vbs" $NAMEONLY($PROJECT) $PROJECT

CompileNETCF.vbs

' CompileNETCF.VBS
' Compile project with Delphi 2006 .NET Compact Framework Compiler (DCCIL)
' February 2006 - Stefan Cruysberghs
' Delphi tools properties
' * Program : C:\WINDOWS\system32\wscript.exe
' * Parameters : $SAVEALL "path\CompileNETCF.vbs" $NAMEONLY($PROJECT) $PATH($PROJECT)
Dim WshShell, Fso, Args, Exec, i
Set WshShell = CreateObject("WScript.Shell")
Set Fso = CreateObject("Scripting.FileSystemObject")
Set Args = WScript.Arguments

If Args.Count = 0 Then MsgBox "No Delphi project given", 16, "Error" Else strProjectName = Args(0) strProjectFolder = "" For i = 1 To Args.Count-1
strProjectFolder = strProjectFolder & " " & Args(i)
Next strProjectName = Trim(Fso.GetBaseName(strProjectName)) strProjectFolder = Trim(Fso.GetParentFolderName(strProjectFolder)) strProject = strProjectFolder & "\" & strProjectName & ".dpr" If Not Fso.FileExists(q & strProject & q) Then MsgBox strProject & " does not exist", 16, "Error" Else q = Chr(34) 'quote ' Set paths strDCCIL = "C:\Program Files\Borland\BDS\4.0\Bin\dccil.exe" strCFUnits = "C:\Program Files\Borland\BDS\4.0\lib\cf" strLogFileName = strProjectFolder & "\CompileCFNETLog.pas" ' Extra compiler options. If .NET CF assemblies are not available/needed, set it to "" strCompilerOptions = "" 'strCompilerOptions = " -lu" & q & "C:\NETCFAssemblies\Microsoft.WindowsCE.Forms.dll"
' & q & " -lu" & q & "C:\NETCFAssemblies\System.Windows.Forms.DataGrid.dll" & q
' Delete all .dcuil files On Error Resume Next Fso.DeleteFile strProjectFolder & "\*.dc?il", True strCommandLine = "cmd /c > " & q & strLogFileName & q & " " & q & strDCCIL & q & " " & q & strProject & q & " -u" & q & strCFUnits & q & " -luSystem.Windows.Forms -luSystem.Data " & strCompilerOptions ' Compile the project with the DCCIL.exe Set Exec = WshShell.Exec(strCommandLine)

' Wait for application to exit Do While Exec.Status = 0
WScript.Sleep 1
Loop ' Show the log file (when extention of log is PAS, it will be opened in Delphi) WshShell.Run(q & strLogFileName & q) End If End If

Remove the enters in the command line rule because this should be one source line ! 

A much better way, which was brought to my attention by Marco Cantu , is to change the Library Path . Go to Tools|Options|Delphi options|Library - NET and change the path $(BDS)\lib\ into $(BDS)\lib\cf (use the Replace button). Now you can use the Run , Compile and Build actions. And what is more, also the Code Insight features will function. So without compiling you can see the errors introduced by the WinForms designer in the messages log (Error Insight) and also Code Completion is working great.

Next screenshot shows the difference between the methods of the MessageDlg class in .NET Framework and in the .NET Compact Framework after changing the Library Path into \Lib\CF.
Hide image
Click to see full-sized image

    Solve the compilation errors

After changing the Library Path run the application (or run the script). As you can see, the compilation failed with many Undeclared identifier errors. As stated before, not all properties, methods and classes which are available on the full .NET Framework have been implemented on the Compact Framework.

Borland Delphi for .NET compiler version 18.0
Copyright (c) 1983,2005 Borland Software Corporation
.NET Framework v1.1.4322 loaded

Project1.dpr(8) Warning: W1026 File not found: 'WinForm.TWinForm.resources'

WinForm.pas(49) Error: E2003 Undeclared identifier: 'SuspendLayout'

WinForm.pas(54) Error: E2003 Undeclared identifier: 'Name'

WinForm.pas(55) Error: E2003 Undeclared identifier: 'TabIndex'

WinForm.pas(60) Error: E2003 Undeclared identifier: 'AutoScaleBaseSize'

WinForm.pas(63) Error: E2003 Undeclared identifier: 'Name'

WinForm.pas(65) Error: E2003 Undeclared identifier: 'ResumeLayout'

Project1.dpr(15) Fatal: F2063 Could not compile used unit 'WinForm.pas'

When a class is undeclared, we have to remove it from the form (or in the source code).

The TabIndex is one of the properties that does not exist in the .NET CF and therefore the compiler will return an Undeclared identifier "TabIndex" error . Other properties and methods which should be deleted or commented out in the InitializeComponent method of the form (Windows Form Designer generated code) are :

  • Name
  • TabIndex
  • TabStop
  • Index
  • AutoScaleBaseSize
  • ResumeLayout
  • SuspendLayout
  • AddRange

Hide image
InitializeComponent

In the project file you have to remove the [STAThread] attribute.

To resolve the File not found: 'WinForm.TWinForm.resources' warning, you need to remove the WinForm.resx file from the project. This can be done by deleting the reference in the project file. You can also select the menu item Project|Remove from Project..., click on the WinForm.resx item in the list, and press the OK button.

{$R 'WinForm.TWinForm.resources' 'WinForm.resx'}

After solving all errors, try to compile the project again by invoking the VBScript.

    Test the application with the emulator

Now we can copy the EXE to the shared folder. Start the emulator and make sure the same shared folder has been set. Run the Explorer, go to the Storage Card (=shared folder) and start the executable. Your first Delphi Pocket PC application is running !

Hide image
Click to see full-sized image

When the .NET CF is installed on your mobile device, you can also copy the EXE to your Pocket PC with ActiveSync.

    Delphi plugins

Of course you noticed that the Delphi .NET CF compiler works fine but without designers it is a lot of work to change the generated source code manually. Fortunately some Delphi developers did a great job by creating some Delphi plugins.

    Compact Framework Project Preprocessor

Chee Wee Chua has posted a Compact Framework Project Preprocessor (MakeCFCompatible) to the Borland CodeCentral. This little program will modify your code automatically by commenting out every line that raises error E2003.

    Compact Framework Build Helper

And Jeremy North created a great plugin called the Compact Framework Build Helper (CFBH) which adds some extra toolbars, menus and templates in Delphi 2006 (and 2005). CFBH makes it very easy to start new .NET CF projects, compile .NET CF projects within the Delphi IDE, launch different emulators and deploy the applications to the emulators or Pocket PC. It also includes some nice PDF manuals and on the website you will find several comprehensive videos.

    Installation and configuration

1) Download the Compact Framework Project Prepocessor, unzip it and place all files in a folder

2) Make sure Borland Developer Studio is closed

3) Download the Compact Framework Build Helper and run the setup

4) Settings : Check the checkbox as a result of which a new menu item with all CFBH features will be created

5) Folder Setup

  • Refer to the BDS 4.0 compiler and library path
  • Check Auto Fix Errors and refer to the MakeCFCompatibleDLL.DLL of the Compact Framework Project Prepocessor
  • The other options will be explained later on in this article

Hide image
Click to see full-sized image

6) Emulators

  • Here you can define several emulators, images and configuration options for each emulator
  • Set the path for the emulator and the Windows Mobile image
  • The shared path allows you to treat a folder as if it was a storage card in the emulator. Make sure this shared folder is different from your Delphi project folder.
  • On the other tabsheets you can define the screen resolution, skin, network options, ...

Hide image
Click to see full-sized image

7) Deploy

  • The deployment page allows you to set the default deployment method for your applications once they run.

    Create, compile and deploy a Delphi Pocket PC application with CFBB

Now that we have installed the CFHB, we will create the same example again. This time you will notice that compiling and deploying the application can be done very quickly

1) File|New|Other and choose the Smart Device Application

2) Set Text property of form : "Pocket PC"

3) Add a Button and change the Text : "Delphi"

4) In the CFBH toolbar, choose Selected Emulator, choose an emulator and press the CFBH Run button. The emulator will be started. The EXE is automatically copied to the shared folder so in the emulator you can start the EXE by exploring the Storage Card.

5) Make sure your Pocket PC is connected to your PC and Microsoft ActiveSync is active. Choose Active Sync (device) and press the CFBH Run button. Your new application will be launched automatically on your mobile device.

Hide image
Delphi IDE and CFBH

Cool, isn't it ? When you take a closer look at the source code you will notice that all lines which raise error E2003 have been modified. The compiler directive CF is added by which this source will not be compiled when using the Compact Framework DDCIL compiler. The combination of the Compact Framework Project Prepocessor and the Compact Framework Build Helper is really helpful compiling your projects.


    Oosterkamp Class Helpers

The drawback of this preprocessor is that if an error is spread over more than one line, only one line is commented out, resulting in an application that does not compile at all. Because not all issues can be handled by the preprocessor, Jeroen Pluimers wrote the Oosterkamp Class Helpers. They will add some extra properties and methods to existing .NET CF classes (TextBox, TreeView, Toolbar, TrackBar, NumericUpDown, MainMenu, ImageList, Timer, DataGrid, Application, ...).

When you start with a CFBH template (File|New|Other|Smart Device Application) an extra unit will be included

{$IFDEF CF_Oosterkamp}
uses
OosterKamp.WindowsCE.ClassHelpers;
{$ENDIF CF_Oosterkamp}

To use these classhelpers you have to add some extra compiler options. They are available in the list so check them in CFBH Options|Folder setup.

-DCF_Oosterkamp
-U"C:\Program Files\CFBuild2006\ClassHelpers"

Compile your application again. You will notice that all compiler directives {$IFNDEF CF} will disappear. By using these Oosterkamp Class Helpers all properties in the InitializeComponent method do exist and therefore they don't have to be commented out. Some of them are implemented and others are fake.

When the class helper units are included, you can decide to turn off the Auto Fix Preprocessor option. Usually it works better without using the preprocessor. More information about these class helpers can be found in C:\Program Files\CFBuild2006\classhelpers.

When using CFBH and the Oosterkamp Class Helpers your Pocket PC application will compile without making any changes manually. But there are more differences between the .NET framework and the .NET CF. In the next chapters I will show you which controls are supported and how you can write some code which will work fine on a Pocket PC.

    System.Windows.Forms


Hide image
Click to see full-sized image

    Forms

On a Pocket PC, top-level forms with a border are always shown full screen and they can not be moved or resized. Therefore it is better setting the Size property in design-time; Width=240; Height=320. In run-time the Size and Location properties will be ignored. When using the CFBH templates (File|New|Others|Smart device) the size of the forms will be set automatically.

Common screen resolutions are :

 
176x220
240x320
320x240
240x240
480x480
640x480
Windows Mobile 5.0  
X
X
X
X
X
Windows Mobile 2003 SE  
X
X
     
Windows Mobile 2003  
X
       
Smart Phone 2003
X
         

In order to display a non-full screen form on a Pocket PC, you should set the form's FormBorderStyle property to FormBorderStyle.None. The position of the close button is still in the right top corner.

The StartPosition property which can be used to set a form in the screen center, is not supported in the .NET CF. But this can be solved easily by calculating the center of the screen in the Load event of the form.

procedure TWinForm1.TWinForm1_Load(sender: System.Object; e: System.EventArgs);
begin Self.Location := System.Drawing.Point.Create( (Screen.PrimaryScreen.Bounds.Width - Self.Width) div 2,
(Screen.PrimaryScreen.Bounds.Height - Self.Height) div 2);
end;

Pocket PC applications normally remain running indefinitely to allow users to quickly switch between applications. So, a Pocket PC application does not close, instead it minimizes. When the form property MinimizeBox is True (default), a "smart minimize" X-button will be displayed in the top right, whereas MinimizeBox=False will show a little close OK-button. In a CFBH template the MinimizeBox property will be False.

X
MinimizeBox=True Minimize
OK
MinimizeBox=False Close

When you want to know when a window is being minimized, trigger the Deactivate event.

In order to close a minimized application on your Pocket PC, you have to go to Settings|Systems|Memory|Running Programs. A list of all running applications will be shown and you can stop or activate them.

The TabIndex property does not exist in the .NET CF. Tabbing is based on z-order, the order in which the controls have been added to the Controls collection of the form. The first focusable control in a Pocket PC application is the mainform itself.

    Hardware buttons

Hide image
Hardware buttons

A Pocket PC has a directional pad with 5 buttons that can be used to interact with applications. It is possible to capture these keys in the KeyDown event. The KeyDown event receives a KeyEventArgs object which exposes a KeyCode property. By comparing the value of the KeyCode property we can determine which button was pressed.

procedure TWinForm1.TWinForm1_KeyDown(sender: System.Object; e: System.Windows.Forms.KeyEventArgs);
begin case e.KeyCode of Keys.Up : ... Keys.Down : ... Keys.Left : ... Keys.Right : ... Keys.Return : ... end;
end;

    Application

Most properties of the Application class which you can use in .NET are not implemented in the .NET CF. So properties like CommonAppDataPath, ExecutablePath, CompanyName, ProductName, ProductVersion, ... are not available.

Another way to get the path of the executable is to access the FullyQualifiedName property of the first module of the assembly. For accessing the Assembly object you have to add the System.Reflection namespace. File IO functions are included in the System.IO namespace.

uses
  ..., System.Reflection, System.IO;

strExecutableFullName := System.Reflection.Assembly.GetExecutingAssembly.GetModules[0].FullyQualifiedName;
strExecutableName := System.IO.Path.GetFileName(strExecutableFullName);
strExecutablePath := System.IO.Path.GetDirectoryName(strExecutableFullName);

The AssemblyVersion (=ProductVersion) is specified in the project file. In a Pocket PC application this version number can be accessed by using the GetName.Version property of the assembly.

[assembly: AssemblyVersion('1.2.0.0')]
strProductVersion := System.Reflection.Assembly.GetExecutingAssembly.GetName.Version.ToString;

When you have started with a new smart device project (CFBH template), then one of the Oosterkamp Class Helper units has been added. To use them in all their glory, also add a second unit called OosterKamp.WindowsCE.ClassHelpersEx.

When these class helpers units are included, you can address some of the Application properties as in the full .NET framework. The namespaces System.IO and System.Reflection are not needed.

Label1.Text := Application.ProductName;
Label2.Text := Application.ProductVersion;
Label3.Text := Application.ExecutablePath;

    Menu, ListBox, ListView, TreeView & ToolBar

When creating items for menus, listboxes, listviews, treeviews or toolbars in design-time, you will get undeclared identifier errors on the AddRange method. This can be solved easily by replacing these lines by using the Add or Insert method.

When using the Oosterkamp Class Helpers, you do not have to modify anything. The AddRange method in the class helpers (unit Oosterkamp.WindowsCE.ClassHelpers) will solve this .

On a Pocket PC the mainmenu and the toolbar will be shown at the bottom of the screen.

Self.MainMenu1.MenuItems.AddRange(TArrayOfSystem_Windows_Forms_MenuItem.Create(Self.MenuItemFile));
Self.MenuItemFile.MenuItems.AddRange(TArrayOfSystem_Windows_Forms_MenuItem.Create(Self.MenuItemClose));
Self.ListBox1.Items.AddRange(TArrayOfSystem_Object.Create('Item0', 'Item1')); Self.TreeView1.Nodes.AddRange(TArrayOfSystem_Windows_Forms_TreeNode.Create(
System.Windows.Forms.TreeNode.Create('Node0',
TArrayOfSystem_Windows_Forms_TreeNode.Create(
System.Windows.Forms.TreeNode.Create('Node00'),
System.Windows.Forms.TreeNode.Create('Node01'))),
System.Windows.Forms.TreeNode.Create('Node1',
TArrayOfSystem_Windows_Forms_TreeNode.Create(
System.Windows.Forms.TreeNode.Create('Node10')))));
Self.MainMenu1.MenuItems.Add(Self.MenuItemFile);
Self.MenuItemFile.MenuItems.Add(Self.MenuItemClose);

Self.ListBox1.Items.Add('Item0');
Self.ListBox1.Items.Add('Item1');
Self.TreeView1.Nodes.Insert(0,System.Windows.Forms.TreeNode.Create('Node0'));
Self.TreeView1.Nodes.Item[0].Nodes.Insert(0,System.Windows.Forms.TreeNode.Create('Node00'));
Self.TreeView1.Nodes.Item[0].Nodes.Insert(1,System.Windows.Forms.TreeNode.Create('Node01'));
Self.TreeView1.Nodes.Insert(1,System.Windows.Forms.TreeNode.Create('Node1'));
Self.TreeView1.Nodes.Item[1].Nodes.Insert(0,System.Windows.Forms.TreeNode.Create('Node10'));

Hide image
Items - IDE Hide image
Items - Emulator

 

    PictureBox

When opening an image in a PictureBox in design-time, this image will be stored in a XML based resource file (.RESX).

<data name="PictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing, 
Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
Qk3OOgAAAAAAADYAAAAoAAAAZAAAADIAAAABABgAAAAAAAAAAADEDgAAxA4AAAAAAAAAAAAA49/g49/g
49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g49/g
...
</value>
</data>
This resource file will be included in the project file ( .DPR ).
{$R 'MainForm.TWinForm.resources' 'MainForm.resx'}

In the InitializeComponent method of the form, a ResourceManager object will be created and with the GetObject method the resources can be accessed.

var
  resources: System.Resources.ResourceManager;
begin
  resources := System.Resources.ResourceManager.Create(TypeOf('TWinForm'));
Self.PictureBox1.Image := (System.Drawing.Image(resources.GetObject('PictureBox1.Image')));
end;

This works fine in a .NET application and it will also compile in a .NET CF application. But when running the EXE in the emulator or on your Pocket PC, you will get a MissingManifestResourceException error. I tried several things but I did not find a way to solve this problem with the Resource Manager. Also the class helpers can't help with this issue.

So I found two other ways to show an image :

The first way is to load a bitmap from a file. This can be done by specifying the image filename in the Create method of the Bitmap class. After creating the bitmap, this object can be assigned to the Image property of the PictureBox.

var
  objBitmap : System.Drawing.Bitmap;
  strExecutablePath : String;
begin strExecutablePath := Path.GetDirectoryName(Assembly.GetExecutingAssembly.GetModules()[0].FullyQualifiedName); objBitmap := Bitmap.Create(strExecutablePath+'\MyBitmap.bmp');
Self.PictureBox1.Image := objBitmap;
end;

Or shorter when using the class helpers :

Self.PictureBox1.Image := Bitmap.Create(Path.GetDirectoryName(Application.ExecutablePath)+'\MyBitmap.bmp');
It is also possible to include a bitmap into your EXE. In stead of using a resource file, you have to include a bitmap into your project file by using the $R compiler directive. The same can be done by clicking on the " Add to project " menu item.

First you have to create a Stream object and read the bitmap into this stream with the GetManifestResourceStream method of the Assembly. Then you can create a Bitmap object and pass the stream as a create-parameter.

{$R 'MyBitmap.bmp' 'MyBitmap.bmp'}

var
  objStream: System.IO.Stream;
  objBitmap : System.Drawing.Bitmap;
begin
  objStream := Self.GetType.Assembly.GetManifestResourceStream('MyBitmap.bmp');
objBitmap := Bitmap.Create(objStream);
Self.PictureBox1.Image := objBitmap;
end;

Hide image
Image - Emulator

    ImageList

When using the ImageList class we encounter the same problem as with the PictureBox class. What’s more, images can be added to the collection in design-time but the project will not compile. A solution is to only create and link the ImageList in design-time and to load the images from files (or from resources) and set the imageindexes in run-time.

procedure frmMain.frmMain_Load(sender: System.Object; e: System.EventArgs);
var strPath : String;
begin strPath := Path.GetDirectoryName(Application.ExecutablePath)+'\Images\';

// Add images from file to ImageList1 ImageList1.Images.Add(Bitmap.Create(strPath+'Open.bmp'));
ImageList1.Images.Add(Bitmap.Create(strPath+'Save.bmp'));
ImageList1.Images.Add(Bitmap.Create(strPath+'Info.bmp'));

// Linked to ImageList1 ToolBar1.Buttons[0].ImageIndex := 0; ToolBar1.Buttons[1].ImageIndex := 1; ToolBar1.Buttons[2].ImageIndex := 2; TreeView1.Nodes[0].ImageIndex := 0; TreeView1.Nodes[0].SelectedImageIndex := 0; TreeView1.Nodes[0].Nodes[0].ImageIndex := 2; TreeView1.Nodes[0].Nodes[0].SelectedImageIndex := 2; // Add images from file to ImageList2 ImageList2.Images.Add(Bitmap.Create(strPath+'Delphi2006.bmp'));
ImageList2.Images.Add(Bitmap.Create(strPath+'NETCF.bmp'));

// Linked to ImageList2 ListView1.Items[0].ImageIndex := 0; ListView1.Items[1].ImageIndex := 1; PictureBox1.Image := ImageList2.Images[0]; end;

Hide image
Click to see full-sized image

    MessageBox

When you take a look at the Microsoft .NET Class Library you will see that the .NET MessageBox class supports 12 overloaded Show methods. In the .NET Compact Framework only 3 of them are implemented. If you try another one, the compiler will give an error :

Error: E2250 There is no overloaded version of 'Show' that can be called with these arguments
So these 3 examples will work :
MessageBox.Show('Message', 'Caption', MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
MessageBox.Show('Message', 'Caption');
MessageBox.Show('Message');

    Timer

After dropping a Timer component on your form in design-time, you only have to change the Create method in the InitializeComponent procedure. The container-parameter should be removed. When using the class helpers this is not necessary.
procedure TWinForm1.InitializeComponent;
begin Self.Timer1 := System.Windows.Forms.Timer.Create; //(Self.components); Self.Timer1.Enabled := True; Self.Timer1.Interval := 1000; Include(Self.Timer1.Tick, Self.Timer1_Tick); end;

procedure TWinForm1.Timer1_Tick(sender: System.Object; e: System.EventArgs);
begin Text := DateTime.Now.ToString; end;

    OpenFileDialog & SaveFileDialog

The OpenFileDialog and SaveFileDialog are also supported in the .NET Compact Framework. But the OpenFileDialog is restricted to the "My Documents" folder. Apparently this restriction is imposed by the operating system to help users to organize their files under the standard "My Documents" folders.

Hide image
Dialogs - Emulator

    Microsoft.WindowsCE.Forms

There are also some classes which are exclusive to the .NET Compact Framework. They are included in the System.Net.Sockets, System.Data.SqlServerCE, System.Net.IrDA and Microsoft.WindowsCE.Forms namespaces.

    Install .NET CF 1.0 assemblies

Before employing them in Delphi, these .NET CF assemblies should be available on your PC because their corresponding .dcpil files are not available in the C:\Program Files\Borland\BDS\4.0\lib\cf folder. First download NET Compact Framework 1.0 SP3. There are two ways to copy these DLL's to your PC :

ConvertGAC.bat

  • Install the .NET CF on your Pocket PC
  • Copy all GAC_ DLL's from the global assembly cache at your Pocket PC to your PC
  • Execute this batch file which will rename the GAC DLL's
copy GAC_Microsoft.VisualBasic_v7_0_5000_0_cneutral_1.dll microsoft.visualbasic.dll
copy GAC_Microsoft.WindowsCE.Forms_v1_0_5000_0_cneutral_1.dll microsoft.windowsce.forms.dll copy GAC_mscorlib_v1_0_5000_0_cneutral_1.dll mscorlib.dll copy GAC_System.Data_v1_0_5000_0_cneutral_1.dll system.data.dll copy GAC_System.Drawing_v1_0_5000_0_cneutral_1.dll system.drawing.dll copy GAC_System.Net.IrDA_v1_0_5000_0_cneutral_1.dll system.net.irda.dll copy GAC_System.Web.Services_v1_0_5000_0_cneutral_1.dll system.web.services.dll copy GAC_System.Windows.Forms.DataGrid_v1_0_5000_0_cneutral_1.dll system.windows.forms.datagrid.dll copy GAC_System.Windows.Forms_v1_0_5000_0_cneutral_1.dll system.windows.forms.dll copy GAC_System.Xml_v1_0_5000_0_cneutral_1.dll system.xml.dll copy GAC_System_v1_0_5000_0_cneutral_1.dll system.dll pause

ConvertCAB.bat

  • Extract all files in netcf.core.ppc3.ARM.cab (=installation files of .NET CF)
  • Execute this batch file which will rename and delete the files
del NE8A18~1.000
del 0mscoree.001
del MSCORE~1.002
del cgacutil.003
del NETCF1~1.004
del NETCFA~1.005
del CALEND~1.006
del CHARIN~1.007
del CULTUR~1.008
del CULTUR~2.009
del REGION~1.010
ren mscorlib.011 mscorlib.dll
ren 00system.012 system.dll
ren SYSTEM~3.013 system.drawing.dll
ren SYC6B2~1.014 system.web.services.dll
ren SYSTEM~4.015 system.windows.forms.dll
ren SY9B57~1.016 system.windows.forms.datagrid.dll ren SY40C7~1.017 system.xml.dll ren SYSTEM~1.018 system.net.irda.dll ren SY4317~1.019 system.data.dll ren MICROS~2.021 microsoft.visualbasic.dll ren MICROS~3.020 microsoft.windowsce.forms.dll del NETCF_~1.999 pause

When compiling/linking a project which uses classes from the Microsoft.WindowsCE.Forms (or System.Windows.Forms.Datagrid) assembly, you have to refer to this assembly by using the -lu parameter of DCCIL compiler.

If you are using the VBScript to compile your .NET CF projects, you have to add these parameters.

-lu"C:\Program Files\NET CF\Assemblies\Microsoft.WindowsCE.Forms.dll"
-lu"C:\Program Files\NET CF\Assemblies\System.Windows.Forms.DataGrid.dll"

When using the CFBH, you have to set the Assembly Path and alter the Compiler Options.

Hide image
CFBH - Compiler Options

 

    Versions

If you would like to check your Windows Mobile version on your Pocket PC, go to Settings|System|About. To check the Compact Framework version you have to start the application CGACUTIL.EXE in the Windows folder.

To determine the versions programmatically you can use :

Environment.OSVersion;
Environment.Version;

The other Environment properties and methods like CommandLine, MachineName, GetFolderPath, GetCommandLineArgs (and Delphi equivalent ParamStr) ... are not supported in the .NET CF.

Compact Framework versions

CF 1.0 1.0.2268.0
CF 1.0 SP1 1.0.3111.0
CF 1.0 SP2 1.0.3316.0
CF 1.0 SP3 1.0.4292.0 (1.0.4292.2 for Windows Mobile 5.0)
CF 2.0 (Beta 2) 2.0.5056.0
CF 2.0 2.0.5238.0

Windows Mobile versions

Windows Mobile 2003 4.20.1081
Windows Mobile 2003 Second Edition 4.21.1088
Windows Mobile 5.0 5.1.1700

 

    InputPanel

The Microsoft.WindowsCE.Forms assembly contains 3 classes; InputPanel, Message, MessageWindow. The InputPanel provides programmatic control of the SIP (soft input panel) on Pocket PCs.

The SIP can be activated from the Pocket PC taskbar. In .NET CF-based applications, the taskbar is visible only if the active form has an associated mainmenu. When adding a MainMenu to your form, the SIP will automatically be visible when running the EXE without creating it yourself. So the user of the Pocket PC has access to this virtual keyboard to fill in textboxes or other input controls.

When you like to have control of the SIP and popup it when a control gets focused, you have to create an InputPanel object. Therefore you have to add Microsoft.WindowsCE.Forms to the uses of your form. In the GotFocus and LostFocus events of a textbox you can enable (popup) or disable the SIP.

uses
  ..., Microsoft.WindowsCE.Forms;

procedure TWinForm.InitializeComponent;
begin MainMenu1 := System.Windows.Forms.MainMenu.Create; Self.Menu := MainMenu1; Self.InputPanelMain := Microsoft.WindowsCE.Forms.InputPanel.Create; Self.TextBox1 := System.Windows.Forms.TextBox.Create; Include(TextBox1.GotFocus, TextBox1_GotFocus); Include(TextBox1.LostFocus, TextBox1_LostFocus); end;

procedure TWinForm.TextBox1_GotFocus(Sender: TObject; Args: System.EventArgs);
begin InputPanelMain.Enabled := True; end;

procedure TWinForm.TextBox1_LostFocus(Sender: TObject; Args: System.EventArgs);
begin InputPanelMain.Enabled := False; end;

Hide image
InputPanel - Emulator

The EnabledChanged event occurs whenever the SIP is enabled or disabled, either by the user or programmatically. You can trigger this event to rearrange the layout of the form by moving or resizing some controls. To get the SIP's size, you can access the Bounds property. To determine the size of the form area not occupied by the SIP you have to use the VisibleDesktop property.

The Bounds property always returns a width of 240 pixels and a height of 80 pixels for Pocket PCs, regardless of whether or not the SIP is enabled. If the SIP is disabled, you have to take in account that the mainmenu has a height of 26 pixels.

An example of how to place a textbox at the bottom of the screen :

procedure TWinForm.InitializeComponent;
begin Self.InputPanelMain := Microsoft.WindowsCE.Forms.InputPanel.Create; Include(InputPanelMain.EnabledChanged, InputPanelMain_EnabledChanged); end;

procedure TWinForm.InputPanelMain_EnabledChanged(Sender: TObject; Args: System.EventArgs);
begin if InputPanelMain.Enabled then Self.TextBox1.Top := InputPanelMain.VisibleDesktop.Height - Self.TextBox1.Height - 2 else Self.TextBox1.Top := InputPanelMain.VisibleDesktop.Height - Self.TextBox1.Height - 26 - 2; end;

Probably you are already thinking : "can this be done easier with the class helpers ?". Yes, of course. The class helper for the InputPanel class adds 3 new methods.

procedure HookTextBoxBase(TextBoxBase: System.Windows.Forms.TextBoxBase);
procedure HookAllTextBoxBases(ContainerControl: System.Windows.Forms.Control);
procedure HookTextBoxBases(TextBoxBases: array of System.Windows.Forms.TextBoxBase);

Hide image
CFBH - Templates

When starting with the CFBH template Portrait form with Input Panel or Landscape form with Input Panel, a MainMenu and an InputPanel component will be added in design-time. In run-time the HookAllTextBoxBases method will be called to hook the GotFocus and LostFocus events of all textboxes on the form. So you do not have to write any extra code, just start with one of these templates and the SIP will work with all your textboxes.

    System.IO

This is a small example of how to use some of the System.IO classes (Path, FileInfo) and their methods. This procedure will retrieve some system information (path, version, file size, creation time, ...) about the executable.

var
  objFileInfo : System.IO.FileInfo;
  strExecutableFullName : String;
strExecutableName : String;
strExecutablePath : String;
strProductVersion : String;
intFileSize : Integer;
strFileCreationTime : String;
begin strExecutableFullName := System.Reflection.Assembly.GetExecutingAssembly.GetModules[0].FullyQualifiedName; strExecutableName := System.IO.Path.GetFileName(strExecutableFullName); strExecutablePath := System.IO.Path.GetDirectoryName(strExecutableFullName); strProductVersion := System.Reflection.Assembly.GetExecutingAssembly.GetName.Version.ToString; objFileInfo := System.IO.FileInfo.Create(strExecutableFullName); try intFileSize := objFileInfo.Length; strFileCreationTime := objFileInfo.CreationTime.toString; //... finally objFileInfo.Free; end;
end;

 

    System.Net

In the System.Net namespace you will find a static class called DNS. It retrieves information about a specific host from the Internet Domain Name System. The GetHostName method can be used to get the name of your Pocket PC.

When using the GetHostByName method, the result will be returned in an instance of the IPHostEntry class. IPHostEntry contains multiple IP addresses and aliases. By accessing the first address in the list you will get the IP-address of your Pocket PC. When it differs from 127.0.0.1, you will have a network connection (cradled and ActiveSync running or WiFi connection).

uses 
...,
System.Net; var objIPHost : System.Net.IPHostEntry; strHostName : String;
strHostIP : String;
begin strHostName := DNS.GetHostName; objIPHost := DNS.GetHostByName(strHostName); strHostIP := objIPHost.AddressList[0].ToString; end;


    System.Data

Text or INI files and the Windows registry are generally things of the past for .NET applications. XML files are the appropriate way to store data. There is a System.XML namespace with a lot of classes which look quite complicated. Fortunately, there is a very easy way in .NET to solve this; the Dataset class in the System.Data namespace. Delphi developers are undoubtedly familiar with clientdatasets which can store data locally. Well, the Dataset class is also an in memory dataset with almost the same features.

    Write and read XML

In this example, I’ll show how you can easily create a dataset (with one table and with an integer Index field and a string Description field), load a XML file and save the dataset contents to a XML file with just a few lines of code.

function TWinForm.GetExecutablePath : String;
begin Result := System.IO.Path.GetDirectoryName( System.Reflection.Assembly.GetExecutingAssembly.GetModules()[0].FullyQualifiedName); end;

procedure TWinForm.CreateDataset;
var DataTable1: DataTable; begin // Dataset1 has been created in design-time DataTable1 := Dataset1.Tables.Add('Table1');
DataTable1.Columns.Add('Index', typeof(Integer));
DataTable1.Columns.Add('Description', typeof(String));
end;

procedure TWinForm.LoadDataset;
var strFileName : String;
begin strFileName := GetExecutablePath + '\Dataset.xml';
if (System.IO.File.Exists(strFileName)) then Dataset1.ReadXML(strFileName); end;

procedure TWinForm.SaveDataset;
begin Dataset1.WriteXML(GetExecutablePath + '\Dataset.xml');
end;

The XML file could look like this :

<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<Table1>
<Index>0</Index>
<Description>Borland</Description>
</Table1>
<Table1>
<Index>10</Index>
<Description>Delphi</Description>
</Table1>
<Table1>
<Index>20</Index>
<Description>.NET CF</Description>
</Table1>
</NewDataSet>

    Locate function

If you like to create a kind of 'Locate' function, then you have to use the Find method of the Rows collection. This Find method returns a DataRow object. This method requires that you set the PrimaryKey property of the DataTable before using it. If you don't, you will get an exception.

procedure TWinForm.CreateDataset;
var DataTable1: DataTable; arrPrimKey : array[0..0] of DataColumn;
begin DataTable1 := Dataset1.Tables.Add('Table1');
DataTable1.Columns.Add('Index', typeof(Integer));
DataTable1.Columns.Add('Description', typeof(String));

arrPrimKey[0] := DataTable1.Columns.Item['Index'];
DataTable1.PrimaryKey := arrPrimKey;
end;

procedure TWinForm.LoadDataset;
var strFileName : String;
arrPrimKey : array[0..0] of DataColumn;
begin strFileName := GetExecutablePath + '\Dataset.xml';
if (System.IO.File.Exists(strFileName)) then begin Dataset1.ReadXML(strFileName); arrPrimKey[0] := Dataset1.Tables.Item['Table1'].Columns.Item['Index'];
Dataset1.Tables.Item['Table1'].PrimaryKey := arrPrimKey;
end;
end;

function TWinForm.FindDescription(const aintIndex : Integer) : String;
var objRow : DataRow; begin Result := '';
objRow := Dataset1.Tables.Item['Table1'].Rows.Find(aintIndex);
if Assigned(objRow) then Result := objRow.Item['Description'].ToString;
end;

    XSD schema

It is also possible to use more of the power of an ADO.NET Dataset. A Dataset and its DataTables have a lot of extra Column and Constraints properties and they can be stored as a XSD schema. The WriteXML method in the .NET CF does not support the WriteSchema option (inline XSD schema in XML file), but the Dataset class has a WriteXMLSchema and ReadXMLSchema method which can be used.

Hide image
ADO.NET Dataset

Now you can create a small database application based on local files by loading and saving XML files with data and XSD files with the relational structure.

procedure TWinForm.CreateDataset;
var DataTable1: DataTable; arrPrimKey : array[0..0] of DataColumn;
begin DataTable1 := Dataset1.Tables.Add('Table1');
with DataTable1.Columns.Add('Index', typeof(Integer)) do begin Unique := True; AutoIncrement := True; AutoIncrementStep := 10; end;
with DataTable1.Columns.Add('Description', typeof(String)) do MaxLength := 50; arrPrimKey[0] := DataTable1.Columns.Item['Index'];
DataTable1.PrimaryKey := arrPrimKey;
end;

procedure TWinForm.LoadDataset;
var strFileName : String;
begin strFileName := GetExecutablePath + '\Dataset.xsd';
if (System.IO.File.Exists(strFileName)) then begin Dataset1.ReadXMLSchema(strFileName); strFileName := GetExecutablePath + '\Dataset.xml';
if (System.IO.File.Exists(strFileName)) then Dataset1.ReadXML(strFileName); end;
end;

procedure TWinForm.SaveDataset;
begin Dataset1.WriteXML(GetExecutablePath + '\Dataset.xml');
Dataset1.WriteXMLSchema(GetExecutablePath + '\Dataset.xsd');
end;

    Live templates

Because I really love the live templates in Delphi 2006, I will show 2 templates for a LoadDataset and SaveDataset method. Just add these XML files in the C:\Program Files\Borland\BDS\4.0\Objrepos\code_templates\delphi folder. Type LoadDataset or SaveDataset and press CTRL+J to invoke the live templates. Now you can fill in and tab through the parameters.

<?xml version="1.0" encoding="utf-8" ?>
<codetemplate xmlns="http://schemas.borland.com/Delphi/2005/codetemplates" version="1.0.0">
<template name="LoadDataset" invoke="manual">
<description>Load dataset from local XML/XSD file</description>
<author>Stefan Cruysberghs, http://www.scip.be</author>
<point name="FileNameDataset">
<text>MyDataset</text>
<hint>Filename (and folder) of local dataset (without extension XML/XSD)</hint>
</point>
<point name="VarFileName">
<text>strFileName</text>
<hint>Variable to store filename</hint>
</point>
<point name="Dataset">
<text>Dataset1</text>
<hint>ADO dataset object</hint>
</point>
<script language="Delphi" onenter="false" onleave="true">
DeclareVariable(|VarFileName|); </script> <code language="Delphi" delimiter="|"><![CDATA[ |VarFileName| := string(Path.GetDirectoryName(Application.ExecutablePath) + '\|FileNameDataset|.xsd');
if (System.IO.File.Exists(|VarFileName|)) then
begin
|Dataset|.ReadXMLSchema(|VarFileName|);
|VarFileName| := string(Path.GetDirectoryName(Application.ExecutablePath) + '\|FileNameDataset|.xml');
if (System.IO.File.Exists(|VarFileName|)) then
|Dataset|.ReadXML(|VarFileName|);
end;
|end|]]></code>
</template>
</codetemplate>
<?xml version="1.0" encoding="utf-8" ?>
<codetemplate xmlns="http://schemas.borland.com/Delphi/2005/codetemplates" version="1.0.0">
<template name="SaveDataset" invoke="manual">
<description>Save dataset to local XML/XSD file</description>
<author>Stefan Cruysberghs, http://www.scip.be</author>
<point name="FileNameDataset">
<text>MyDataset</text>
<hint>Filename (and folder) of local dataset (without extension XML/XSD)</hint>
</point>
<point name="Dataset">
<text>Dataset1</text>
<hint>ADO dataset object</hint>
</point>
<code language="Delphi" delimiter="|"><![CDATA[ |Dataset|.WriteXML(Path.GetDirectoryName(Application.ExecutablePath) + '\|FileNameDataset|.xml');
|Dataset|.WriteXMLSchema(Path.GetDirectoryName(Application.ExecutablePath) + '\|FileNameDataset|.xsd');


|end|]]></code>
</template>
</codetemplate>

    DataGrid

The easiest way to display the data of a DataTable is to add a DataGrid. But when putting a DataGrid component on a form, you will get an error :

Error: E2003 Undeclared identifier: 'DataGrid'

The DataGrid component also exists in the .NET CF, but it is not included in the file System.Windows.Forms.dll. It is included in the .NET CF assembly System.Windows.Forms.DataGrid.dll. So you have to refer to this assembly in your DCCIL compiler parameters or in your CFBH compiler options.

-lu"C:\Program Files\NET CF\Assemblies\System.Windows.Forms.DataGrid.dll"

The DataGrid component in the.NET CF does not provide rich editing capabilities, such as its .NET counterpart does. Also the DataMember property and the SetDataBinding method do not exist. The DataGrid cannot handle displaying rows from multiple DataTable objects so connecting the DataTable in stead of the DataSet to the DataGrid.DataSource property solves the problem :

Self.DataGrid1.DataSource := Self.DataSet1.Tables['Table1'];

    Packaging and deploying with CAB files

To distribute your applications, you need to package the application for installation and certify it for deployment to Windows Mobile-based devices. When you have installed the Windows Mobile SDK, you will find an application called CAB Wizard (CabWiz.exe). This console application needs an INF file as input parameter.

The Information (INF) file describes the application installation instructions; source files (exe, assemblies, images, media, xml, ...), destination folders, registry keys, shortcuts, ... Once the CAB file is created, you only have to copy it to your Pocket PC. Just double click on it and your application will be installed automatically.

More information about this CAB Wizard, the available sections and variables, the directory identifiers, ... can be found in the SDK help file.

[Version]
Signature   = "$Windows NT$"        
Provider    = "SCIP.be Stefan Cruysberghs"
CESignature = "$Windows CE$"        
 
[CEStrings]
AppName     = "ShowImageList"       
InstallDir  = %CE1%\%AppName%       ; Program Files\ShowImageList
 
[SourceDisksNames]                  ; directory that holds the application's files
1 = , "Common Files",,
 
[SourceDisksFiles]                  ; list of files to be included in .cab
ShowImageList.exe = 1
Open.bmp = 1
Save.bmp = 1
Info.bmp = 1
Delphi2006.bmp = 1
NETCF.bmp = 1
 
[DefaultInstall]                   ; operations to be completed during install
CopyFiles   = CopyToProgramFiles, CopyToImages
CEShortcuts = Shortcuts   
 
[DestinationDirs]                   ; default destination directories for each operation section
CopyToProgramFiles = 0, %InstallDir%
CopyToImages = 0, %InstallDir%\Images
Shortcuts   = 0, %CE11%             ; \Windows\Start Menu\Programs
 
[CopyToProgramFiles]                ; copy operation file list
"ShowImageList.exe", ShowImageList.exe

[CopyToImages]                      ; copy operation file list
"Open.bmp", Open.bmp
"Save.bmp", Save.bmp
"Info.bmp", Info.bmp
"Delphi2006.bmp", Delphi2006.bmp
"NETCF.bmp", NETCF.bmp
 
[Shortcuts]                         ; Shortcut created in destination dir, %CE11%
%AppName%,0,ShowImageList.exe

    Conclusion

Exploring the Delphi .NET CF compiler and all Pocket PC features is quite exciting. Delphi 2006 is very stable, quite fast and I like the new IDE features (change bars, live templates, history, ...). Jeremy North and Chee Wee Chua did a great job by creating some nice plugins and tools. And especially the Oosterkamp Class Helpers written by Jeroen Pluimers are very helpful. By using them you almost do not have to modify any code generated by the .NET WinForms designers. Also don't forget to change the Library Path so all Code Insight features can help you. If you have to write small Pocket PC applications, do not hesitate any longer and use Delphi 2006 together with these plugins !

I’m already looking forward to a future Delphi release (codename Highlander) with .NET CF designers. According to the Delphi roadmap, the .NET CF designers will be based on the VCL, not on WinForms. I'm sure the VCL controls will be even more powerful then the standard .NET CF controls. The future for Delphi is looking bright.

    About the author

Stefan Cruysberghs is a Belgian software developer and author of some Delphi components, tools and articles. More can be found at his personal website : http://www.scip.be.

Server Response from: ETNASC04