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;
}