Windows CE Kiosk Mode - Part 11

In last weeks nail-biting installment of Windows CE Kiosk Mode we left our hero at the point where he was going to create a \Startup folder on the device and drop an application into the folder to be launched on startup of the Windows CE operating system image.

There's a couple of things we need to think about...

  1. How to create the \Startup folder as part of the operating system image
  2. How to include our application(s) in the \Startup folder so they launch at boot time
  3. How to create a "Launch" application that examines the \Startup folder and launches any applications found here

Before we get into the specifics of making this work we should determine why I'm doing this... In many ways I've been meaning to get this working since Windows CE 3.0, just before Windows CE 3.0 launched I sat in on an internal training session being delivered by the Windows CE development team for the world-wide system engineering groups - the training covered theory in the mornings and had us writing boot-loaders, ISR's etc... in the afternoon and sometimes late into the evenings, I think we were working on a MIPS board at the time - one of the things I wanted to get running was a custom shell on the device - even then, modifying the existing taskman and Explorer shell was certainly more than a few hours work.

The Windows Shell exposes a mechanism to enumerate all files in the \Start Menu\Programs\Startup folder and launch the processes - this is an ideal solution because the applications being launched don't need to be modified to be started in this way - the other option for launching an application at boot time is to use the HKLM\Init keys, this requires that the application call back into the operating system by calling SignalStarted( ); to let the O/S know that the application is running - the reason for this is that the HKLM\Init keys have two parts LaunchXX and DependXX - the LaunchXX key (where XX is a numeric value) simply points at the executable to launch, for example, take the following snippet from my o/s image registry file (once the O/S is built, the complete registry can be examined in text form in the build release folder as reginit.ini)

[HKEY_LOCAL_MACHINE\init]
"Launch10"="shell.exe"
"Launch20"="device.exe"
"Depend20"=hex:0a,00
"Launch30"="gwes.exe"
"Depend30"=hex:14,00

The snippet from the registry above launches three processes, Shell, Device, and Gwes - Notice that device.exe (Launch20) has a dependency on Hex:0a (10 decimal), this of course equates to Launch10, or Shell.exe - so the Shell process needs to signal the o/s that it's up and running so that any dependencies (in this case device.exe) can then be started - the same is also true of gwes.exe (launch30), Device.exe depends on hex:14 (20 decimal), so Gwes can't run until device.exe calls SignalStarted - you can have multiple dependencies - a Thin Client O/S image will have the WBT Shell depening on hex:14,00,1e,00 (Launch20, and Launch30) - so both Device, and Gwes need to be up and running before the shell starts - make sense?

So, here's the plan, I've created a new Platform Wizard based on the existing Thin Client template - you can download the updated file here - drop the new template file in the Windows CE 5.0 Platform Wizard folder \WINCE500\PUBLIC\COMMON\OAK\CATALOG\NEWPLATFORMWIZARDS

When you restart Platform Builder you will have a new Kiosk template to work with - this is an exact mirror of the Thin Client template file with the WBT/RDP shell removed. The next step is to replace the "old RDP shell" with some simple boot time code that examines the \Startup folder (which we will need to create) and launches any applications that we find.

Interestingly, the code to do this already exists in the existing Windows CE Explorer Shell code (\wince500\public\shell\oak\hpc\explorer\main\explorer.cpp) - take a look at the ProcessStartupFolder code.

Here's my simple boot time application, I've called this PlatformStartup.exe - the meat of the application is only about 10 lines of code - this uses the Win32 FindFirstFile, FindNextFile API's to walk through the files in the \Startup folder and launch the applications.

// PlatformStartup.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

void ProcessStartupFolder();

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
// Since this is application is launched through the registry HKLM\Init we need to call SignalStarted passing in the command line parameter
SignalStarted(_wtol(lpCmdLine));
ProcessStartupFolder( );

 return 0;
}

// The purpose of this function is to start all applications found in our custom Startup folder
// the Windows Explorer shell contains a "Special Folder" \Start Menu\Programs\Startup - CSIDL_STARTUP
// For this demo I'm going to override this with a fixed location folder called "\Startup"
// This code is taken from the Microsoft Windows CE Shared Source in the following location.
// c:\wince500\public\shell\oak\hpc\explorer\main\explorer.cpp

void ProcessStartupFolder()
{
WCHAR szPath[MAX_PATH];
HANDLE hFind = NULL;
WIN32_FIND_DATA fd = {0};
WCHAR pszFileName [MAX_PATH];

  // Note that we will need to create the Startup folder
// this can be achieved in one of two ways
//
// 1. Create the Startup Folder using this projects .DAT file
// or..
// 2. Map our applications into the Startup Folder using CEFileWiz (my prefered option)

 lstrcpy(szPath,L" \\Startup\\ *.*");

    hFind = FindFirstFile(szPath, &fd);
if (INVALID_HANDLE_VALUE != hFind)
{
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.nShow = SW_SHOWNORMAL;
sei.lpFile = pszFileName;

        do
{
lstrcpy(pszFileName,L"
\\Startup\\ ");
if ((!(FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes)) &&
(0 != _tcsicmp(TEXT("desktop.ini"), fd.cFileName)))
{
lstrcat(pszFileName, fd.cFileName);
ShellExecuteEx(&sei);
}
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
}
}

The next step is to have the application start at boot time - With Windows CE 5.0 this is super easy - each project contains it's own personal reg, bib, dat etc.. files, so we can add the following to our projects .reg file to start the application after device and gwes are up and running.

[HKEY_LOCAL_MACHINE\init]
"Launch50"="PlatformStartup.exe"
"Depend50"=hex:14,00,1E,00

The final step is to add our custom shell application - in this case I've written a very simple (very simple) C# application that contains a Button and TextBox, click the button and "Cool Tools!" appears in the TextBox - but that's not the point, right ? - what I'm showing here is that I can build a kiosk type device and build my custom shell using Win32/C/C++/C#/VB without needing to know (at the application level) anything about the boot process of the Windows CE operating system.

I've used CEFileWiz to create a custom CEC file that gives me everything I need to directly add the C# application binary to the Windows CE operating system image (there's a tutorial here that shows how to do this). The interesting thing is how to map the ShellApp.exe from the \Windows folder (the default location for any application or file in the Windows CE O/S) - CEFileWiz creates a project file that doesn't compile/link anything, but instead makes sure the bib, dat, reg etc.. are setup correctly to include the application/file into the o/s - for our ShellApp.exe we want to map the application from the \Windows folder to the \Startup folder, this is very easy - here's the ShellApp.dat file.

root:-Directory("\"):-Directory("Startup")
Directory("\Startup"):-File("ShellApp.exe","\windows\ShellApp.exe")

And the end result ? - a Windows CE operating system image that can still be customized by adding the technologies you need, and at the same time makes it super easy to create kiosk type devices, perhaps an airport check in terminal, a ticket machine at a train station etc...

And here's the final custom shell application up and running...

 

My plan is to write this up as a complete end-to-end MSDN article - look out for that being posted shortly - any questions, comments, do let me know...

- Mike