Customizing a UI Component

In this blog post I will show you how you can customize all Windows CE dialogs without the need to change ANY file in the PUBLIC tree. As you no doubt know changing any code in the Windows CE PUBLIC (& PRIVATE) tree is a bad idea. It is a bad idea because it is very hard to maintain, your changes may be overwritten at any time by a Windows CE Update and worst of all your changes affect ALL OS Designs (not just the OS Design you need the changes for). Besides that you often need to do a "Build & Sysgen" to get your changes into your kernel image and we all know you should NEVER EVER do a "Build & Sysgen".

However, MSDN is still full of articles with instructions telling you to change code in the PUBLIC tree. An example of this is the "Customizing a UI Component" article in MSDN. It talks about modifying files in %_WINCEROOT%\Public\Common\Oak\Drivers and changing \WINCE600\PUBLIC\CEBASE\OAK\MISC\Winceos.bat... Bad, bad, bad!

Time for an update on this article:

Customizing a UI Component

To suit the requirements of your target device, Windows CE allows you to customize and replace certain user interface (UI) components. Windows CE provides these UI components in the form of libraries that you can either use as supplied or replace with your own custom UI components. The following table shows these UI components.

Component Description
Oomui Controls the appearance and behavior of your target device in out of memory conditions.
Startui Controls the appearance and behavior of system startup windows.
Calibrui Controls the appearance and behavior of your target device during calibration of the touch screen.

For detailed instructions on how to clone Calibrui please see Cloning CalibrUi in Windows CE 6.0. This article deals with STARTUI and OOMUI

To customize a UI component, we first need to clone the component into our OS Design and change it so that the build system uses our cloned binaries when linking GWES. We'll take STARTUI as an example for this exercise:

  1. Copy \WINCE600\PUBLIC\COMMON\OAK\DRIVERS\STARTUI to \WINCE600\OSDesigns\YourOSDesign\YourOSDesign\STARTUI
  2. Open the 'sources.' file in \WINCE600\OSDesigns\YourOSDesign\YourOSDesign\STARTUI and add the following to the top of the file (right under the !endif):
  3. _OEMINCPATH=$(_WINCEROOT)\public\common\ddk\inc;$(_WINCEROOT)\public\common\oak\inc;$(_WINCEROOT)\public\common\sdk\inc;
    __PROJROOT=$(_PROJECTROOT)
    _COMMONPUBROOT=$(_WINCEROOT)\public\common

    WINCEOEM=1
    PBP_PRESYSGEN=1

  4. Append '_clone' to the TARGETNAME:
  5. TARGETNAME=startui_clone

  6. Add RELEASETYPE and TARGET_PDB_NAME under TARGETTYPE=LIBRARY:
  7. RELEASETYPE=OAK
    TARGET_PDB_NAME=$(_RELEASELIBDIR)\$(TARGETNAME).pdb

  8. Remove the INCLUDES. In this case nothing is included from ..\..\inc but if it would be we would have to include the correct absolute path to the required include files.
  9. Remove any #xref lines (not used anymore since CE 2.12)
  10. The final sources file should look something like this:

    _OEMINCPATH=$(_WINCEROOT)\public\common\ddk\inc;$(_WINCEROOT)\public\common\oak\inc;$(_WINCEROOT)\public\common\sdk\inc;
    __PROJROOT=$(_PROJECTROOT)
    _COMMONPUBROOT=$(_WINCEROOT)\public\common

    WINCEOEM=1
    PBP_PRESYSGEN=1

    TARGETNAME=startui_clone
    TARGETTYPE=LIBRARY
    RELEASETYPE=OAK
    TARGET_PDB_NAME=$(_RELEASELIBDIR)\$(TARGETNAME).pdb

    MODULES=gwes
    CDEFINES= $(CDEFINES) -D__USERDECL_H__ -D__PRIVDECL_H__ -DGWE

    !IFDEF MEM_ACCOUNT
    CDEFINES=$(CDEFINES) -DMEM_ACCOUNT=1
    !ENDIF

    WINCETARGETFILES= \
      $(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\$(TARGETNAME).res \
     
    SOURCES=startui.cpp                

  11. Now add this project to your OS Design's subprojects
  12. by right clicking the "SubProjects" node in the Solution Explorer and choosing "Add existing subproject...". Then browse to the STARTUI folder in your OS Design folder and be sure to select "Sources/Dirs Files (sources;dirs)" in the "Files of type" drop down list to be able to select the STARTUI sources. file.

    This action creates several files and adds several lines to our sources file that we do not need...

  13. From the Solution Explorer select all the files in the "Parameter Files" node (ProjSysgen.bat, STARTUI.bib, .dat, .db & .reg) of the STARTUI subproject, right click and "Remove and delete" them.
  14. If you want you can add "startui.rc" and "windowsce.bmp" to the "Resource Files" node in the subproject (but it is not necessary for building).
  15. Now we need to tell Platform Builder that this component needs to built before we sysgen the common tree so GWES can link our binaries instead of the default ones. We do this by editing the STARTUI.pbpxml file. Make sure the STARTUI.pbpxml file is as follows:
    <?xml version="1.0"?>
    <PBProject DisplayName="STARTUI (clone)" SysgenDeptree="common" SysgenModule="gwe2" xmlns="urn:PBProject-schema" />

    I added "(clone)" to the display name just to make clear this is a cloned component; it is not necessary for building. SysgenDeptree tells Platfom Builder this needs to be built before sysgenning the common dependency tree and SysgenModule specifies the module this component needs to be linked with.
  16. Since we named our project STARTUI_clone we need to rename the startui.rc file to startui_clone.rc (in preparation for the step below).
  17. The final step involves building the resources for all possible locales. The script used was taken from the Calibrui clone operation described in Cloning CalibrUi in Windows CE 6.0. The only thing added is setting the INCLUDE environment variable so that windows.h can be found by the resource compiler (startui_clone.rc needs windows.h). Copy the following into the prelink.bat file:
    @echo off
    @REM This is called before build to create all the RES files needed for all locales
    @REM WARNING: Do not change the names of any files because SYSGENMAKE.EXE assumes their location for cloning
    (IF NOT EXIST obj mkdir obj || goto EXIT_ERROR
    (IF NOT EXIST obj\%_TGTCPU% mkdir obj\%_TGTCPU%) || goto EXIT_ERROR
    (IF NOT EXIST obj\%_TGTCPU%\%WINCEDEBUG% mkdir obj\%_TGTCPU%\%WINCEDEBUG%) || goto EXIT_ERROR

    dir /AD /B %_PUBLICROOT%\COMMON\OAK\LIB\%_TGTCPU%\%WINCEDEBUG% > obj\%_TGTCPU%\%WINCEDEBUG%\clone_locales.txt

    set INCLUDE=%_WINCEROOT%\public\common\sdk\inc;
    set _PRELINK_LOCALE=%LOCALE%
    for /f %%L in (obj\%_TGTCPU%\%WINCEDEBUG%\clone_locales.txt) do call :COMPILE_RC %%L
    set LOCALE=%_PRELINK_LOCALE%
    goto :END_CLONING

    :COMPILE_RC
    (set LOCALE=%1 && nmake startui_clone.res) || goto EXIT_ERROR
    (IF NOT EXIST obj\%_TGTCPU%\%WINCEDEBUG%\%1 mkdir obj\%_TGTCPU%\%WINCEDEBUG%\%1) || goto EXIT_ERROR
    move /Y startui_clone.res obj\%_TGTCPU%\%WINCEDEBUG%\%1\startui_clone.res || goto EXIT_ERROR
    goto EOF

    :EXIT_ERROR
    exit /b -1

    :END_CLONING
    @REM Place any additional steps after END_CLONING but before EOF
    :EOF

That's it! You can now modify the STARTUI sources and build (sysgen) your OS Design to see your changes included in GWES, all without modifying one single file in the PUBLIC tree. The same instructions can be used to clone and modify OOMUI (the "out of memory" dialogs).

Good luck!