Skip to content

Getting the shell to run an application for you – Part 2: How

by Brandon on April 27th, 2008

The key to getting Explorer to do your dirty work lies in the IShellDispatch2 interface.  Particular, the ShellExecute method

IShellDispatch2 is one of the shell automation objects used to support scripting languages.  However, that doesn’t mean you have to use VBScript to gain some value from it.  In this case, IShellDispatch2::ShellExecute is exactly what we want, because it wraps the normal ShellExecute call but runs it from the context of the object implementing the interface – in this case, we want the IShellDispatch2 associated with the desktop shell.

Knowing this is only half the battle, though.  The next trick is to figure out just how to get to the right IShellDispatch2 object (the one for the desktop shell instance of Explorer.exe).

Fortunately, one of our architects, Chris Guzak (seen on C9 here), was able to point me in the right direction and connect up all the dots.

Our hunt begins with the IShellWindows interface, which can be used not only to reliably find the HWND for the desktop shell window, but also to get an IDispatch interface for it:

IShellWindows *psw;
    HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
    if (SUCCEEDED(hr))
    {
        HWND hwnd;
        IDispatch* pdisp;
        VARIANT vEmpty = {}; // VT_EMPTY
        if (S_OK == psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp))
        {

Next, we need the IShellBrowser interface, and we get that by querying for IServiceProvider and asking the SID_STopLevelBrowser service for an IShellBrowser interface.  And from there we can get the IShellView.

IShellBrowser *psb;

hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
if (SUCCEEDED(hr))
{
    IShellView *psv;
    hr = psb->QueryActiveShellView(&psv);

From there we need to get to that IShellDispatch2 interface that started this whole adventure. 

IDispatch *pdispBackground;
HRESULT hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
if (SUCCEEDED(hr))
{
    IShellFolderViewDual *psfvd;
    hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
    if (SUCCEEDED(hr))
    {
        IDispatch *pdisp;
        hr = psfvd->get_Application(&pdisp);
        if (SUCCEEDED(hr))
        {
             IShellDispatch2 *psd;
             hr = pdisp->QueryInterface(IID_PPV_ARGS(&psd));

At this point you should be able to figure out where to go from here.

If that’s not easy enough, watch out for Part 3 of this series in the next day or two.  It will contain a sample and describe how the Start++ installer makes use of it.

From → Uncategorized

7 Comments
  1. Dude. That’s genius. Scary-ass complicated, but genius.

    Now all that’s needed is to package this up in a Wix installer extension, and Bob’s yer uncle. Oh, and any idea on version compatibility?

    Nice one.
    Matt

  2. Looking for this code for weeks!!!!!!

  3. Sascha permalink

    The window of the launched application does not always appear in the foreground. It seems to work only if my launcher application is started from an explorer window, but not if I start it from the run dialog (Win+R) or an alternative file manager.
    Unfortunately there is no HWND parameter to pass the parent window like in the global ShellExecute function.
    Do you know a way around this?

  4. Andrew Schaps permalink

    Was the sample code ever posted anywhere?

  5. Misak Nalbandyan permalink

    Is there any sample for this solution?

Trackbacks & Pingbacks

  1. Brandon Paddock's Blog - Desktop Search and more » 2008 » April » 27

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS