Wednesday, April 15, 2009

If you've ever needed to automatically launch an application under Windows CE then you probably know that one of the most common ways is to put an entry into the device registry under HKLM\Init.  This works great for native applications, but for CF applications success is hit or miss.  The reason is that CF applications require certain system APIs to be up and running before they can run.  For a native app that has this requirement, the solution is simple: you call either IsApiReady or WaitForAPIReady (depending on the OS version) and then continue when you're satisfied.  For CF apps it won't work.  The APIs need to be ready long before Main is entered and any of your managed code is running.

Of course the "right" solution is that the CF team should have put that check into the loader so we could launch this way, but they didn't so we have to work around it.

So how do we get around this?  Well we leverage the initialization process itself.  When an app is launched from HKLM\Init, it is responsible for calling SignalStarted once it is running.  This allows any other items launching from HKLM\Init to set up dependencies, for example if item 60 depends on item 50, item 60 won't launch until item 50 has called SignalStarted.  What we can do is create a native application that acts as a launch gate.  This gate app will call the appropriate wait function, and only after the APIs we need are available does it call SignalStarted.  We can then launch any CF application using HKLM\Init by simply having the gate application launch first, and then having the CF app depend on the gate.

So in the registry, it would look like this:

[HKEY_LOCAL_MACHINE\Init]
    "Launch90"="gateapp.exe"
    "Depend90"=hex:1e,00 ; depend on GWES, which is at 30
    "Launch91"="MyCFApp.exe"
    "Depend91"=hex:5a,00 ; depend on gateapp

And here's the source code for a basic gate app:

extern "C" DWORD WaitForAPIReady(DWORD, DWORD);
extern "C" BOOL IsAPIReady(DWORD hAPI);

int _tmain(int argc, _TCHAR* argv[])
{
    // quick sanity check - HKLM\Init will send in our order number
    if(argc == 0) return 0;

    BOOL success = FALSE;

    // wait for window manager - that should be enough for us
    #if _WIN32_WCE > 0x500
        success = (WaitForAPIReady(SH_WMGR, 5000) == WAIT_OBJECT_0);
    #else
        int i = 0;
        while((! IsAPIReady(SH_WMGR)) && (i++ < 50))
        {
             Sleep(100);
        }

        success = (i < 50);
    #endif

    if(success)
    {
        int launchCode = _ttoi(argv[1]);
        SignalStarted(launchCode);
    }
    else
    {
        RETAILMSG(TRUE, (_T("CFInitGate timed out - SH_WMGR was not ready after 5 seconds\r\n")));
    }

    return 0;
}

4/15/2009 1:46:38 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2]  | 
5/16/2009 10:35:06 PM (Eastern Daylight Time, UTC-04:00)
Just wanted to thank you for this article. If only MSDN could be this clear and concise.....
5/20/2009 9:29:18 AM (Eastern Daylight Time, UTC-04:00)
An excellent code snippet.

Should the line

#if _WIN32_WCE > 0x500

not be

#if _WIN32_WCE >= 0x600

otherwise minor versions of Windows CE 5 try to compile
the WaitForAPIReady block of code.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):