Why your application shouldn’t call CreateProcess(services.exe,configuration command,…) directly


On Windows CE, services.exe has a command line interface that allows you to control the state of running services.  See the services.exe white paper, "Services.exe Command-Line Interface and Service Configuration" for details about how this works.


This cmd line interface was really only intended for development purposes.  It assumes you, the engineer, are developing a service and you need to stop/restart it, or you need to unload it, recompile the DLL, and reload it.  Being able to use the common command line parser to do common functionality makes this easier.


For applications that need to control services on shipping devices, we have an API family to do this.  For instance RegisterService, DeviceIoControl (with well defined IOCTLs in service.h.), EnumServices, and so on.  Unfortunately a number of applications don't call these APIs but instead do a CreateProcess(L"services.exe","Command I want to run",...).


This is bad for two reasons.  First, it's less efficient.  When you make an API call you only have to do an inter-process communication call which is pretty cheap.  CreateProcess is much more expensive.


The second, bigger problem is that it won't work anymore on PocketPC and SmartPhone 2005 devices or later.  This functionality is now disabled by default.  It can only be reenabled by setting the registry or value HKLM\Services\AllowCmdLine to be non-zero.  Note that this is a protected registry key, so your application if it's not trusted will not have access to go change this on its own. 


Services command line parsing was disabled due to security concerns - basically an untrusted application could call CreateProcess("services",...) and the service would be tricked into thinking that a trusted process had initiated whatever command line request it received.  Now that CE has the trusted/untrusted model on shipping devices, we had to fix this.


So the bottom line is have fun with the cmd line interface for your debugging, but don't rely on it in the actual product.


[Author: John Spaith]

Comments (6)

  1. bincbom says:

    Prohibiting the use of CreateProcess() is just unrealistic.  Without CreateProcess() it’s no exaggeration to say that we would simply be unable to have a product on the CE platform at all.

    You people over at Microsoft need to get your act together.  Not everyone is writing chintzy two-bit "Pocket Pool" applications.  Some of us are writing some pretty big software systems, for which CreateProcess() is nothing short of pivotal.

    Security is important, but it must be done in ways that don’t render CE useless.  Prohibiting access to CreateProcess() for "security reasons" is really stupid, and really hard on your ISV’s that make use of CE.

  2. cenet says:

    I never said you were prohibited from using CreateProcess in general.  That would kill the platform, you’re correct.  I said that you cannot call CreateProcess("services.exe",…) anymore on WM5.  This will impact a very tiny segment of legacy apps and make the platform significantly insecure.

    John Spaith

  3. In CE 6.0, one of the things we changed was naming services.exe to be servicesd.exe. I mentioned it at

  4. Christopher Piggott says:

    Can calling EnumServices() be accomplished from .net?  I tried this, but I get a NotSupportedException.

           [DllImport("coredll.dll", SetLastError=true)]

           public static extern int EnumServices(

               [MarshalAs(UnmanagedType.LPArray)]

               IntPtr bufptr,

               out int entries,

               ref int ServiceLen)

           public static void ReadServices()

           {

               const int count = 100;

               try

               {

                   ServiceEnumInfo[] buf = new ServiceEnumInfo[count];

                   int buflen = 10000;

                   IntPtr ptr;

                   ptr = Marshal.AllocHGlobal(buflen);

                   int numRunningServices;

                   int rc = EnumServices(ptr, out numRunningServices, ref buflen);

                   Marshal.FreeHGlobal(ptr);

               }

               catch (Exception e)

               {

               }

Skip to main content