Getting the shell to run an application for you – Part 1: Why
Some people seem to think that calling ShellExecute or ShellExecuteEx and passing the path to an executable will have the effect of telling the shell to launch an application for you. However, that’s not quite what happens. These functions simply allow your application to take an action (like launching a process) in the manner the shell typically would (for instance, recreating the behavior of using the Run box).
What’s the difference? Well, child processes inherit several things from their parents. They inherit processor affinity, for example. Most important these days, it seems, is the fact that processes inherit Integrity Level from the process that started them. That is, except in the case of elevation from medium to high IL (the operation that causes the UAC dialog to appear).
So let’s say you write an installer, and you want to run the application installed at the end of the installation process. Or heck, maybe you just want to call the application once with a special “/setup” parameter that tells it to do something you couldn’t do from your chosen setup utility.
Where do problems begin? Well, installers typically run with admin rights. When your installer launches your application as a convenience to the user, it’s now running with admin rights. The primary concern here isn’t even the security implications of running at high IL. After all, the next time your program starts it will run at its normal privilege level.
The problem is that your program is now running in a manner you may not have accounted for when writing it. For example, users won’t be able to drag-and-drop anything to your application from normal processes. If your program interacts with other non-admin processes, you may face other problems. My own Start++ application faced this problem, as it needs to inject a small amount of code into Explorer.exe and then communicate with that code to coordinate its magic. It can’t do this when running as an admin.
The recent 0.8.x release of Start++ solved this issue, and Start++ now runs immediately after install, in the proper non-admin context. This is accomplished by having Explorer launch the application on the installer’s behalf.
Others have addressed this problem in the past. However, the proposed solutions always seem to take one of these forms:
- Write an invoker process that runs with non-admin privileges, starts the elevated installer, and then runs your app at the end.
- Use the task scheduler to launch the app.
- Inject some code into Explorer and launch from there.
I don’t like #1 because it’s cumbersome and limits how you can name your installer.
#2 also seems cumbersome to me, and is a bit hacky.
#3 is totally hacky, and not something I trust people to do without accidentally destabilizing Explorer (there’s a reason the code Start++ injects into Explorer is incredibly tiny and very carefully crafted). It can also introduce complications when dealing with multiple target platforms (x86 versus x64). For example, the CodeProject sample linked to above doesn’t even restrict its WH_CALLWNDPROCRET hook to the thread that it’s targetting. That means this hook code is immediately going to be loaded by every process on the desktop with a window. Yuck.
Fortunately, there is a better way.
In Part 2, I describe a better way to get Explorer to run code for you, without having to inject anything into Explorer or use cumbersome workarounds like those described above.