Click here to Skip to main content
11,798,573 members (46,838 online)
Click here to Skip to main content

Changing the color of PPC controls

, 3 Mar 2006 CPOL 45.2K 575 29
Rate this:
Please Sign up or sign in to vote.
How to change the color of Windows CE components without modifying your program.

Sample Image - PPCColorConfig.jpg


This article intends to show you how to change the color of a Windows CE device's components in a simple way. What would you do if, for example, you want to display a red background for your button? Most likely, you will have to catch the WM_PAINT message and do all the work by yourself. And what about changing the text color displayed in the title bar? You'll have to redraw the non-client area.

Furthermore, let's say that your customer wants you to create themes in your system. That is, for the theme X, I want a pink background for the windows, a red background for the buttons, white color for the static text, and a light blue background for the menus. And that is not all: the next month, the customer will want another theme.

In fact, the last paragraph described my situation not so long ago. What to do? Rewriting the application, catching the WM_PAINT, and doing the redraw for each control was not an option. Fortunately, after some research, I found a solution. And after finding it, I decided to create an application that allows me to change the system's color easily.

The purpose of this article is, thus, to show how to change the system's colors, and also to show how this application works and how to use it.


Windows CE has many configurable stuff. The one that we are interested in, the system colors, is located in the registry, in the following path:


This registry key is a 116-byte binary buffer. Each system color uses four bytes: one byte for red color, one for green, and one for blue; the fourth is always 0, although it is believed that for further versions of Windows CE, this byte will be used as the alpha color.

Therefore, there are twenty nine system components whose color can be changed (116/4 = 29). The following is a list of those components.

Registry Order System color Description
0 COLOR_SCROLLBAR Color of the gray area of a scroll bar.
1 COLOR_BACKGROUND Background color of the desktop window.
2 COLOR_ACTIVECAPTION Color of the title bar of an active window.
3 COLOR_INACTIVECAPTION Color of the title bar of an inactive window.
4 COLOR_MENU Background color of a menu.
5 COLOR_WINDOW Background color of a window.
6 COLOR_WINDOWFRAME Color of a window frame.
7 COLOR_MENUTEXT Color of the text in a menu.
8 COLOR_WINDOWTEXT Color of the text in a window.
9 COLOR_CAPTIONTEXT Color of the text in a title bar and of the size box and scroll bar arrow box.
10 COLOR_ACTIVEBORDER Color of the border of an active window.
11 COLOR_INACTIVEBORDER Color of the border of an inactive window.
12 COLOR_APPWORKSPACE Background color of multiple document interface (MDI) applications.
13 COLOR_HIGHLIGHT Color of an item selected in a control.
14 COLOR_HIGHLIGHTTEXT Color of the text of an item selected in a control.
15 COLOR_BTNFACE Color of the face of a button.
16 COLOR_BTNSHADOW Shadow color of buttons for edges that face away from the light source.
17 COLOR_GRAYTEXT Color of shaded text. This color is set to 0 if the current display driver does not support a solid gray color.
18 COLOR_BTNTEXT Color of the text for push buttons.
19 COLOR_INACTIVECAPTIONTEXT Color of the text in the title bar of an inactive window.
20 COLOR_BTNHIGHLIGHT Highlight color of buttons for edges that face the light source.
21 COLOR_3DDKSHADOW Color of the dark shadow for three-dimensional display elements.
22 COLOR_3DLIGHT Highlight color of three-dimensional display elements for edges that face the light source.
23 COLOR_INFOTEXT Color of the text for ToolTip controls.
24 COLOR_INFOBK Background color for ToolTip controls.
25 COLOR_STATIC Background color for static controls and dialog boxes. Supported in Windows CE 2.0 and later.
26 COLOR_STATICTEXT Color of the text for static controls. Supported in Windows CE 2.0 and later.
27 COLOR_GRADIENTACTIVECAPTION Color of the title bar of an active window that is filled with a color gradient.
28 COLOR_GRADIENTINACTIVECAPTION Color of the title bar of an inactive window that is filled with a color gradient.

So, for locating the bytes you have to change, simple multiply the registry order times four. For example, for modifying the background color of a menu to red, you'll have to change the 16th byte to FF, the 17th to 00, and the 18th to 00 as well. Update the registry and perform a soft reset. Note: A soft reset (also called a warm boot) is needed, since these properties are read only when the system boots.

As you can see, the principle is somewhat simple. So, what a program must do is simply take the buffer, modify it, update the registry, and soft reset the device. Or you can do, as I did, modify the registry and create a CAB file that updates the registry. Either way, you need a way to modify the registry, so I made a little application that runs on the mobile device and updates the registry.

PPC Color Configurator

This is the application I made; you will find the source code at the beginning of this article. I tested it under Windows CE .NET (4.2), for Symbol MC50, an iPaq, and an Intermec 730 machines.

The application is dialog-based. Here is the declaration of the class for the main dialog:

class CPPCColorConfigDlg : public CDialog
        CPPCColorConfigDlg(CWnd* pParent = NULL);
        // standard constructor


            HICON m_hIcon;

        virtual void DoDataExchange(CDataExchange* pDX);
        // DDX/DDV support

        virtual BOOL OnInitDialog();
        virtual void OnSelectOption();
        virtual void OnColorChange();
        virtual void OnPaint();
        virtual void OnUpdateSettings();

        void FillColorOptions();
        void InitVectors();
        void DisplayDescription();
        void UpdateRegistry();
        void ResetRegistry();
        void ParseItemColors(int* pRed, int* pGreen, int* pBlue);
        void SetColorsInBoxes(int iRed, int iGreen, int iBlue);

        int m_iSelected;
        int m_iRed;
        int m_iGreen;
        int m_iBlue;
        BYTE m_pNewBuffer<BUFFER_SIZE>;
        BYTE m_pOldBuffer<BUFFER_SIZE>;


The member variable m_iSelected holds the index of the selected attribute (i.e., menu background color) and is changed within the OnSelectOption method. The properties m_iRed, m_iGreen, and m_iBlue hold the values for the red, green, and blue textboxes, and whose value will determine the color of the component. Finally, the properties m_pNewBuffer and m_pOldBuffer hold the buffer of memory that will be taken from the registry and whose value will be eventually updated to the registry as well. The macro BUFFER_SIZE is defined as 116.

The method InitVectors initializes the memory buffers, taking the values from the registry. Here is the code:

void CPPCColorConfigDlg::InitVectors()
    HKEY hKey;
    DWORD dwType;
    DWORD dwSize;    

    dwType = 0;
    dwSize = BUFFER_SIZE;
    memset(m_pNewBuffer, 0, BUFFER_SIZE);
    memset(m_pOldBuffer, 0, BUFFER_SIZE);

    ::RegQueryValueEx(hKey, _T("SysColor"), NULL, &dwType, m_pOldBuffer, &dwSize);
    ::RegQueryValueEx(hKey, _T("SysColor"), NULL, &dwType, m_pNewBuffer, &dwSize);

    ASSERT(!memcmp(m_pOldBuffer, m_pNewBuffer, BUFFER_SIZE));

I created a global variable, g_vtrShowStrings, which is a 2D matrix that holds the strings displayed in the combo box, and its description. The position within the matrix will determine the position within the buffer whose bytes will be modified. The macro OPTION_VECTOR_SIZE is defined as 29.

CString g_vtrShowStrings[OPTION_VECTOR_SIZE][2] =
    { CString("Scrollbar"),    CString("Color of the gray area of a scroll bar") },
    { CString("Background"), CString("Background color of the desktop window") },
    { CString("Active Caption"), CString("Color of the title bar of an active window") },

Just for the records, I present the message map. As you noticed from the picture, there are some controls: a combo box that holds the components of the system that can be updated, four text boxes whose purpose is to allow the user to change the color of the component (the first one is for red, the second is for green, and the third is for the blue color; the fourth is unused, and it will be for the alpha, when WinCE admits that parameter). There is also a button, that -when clicked- will update the registry through the memory buffer. Here is the message map:

    ON_EN_CHANGE(IDC_TXT_RED, OnColorChange)

The next thing is the OnInitDialog. In such a dialog, we do two tasks: initialize the combo box according to the g_vtrShowStrings matrix (the combo box's property "sorted" was unmarked in the dialog resource editor), and select the first item of the combo box. The method looks as follows:

BOOL CPPCColorConfigDlg::OnInitDialog()

    SetIcon(m_hIcon, TRUE);            
    SetIcon(m_hIcon, FALSE);        


    SetDlgItemText(IDC_TXT_OTHER, _T("0"));
    SetDlgItemText(IDC_TXT_RED, _T("0"));
    SetDlgItemText(IDC_TXT_GREEN, _T("0"));
    SetDlgItemText(IDC_TXT_BLUE, _T("0"));

    CComboBox* pColorOptions;
    pColorOptions = reinterpret_cast<CComboBox*&gt(GetDlgItem(IDC_CMB_COMPONENTS));

    return TRUE;  

The method FillColorOptions will fill the combo box. It will iterate over the first dimension of the global matrix. Here's the code:

void CPPCColorConfigDlg::FillColorOptions()
    CComboBox* pColorOptions;

    pColorOptions = reinterpret_cast<CComboBox*>(GetDlgItem(IDC_CMB_COMPONENTS));

    for (int i = 0; i < OPTION_VECTOR_SIZE; i++)

As explained before, the m_iSelected holds the position of the matrix that is going to be changed. This member is updated when the index of the combo box is changed. Here is the code that responds to such an event:

void CPPCColorConfigDlg::OnSelectOption()
    CComboBox* pColorOptions;
    int iRed, iGreen, iBlue;
    pColorOptions = reinterpret_cast<CComboBox*>(GetDlgItem(IDC_CMB_COMPONENTS));
    m_iSelected = pColorOptions->GetCurSel();

    // fill the current text boxes with the current color

    iRed = iGreen = iBlue = 0;

    ParseItemColors(&iRed, &iGreen, &iBlue);
    SetColorsInBoxes(iRed, iGreen, iBlue);


This method does two actions. First, updates the m_iSelected member. Second, it will also update the text boxes' values, according to the values within the buffer. The method ParseItemColors gets the three colors from the memory buffer. The method SetColorsInBoxes updates the textboxes' values. Here is the code:

void CPPCColorConfigDlg::ParseItemColors(int* pRed, int* pGreen, int* pBlue)
    int iPosition;
    iPosition = m_iSelected * 4;

    memcpy(pRed, m_pNewBuffer + iPosition++, 1);
    memcpy(pGreen, m_pNewBuffer + iPosition++, 1);
    memcpy(pBlue, m_pNewBuffer + iPosition++, 1);

void CPPCColorConfigDlg::SetColorsInBoxes(int iRed, int iGreen, int iBlue)
    CString strRed, strGreen, strBlue;

    strRed.Format(_T("%d"), iRed);
    strGreen.Format(_T("%d"), iGreen);
    strBlue.Format(_T("%d"), iBlue);

    SetDlgItemText(IDC_TXT_RED, strRed);
    SetDlgItemText(IDC_TXT_GREEN, strGreen);
    SetDlgItemText(IDC_TXT_BLUE, strBlue);

Notice that in ParseItemColors, we use m_iSelected to determine the position within the memory buffer.

When an item is selected from the combo box, it will also call the DisplayDescription method, which will show a brief description about the system's component. It takes such a value from the global matrix.

void CPPCColorConfigDlg::DisplayDescription()
    CStatic* pDescription;

    pDescription = reinterpret_cast<CStatic*>(GetDlgItem(IDC_LBL_DESCRPT));

When we change the value in one of the three text boxes available, the method OnColorChange will be called. This method gets such values and update the local ones. Also, it performs some validations, so that the value is always between 0 and 255.

void CPPCColorConfigDlg::OnColorChange()
    CEdit* pRed, * pGreen, * pBlue;
    int iRed, iGreen, iBlue;
    CString strRed, strGreen, strBlue;

    pRed = reinterpret_cast<CEdit*>(GetDlgItem(IDC_TXT_RED));
    pGreen = reinterpret_cast<CEdit*>(GetDlgItem(IDC_TXT_GREEN));
    pBlue = reinterpret_cast<CEdit*>(GetDlgItem(IDC_TXT_BLUE));


    iRed = _ttoi(strRed.GetBuffer(0));
    iGreen = _ttoi(strGreen.GetBuffer(0));
    iBlue = _ttoi(strBlue.GetBuffer(0));

    if (iRed < 0 || iRed > 255) 
        MessageBox(_T("Red tone must be between 0 and 255"), 
                   NULL, MB_ICONEXCLAMATION);
    else if (iGreen < 0 || iGreen > 255) 
        MessageBox(_T("Green tone must be between 0 and 255"), 
                   NULL, MB_ICONEXCLAMATION);
    else if (iBlue < 0 || iBlue > 255) 
        MessageBox(_T("Blue tone must be between 0 and 255"), 
                   NULL, MB_ICONEXCLAMATION);
        m_iRed = iRed;
        m_iGreen = iGreen;
        m_iBlue = iBlue;

Notice that in the end, if everything is alright, we call InvalidateRect. This function will invoke the WM_PAINT message, so that the window will be redrawn. We then catch such a message within the OnPaint method, and then we draw a simple rectangle in the window, whose color is the one selected by the user. This will help the user to preview the color that is being selected. The method is simple.

void CPPCColorConfigDlg::OnPaint()

    CClientDC dc(this);
    CBrush brush;

    brush.CreateSolidBrush(RGB(m_iRed, m_iGreen, m_iBlue));
    dc.Rectangle(12, 90, 215, 150);

Finally, when we select the system's component and decide its color, we have to push the "Update settings" button. This will call the OnUpdateSettings method that will update the memory buffer and -eventually- the registry.

void CPPCColorConfigDlg::OnUpdateSettings()
    int iPosition;

    iPosition = m_iSelected * 4;
    memcpy(m_pNewBuffer + iPosition++, &m_iRed, 1);
    memcpy(m_pNewBuffer + iPosition++, &m_iGreen, 1);
    memcpy(m_pNewBuffer + iPosition++, &m_iBlue, 1);

      _T("A soft reset must be made before changes take effect."));

The member UpdateRegistry... guess what? Updates the registry...

void CPPCColorConfigDlg::UpdateRegistry()
    HKEY hKey;
    DWORD dwType;
    DWORD dwSize;

    dwType = 0;
    dwSize = BUFFER_SIZE;

    ::RegQueryValueEx(hKey, _T("SysColor"), NULL, 
                      &dwType, m_pOldBuffer, &dwSize);
    ::RegSetValueEx(hKey, _T("SysColor"), NULL, 
                    dwType, m_pNewBuffer, BUFFER_SIZE);


Well, that's it. You can create all sorts of themes for your Pocket PC. Furthermore, you can include parts of this code in your apps, so you can customize its display, and when the application ends, you could restore the original settings (which are stored in HKEY_LOCAL_MACHINE\System\GWE\DefSysColor). You can even extend this functionality, since you can also change many other keys under HKEY_LOCAL_MACHINE\System\GWE.

Remember that you need to soft reset your device before the changes take effect.

Working with Intermec Devices

If you change the registry to adjust any color as described in the article, the Intermec machine will "reset" your registry to its original value as soon as you soft-reset it. Hence, you will not be able to see the changes. For that, you have two options.

  • Remove the file "registry" under the "\Flash File Store" directory.
  • Once the registry is updated, and before you soft reset, run the RegFlush2.exe program. This program will take a "picture" of your registry and save it under the "\Flash File Store" directory so the next time it boots, it will take that configuration.

For more information, see the 700c WM 2003 how to save the registry article from Intermec's Knowledge Base.


  • [Mar 03, 2006] Main release of the article.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Fernando A. Gomez F.
Architect Praxis
Mexico Mexico
I was born in Mexico City 31 years ago. I lived in Veracruz for seven years, and I'm back to the capital city since 1998.

I'm a mathematics student. Yet I learned programming like ten years ago, and I've been working in several projects for enterprises such as Merck Sharp and Dohme, British American Tobacco, Schering Plough, Bristol Meyers Squibb and Nestle.

Currently I'm designing and developing collaborative software for the private sector, mostly using the SharePoint platform and FAST Search Server. My main employer is Fiat Chrysler Automobile.

My primary language was C++, although since six years ago I turned to develop heavily for .NET and C#. At present, I'm event doing some SharePoint sites.

You may also be interested in...

Comments and Discussions

NewsCE.NET helpfull articles [modified] Pin
codemobile31-Oct-08 19:50
membercodemobile31-Oct-08 19:50 
GeneralThere's a better way to do it. Pin
alnorman6-Mar-06 4:03
memberalnorman6-Mar-06 4:03 
GeneralRe: There's a better way to do it. Pin
Fernando A. Gómez F.6-Mar-06 4:33
memberFernando A. Gómez F.6-Mar-06 4:33 
GeneralRe: There's a better way to do it. Pin
burakb26-Apr-06 5:13
memberburakb26-Apr-06 5:13 
AnswerRe: There's a better way to do it. Pin
JP081528-Apr-08 4:38
memberJP081528-Apr-08 4:38 
GeneralRe: There's a better way to do it. Pin
Fernando A. Gomez F.29-Apr-08 6:58
memberFernando A. Gomez F.29-Apr-08 6:58 
GeneralRe: There's a better way to do it. Pin
twisterat5729-Jan-15 22:35
membertwisterat5729-Jan-15 22:35 
GeneralNice Pin
Est Solarus3-Mar-06 21:04
memberEst Solarus3-Mar-06 21:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.151002.1 | Last Updated 3 Mar 2006
Article Copyright 2006 by Fernando A. Gomez F.
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid