WinCE Programming Tips
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
- Lazarus known issues (things that will never be fixed) - A list of interface compatibility issues
- Win32/64 Interface - The Windows API (formerly Win32 API) interface for Windows 95/98/Me/2000/XP/Vista/10, but not CE
- Windows CE Interface - For Pocket PC and Smartphones
- Carbon Interface - The Carbon 32 bit interface for macOS (deprecated; removed from macOS 10.15)
- Cocoa Interface - The Cocoa 64 bit interface for macOS
- Qt Interface - The Qt4 interface for Unixes, macOS, Windows, and Linux-based PDAs
- Qt5 Interface - The Qt5 interface for Unixes, macOS, Windows, and Linux-based PDAs
- GTK1 Interface - The gtk1 interface for Unixes, macOS (X11), Windows
- GTK2 Interface - The gtk2 interface for Unixes, macOS (X11), Windows
- GTK3 Interface - The gtk3 interface for Unixes, macOS (X11), Windows
- fpGUI Interface - Based on the fpGUI library, which is a cross-platform toolkit completely written in Object Pascal
- Custom Drawn Interface - A cross-platform LCL backend written completely in Object Pascal inside Lazarus. The Lazarus interface to Android.
Platform specific Tips
- Android Programming - For Android smartphones and tablets
- iPhone/iPod development - About using Objective Pascal to develop iOS applications
- FreeBSD Programming Tips - FreeBSD programming tips
- Linux Programming Tips - How to execute particular programming tasks in Linux
- macOS Programming Tips - Lazarus tips, useful tools, Unix commands, and more...
- WinCE Programming Tips - Using the telephone API, sending SMSes, and more...
- Windows Programming Tips - Desktop Windows programming tips
Interface Development Articles
- Carbon interface internals - If you want to help improving the Carbon interface
- Windows CE Development Notes - For Pocket PC and Smartphones
- Adding a new interface - How to add a new widget set interface
- LCL Defines - Choosing the right options to recompile LCL
- LCL Internals - Some info about the inner workings of the LCL
- Cocoa Internals - Some info about the inner workings of the Cocoa widgetset
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:
- http://forum.lazarus.freepascal.org/index.php/topic,7161.0.html
- http://forum.lazarus.freepascal.org/index.php/topic,5524.0.html
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.