How can I detect that my program was run from Task Scheduler, or my custom shortcut, or a service, or whatever


Suppose you want your program to behave differently depending on whether it is launched from the Start menu, or by clicking the pinned icon on the taskbar, or by Scheduled Task, or from a service, or whatever. How can a program detect and distinguish these scenarios?

The answer is you don't. And you shouldn't try.

Instead of trying to guess how your program was executed, you should have the launcher tell you how they are executing your program. You do this by registering a different command line for each of the scenarios, and then checking for that command line in the program. (We saw a variation of this a little while ago.)

For example, you could have your Start menu shortcut contain one command line parameter, give the taskbar pinned shortcut a different command line parameter, register yet another command line parameter with the task scheduler, and have the service launch the program with a still different command line parameter.

They all run the same program, but the command line parameter lets the program know what context it is being run in and alter its behavior accordingly.

It's like creating multiple email addresses that all map to the same inbox. Many email services let you take an email address and insert a plus sign followed by anything else you like before the at-sign, and it'll all get delivered to the same inbox. The thing after the plus-sign is ignored for delivery purposes, but you can use it to help organize your inbox, so you know that the message sent to bob+expos@contoso.com is related to your fantasy baseball team, whereas bob+ff@contoso.com is something about your frequent flier account.

One thing you shouldn't do is try to guess, however. Programs that magically change their behavior based on details of the environment lead to problems that are very difficult to debug.

Given this discussion, perhaps you can provide guidance to this customer:

How can my DLL detect that it is running inside a service?

Comments (53)
  1. Damien says:

    Have the service tell the DLL during an initialization stage or during usage "hey, I'm a service by the way."

  2. Paul Z says:

    I assume the answer to the question is: It can't, and you are a bad person for wanting to. If you really need to know, arrange for the service to tell you, by calling an API that means "Hey, you're inside a service!"

  3. Joshua says:

    I normally would respond with something like "What are you planning on doing with that information?" Also, if you think you know how to check you don't. I use a program regularly that runs as a service but services.exe is not the parent process and spawns interactive children.

  4. SMW says:

    It's a shame that Bob is still using the Expos in his fantasy baseball league.  No wonder he keeps doing poorly.

  5. Ian Boyd says:

    Bonus Chatter

    As of Windows Vista, the "Allow service to interact with desktop" checkbox on a service's "Log On" tab is vestigial. It does nothing.

    If you attempt to start any service with the option checked, the Service Control Manager will generate an event 7030 (EVENT_SERVICE_NOT_INTERACTIVE) in the System event log:

    > The %1 service is marked as an interactive service. However, the system is configured to not allow interactive services. This service may not function properly.

    The "Interactive Services Detection" service (aka UI0Detect) will attempt to watch for services that have created a UI and are awaiting user input. The service was enabled by default of Windows Vista and disabled by default on Windows 7.

  6. Mungo says:

    I will stop all running services trough the servicemanager and if I am still running at the end I know I am not running inside a service.

  7. Peter says:

    Many of you seem to assume that the comustomer controls the code for both the service and the DLL.  What's he supposed to do if his DLL is being loaded by someone else's service?

    A common reason for wanting to know how you were launched is so that you can display additional output when you're run from the command line.  A command line parameter is not reliable in this case since the end user has to remember to enter it.

    @Mungo: Awesome. :)

  8. Damien says:

    @Peter – no, I assume that I get to choose what contract I expose from my DLL for consuming applications to use. If they don't follow the contract I'm providing, then I may do stupid things, but no more stupid than if I'd created some heuristics that "guessed" at my runtime environment and got it wrong.

    I'm also liking @Mungo's solution though.

  9. dmex says:

    > How can my DLL detect that it is running inside a service?

    GetModuleHandle("svchost.exe")

    [Not all services are called svchost.exe -Raymond]
  10. ZLB says:

    This only works under Vista and onwards but if ProcessToSessionId(…) for current process returns session id 0, then we're running in a service or a child process of a session.

  11. Ben says:

    +1 @Mungo, a neat solution which fulfils the requirements. :-)

    Probably the real issue is that when running as a service they run as localsystem so a bunch of stuff they expect isn't set up. So just check who you are.

  12. Random User 4328545734 says:

    And here I thought Bob's second address was going to have something to do with Final Fantasy.

  13. When you install the service, add "… /service" to the lpBinaryPathName argument to CreateService.

  14. It would never occur to me to do this any other way other than by checking the command line for some switch that told me it was launching via the scheduler.

  15. Gabe says:

    Detecting whether you're running in a service can be accomplished in several ways, some better than others:

    1. Check to see if your parent (or ancestor) process is services.exe.

    2. Check to see if you're running in session 0.

    3. Check to see if your process token has the SE_SERVICE_LOGON_NAME privilege.

    [I guess you didn't learn the lesson from today's article. -Raymond]
  16. smf says:

    the only way to know how you are running is to run:

    shutdown -s -t 0

    After that command has done it's job then you know you aren't running.

  17. Yuri says:

    The question is probably misguided, why does the customer wants to know if his DLL is running inside a service? If the customer reply back, refer him to the preferred way to check if Contoso widget feature is enabled at runtime.

  18. JM says:

    Mungo's solution does not work because it assumes all services being asked to stop will stop. However, a stop request is just that, and if a service does nothing with it, at the end the best you know is that you're either not in a service, or you are in one of the services that didn't stop (for whatever reason). To correct the solution, all you have to do is find the processes hosting the services and kill them instead. If you're going for this approach, I recommend taking some measures against people who are willing to find you and kill you instead.

  19. Stefan Kanthak says:

    There's but a BUT: Windows has NO separate directory where the service binaries are stored.

    Joe Average, exploring the "System" directory and double-clicking for example "services.exe" might wonder why this program produces no output at all but keeps running.

    And of course your own "service.exe" has to behave differently: when started as service all STD_*_HANDLE are 0L.

  20. Ken Hagan says:

    Some interesting ideas here for service detection, but we should all bear in mind that (to justify its existence) any such proposal needs to be significantly more reliable than the following, which is nearly always accurate and (probably) "fails safe".

      bool IsRunningAsService()

      {

         return false; // No you aren't, and even if you are then it's none of your business!

      }

    (I was inspired by the implementation of std::uncaught_exception() in an unnamed C++ compiler from the late 1990s.)

  21. Joshua says:

    Indeed Gabe did not. I already posted the existence of a counterexample to #1 which happens. #3 happens whenever the running user can log in as a service.

  22. Joshua says:

    Huh what happened to my content. Should have read "counterexample to #1 which happens to also be a counterexample to #2".

  23. hold on says:

    What if the customer's DLL is being loaded by another DLL?  Do we know it is feasible to expect all upstream DLLs to all update their API contracts to expose "I'm running as a service" to accommodate the downstream DLL's supposed need to determine its execution environment?

    As you can see, without sufficient additional information or context about the actual situation and problem, I remain unconvinced that Raymond's "lesson" here fully or necessarily applies to the customer's case–to my mind it isn't 100% analogous to the program-launch scenario the post started with.

    Anyway, there is nothing difficult about this.  You can easily get your own process ID, and there's a set of APIs to enumerate installed services and get their current execution statuses including process ID [msdn.microsoft.com/…/ms685974(v=vs.85).aspx].  So there is no need for additional assumptions on session ID, ancestor processes or what-not.  That said, this assumes "detecting whether I'm running inside a service" is even the correct approach to whatever the underlying problem is.  It seems more likely that the underlying problem is merely exposed as a side effect of running inside a service, but may well also manifest in other configurations, in which case detecting whether you are running inside a service is only a partial fix at best.

  24. Evan says:

    @Ken: "bool IsRunningAsService() { return false; }"

    Also similar to blogs.msdn.com/…/71307.aspx

    That's one of my favorite stories from this blog.

  25. Nick says:

    Well you can ask the service controller for extended status of all services, look for one with your PID, and ignore the answer anyway because it shouldn't matter to you whether you're in a service, you're a DLL, you have one job.

  26. All of the "check the list of services" solutions require that the service be running under a token that has at least SC_MANAGER_CONNECT and SC_MANAGER_ENUMERATE_SERVICE access, which may not be the case.

  27. Ben says:

    Now, obviously you shouldn't, but you can find out if you are running in a service using WMI, which exists to provide this kind of diagnostic information… just ask for the Win32_Service with your ProcessId. If there is one, you are running in a service process.

    However this probably won't achieve what you want because you might be running as a worker process for the service, or in a COM out-of-process host on behalf of a service, and so on, and you probably want these scenarios to be treated the same way as running in a service.

    So the real answer, as Frozone's wife said, is "WHY do you WANT to KNOW?"

  28. Crescens2k says:

    Just because I feel like bucking the trend a little bit.

    If the DLL asks am I running as a service, the answer should always be yes. A definition of the English word Service is "The action of helping or doing work for someone." All programs exist to do work on behalf of the users, so it is a service.

    Even when Skynet comes online, again, it will be a service but this time the service will be on behalf of an insane AI, not a human.

  29. hold on says:

    The more I think about it, the more likely it seems that the underlying problem has to do with either running in a non-interactive session, or differences in privileges or access permissions (note: the exact differences will vary depending on exactly which privileges the service asks for, plus other factors).  The fact that those issues happen to come up as a result of running inside a service is just a surface trigger and not the root cause.

    So in one sense Raymond's "lesson" applies, in that "how do I detect I'm running in a service" is usually probably not the right question to ask.  But I don't think "let the client tell you" is necessarily a good answer either for that.

  30. Joshua says:

    Let me make one thing abundantly clear. If your DLL does something different depending on whether or not it is on an interactive session, we will not be accepting it as a dependency. We have a testing policy that results in the spinning up of pseudo-services that work exactly like services except they aren't started via the service manager and are on an interactive session (to stop service we just kill them). If your code tries to display any UI you could hang the autotester. Other sites may need to do likewise. So, don't.

  31. ChrisR says:

    @Joshua:  I doubt anybody here knows what product you work on, and to be honest, nobody probably even cares that you won't take them as a dependency.

  32. Peter says:

    @Damien I also think that's a bad assumption.  If you're being loaded by someone else's services, then chances are that they dictated the API you must expose in order to work with them.  You also have to consider the case where your DLL is *accidentally* pulled into a service (perhaps through a dependency in another DLL, for example).

  33. Damien says:

    @Peter – in most plug-in scenarios, the host is well known, and will not be switching between being e.g. an application or a service. Those are the type of scenarios where I think it's likely that the host dictates the API – or are you thinking of something else.

    Most DLLs I work with are still ones that offer some service *to* the executable that loads them, and the contract is dictated by the DLL side.

  34. Deduplicator says:

    Heisenbugs we love you. So much we want the 'next best thing' whenever we are not multithreaded…

  35. Damien says:

    I also think that the question is misguided because it's probably at too high a granularity. If the DLL wants to (say) know whether it can or should display UI, it should get its callers to specify *that* rather than that they're a service. Some callers might want the DLL to work or fail silently (via error codes or exceptions) because, for whatever reason, the DLLs attempts at UI will be misinterpreted, confusing, etc.

    This does also go back to @Peter's "know whether you're running on the command line and display extra output" – so, the main executable has been told "shut up, show no output via flags or configuration", but the DLL should decide for *itself* whether to output additional information? That feels wrong too.

  36. Joshua says:

    @Harry Johnston: Not if you have no code that cares. All (100%) of bugs thought due to service context turned out to be differences in permissions between the testing account and the target service account or somebody trying to use a map drive from a service context.

    We got very lucky had had no session 0 violation.

  37. Myria says:

    This is to be used *for diagnostic and survey purposes only*, but you can use a CreateToolhelp32Snapshot + Process32FirstW + Process32NextW loop once to find yourself and get your parent process ID, then another such loop to find your parent process's information.  Do this as near to process startup as possible, because your parent is free to terminate whenever it feels like it.  This can fail, you can sometimes get the wrong answer for several reasons, and Raymond's caveats that are the subject of this blog post still apply, so *please* only use this for diagnostic and survey purposes only.  Your customers will thank you.

    Two reasons you can get the wrong answer:  1. Parent process can terminate and another new process can take its former PID.  2. The process that spawned you specified a different process than itself to be your "parent" (possible since NT began but only via undocumented APIs; Vista added a documented way).  And these are just two reasons why it's unreliable.

  38. Harry Johnston says:

    @Joshua: there are any number of other potentially relevant differences, such as (to choose a few at random from the top of my head) the presence or absence of explorer or other processes in the same session, the contents of the environment block, and registry entries that are only in HKEY_CURRENT_USER if the user account in question has been logged in interactively.

    But, yeah, if you're confident in the part of the code that interacts with Windows and only need to test the logic flow, I guess you'd be OK.

  39. Harry Johnston says:

    @Gabe: off-topic, but #3 won't work anyway.  I was recently surprised to discover that the logon rights (such as SeInteractiveLogonRight and SeServiceLogonRight) are not included in logon tokens.  

    As far as I can tell this is the only way that account rights (Se*Right) differ from account privileges (Se*Privilege).  I always thought they were the same thing apart from what they were used for, but it turns out they're not, quite.

    @Joshua: I hope you also do thorough testing in a genuine service context?  It is possible for code to have bugs that only exhibit when run as a service.  I don't think it's even all that uncommon.

  40. Azarien says:

    @Ian Boyd: You contradicted yourself. First you write that "Allow service to interact with desktop" option does nothing on Vista, then you elaborate what it does on Vista.

  41. Neil says:

    Sadly program "Z Server", despite its name, doesn't actually work as a service, which is what you really want things running on a server to be, so that you don't actually have to log it on after a reboot.

    I assume the creator of program "Z Server" wants to improve the program so that it works as a service. They've decided that the best way is to find out what breaks, and detect whether they're a service in order to use a different method for performing the problematic operation.

    Of course what they really need to do is to redesign their program, for example by splitting it up into UI and non-UI portions.

  42. 640k says:

    The Answer to the original question:

    msdn.microsoft.com/…/system.environment.userinteractive%28v=vs.110%29.aspx

    (and if you're writing a non-.net library, you can investigate the .net code and do the same)

  43. Harry Johnston says:

    @Azarien: no, I disagree.  Those aren't things the flag does, they're side-effects, things other system components do in response.  (By way of analogy, if I call WriteProcessMemory, the anti-virus software might terminate my program, but that doesn't mean that terminating the program is what WriteProcessMemory "does".)

    On the other hand, "allow service to interact with desktop" does (presumably) cause the process to be launched on the Winsta0Default desktop rather than in a separate per-service window station.  That no longer makes it possible to directly interact with the user, but it nonetheless affects the program's context in a way that (IMO) counts as something the flag does. :-)

  44. Joker_vD says:

    Also, one can call StartServiceCtrlDispatcher with a more-or-less valid service table.

    If it returns zero and GetLastError returns ERROR_FAILED_SERVICE_CONTROLLER_CONNECT — then you're not running in a service.

    If it returns zero and GetLastError returns ERROR_SERVICE_ALREADY_RUNNING — then you're running in a service.

    If it returns a non-zero value… well, I guess you screwed up someone's service. Oops.

  45. xpclient says:

    Is there a way for a launcher to specify which on monitor an app should open in a multi-mon configuration? The shortcut file (.lnk) should have the ability to store this just like it can specify a window state (Normal, Maximized, Minimized).

  46. cheong00 says:

    > How can my DLL detect that it is running inside a service?

    But there's something different about running in a service.

    Say, if you write library for Win7+ and it runs under desktop application, maybe you don't need to care much about hybrid sleep because most users still used to shutdown everything because hitting the "power button like button" on start menu. If your library is running under a service, you have no choice but to properly handle power events if your code acquires external resources. In such case maybe it's desirable to make it generates an error condition in case it detects the code run in service environment (fail fast).

  47. Henke37 says:

    Hmm, why not do whatever Process Explorer does to demultiplex the services? Then it is just a matter of finding the process you are in.

  48. Anon says:

    @Henke37

    I think you missed the point of the article, which is that you shouldn't try, and have no good reason to do so.

    If you think you do, you're wrong.

  49. Zan Lynx' says:

    The extra annoying thing about command line arguments to determine where the program is running from is the general lack of documentation.

    So what happens if I want to create my own scheduler item for the program? I have to know about the magic command line argument. Same for when I want to make a quick launcher or my own shortcut.

    Please, if you do it, make the program show a command line options dialog box when it is run without the right options.

  50. Gabe says:

    I'm not sure what lesson I didn't learn. The lesson for "how can I detect how my process was launched?" is that you should just have the user tell you rather than guessing. Let them put a "/quiet" switch on the command line if they don't want the dialog popping up from their scheduled task.

    The exercise to answer "how can my DLL detect that it is running inside a service?" implies to me that the DLL is being loaded into an unknown process (like a COM object, perhaps), so there is no way to get the loading process to tell you how it was launched. Thus, you have to guess.

    I suppose in the general case you can make the argument that you shouldn't detect things like whether you're running in a service, or how much RAM the machine has, or whether you're running in a VM.

    In reality, though, it's useful information to put in your debug log because environmental conditions that "shouldn't matter" could be the key hint as to why your product is failing in ways you can't reproduce.

    So let's say that when somebody tries to use my plug-in, I check to see if their trial period expired. If it did, I want to put up a dialog to give them a chance to purchase it. Of course putting up a dialog in a service will just hang the thread, (possibly hanging the service) because no user will ever be around to click on it. I want to detect that there's no user around to click and just log an error message.

    Depending on what I really want to know, I might want to look for session 0, check to see if my process token doesn't have the S-1-5-4 SID (the user logged on interactively), figure out if my process ID matches that of a known service, or maybe check to see if my window station doesn't have the WSF_VISIBLE flag.

    For my debug log I probably want to know what services are running in the same process. For determining whether to show a message box, MSDN suggests (indirectly, via msdn.microsoft.com/…/system.environment.userinteractive%28v=vs.110%29.aspx) checking the WSF_VISIBLE flag.

  51. Anon says:

    @Gabe

    In reality, the environmental conditions shouldn't matter. You shouldn't care, say, whether you're in a VM, all you should care about is that you're failing when you get Data X, and you need to properly handle Data X.

    What I often find when I open an application's log after a crash is that the application has logged everything about the user's environment and nothing useful about the actual cause of the crash.

    Developers spend an enormous amount of time trying to figure out how to control the user's environment when they should instead be controlling how their code responds to the user's environment. I see this all the time in .NET code where the developer spends an enormous amount of time creating complex error handling routines for user input, but doesn't do, say, null checking when internally passing values to code that chokes on nulls.

  52. Joshua says:

    @Harry Johnston: You are confusing non-interactive with service.

  53. Mr Cranky says:

    Every morning before I leave for work, I check to make sure I have my bullet-proof vest on by shooting myself in the stomach.  

    So far, I've never forgotten it.

Comments are closed.