Mobile Development: Disable Windows Mobile 6.5 Start and Close Button

Hello

here is one other way to write a kios mode .NET application using a technique called SubClassing. The idea was born by a comment of redwolf2222 on this blog about how to Hide Start and Close buttons on Windows Mobile 6.5 devices. Redwolf2222 also provided a code snippet. Unfortunately it was incomplete and so I wrote my own class.

Disable clicks on Start and Close button

The demo project shows one dialog with two check boxes and you can easily test the function. If “StartButton Disabled” or “Close Button disabled” is checked, you cannot ‘click’ the corresponding button any more:

You still ‘click’ the buttons but the subclassed window will not ‘execute’ your click. The buttons are part of the toolbar32 window which is a child of the menu_worker window. So first we have to follow the window tree.

Find the right window

/// <summary>
/// SubClassing: Install the wndproc hook
/// </summary>
/// <returns></returns>
private bool hookWindow()
{
    //find taskbar
    IntPtr hWndTaskbar = FindWindow("HHTaskbar", IntPtr.Zero);
    if (hWndTaskbar == IntPtr.Zero)
        return false;
    //enable the taskbar, not realy necessary
    EnableWindow(hWndTaskbar, true);
    //already installed?
    if (oldWndProc == IntPtr.Zero)
    {
        //find the menu_worker window
        IntPtr hwndMenu_Worker = FindWindow("menu_worker", IntPtr.Zero);
        if (hwndMenu_Worker != IntPtr.Zero)
        {
            //get the child window which has the buttons on it
            IntPtr hwndToolbar = GetWindow(hwndMenu_Worker, GetWindow_Cmd.GW_CHILD);
            if (hwndToolbar != IntPtr.Zero)
            {
                _mHwnd = hwndToolbar;       //store to remember
                SubclassHWnd(hwndToolbar);  //subclass the wndproc
            }
        }
    }
    return true;
}

Subclassing

Now, as we have the window handle, the subclassing can be started:

private void SubclassHWnd(IntPtr hWnd)
{
    // hWnd is the window you want to subclass..., create a new
    // delegate for the new wndproc
    newWndProc = new Win32WndProc(MyWndProc);
    // subclass
    oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, newWndProc);
}

The installation of the ‘hook’ is very simple. Just use SetWindowLong with the new window procedure. The old, original window procedure is saved for later use. We need it for example to call it for clicks outside the buttons and for all messages we don’t care about.

The ‘hook’ or better the redirection will remain active until you install the old window procedure. So your device’s start and close button will not ‘work’ as long as the hook is in place.

The new window procedure

// this is the new wndproc, just show a messagebox on left button down:
private IntPtr MyWndProc(IntPtr hWnd, int msg, int wParam, int lParam)
{
    //is this a message for us?
    if (((msg == (int)WM_LBUTTONDOWN) || (msg == (int)WM_LBUTTONUP)) && (this._mIsStartButtonDisabled || this._mIsCloseButtonDisabled) )
    {
        int x = ((int)lParam) & 0xFFFF;
        int y = ((int)lParam) >> 16;

        bool isVGA;
        bool isQVGA;
        using (System.Windows.Forms.Control detector = new System.Windows.Forms.Control())
        {
            using (System.Drawing.Graphics gr = detector.CreateGraphics())
            {
                isVGA = gr.DpiY == 192;
                isQVGA = gr.DpiY == 96;
            }
        }

        RECT rect;
        GetWindowRect(hWnd, out rect); //get the rectangle of the menu_bar

        int width = Math.Max(rect.Left, rect.Right) - Math.Min(rect.Left, rect.Right);
        int height = Math.Max(rect.Bottom, rect.Top) - Math.Min(rect.Bottom, rect.Top);

        //width values are assumed
        int buttonWidth = (isQVGA | isVGA) ? 92 : 46;
        int buttonHeight = height; //(isQVGA | isVGA) ? 72 : 36;

        System.Drawing.Rectangle rectStartButton = new System.Drawing.Rectangle(0, 0, buttonWidth, buttonHeight);
        System.Drawing.Rectangle rectCloseButton = new System.Drawing.Rectangle(width - buttonWidth, 0, buttonWidth, buttonHeight);

        //check if enabled and click is inside the start or close button rectangle
        if(this._mIsStartButtonDisabled && rectStartButton.Contains(x, y))
            return IntPtr.Zero;
        if (this._mIsCloseButtonDisabled && rectCloseButton.Contains(x, y))
            return IntPtr.Zero;

        //if both are false, we have to provide the click to windows
        return CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam);
    }
    else
        return CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam);
}

Subclassing the window means that we redirect the window message procedure of the found window to our own, custom window procedure. This new procedure checks for WM_LBUTTONDOWN and WM_LBUTTONUP messages. Then the click position is checked and discarded if within the rectangle area of the Start and/or Close button. If the position is outside the calculated rectangles, the original window procedure is called.

The demo code

public partial class StartButtonControl : Form
{
    StartButtonWM65.hwndutils _hwndutils = new StartButtonWM65.hwndutils();
    private bool _bInitializing = true;
    public StartButtonControl()
    {
        InitializeComponent();
        this.chkDisableStartButton.Checked = this._hwndutils.StartButtonDisabled;
        _bInitializing = false;
    }

    private void chkDisableStartButton_CheckStateChanged(object sender, EventArgs e)
    {
        if (_bInitializing)
            return;
        this._hwndutils.StartButtonDisabled = chkDisableStartButton.Checked;
    }

    private void mnuExit_Click(object sender, EventArgs e)
    {
        _hwndutils.Dispose();
        Application.Exit();
    }

    private void StartButtonControl_Closing(object sender, CancelEventArgs e)
    {
        _hwndutils.Dispose();
        Application.Exit();
    }

    private void chkCloseButton_CheckStateChanged(object sender, EventArgs e)
    {
        if (_bInitializing)
            return;
        this._hwndutils.CloseButtonDisabled = chkCloseButton.Checked;
    }
}

As you see, the usage of the class hwndutils is very simple. Dont forget to Dispose the hwndutils object before you exit your app.

Downloads
Visual Studion 2008 solution with demo project targeting Windows Mobile 6 SDK: [Download not found]

Thanks to redwolf2222 for the great idea.

2 Comments

  1. Samran says:

    Is it possible to reduce size of the menu bar?

    I’ve tried to run application that developped for WM6.1 and I founded that the menu bar is overwrite some customer button.

  2. admin says:

    Hello Samran

    there are posts where the taskbar (top line) is ‘hidden’ by moving it outside the screen (SetWindowPos with the handle to HHTaskbar). But I have not yet seen any one tried to resize the taskbar.
    I assume there will be no working solution resizing the menu_worker or toolbar32 window. Resizing would mean you have to rewrite the WM_PAINT message code without really knowing what to draw. Possibly you will get a somehow working result, if you enable textmode menu and hide start and close button.
    I would go another way and write a fullscreen app covering the whole screen (see my fullscreen engine class: http://www.hjgode.de/wp/2010/09/02/mobile-development-yet-another-kiosk-mode-library/). Then you mimic the menu bar if needed with two buttons at the bottom of the form. To mimic the start button function you have to send a keyboard event for VK_LWIN.
    Windows Mobile is not that flexible as you need it.

    regards

    Josef

Leave a Reply