Getting the shell to run an application for you – Part 2: How
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.
Trackbacks & Pingbacks
Comments are closed.
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
Looking for this code for weeks!!!!!!
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?
Was the sample code ever posted anywhere?
here is another solution for it.
http://mdb-blog.blogspot.com/2013/01/nsis-lunch-program-as-user-from-uac.html
Is there any sample for this solution?