Mobile Development: Yet another kiosk mode library

Hello

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 dot 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(); // this will install the hook (subclass the taskbar window)
void __stdcall UnlockStartMenu();   // this will unhook TaskbarWindowProc from taskbar
void __stdcall LockStartBar();  // this disables the whole taskbar
void __stdcall UnlockStartBar();    // this enables the taskbar window
bool __stdcall Lockdown(TCHAR*);    // this will make the application with the window title fullscreen etc
bool __stdcall Unlockdown();    // this will 'normalize' the fullscreen window

I have included a deno application in C and .NET

The left shows normal window 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 dont 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 (WM_LBUTTONDOWN). 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);

        //the hot area of the StartMenu extends with the length of the current window title text
        //we get the actual screen width and then assume half the width as hot area
        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 (VK_LWIN) would bring up the start menu too. The function TaskbarWindowProc therefor 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 DotNet 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 like 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 click as long as the window is disabled.

void __stdcall LockStartBar()
{
 //Disable the whole HHTaskbar window
 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 the application is already locked down, don't attempt to lock it down again.
 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);     // Required before SHFullScreen Calls
   SHDoneButton(hWnd, SHDB_HIDE);
   SHFullScreen(hWnd, SHFS_HIDESTARTICON|SHFS_HIDETASKBAR|SHFS_HIDESIPBUTTON);
   MoveWindow(hWnd, 0,0, 240,320, TRUE);  // Expand to use the entire screen 

   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 MS 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 wrote these two LockDown functions? They make it very easy for DotNet and JAVA programmers to make there 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";
   }
 }

(See also the video at community.intermec.com.)

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

Downloads

[Download not found]


[Download not found]