WinCE Programming Tips

From Lazarus-ccr

Jump to: navigation, search

English (en) 한국어 (ko) Русский (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.

Contents

[edit] Other Interfaces

[edit] Platform specific Tips

  • OS X Programming Tips - Lazarus installation, useful tools, Unix commands, and more...
  • WinCE Programming Tips - Using the telephone API, sending SMSes, and more...
  • Windows Programming Tips - Desktop Windows programming tips.

[edit] Interfaces Development Articles

[edit] TIPS / FAQ

[edit] 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).

Possible causes are:

[edit] Missing aygshell.dll

This error 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:

And here a number of DLLs to substitute missing ones and make applications work:

[edit] Wrong architecture

Maybe your device runs a x86 or mips processor instead of the more common ARM.

[edit] Missing functions due to Operating System version

Possibly your operating system is very old and doesn't have some functions required by Lazarus.

[edit] 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

 
 
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;
 

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

[edit] Get Device Name

Easy to get it from registry

 
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;
 

[edit] Show/Hide SIP Panel

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

 
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;
 
 

Microsoft documentation for the SipShowIM routine: [1]

[edit] 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.

 
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);
 

[edit] 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.

 
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';
 

Examples

 
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;  
 
 


Vibrator it is the last LED in your device, if you can write some music you can now make your phone dance.

 
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;  
 

[edit] Getting Battery Status

For more information : MSDN GetSystemPowerStatusEx

 
//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;
 

[edit] 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:

 
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'; 
 

Then in your Forms, OnCreate or OnShow (better on the OnShow) event add the following code:

 
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;              
 


[edit] Prevent Phone from Entering in Standby Mode

Inside a timer event add the following line of code:

 
keybd_event(VK_F24, 0, KEYEVENTF_KEYUP or KEYEVENTF_SILENT, 0);
 


[edit] 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):

 
[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%
 

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...):

 
[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
 

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)

[edit] 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.

 
uses LCLProc;
 
DbgAppendToFile(ExtractFilePath(ParamStr(0)) + '1.log', 'Some text');
 

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

[edit] Current Directory and Placement of DLLs

Windows CE does not have the concept of current directory, so there is no point in placing DLLs in the same folder of the executable, as they will not be loaded from there. They need to be on the \Windows directory or another suitable location.

[edit] Database Tips

[edit] Pre-compiled sqlite dll

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

[edit] Links

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

Windows CE Development Notes

WinCE port of KOL GUI library