CreateProcess does not wait for the process to start

Date:January 19, 2005 / year-entry #17
Orig Link:
Comments:    16
Summary:The CreateProcess function creates a new process, but it doesn't wait for the process to get off the ground before returning. It just creates the process object and lets it go to do its thing. The Win32 process model is that each process initializes itself in context. When a process object is created, it is...

The CreateProcess function creates a new process, but it doesn't wait for the process to get off the ground before returning. It just creates the process object and lets it go to do its thing.

The Win32 process model is that each process initializes itself in context. When a process object is created, it is practically empty, save for enough information to get the program execution procedure started. When the thread in the process object is scheduled, it starts doing the real work of loading DLLs, initializing them in the correct order, then calling to the program's entry point.

If, along the way, one of these process startup steps fails, the process is killed, and the exit code is the reason why the process couldn't get started. For example, if the problem was that a function could not be found in a DLL, the exit code will be STATUS_ENTRYPOINT_NOT_FOUND.

(And don't forget that you can use the SetErrorMode function to disable the error dialog.)

Comments (16)
  1. Foolhardy says:

    Besides, at what point would the process be considered "started"?

    When the kernel object is created? (like it is now)

    When the first thread is created?

    When all the libraries have been loaded?

    When they’ve been initialized?

    When the process begins to listen for messages? –this won’t work if the process doesn’t create any windows.

    Since there isn’t a defined point for a process being "started", CreateProcess can’t very well wait for it to happen.

  2. Stuart Dootson says:

    Best you can do (if you can’t code in an explicit synchronisation) is to use WaitForInputIdle, always assuming that your child process meets the requirements needed for WaitForInputIdle to indicate the process is up and running! In fact, if you think about it, the only signal you can guarantee getting about a process is that it’s terminated, if you wait on it’s handle. Don’t think that really counts as ‘the process has started’ :-).

  3. Perhaps a Monty Python-esque "The process is not quite dead, yet?" is all we can hope for, since there really is no other metric….

  4. I do builds of Win32 software using gcc on Linux and Windows using mingw and my mingw cross-compiler on the same box is almost twice as fast under Linux. Does windows do more magic with process accounting or is there overhead from the VM and Cache Manager?

  5. Re: why is it slow:

    Because Windows processes, like VMS processes before them and RSX processes before them are large complex security containers.

    This topic gets plenty of attention internally and over time you should see the cost of NT processes approach other OSs’ costs.

    The typical debate here centers around use of fork() for multithreaded applications. You can imagine what it looks like: side #1: fork is easy and you get cheap parallelism out of it side #2: fork is expensive and most of the time you fork and then exec anyways.

    Maybe the old timer NT guys have another perspective but it would seem to be the same debate as back at Digital when VMS’s process creation was slammed in comparison to fork().

    The key things that might shift the balance here about which tradeoff is better are (a) 64 bit machines with plenty of kernel mode address space and (b) the move away from running extensibility points in-process due to reliability and security concerns.

    Honest answer to the simple question: it’s the usual story. The perf team gets the badness that was added to the create process code path in version n cleaned up in version n+1 but then so many people want to do interesting things at process creation time, someone often uses up the headroom gained. (My team unfortunately did this in XP; the kernel team did a lot of good work to speed up process creation and then we added manifest processing without caching and basically it ended up a wash. This one in particular is getting fixed in LH but I’m sure there’s several new teams planning to add all kinds of cool stuff to the process creation/initialization path.)

  6. Mike Weiss says:

    I bet we’ve all seen code that works around this like so:


    Sleep(5000); // wait for sub-process to start

    Great, right?

  7. Confused says:

    Raymond’s post sounds contradicting. Does CreateProcess wait for the subprocess to load it’s dlls or not?

    If not ("it doesn’t wait for the process"), how could CreateProcess return STATUS_ENTRYPOINT_NOT_FOUND?

  8. Raymond Chen says:

    Please read again: "The exit code is the reason why the process couldn’t get started."

    The exit code of the process, not the return value of CreateProcess.

  9. Dave says:

    CreateProcess is another function that causes long-term heartache by doing a favor for sloppy people. I just love the way it parses an ambiguous application name that includes spaces. It also uses the current directory and appends .exe for you if you forgot. That can cause trouble too.

  10. Ivan says:

    Whe windows starts kernel services (for example when a driver is loaded), is the service started with CreateProcess? If yes, as DriverEntry can return immediatly, how does the process stay loaded?

  11. Mike says:

    CreateProcess is a user-mode win32-specific function. Driver entry points are called by (I assume) the kernel initialization thread (for boot-start drivers), and the entry points are called directly. Drivers in kernel mode are analogous to user-mode DLLs. DriverEntry behaves similarly to DllMain.

  12. Jonathan Wilson says:

    The obvious question is why it wasnt designed so that CreateProcess returns only after the operating system has loaded the process and is going to call the process entry point (specified in the PE header).

    That makes sense to me (although to someone more versed in the internals of win32, it probobly doesnt)

  13. Raymond Chen says:

    DLL initialization occurs before the process entry point is called. If a DLL hangs in its initialization function, do you want CreateProcess to hang also?

  14. Eric TF Bat says:

    Dave’s right – if Raymond’s experiences are anything to go on, MSFT systems programmers spend an awful lot of their time working around the sloppiness of non-MSFT applications programmers. I hereby coin a new term to describe the reason they have to put so much rubbish into their libraries, in an attempt to help the users who are stuck with poorly-written software that relies on bugs and coincidences: I call it slopward compatibility. Now, next time you curse the bizareness of Windows libraries, you know what to call it, and who REALLY deserves the blame.

  15. Dave says:

    "…who REALLY deserves the blame."

    I wasn’t trying to place blame as much as express frustration with the process. There is no "right" solution. Some programmer made one or more mistakes that weren’t caught before their program sold thousands of copies. Then Microsoft makes a change in the OS that reveals the mistake. MS can either work around the problem or let them suffer. Unfortunately, "them" isn’t just the poor developers that overlooked a bug, but thousands of their innocent customers.

    But back to blame. When someone upgrades their version of Windows and an app breaks, who do users blame in most cases whether they deserve it or not? (Nine letters, starts with M.) Yes, workarounds make the APIs more complicated for developers. If you want to minimize total worldwide units of Microsoft blame, though, it is more economical to annoy developers instead of users.

  16. CreateProcess says:

    What would cause the hThread, dwProcessID and dwThreadID in lpProcessInformation to be unitialized after calling CreateProcess()? I have seen this but the documentation implies that these values will always be "returned".

Comments are closed.

*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index