Is it wrong to call SHFileOperation from a service?

A customer had a simple question: "Is it wrong to call SHFile­Operation from a service?"

I don't know if I'd call it wrong, but I'd call it highly inadvisable.

  • SHFile­Operation was designed for interactive operations, so you're using it outside its original design parameters.
  • Many shell extensions ignore "no UI" flags and put up UI anyway. As a result, your call to SHFile­Operation may end up getting stuck on unexpected UI. Now you have a service displaying UI, and that's just asking for trouble.
  • The shell for the most part does not expect to be called while impersonating. There are a few functions specifically designed for use while impersonating; those exceptions are called out explicitly in their respective documentation. SHFile­Operation is not one of those functions.
  • Since SHFile­Operation uses the shell namespace, you are at risk of loading shell extensions into a service. Shell extensions typically are not written with the strict security requirements of a service in mind, and you may end up creating a security hole. Somebody could plant a desktop.ini into a directory your service operates on, and now your service has been tricked into loading a shell namespace extension. The bad guys are constantly searching for buggy shell extensions that they can use as an attack point. And if they can get into a service, well, then they just hit the jackpot!

Update: See Is it wrong to call SHFileOperation from a service? Revised.

Comments (20)
  1. Nicholas says:

    I've always found shell extensions to be very interesting from a cost/benefit point of view.  The best of them are extremely powerful tools that do some wonderful things, but even the average ones tend to cause very noticeable problems, and the good/bad distribution is far from normal.  I imagine there have been some interesting internal discussions about the future of them.

  2. My answer without reading the post: Hell, yes, it is wrong!

  3. Antonio 'Grijan' says:

    Apart from the first three reasons (#1 and #2 should be immediately obvious to anyone writing a service!), the fourth one is worrisome. When I used Windows XP, I had many of problems with Explorer (hangs, protection errors, etc.), and I could trace every single of them to a buggy shell extension. It looks like Windows 7 64-bit has a lot less trouble, maybe because most software publishers don't provide 64-bit versions of their shell extensions. I think shell extensions should always be an opt-in feature in installers (as browser toolbars are, at least some times *cough*), but I'm getting out of topic :-) .

  4. Gabe says:

    Does this mean it's also inadvisable to use the IFileOperation interface from a service? I suspect that since it's more powerful than SHFileOperation, then it must be even more inadvisable.

  5. Klimax says:


    It's in Shell interfaces, and provides dialogs. Also replaces SHFileOperation.

    Answer I think is quite obvious…

  6. 640k says:

    Windows should provide pit-of-success. In this case, don't allow a non-interactive service to call a interactive shell function, it should throw an exception. Don't document "it's inadvisable" in some non-official documentation. It should be strictly forbidden and technically impossible.

    [How hard should an API check that it is not being used incorrectly? Does every function begin with "if (is_running_in_service() || is_called_from_dllmain() || is_impersonating() || …)"? Soon, you'll find that 10% of your CPU time is spent just checking the prerequisites and not doing actual work. -Raymond]
  7. Western Infidels says:

    I'm working on Windows Service project where this information is relevant, but it's news to me. That's really bad.

    Is there some part of the API documentation I'm missing? The page for SHFileOperation(), for example, says "[desktop apps only]" but has no further explanation and no link to an explanation. It's not even clear what "desktop" means in this context; I mean, my service isn't specifically targeting the Server versions of Windows.

    Why didn't I understand this already? What other calls in my service are fraught with complex side effects that are difficult to glean from the docs?

    Whatever the answer, I'm glad my RSS has The Old New Thing in it.

    [In MSDN, "desktop app" means "not a Windows Store app". You should assume nothing works in a service unless it is documented to work in a service. (I know this doesn't pan out in practice because HeapAlloc works in a service but there is no mention of service-friendliness.) One access to HKEY_CURRENT_USER and everything explodes. -Raymond]
  8. Harry Johnston says:

    Probably the best rule of thumb is to check which DLL the function is contained in.  MSDN almost always documents this, under the heading "Requirements", usually towards the end of the article.

    Off the top of my head:

    If it's in kernel32.dll or advapi32.dll, it should be safe in pretty much any context unless otherwise documented.

    Functions in user32.dll are probably safe in a service if used appropriately but should be treated with some caution.

    Functions in shell32.dll, gdi32.dll, etc., should probably be avoided.

    Anybody got a more complete list?

  9. Joker_vD says:

    "One access to HKEY_CURRENT_USER and everything explodes."

    So LOCAL SERVICE/NETWORK SERVICE/LOCAL SYSTEM are fake users, and HKEY_CURRENT_USER keys are not set up for them? Ugh, that's bad.

  10. Anon says:


    Why is that bad? They aren't for User applications, they're for Services. Services shouldn't be accessing HKCU.

  11. Karellen says:

    @Anon – So, services shouldn't access HKCU because they run as $SPECIAL_USER$, but $SPECIAL_USER$ doesn't have HKCU set up because they're "only" for running services? Isn't that reasoning somewhat circular?

    All processes run in the context of a user account. Some user accounts are meant for people to log in with, and some aren't – so far, so normal. But if simply trying to access a user-specific piece of data for some types of user suddenly means that "everything explodes", well, I'm still left scratching my head and asking "Why would it explode?". (I'm also wondering what the definition of "everything explodes" is. System bluescreen? All system services crash? Single service crash? Single service restart? Or is RegOpenKeyEx(HKCU) returning a non-zero error code now classified as an "everything has exploded" scenario?)

    What about services that run as a "real" user, for which HKCU should exist?

  12. Anon says:


    I'm not sure what's circular… Services should run as special accounts. Special accounts don't have HKCU. Therefore, Services don't have HKCU.

    Services should never assume a "real" user. Why would they? What possible good purpose could that have? Services aren't supposed to be interactive. If you want an interactive service, you run something at login.

  13. Joshua says:

    HKEY_CURRENT_USER works just fine in a service. Sure it fails when impersonating, but I can count on zero hands the number of times I've used impersonation for production services.

    [The problem is that not only does it not work when impersonating, using it while impersonating screws up future use of HKCU even after you stop impersonating. -Raymond]
  14. Karellen says:

    @Anon "Services should never assume a "real" user. Why would they?"

    Off the top of my head, how about a per-user cron job? User "Alice" wants to download the contents of a URL every hour, whether she's logged in or not. You just want a small service to grab the contents of a URL and write it to a file, whose filename contains a timestamp. In order that the service can write the file into Alice's profile folder, but not accidentally (or maliciously) write to Bob's profile folder, the service should run as user Alice.

    How about a DVCS server? Carol wants to run one to share her public repositories to her co-workers, and wants to allow people to submit patches – which get saved into the object store in the DVCS, which simply works on flat files in the filesystem, in her user profile. Although it might require the Admin to set up the service, the configuration of which repos to share and where they are, should be up to Alice. However, she shouldn't be able to misconfigure it to write stuff to Dave's profile, so it ought to run as her. As a non-interactive service. When she's logged out.

  15. Nick says:

    @Karellen: There's a tool for your per-user cron jobs, it's called the "Task Scheduler."

  16. Hm says:

    @Nick: What do you think how Task Scheduler is implemented? How does it map the correct registry part to the HKCU alias? Just magic?

    @Raymond, in your linked article:

    "but if your COM server then calls a helper object which calls SHGetKnownFolderPath and passes NULL for the impersonation token" Why is there a token needed? The thread is impersonated, so SHGetKnownFolderPath() should work correctly by default, assuming the impersonated user account of its calling thread when called without an explicit token.

    "The registry keys HKEY_CURRENT_USER and HKEY_CLASSES_ROOT don't work when you're impersonating. (You have to use RegOpenCurrentUser or RegOpenUserClassesRoot.)" Why is the Registry API not doing providing HKCU and HKCR as virtual aliases, based on the user the calling thread is impersonating?

    "The problem is all the code that's not part of the system which assumes identity does not change (because it rarely does)." I consider the Shell API part of the system because it is designed by the Windows team, shipped with Windows, documented as Windows API. Why do you (or the Shell team) deny the task to make SHGetKnownFolderPath() just work according to the impersonation of the callig thread?

  17. Rick C says:

    @hm, Task Scheduler probably maps the registry by virtue of you putting in credentials when you create the task.  All it's got to do is run the task as you instead of impersonating.

  18. Karellen says:

    @Anon "Services should run as special accounts. Special accounts don't have HKCU. Therefore, Services don't have HKCU"

    OK then, the question is – why don't special accounts have HKCU? Given that making exceptions and special cases generally adds complexity to a system (which generally is not helpful), and that this special case can cause an "everything explodes" scenario (which is actively bad), what is the upside in not providing an HKCU to these special accounts, while other accounts do get HKCU?

  19. ErikF says:

    @Hm: Not all APIs are designed to work for all parts of the system (for example, DirectX is part of the system and designed by the Windows team, but it certainly was not tested with services in mind.) I treat services in the same category as POSIX subsystem programs: yes, they run in Windows, but they don't run as standard Win32 user applications.

    @Karellen: I'm guessing here, but some of the services load pretty early and might not even have a full registry available yet (i.e. SAM), so having HKCU for them seems pointless for the number of times that it would be required; as documented, services should be using HKLM. Anyways, the builtins have big issues when you're trying to determine which service managed to pooch the system; the less things that share the accounts, the better!

  20. Joker_vD says:

    Aaaaaand it turns out that special accounts *do* have HKCU! Look:…/ms684188%28v=vs.85%29.aspx (LocalService) — "The LocalService account has its own subkey under the HKEY_USERS registry key. Therefore, the HKEY_CURRENT_USER registry key is associated with the LocalService account."…/ms684190%28v=vs.85%29.aspx (LocalSystem) — "The registry key HKEY_CURRENT_USER is associated with the default user, not the current user. To access another user's profile, impersonate the user, then access HKEY_CURRENT_USER."…/ms684272%28v=vs.85%29.aspx (NetworkService) — "The NetworkService account has its own subkey under the HKEY_USERS registry key. Therefore, the HKEY_CURRENT_USER registry key is associated with the NetworkService account."

    And wait, didn't the second link just said "Using HKCU while impersonating is OK"?

Comments are closed.

Skip to main content