Mobile Development: Yet Another Kiosk Mode Library

0
23

Introduction

Here is another kiosk mode library. It supports disabling clicks/taps on start menu icon and opening the Windows Mobile start menu using the win key (VKLWIN). Additionally, there is a function to disable the whole StartMenu bar and one to make a window fullscreen without Done and Close button (uses SHFullScreen).

The functions are implemented in a DLL, so you can easily use them from C/C++, the .NET Compact Framework (CSharp or VB.NET), Java and so on.

Here is a list of the functions exported by the DLL:

void __stdcall LockStartMenu(); 
 
void __stdcall UnlockStartMenu(); 
void __stdcall LockStartBar(); 
void __stdcall UnlockStartBar(); 
bool __stdcall Lockdown(TCHAR*); 
 
bool __stdcall Unlockdown(); 

I have included a demo application in C and .NET.

The left shows normal Windows CE window and the right the same window after pressing the [Lockdown window].

Usage and Function

LockStartMenu and UnlockStartMenu

To disable clicks on the StartMenu, you use the function LockStartMenu(). This function subclasses (hooks) the HHTaskbar window procedure.

void __stdcall LockStartMenu()
 {
 taskbarhWnd = FindWindow(TEXT("HHTaskBar"), NULL);
 if (taskbarhWnd != NULL)
 {
 WNDPROC p = TaskbarWindowProc;
 oldWindowProc = (WNDPROC)SetWindowLong(taskbarhWnd, GWL_WNDPROC, (long)p);
 }
 }

Note about subclassing in Windows:

Subclassing a window means that your code gets executed on every message received by the original window. As you know, all windows have a central window procedure (wndproc). Every message to your window is going to this procedure. Normally you have a switch statement and case statements for the window messages you are interested to react on, like WM_CREATE, WM_PAINT, WM_LBUTTON, WM_KEYDOWN, WM_DESTROY etc. When you subclass a window, you install a new wndproc that is called before the original wndproc of the window. So you can manipulate things outside the original WndProc. You don’t need the source code of the original window or application. Subclassing is used here to hook into the wndproc of HHTaskbar (the window class name of the window that is responsible for the windows mobile taskbar at the top of your screen).

The new window procedure checks the click coordinates for left button clicks (WMLBUTTONDWON). If the x and y values of the click location are within the height of the taskbar and up to 2/3 of the screen width, the click is not forwarded to the taskbar window. So HHTaskbar does not get notified about the click and will not open the StartMenu.

LRESULT CALLBACK TaskbarWindowProc
 (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
 if (message == WM_LBUTTONDOWN)
 {
 POINTS pts;
 pts.x = LOWORD(lParam);
 pts.y = HIWORD(lParam);
 
 
 
 
 int screenwidth = GetSystemMetrics(SM_CXSCREEN);
 if(screenwidth!=0)
 MAX_START_X = (int)(screenwidth * 2 / 3);
 if (pts.y < MAX_START_Y && pts.x <)
 return TRUE;
 }
 ...

The new Taskbar window procedure also checks if the user has pressed the Win key. The win key (VKLWIN) would bring up the start menu too. The function TaskbarWindowProc therefore also checks for key messages and filters the VKLWIN keypress. So the user is not able to invoke the start menu by pressing the win key.

...
 if (message == WM_KEYDOWN && wParam == VK_LWIN && lParam == 1)
 return TRUE;
 return CallWindowProc(oldWindowProc, hWnd, message, wParam, lParam);
}

Messages that are not filtered are forwarded to the original window procedure of HHTaskbar.

Why not just disable the whole taskbar? This is for applications that will not lock you down completely. If you disable the whole taskbar (see also LockStartBar()), you will not be able to click the connection, volume and close symbols on the taskbar.

Here is a screen shot of the .NET demo app with a long title. Only the area inside the red rectangle is blocked. The remainder of the taskbar is clickable.

As you see with the .NET demo app, you can lock the user from opening the startmenu, but you can launch an external app (like the calculator) and the startmenu is still blocked. In the external app, the user can still click the (X) to close (hide) the app and is back in our kiosk application demo.

The second function UnlockStartMenu() will restore the original WndProc of HHTaskbar and ‘unhook’ our window procedure. So the start menu will then work as usual.

void __stdcall UnlockStartMenu()
{
 if(bStartmenuLocked){
 if (oldWindowProc!=NULL)
 oldWindowProc = (WNDPROC)SetWindowLong
 (taskbarhWnd, GWL_WNDPROC, (long)oldWindowProc);
 oldWindowProc = NULL;
 bStartmenuLocked=false;
 }
}

This function will restore the original window procedure and unhooks our new wndpro. So HHTaskbar should then behave normally.

LockStartBar and UnlockStartBar

The second pair of the DLL functions is called LockStartBar() and UnLockStartBar(). The first one looks for the window with the class name HHTaskbar and will then disable the window. Disabling a window prevents the window procedure from getting any more window messages. So HHTaskbar will not receive any clicks as long as the window is disabled.

void __stdcall LockStartBar()
{
 
 if(!bStartbarLocked){
 taskbarhWnd = FindWindow(TEXT("HHTaskBar"), NULL); 
 if (taskbarhWnd != NULL)
 {
 EnableWindow(taskbarhWnd, false);
 bStartbarLocked=true;
 }
 }
}

This function and the UnlockStarBar() function are very simple.

void __stdcall UnlockStartBar()
{
 if (bStartbarLocked)
 {
 taskbarhWnd = FindWindow(TEXT("HHTaskBar"), NULL);
 if (taskbarhWnd != NULL)
 {
 EnableWindow(taskbarhWnd, true);
 bStartbarLocked=false;
 }
 }
}

But as some .NET and JAVA and other non-native programming languages don’t have the essential windows functions like FindWindow, etc., I have included these functions in the DLL for easy use.

LockDown(Window Title) and UnLockDown()

The last pair of functions is using a combination of kiosk mode techniques I am aware of: LockDown(TCHAR*) and UnLockDown().

bool __stdcall Lockdown(TCHAR *windowText)
{
 
 if (hWndLockdown)
 return TRUE;
 if(!bLockedDown){
 HWND hWnd = 0;
 TCHAR *str;
 str = (TCHAR*) malloc( MAX_PATH * sizeof(TCHAR)); 
 wcscpy (str, windowText);
 if ((!str) || (wcslen(str) <= 0))
 hWnd = FindRootWindowByFocus();
 else
 hWnd = FindRootWindowByName(str);
 if (!hWnd)
 return FALSE;
 free(str);
 SetForegroundWindow(hWnd); 
 SHDoneButton(hWnd, SHDB_HIDE);
 SHFullScreen(hWnd, SHFS_HIDESTARTICON|SHFS_HIDETASKBAR|SHFS_HIDESIPBUTTON);
 MoveWindow(hWnd, 0,0, 240,320, TRUE); 
 
 hWndLockdown = hWnd;
 LockStartMenu();
 bLockedDown=true;
 }
 return TRUE;
}

First the function tests, if the lockdown for the window with the given title text was already done. If not, it searches the window handle of a window with the title specified. If a window is found, it will be set to the foreground and then the Microsoft API calls to make a window fullscreen are invoked for the window handle. The window is also made fullscreen (with currently hardcoded screen dimensions, change this, if you need) and our LockStartMenu function is called.

The last function in our list tries to revert the function of LockDown and tries to restore the window to a normal state.

Why did I write these two LockDown functions? They make it very easy for .NET and JAVA programmers to make their Windows mobile application a fullscreen kiosk mode application by just calling LockDown(window title) with the title of the window to make fullscreen.

private void btn_LockDown_Click(object sender, EventArgs e)
 {
 string title = this.Text;
 if (!_bFullScreen)
 {
 StartLock.Lockdown(title);
 _bFullScreen = true;
 label1.Text = "Form made fullscreen";
 }
 else
 {
 label1.Text = "Form was already fullscreen";
 }
 }

Doing fullscreen from Win32 native C is much easier than writing all the PInvokes you needed to get the same result.

LEAVE A REPLY