WinCE Programming Tips

From Free Pascal wiki
Jump to navigationJump to search
WinCE Logo.png

This article applies to WinCE only.

See also: Multiplatform Programming Guide

English (en) русский (ru)

This page is a under construction reference to help in the development for the Windows CE platform, covering common programming topics specific to it.

Other Interfaces

Platform specific Tips

Interface Development Articles

TIPS / FAQ

Application runs on Windows Device Emulator, but not on physical device

When running a compiled application on the Windows Device emulator, it works fine, but running it on the physical device you get the error:

Cannot find 'project1' (or one of its components).

This is usually indicative of missing DLLs on the target device, especially if you have a very simple "Hello World" type of application. In many instances, it is related to the aygshell.dll file, which is not present on many industrial-type devices running a bare Windows CE version - normally devices running "Windows Mobile" versions of Windows CE will not have this problem.

This problem has been reported on Motorolla/Symbol MC9000 and MC1000 barcode scanners running both Windows CE 4.2 and 5.

To resolve, do a search for "aygshells.zip" in a search engine - there are some "Dummy" aygshell.dll files available that can be copied to the device to overcome this problem.

Here is a list of forum topics where people had trouble with aygshell.dll and found a solution:

Get Device ID

Get and ID of your device useful for protect your application. This work only on Windows Mobile 5.0 and Windows CE 5.1 <delphi>

function GetDeviceUniqueID(AppData:LPCWSTR; cbApplictionData:Integer; dwDeviceIDVersion:Integer;

var deviceIDOuput; var pcbDeviceIDOutput:DWORD):Integer; external 'coredll.dll' name 'GetDeviceUniqueID';

function GetDeviceID: string; var

 AppData: array[0..19] of WideChar;
 DeviceID : array[0..19] of Byte;
 Count: DWORD;
 s: string;
 Res, i:Integer;

begin

 //not sure about Unicode
 AppData := Utf8Decode('MY_SIG');//any string you like
 Count := SizeOf(DeviceID);
 FillChar(DeviceID, Count, #0);
 Res := GetDeviceUniqueID(AppData, SizeOF(AppData), 1, DeviceID, Count);
 if Res = 0 then
 begin
   Result := ;
   for i := 0 to Count -1 do
   begin
     if (i > 0) and ((i mod 2) = 0) then
       Result := Result + '-'; //add space make the string wrap in label
     Result := Result + IntToHex(DeviceID[i], 2);
   end;
 end
 else
   Result := ;//error accord

// you can MD5 it with your string // Result := MD5Print(MD5Buffer(DeviceID, Count)); end; </delphi> Reference pages: http://msdn2.microsoft.com/en-us/library/ms893522.aspx http://peterfoot.net/RetrieveIMEIThroughTAPI.aspx http://blogs.msdn.com/jehance/archive/2004/07/12/181067.aspx

Get Device Name

Easy to get it from registry

<delphi> function GetDeviceName: string; var

 aReg:TRegistry;

begin

 aReg := TRegistry.Create(KEY_READ);
 try
   aReg.RootKey := HKEY_LOCAL_MACHINE;
   aReg.OpenKey('Ident', False);
   if aReg.ValueExists('Name') then
     Result := aReg.ReadString('Name')
   else
     Result := 'GUEST';
 finally
   aReg.Free;
 end;

end; </delphi>

Show/Hide SIP Panel

SIP: Software Input Panel button, it is a keyboard come with WinCE for touch screen devices.

<delphi> const

 //some of consts already found in Windows
 SIPF_OFF    =	$00000000;
 SIPF_ON     =	$00000001;
 SIPF_DOCKED =	$00000002;
 SIPF_LOCKED =	$00000004;

function SipShowIM(IPStatus:DWORD):Integer; stdcall; external 'coredll.dll' name 'SipShowIM';

begin

 SipShowIM(SIPF_ON)

end;

</delphi>

Microsoft documentation for the SipShowIM routine: [1]

Wakeup Device/ Power On

If you like to make alarm application this function make your device power on, you need also make some sounds with it.

<delphi> function SetSystemPowerState(psState: PWideChar; StateFlags: DWORD; Options : DWORD):DWORD;

stdcall; external 'coredll.dll' name 'SetSystemPowerState';


 SetSystemPowerState(nil, POWER_STATE_ON, POWER_FORCE);
 Application.BringToFront;
 ShowWindow(Handle, SW_SHOW);

</delphi>

LED / Vibrator

You can turn on/off then LED/vibrator in, your device, it worked for me but not as like as i want, may be it need some improvements.

<delphi> const

 NLED_COUNT_INFO_ID	= 0;
 NLED_SUPPORTS_INFO_ID	= 1;
 NLED_SETTINGS_INFO_ID	= 2;

type

 TNLED_COUNT_INFO = record
   cLeds: DWORD;
 end;
 
 TNLED_SETTINGS_INFO = record
   LedNum: DWORD;                 // LED number, 0 is first LED
   OffOnBlink: Integer;           // 0 = off, 1 = on, 2 = blink
   TotalCycleTime: DWORD;         // total cycle time of a blink in microseconds
   OnTime: DWORD;                 // on time of a cycle in microseconds
   OffTime: DWORD;                // off time of a cycle in microseconds
   MetaCycleOn: Integer;          // number of on blink cycles
   MetaCycleOff: Integer;         // number of off blink cycles
  end;
 function NLedGetDeviceInfo(nID:Integer; var pOutput): WordBool;
  stdcall; external 'coredll.dll' name 'NLedGetDeviceInfo';
 function NLedSetDevice(nID: Integer; var pOutput): WordBool;
  stdcall; external 'coredll.dll' name 'NLedSetDevice';

</delphi>

Examples

<delphi> function TForm1.MakeLEDOn; var

 Countnfo: TNLED_COUNT_INFO;
 Info:TNLED_SETTINGS_INFO;

begin

 NLedGetDeviceInfo(NLED_COUNT_INFO_ID, Countnfo);
 //with Countnfo.cLeds you can check if your device support LEDs;
 Info.LedNum := 0; //<--- First LED
 Info.OffOnBlink := 1;
 Info.OffTime := 0;
 Info.MetaCycleOff:= 50;
 Info.MetaCycleOn:= 50;
 Info.TotalCycleTime := 100;
 NLedSetDevice(NLED_SETTINGS_INFO_ID, Info);

end;

procedure TForm1.MakeLedOff; var

 Info:TNLED_SETTINGS_INFO;

begin

 Info.LedNum := 0;
 Info.OffOnBlink := 0;
 NLedSetDevice(NLED_SETTINGS_INFO_ID, Info);

end;

</delphi>


Vibrator it is the last LED in your device, if you can write some music you can now make your phone dance. <delphi> function TForm1.MakeVibratorOn; var

 Countnfo: TNLED_COUNT_INFO;
 Info:TNLED_SETTINGS_INFO;

begin

 NLedGetDeviceInfo(NLED_COUNT_INFO_ID, Countnfo);
 Info.LedNum := Countnfo.cLeds -1;
 Info.OffOnBlink := 1;
 NLedSetDevice(NLED_SETTINGS_INFO_ID, Info);

end;

function TForm1.MakeVibratorOff; var

 Countnfo: TNLED_COUNT_INFO;
 Info:TNLED_SETTINGS_INFO;

begin

 NLedGetDeviceInfo(NLED_COUNT_INFO_ID, Countnfo);
 Info.LedNum := Countnfo.cLeds -1;
 Info.OffOnBlink := 0;
 NLedSetDevice(NLED_SETTINGS_INFO_ID, Info);

end; </delphi>

Getting Battery Status

For more information : MSDN GetSystemPowerStatusEx

<delphi> //by Philip Heinisch

type
 TBAT_INFO = record
 ACLineStatus:byte; //0=Offline, 1=Online, 2=Backup Power,3= Unknown status 
 BatteryFlag:byte; //0=High, 1=Low, 2=Critical, 3=Charging, 4=No Battery, 5=Unknown
 BatteryLifePercent:byte; //0..100 Battery Life in Percent
 Reserved1:byte; //always 0
 BatteryLifeTime: DWORD; //remaining time in seconds
 BatteryFullLifeTime: DWORD; //max usage time in seconds
 Reserved2:byte; //always 0
 BackupBatteryFlag:byte; //0=High, 1=Low, 2=Critical, 3=Charging, 4=No Battery, 5=Unknown
 BackupBatteryLifePercent:byte; //0..100 Backup Battery Life in Percent
 Reserved3:byte; //always 0
 BackupBatteryLifeTime: DWORD; //remaining time in seconds
 BackupBatteryFullLifeTime: DWORD; //max usage time in seconds
end;        

 function GetSystemPowerStatusEx(var pOutput;fUpdate:boolean ): WordBool;
  stdcall; external 'coredll.dll' name 'GetSystemPowerStatusEx'; 

//Usage Example:

function getbat : byte; var

 batinfo: TBAT_INFO;

begin if GetSystemPowerStatusEx(batinfo,True) then getbat:=batinfo.BatteryLifePercent else getbat:=255; //255=Function Call Failed end; </delphi>

Making a Fullscreen Application

Make sure that you have included the windows unit to your program (uses windows;) In the INTERFACE section of your program paste the following code: <delphi> const

 //ShFullScreen
     SHFS_SHOWTASKBAR   = $01;
     SHFS_HIDETASKBAR   = $02;
     SHFS_SHOWSIPBUTTON = $04;
     SHFS_HIDESIPBUTTON = $08;
     SHFS_SHOWSTARTICON = $10;
     SHFS_HIDESTARTICON = $20;  
 function SHFullScreen(hwndRequester: hWnd; dwState: DWord): WINBOOL;
  stdcall; external 'aygshell.dll' name 'SHFullScreen'; 

</delphi> Then in your Forms, OnCreate or OnShow (better on the OnShow) event add the following code: <delphi> procedure TForm1.FormCreate(Sender: TObject); var

 Rect:TRect;
 hTaskBar:THandle;
 menuh:Integer;

begin

 hTaskBar := FindWindow('HHTaskBar',);
 GetWindowRect(hTaskBar,rect);
 menuh:=Rect.Bottom-Rect.Top;
 GetWindowRect(Form1.Handle,Rect);
 SHFullScreen(Form1.Handle,SHFS_HIDETASKBAR or SHFS_HIDESTARTICON or SHFS_HIDESIPBUTTON);
 movewindow(Form1.Handle,Rect.Left,Rect.Top-menuh,Rect.Right,Rect.Bottom+menuh,True);

end; </delphi>


Prevent Phone from Entering in Standby Mode

Inside a timer event add the following line of code: <delphi> keybd_event(VK_F24, 0, KEYEVENTF_KEYUP or KEYEVENTF_SILENT, 0); </delphi>


Installation of an app build with Lazarus on a WinCE device

//Original de Hinnack (from Lazarus forum) Hi,

it took me a long time to figure out, how to install my app on a wince device (yes, I know I can do copy and paste...), so I thought I should share it here: 1) one needs a inf file (maybe Lazarus could generate one on his own some time...). A good brief on how to create one can be found here: http://web.archive.org/web/20080205125046/http://www.sundialsoft.freeserve.co.uk/cabinfo.htm (original website is dead) I used the following (only for ARM prozessors):

<delphi> [Version] ; Required section Signature = "$Windows NT$" Provider = "bilettiX" CESignature = "$Windows CE$"

[CEDevice.ARM] ProcessorType = 2577 ; processor value for ARM

[DefaultInstall.ARM] CopyFiles = Files.ARM

[Files.ARM] bilettixscan.exe,,,0 sqlite3.dll,,,0

[DefaultInstall] ; Required section CEShortcuts = Shortcuts.All AddReg = RegData

[SourceDisksNames] ; Required section 1 = ,"common files",,C:\Dokumente und Einstellungen\xyz\Dev;A existing folder on your HD, where the common files to be copied are found (not processor specific)

[SourceDisksNames.ARM] 2 = ,"ARM files",,arm;folder below the common files folder above for ARM specific files

[SourceDisksFiles] ; Required section, application binary, files to copy bilettixscan.EXE = 2 sqlite3.DLL = 2 bilettix.db = 2

[DestinationDirs] ; Required section Shortcuts.All = 0,%CE11% DefaultDestDir = 0,%InstallDir%

[CEStrings] ; Required section AppName = Ticket Validator InstallDir = %CE1%\%AppName%

[Shortcuts.All] %AppName%,0,bilettixscan.exe

[RegData] HKLM,Software\bilettix\%AppName%,MajorVersion,0x00010001,1 HKLM,Software\bilettix\%AppName%,MinorVersion,0x00010001,0 HKLM,Software\bilettix\%AppName%,Installpath,0x00000000,%InstallDir% </delphi>

2) now create a cab file out of your files using this inf file by using cabwiz.exe from the platform SDK of MS 3) generate a install.ini - my looks like this (icon-section is nor really needed...): <delphi> [CEAppManager] Version = 1.0 Component = Ticket Validator

[Ticket Validator] Description = bilettiX Ticket Validator Uninstall = bilettiXscan DeviceFile = bilettiXscan.exe IconFile = bilettix.ico IconIndex = 0 CabFiles = bilettiXscan.ARM.CAB </delphi> 4) download EzSetup for free from here: http://www.spbsoftwarehouse.com/products/ezsetup/index.html

5) generate the installer using the ini-file, a readme and a eula file you generated as txt files

6) done :-)

be sure to have ActiveSync installed. double-click the installer and have fun :-)

--Blueicaro 21:49, 29 April 2009 (CEST)

Debugging with a log file

Windows CE doesn't ship with a command line, so people used to command-line log debugging may have trouble. In some versions of Windows CE it is possible to install a command-line, but another solution for this is using the logging routines from the LCLProc unit in Lazarus to write log information to a file in the same directory as the executable and then read it, as in the code bellow.

<delphi> uses LCLProc;

DbgAppendToFile(ExtractFilePath(ParamStr(0)) + '1.log', 'Some text'); </delphi>

The unit LCLProc also contains other cool routines for debugging, like GetStackTrace, which returns a string with the stack trace.

Database Tips

Pre-compiled sqlite dll

You can find sqlite3.dll pre-compiled for Windows CE here.

Links

Here are some links that might be useful for creating Windows CE interfaces.

Windows CE Development Notes

WinCE port of KOL GUI library