10

I'm trying to start a user-mode process from a service using CreateProcessAsUser() API similar to this code. My code works fine in 99% of the time, except at times that API succeeds, I get the process handle from the PROCESS_INFORMATION struct but the process itself doesn't appear in the interactive user session that I was intending it to run in.

The interesting thing is that if I call GetExitCodeProcess() on the process handle it succeeds with return code 0xC0000142. Any idea why?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
ahmd0
  • 16,633
  • 33
  • 137
  • 233
  • When the failure occurs, can you start the child process at all? Is it possible that the .exe you are trying to launch depends on DLLs that are missing? – Michael Shmalko Nov 16 '12 at 22:16
  • 1
    The return code certainly fits the behavior, it is STATUS_DLL_INIT_FAILED. Which certainly would prevent the process from starting. One of the DLLs it uses returns FALSE from its DllMain() entrypoint. You'll need to repro this on your dev machine so you can debug it. – Hans Passant Nov 16 '12 at 22:26
  • @HansPassant and MichaelSh Thanks for advice. I need to try to log in to that user session and see if I can start a process manually. The issue is that it doesn't happen all the time... But, the question I have is why does CreateProcessAsUser() return TRUE when it clearly doesn't start anything? – ahmd0 Nov 16 '12 at 22:43
  • 1
    I found this article that may explain what I'm seeing by "desktop heap depletion." It sounds most probable for me: http://blogs.technet.com/b/askperf/archive/2007/07/24/sessions-desktops-and-windows-stations.aspx – ahmd0 Nov 16 '12 at 23:33

2 Answers2

11

Error 0xC0000142 is STATUS_DLL_INIT_FAILED (I determined this using the Error Code Lookup Tool). A quick google found this question, which says:

The most common cause of this problem is that a program that links to user32.dll was run in a context in which it could not talk to the system's window station and desktop. Normally, a service such as the agent runs in its own window station and desktop, and user32 programs run fine, but any program that displayed a dialog box would then hang without any opportunity for a human being to see the error message or close the dialog.

So, if you're not using any functions from user32.dll, you should remove that dependency. If you are using that DLL, then I'm not really sure what you're supposed to do. One option would be to load the DLL dynamically with LoadLibrary and use it if it succeeds (i.e. you have a valid window session) or to fallback on a failure mode if it fails.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • Thanks. I'll try. Although it's kinda hard not to use user32.dll, you know... My code surely doesn't display any user contents. All it does, it opens a global named event and sets it and then returns the result in the error code. – ahmd0 Nov 16 '12 at 22:40
  • Any reason something like this would happen if you run a low integrity process but not if you run medium integrity (or higher) launched from as service? – John Leidegren Feb 08 '13 at 13:51
  • 1
    Upvote for the link to Error Code Lookup Tool, because googling for error codes is a pain in the ass these days. – Florian Winter Jul 08 '15 at 12:53
  • @ahmd0 I'm having exactely the same problem. It works sometimes, and sometimes not. Did you find a solution? Thank you for a reply! – tmighty Jul 08 '16 at 10:55
  • @tmighty: Yes. The only way to be sure that the child process was started OK is to create a (global) named event in your host process, make it accessible in a child process using event's [security descriptor](https://msdn.microsoft.com/en-us/library/windows/desktop/aa379560(v=vs.85).aspx), then when the child process starts up and initializes open that named event and signal it. Lastly from the host process first wait for the process handle and then check if the named event was signaled. – ahmd0 Jul 08 '16 at 18:55
  • @tmighty: In case of a reason why you might get a handle to a child process in the host process but the child process never starts, it is most certainly due to some DLL failing to load in the child process, or if your child process' initialization fails, or child process may simply crash. In the latter case you can use [ProcDump](https://technet.microsoft.com/en-us/sysinternals/dd996900.aspx) to diagnose it. – ahmd0 Jul 08 '16 at 18:58
  • @ahmd0 Thank you. And do you know why it might fail? Did you try LoadLibrary("user32.dll") already before you start the process? This is so weird. The very same application can sometimes be started, sometimes not. Perhaps a timing problem? – tmighty Jul 09 '16 at 16:23
  • @tmighty: There's a million reasons why it may fail. See what `GetExitCodeProcess` gives you when you call it on the process handle. That may be an error code. – ahmd0 Jul 09 '16 at 18:56
  • @ahmd0 Yes, it is always the error C0000142, same as yours. Did you find out why it sometimes occurs and sometimes not? And when it occurs, do you try starting the process again, or how do you handle it? – tmighty Jul 09 '16 at 19:48
  • @ahmd0 Can you reply? – tmighty Jul 11 '16 at 20:15
  • @tmighty: There's no one-fits-all solution to this. `0xC0000142` is `STATUS_DLL_INIT_FAILED`. The best explanation to why it may be issued was given by Adam in his answer above. In my particular case my user process was failing because `user32.dll` was not available in some circumstances when Windows user was logging in or logging out. In case when a log-out was initiated that user account would enter `SM_SHUTTINGDOWN` state that makes all `LoadLibrary` calls fail. But it's just my particular case. [Here's more info](https://support.microsoft.com/en-us/kb/191991). – ahmd0 Jul 12 '16 at 20:36
8

The CreateProcess...() APIs will return TRUE if they can successfully create the internal process object and begin initialization; they don't wait for the process to load and begin running its executable image. In some cases the initialization later fails, but from the kernel's perspective it was still a successful process creation.

HerrJoebob
  • 2,264
  • 16
  • 25
  • Good point. Thanks. So what is the correct way to know that my process has been started "for sure"? – ahmd0 Nov 16 '12 at 23:19
  • Unless you can communicate directly with the process through a pipe or something, I believe the best way is to wait a bit after CreateProcess() and call GetExitCodeProcess() to check for the STILL_ACTIVE return code. – HerrJoebob Nov 16 '12 at 23:21
  • Or use `WaitForInputIdle()`. – Remy Lebeau Nov 17 '12 at 00:25
  • 1
    WaitForInputIdle() doesn't do quite what he wants in the case of a console-mode program. If it's only apps with a message pump then it would work though. – HerrJoebob Nov 17 '12 at 00:32
  • 3
    @RemyLebeau: Besides what HerrJoebob had said, `WaitForInputIdle` cannot be called from a service. It will always fail in that case. – c00000fd Aug 10 '15 at 21:10