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
private bool hookWindow()
{
IntPtr hWndTaskbar = FindWindow("HHTaskbar", IntPtr.Zero);
if (hWndTaskbar == IntPtr.Zero)
return false;
EnableWindow(hWndTaskbar, true);
if (oldWndProc == IntPtr.Zero)
{
IntPtr hwndMenu_Worker = FindWindow("menu_worker", IntPtr.Zero);
if (hwndMenu_Worker != IntPtr.Zero)
{
IntPtr hwndToolbar = GetWindow(hwndMenu_Worker, GetWindow_Cmd.GW_CHILD);
if (hwndToolbar != IntPtr.Zero)
{
_mHwnd = hwndToolbar;
SubclassHWnd(hwndToolbar);
}
}
}
return true;
}
Subclassing
Now, as we have the window handle, the subclassing can be started:
private void SubclassHWnd(IntPtr hWnd)
{
newWndProc = new Win32WndProc(MyWndProc);
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
private IntPtr MyWndProc(IntPtr hWnd, int msg, int wParam, int lParam)
{
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);
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);
int buttonWidth = (isQVGA | isVGA) ? 92 : 46;
int buttonHeight = height;
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);
if(this._mIsStartButtonDisabled && rectStartButton.Contains(x, y))
return IntPtr.Zero;
if (this._mIsCloseButtonDisabled && rectCloseButton.Contains(x, y))
return IntPtr.Zero;
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. Don't forget to Dispose
the hwndutils
object before you exit your app.
Downloads
Visual Studio 2008 solution with demo project targeting Windows Mobile 6 SDK:
Thanks to redwolf2222 for the great idea.