Skip to content

Getting the shell to run an application for you – Part 1: Why

by Brandon on April 26th, 2008

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:

  1. Write an invoker process that runs with non-admin privileges, starts the elevated installer, and then runs your app at the end.
  2. Use the task scheduler to launch the app.
  3. 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.

From → Uncategorized

3 Comments
  1. Your claim that “…this hook code is immediately going to be loaded by every process on the desktop with a window” is incorrect. The hook code gets loaded into a process only when the conditions for the hook to execute for that process occur, not immediately. Since the CodeProject article you mentioned installs the hook for a very short time (just enough to send a message to the shell), and then removes the hook, it means that the hook code will not be loaded into every process or thread, it will only be loaded into the shell process, as it was intended to.

  2. Hi Andrei –

    There is no way to know what other window threads will pump messages while you have the hook installed. The fact is that the hook will almost always be loaded into other processes.

    Since you know which window you’re targetting, you should always restrict the hook to the thread that owns that window. There is no reason *not* to as a call to GetWindowThreadProcessId is cheap and easy as cake. Passing NULL for the fourth parameter to SetWindowsHookEx is meant for the rare case when you actually want to hook every window’s message loop.

  3. Herman permalink

    It seems to me that solution #1 “limits how you can name your installer” is not totally correct. You are supposed to include a manifest in the executable indicating the required execution level, don’t you?. File name detection applies only to executables without a manifest.

Comments are closed.