Introduction
Windows Mobile is a great production device. But by default, the device gives access to all functionalities (e-mail, contacts ...) and in particular context, you will probably be interested to restrict end-user access to some of these functionalities.
You'll have two options:
- Integrate in the standard Sheel
- Customize the system in kiosk mode
I'll focus on the second option. In order to set the system in kiosk mode, we'll have to:
- Write an application with a screen which will work like the 'Today' screen
- Control the hardware button to restrict access to Windows Mobile functionalities
- Of course, have the application launch at startup
- This is a non exhaustive list...
Background
I will not focus on Compact Framework development and OS low level library interaction. But keep in mind the .NET Compact Framework Architecture:
- Framework
- Common Language Runtime
- Windows CE
You will then understand why we reference microsoft.windowsce.forms
for low level functionality.
I also use OpenNETCF www.openNETCF.com for reading/writing registry. The library is fully explained on their web-site.
Using the Code
Our first task is to create a new Smart Device project from Visual Studio. As mentioned in the introduction, we'll set properties of the default Form Form1.cs in order to create a 'Today' like screen. You can do it using the properties windows or by code :
public frmKiosk()
{
InitializeComponent();
ControlBox = false;
FormBorderStyle = FormBorderStyle.None;
MaximizeBox = false;
MinimizeBox = false;
WindowState = FormWindowState.Maximized;
}
Now, we'll have to control hardware button. This is done using the Microsoft.WindowsCE.Form.MessageWindows
. This class will allow us to intercept Windows Messages and decide how to handle them (internal routine, raising events to be handled by other class, or ... do nothing). This will allow us to intercept messages sent by hardware button and simply decide to not react to them!
The logic is quite simple:
- Create a class which will inherit from
MessageWindows
. Override the WndProc
method to catch windows message and implement our own business logic. We will intercept only HOTKEY
message, but the same code could be used to handle all windows message types (full list can be found at http:\\www.pinvoke.net) - Unregister the hardware button: by default, message raised by hardware button is handled by the default process
- Register the hardware button: message raised by hardware button will be handled by our custom
MessageWindows
.
The code of our custom MessageWindows
will be like this:
public class internalMessageWindow : MessageWindow
{
public const int WM_HOTKEY = 0x0312;
Form referedForm;
public internalMessageWindow(Form referedForm)
{
this.referedForm = referedForm;
}
protected override void WndProc(ref Message msg)
{
switch (msg.Msg)
{
case WM_HOTKEY:
return;
}
base.WndProc(ref msg);
}
}
We have now to link our form with our custom WindowsMessage
:
FormCode
{
internalMessageWindow messageWindow;
public Form Constructor()
{
this.messageWindow = new internalMessageWindow(this);
}
}
And unregister/register hardware buttons using UnregisterFunc1
and RegisterRecordKey
from coredll.dll (see http:\\www.pinvoke.net for signature detail.
FormCode
{
public Form Constructor()
{
...
RegisterHKeys.RegisterRecordKey(this.messageWindow.Hwnd);
}
}
public class RegisterHKeys
{
[DllImport("coredll.dll", SetLastError = true)]
public static extern bool RegisterHotKey
...
and
private static extern bool UnregisterFunc1
...
public static void RegisterRecordKey(IntPtr hWnd)
{
UnregisterFunc1(KeyModifiers.Windows, (int)KeysHardware.Hardware1);
RegisterHotKey(hWnd, (int)KeysHardware.Hardware1,
KeyModifiers.Windows, (int)KeysHardware.Hardware1);
}
}
Now, we need to force our application to start every time the Windows Mobile is started. This could be done using the CeRunAppAtEvent
function of the coredll.dll library. This function allows linking an application to a specific event of the device. In our context, we'll link our application to the Wakeup
event. This means that every time the device is started, the Wakeup
event will be raised, and as we will link our application with this event, our application will be started.
To link application to event, we'll use this code:
Win32.CeRunAppAtEvent(_kioskName, NotificationEvent.Wakeup);
And we'll use this code to 'unlink' application / event:
Win32.CeRunAppAtEvent(_kioskName, NotificationEvent.None);
So now, we have a start page, which appears every time the device is started. And we have also caught button events to disable hardware interaction. The final step is to allow the end-user to launch a specific application and wait that this application be closed to return to our start page.
This is a quite an easy step, using ProcessStartInfo
class. This will allow us to start an application in a new process and put our current application in a waiting state, waiting that a specific process exit.
To start a new process, we'll use this code, which will return the process handler:
private static Process LaunchApp(string filename)
{
ProcessStartInfo s = new ProcessStartInfo();
s.FileName = filename;
s.UseShellExecute = true;
return Process.Start(s);
}
And we have just to add routine to start an application, waiting the process to exit, with this code:
private void but_Click(object sender, EventArgs e)
{
this.Hide();
Process ela = LaunchApp(application2);
ela.WaitForExit();
this.Show();
}
Things to be Aware
- Hard reset of the device is not handled. Doing an hard reset will unsubscribe the application from the
Wakeup
event. - This solution is not portable! Form is designed for a specific resolution (240x320 in this sample) and device with the default four hardware buttons. Installing in a device with other specification will fail.
How To
Handling Non Standard Hardware Button
The sample is based on default device, with four buttons. If your target device has more buttons, or that MessageWindow
didn't catch button interaction, you'll have to validate your button code. Use Registry Editor (Remote Registry Editor) and go in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shell\Keys\. You will see multiple "Folders", corresponding to your hardware keys, like 40C1, 40C2, ... Convert the last two letters to Decimal and you will get your button key (C1=193,C2=194,...)
Conclusion
This sample is far from a production product, but show that handling specific device like PocketPC is quite easy. Interacting with such hardware needs a little bit of pinvoke, as compact framework encapsulates some but not all functionalities. Any comments/proposition are welcome.
Reference
History
- 08/10/2007: Posted to CodeProject