So you decided to call SHFileOperation from a service, at least remember to disable copy hooks


I noted some time ago that it is highly inadvisable to call SHFile­Operation from a service, and then I thought about it some more and concluded, it's flat-out wrong, at least in the case where you call it while impersonating.

Now, I'm sure that my opinion won't dissuade many of you, but if you decide to do it anyway, at least disable shell copy hooks by passing the FOFX_NO­COPY­HOOKS flag to IFile­Operation::Set­Operation­Flags. (We've met this flag before.)

By default, shell copy hooks are active during shell file operations, and this creates a number of problems when called from a service.

First of all, those copy hooks are unlikely to be designed to handle being run in a service. (When you write your own shell extension, do you make sure it also works when run in a service?) They're probably going to try to log the file activity, possibly to a back-end service, or maybe check with a back-end service whether this file copy should be allowed, and if not, they're probably going to display a dialog box saying "The file cannot be moved/copied/deleted because I'm a mean person who hates you due to restrictions imposed by your administrator." (Or worse, they may secretly copy the file to an undisclosed location before allowing the operation through.)

Second of all, you probably don't want those copy hooks intefering with your file operation, whatever it is. Your service is trying to clean up files, or it's moving files around for its own internal purposes, and if a copy hook showed up and blocked the operation, your service is now in a weird inconsistent state.

Note that I still consider SHFile­Operation inadvisable from a service. I'm just trying to stop you from digging your hole any deeper than it already is.

Comments (11)
  1. Mordachai says:

    Headlines: Microsoft indicates it is a good idea to use SHFileOperation from a service, according to a senior technology expert at the company.

  2. Henri Hein says:

    You made me nervous about using Shell functions from a service at all. What if I need to call SHChangeNotify() because something on a virtual disk changed? Would I need to impersonate each logged-in user and call SHChangeNotify() in that context? I have found that if I don't use SHChangeNotify(), the user will have to keep hitting F5 to see the changes, and an unwitting user will not know to do that. (What I mean is: I know about it, because I'm looking for the changes, but I assume a normal user would not).

    [Impersonation doesn't help because you're in the wrong session. If by virtual disk you mean VHD, then that should be picked up automatically because VHDs still generate file system notifications. If by virtual disk you mean virtual folder, then you need some way for your service to tell the clients that changes have occurred, and then the clients can do the SHChangeNotify. -Raymond]
  3. Ben Voigt says:

    @Henri Hein, is the virtual disk mounted in the filesystem, or accessed through a shell namespace extension?  In the former case, you can use FindFirstChangeNotification (possibly together with ReadDirectoryChangesW).

  4. Henri Hein says:

    @Raymond: thanks for the tip.  It's a virtual folder (actually mounted as a separate drive letter, but I believe that still puts it in your latter category).  Sending the notifications through the user session client makes sense.

    @Ben: It's in the filesystem, but I'm generating the events, not listening for them.

    [If it's a real file system that supports ReadDirectoryChangesW, then you can just raise standard file system events, and Explorer will pick them up. -Raymond]
  5. Henri Hein says:

    @Raymond: thanks again.  I use a 3rd party library for the driver, but I'll check if they support raising events.

    [If they don't, then apps that call FindFirstChangeNotification are in for a surprise. -Raymond]
  6. Joshua says:

    Well I can imagine how a VFS could fail to raise any or some events. It would indeed be annoying if they declared themselves incorrectly.

  7. Danny says:

    Microsoft still trying, 2 decades later, to shed off their MSDOS legacy? I mean, if you publish API's for your OS expect developers to call them from every possible corner, even using the microscope as a nut cracker.

    [Not sure how MS-DOS fits into the story. Services and copy hooks didn't exist in MS-DOS. -Raymond]
  8. Anon says:

    @Danny

    The difference between Windows and, say, OSX is that if you do that kind of thing in OSX, you might find your code permanently blacklisted.

    You call published APIs according to the published documentation, or you accept the negative consequences of your actions.

  9. Danny says:

    @Ray - [Not sure how MS-DOS fits into the story. Services and copy hooks didn't exist in MS-DOS. -Raymond

    Very simple. MSDOS was the one where u had, as developer, be careful what to call and when, otherwise the OS was very easy to get hung. I mean wasn't that the whole purpose of Windows operating systems? That if a program does crappy code that results in CPU faults the OS catches those and closes the rogue program, so the OS keeps running? And now this API is still careful documented by you to not be called with some value for its parameters? That's where the MSDOS legacy fits in my previous comment.

    [But there's no MS-DOS compatibility here, except possibly compatibility with the "MS-DOS way of thinking". In this case, you're writing a service. A service is fully capable of screwing up the entire operating system. That's sort of the point of services. It stands to reason that if you're writing a service, you have to be very careful. -Raymond]
  10. Danny says:

    @Ray - [A service is fully capable of screwing up the entire operating system. That's sort of the point of services. It stands to reason that if you're writing a service, you have to be very careful]

    I can transform any executable into a service, so the program will run before I login to do his job. Most common example is mail client. I want my mails to be already on my client from the different servers on those different accounts. So you telling me that if I have a faulty mail client, which will NOT run under Admin but merely a restricted user, I still can pawn the OS? Oh boy, so Windows from that perspective still carry the full MSDOS legacy then.

    And furthermore, a service is not a driver, to require high privileges, I can have a service for almost anything, and they run under restricted users. Which means your API function we discuss here still will get the OS hairy? boy, oh boy

    [To transform an executable into a service, you have to add a Service­Main function that responds to service control messages, and the executable needs to adhere to the special rules that a service must follow. I doubt your mail client has one of those. Now, it's true that there are people who have written adapters that have their own Service­Main function and tries to convert service control messages into some form of Create­Process and Terminate­Process, and they hope that the executable doesn't violate any of the rules for services (or if they violate them, that nothing too awful happens). This is the equivalent of building a European-to-US power converter that merely reshapes the plugs without doing any voltage or frequency conversion. If this overheats your lamp and starts a fire, don't blame the power company. -Raymond]
  11. ender says:

    @Danny: there's very little difference between drivers and services actually - both are managed by the same set of APIs, the main difference is that drivers run in kernel mode, while services run in user mode. And while services often don't run with system privileges, being able to run that way is one of the points of services.

    Some APIs simply aren't meant to be run in a non-interactive way, or as a system user. This isn't any different than *nix daemons - you shouldn't be using UI toolkits with them either.

Comments are closed.

Skip to main content