Mobile Development: Yet another kiosk mode library

Print This Post Print This Post

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]

16 Comments

  1. Erik says:

    Thx a lot!

  2. dale says:

    Hi Josef,

    Great stuff!

    Can you check the downloads? I downloaded both StartLockTestCS1.1.zip (DOWNLOAD:DotNet LockDown Demo Application) and StartLockTestCS1.11.zip(DOWNLOAD:StartLock DLL source code and demo app (embedded visual c++ 4.0) ),compared using the windows comp utility, and they appear to be identical.

    thanks
    Dale

  3. admin says:

    Thanks for the hint, I will check this and update the blog.

    regards

    Josef

  4. admin says:

    Hello Dale

    thanks again for the hint. I corrected the downloads and they now are the C++ DLL source and test code and the dotnet demo application.

    Thanks a lot for let me knowing this

    With kind regards

    Josef

  5. Pirras says:

    Nice program.
    Hide Menu is not supported?

  6. admin says:

    Hello

    no, Hide Menu is not a separate function as the menu can be ‘hidden’ by making a winform WITHOUT a menu attached and setting the form size to the display screen size.
    So, I dont see a need for such a function. The menu bar just depends on the form settings.

    regards

    Josef

  7. […] You can find more information on downloading and using the library in the Windows CE Programming’s Mobile Development: Yet another kiosk mode library […]

  8. Eduardo says:

    is this compatible with windows mobile 6.1? and if so when i subclass the taskbar and open another application eg: calculator, windows freezes.

    Thank you.

  9. admin says:

    As I developed the lib I did it against WinMo 5.

    Did you already look at http://www.hjgode.de/wp/2010/10/11/windows-mobile-hide-startbutton-in-winmo-6-5-x/ and http://www.hjgode.de/wp/2010/11/18/mobile-development-disable-windows-mobile-6-5-start-and-close-button/

    I did not heard yet of freezing due to the lib, but who knows. Possibly the freeze is not related to the lib.

    regards

    Josef

  10. Eduardo says:

    thank you for your prompt response. it is working now, thanks again.

  11. Phillip says:

    Hello,

    Thanks for sharing your code. I download your zip for C++, but since I don’t have embedded visual C++ 4.0 so I can not run your project, but I have copied some of your codes and put into a simple MFC dialogbox program but when I run it through debugging mode on WM5/6, my program just freezes. So here is code that what I copied.

    static HWND g_taskbarhWnd;
    static WNDPROC g_oldWindowProc = NULL;
    LRESULT CALLBACK TaskbarWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    DEBUGMSG(1, (L”TaskbarWindowProc invoked!”));

    return CallWindowProc(g_oldWindowProc, hWnd, message, wParam, lParam);
    }


    WNDPROC newWindowProc = TaskbarWindowProc;
    g_oldWindowProc = (WNDPROC)::SetWindowLong(g_taskbarhWnd, GWL_WNDPROC, (long)newWindowProc);

  12. Phillip says:

    So sorry, please disregard my question. I forgot to set the CALLBACK to the origin wndproc. which is also in your code as well:

    g_oldWindowProc = (WNDPROC)::SetWindowLong(g_taskbarhWnd, GWL_WNDPROC, (long)g_oldWindowProc);

  13. alex de souza says:

    Hi, it is working great, thank you. But when i lockdown on 480×640 resolution devices, form is shown incorrectly. What can we do for this resolution problem?

  14. admin says:

    I did use hard coded screen size with 240×320.

    For an screenres independent version look now at my repository: https://code.google.com/p/startlock/source/checkout

  15. alex de souza says:

    i downloaded the code from http://www.hjgode.de/wp/wp-content/plugins/download-monitor/download.php?id=116. I modified the function bool __stdcall Lockdown in StartLock.cpp like below:
    MoveWindow(hWnd, 0,0, 480,640, TRUE);
    and i built the project with visual studio 2008. i am taking the output dll ..\StartLock\Pocket PC 2003 (ARMV4)\Debug\StartLock.dll. When i use the new dll in my application, i see it is not working. What is my fault? How must i compile the project?
    or Can you share StartLock.dll that takes screen dimensions as parameter.

  16. alex de souza says:

    I solved this problem, it is my fault. thanks for supply.

Leave a Reply