Simple things you can do with the ShellExecuteEx function

Here's a tiny little program:

#include <windows.h>
#include <shellapi.h>

int __cdecl main(int argc, char **argv)
  if (argc == 3) {
    SHELLEXECUTEINFO sei = { sizeof(sei) };
    sei.fMask = SEE_MASK_FLAG_DDEWAIT;
    sei.nShow = SW_SHOWNORMAL; // added 27 Nov
    sei.lpVerb = argv[1];
    sei.lpFile = argv[2];
  return 0;

This is a little program that takes two parameters, the first being the verb and the second the file upon which to execute the verb. Notice that since we exit immediately, we need to set the SEE_MASK_FLAG_DDEWAIT flag: Normally, the ShellExecuteEx function assumes that there will be a message pump running after it returns. This allows it to return quickly and continue any necessary DDE conversations as the responses arrive from the DDE server. But if the thread is exiting or if the thread is not a GUI thread (both of which are true here), you want to suppress this behavior because there is no message pump around to complete the DDE conversation. Setting the SEE_MASK_FLAG_DDEWAIT flag indicates that the ShellExecuteEx function should finish its DDE conversation before it returns.

Anyway, I wrote this little program to illustrate two of the canonical verbs that you can use. It seems the people don't realize that ShellExecuteEx can be used to perform these actions, since it gets asked a lot...

  • shex find %windir%
    Opens the search window with a specified folder as the default "Search in" location.

  • shex openas C:\AUTOEXEC.BAT
    Displays the "Open with" dialog for a file.
Comments (29)
  1. Sam says:

    openas is handy indeed! I was still pondering for my pet project how to implement this without having to peruse the registry.

    Now I only got to translate your example to c# and be set, thanks!


  2. Simon says:

    Is there any specific reason why you’d use ShellExecuteEx instead of ShellExecute in situations like the one you cite?

    On a different note: thanks for maintaing this interesting blog. I’ve learned quite a lot so far – and enjoyed it.

  3. Stephane Rodriguez says:

    Now that is interesting. Raymond, what about being able to get over the limitation of the handled size of lpFile ? Would be cool to pass the size of your choice, especially for those of us using shellexecute to perform a mailto:.

    How come winexec is less limited than shellexecuteex in the length you can pass?

  4. Stephane Rodriguez says:

    Speaking of which, you know there is a DDE bug introduced in MFC7 (VC2003), apparently a side effect when buffer overflow checking was added.

  5. wd says:


    Are these verbs tied to any specific versions of Windows? If so, what’s the earliest version of Windows that can use them? (If you don’t know that’s okay. =))

  6. Ben Cooke says:

    That’s neat.

    When I have a spare moment I’m going to make that into a GUI app (just a matter of using WinMain and calling that function to get an argc/argv pair, of course) so that I can add yet another fun thing to my context menus for all files: "Open With…" where I don’t have to hold shift first. :)

  7. Serge Wautier says:


    As an alternative exercise, you might consider upgrading to XP :-)

  8. Martin Kochanski says:

    ShellExecuteEx doesn’t work if you use it in a multi-threaded apartment under Windows XP.

    When you try to open something starting with http: (and probably mailto: etc), ShellExecuteEx returns ERROR_ACCESS_DENIED if you do it from the MTA. The identical call in the main STA works with no problem.

    I had to implement all the registry lookup by hand, to translate "http://&quot; to a command line that could be executed directly by Windows.

  9. Raymond Chen says:

    Yes, the shell functions require STA. This is documented in the KB (probably other places too). Just fire up a worker thread in STA.

    If you grovel the registry directly, then you may run into problems when the registration schema is extended (e.g. on 64-bit Windows).

  10. Martin,

    The thing is that the COM registry is owned by COM, not by you. Groveling the registry is a recipe for disaster, especially if it’s a COM application installed SxS:

    Everything under HKCR is internal and doesn’t necessarily work the way that you think it should.

  11. David Candy says:

    Ben – you want to do things the bloody hard way.

    Add Open With to all files



    @="C:\WINDOWS\rundll32.exe shell32.dll,OpenAs_RunDLL ’%1’"

    On files that are "unknown" by Windows this will add two Open With menu options. This is because Unknown file types already have this command defined for them.

    To restore Open With for unknown types use this reg file.



    @="C:\WINDOWS\rundll32.exe shell32.dll,OpenAs_RunDLL ’%1’"

    Check the path for your OS.

    This still applies to ME/XP as they use verbs for unknown and context menu extension for most files anyway.

  12. Raymond Chen says:

    Noting of course that this entry point is undocumented and therefore can change at any time.

  13. Except that you won’t change it, since people are using it. You never change anything that people use in any program anywhere, no matter how undocumented or internal it is.

  14. Jon Potter says:

    That’s only while the Raymond Chen Camp hold sway at Microsoft, as soon as the MSDN Camp take over, watch out baby! :)

  15. A says:

    FYI, you haven’t initialized nShow. As a result, "shex open notepad.exe" opens Notepad in a hidden window.

  16. David Candy says:

    What does Rats mean.

    Rats as in I’m mad. Rats as in I disagree. Rats as in Opps. And a US meaning I think – Rats as in you are a dobber (this one here is Dog in Australia – cus you shoot them like a mangy dog).

    Set objShell = CreateObject("Shell.Application")

    ‘object.ShellExecute(sFile [,vArguments] [,vDirectory] [,vOperation] [,vShow])

    Is still easier – notepad is very powerful. Does your program Eject the CD-Rom as the above statement won’t. One has to walk the namespace getting references to folders and verbs collections.

    Set objShell = CreateObject("Shell.Application")

    Set Ag = WScript.Arguments

    objshell.ShellExecute Ag(1) ,,,Ag(0)

    Should, in theory (too lazy to cut and paste to notepad to test). duplicate your program in an accessable way.

    Add these lines to add to AppPaths so the system can find it without typing the path (need to run it once whenever one moves it)

    set WshShell = WScript.CreateObject("WScript.Shell")

    WshShell.RegWrite "HKLMSoftwareMicrosoftWindowsCurrentVersionApp Paths" & Wscript.ScriptName & "", Chr(34) & Wscript.ScriptFullName & Chr(34)

    WshShell.RegWrite "HKLMSoftwareMicrosoftWindowsCurrentVersionApp Paths" & Left(Wscript.ScriptName, Len(Wscript.ScriptName)-3) & "exe" & "", Chr(34) & Wscript.ScriptFullName & Chr(34)

    Getting back to apppaths. For non exe files it’s best to register them twice, once under the real name and once with the extension replaced by exe. This second allows the typing of the filename without the extension.

    Although thinking about it I’ve not seen the exe vs other extensions behaviour documented. So use with care.

    Set objShell = CreateObject("Shell.Application")

    Set Ag = WScript.Arguments

    set WshShell = WScript.CreateObject("WScript.Shell")

    objshell.ShellExecute Ag(1) ,,,Ag(0)

    WshShell.RegWrite "HKLMSoftwareMicrosoftWindowsCurrentVersionApp Paths" & Wscript.ScriptName & "", Chr(34) & Wscript.ScriptFullName & Chr(34)

    WshShell.RegWrite "HKLMSoftwareMicrosoftWindowsCurrentVersionApp Paths" & Left(Wscript.ScriptName, Len(Wscript.ScriptName)-3) & "exe" & "", Chr(34) & Wscript.ScriptFullName & Chr(34)

  17. Cooney says:

    Rats – Peanuts qoutation. Means ‘Darn’

  18. Raymond Chen says:

    Rats as in "oops". I fixed the article.

  19. Tom says:

    Does anyone know why

    shex.exe properties C:WINNT


    shex.exe properties C:AUTOEXEC.BAT

    don’t seem to work?

    I get "This file does not have a program associated with it for performing this action. Create an association in the Folder Options control panel"

    It looks like this is what Nero does when you rightclick on files and directories in a compilation, except it works for Nero.

  20. Very good idea with a very simple implementation.

    Please, release it with the PowerToys. :-)

  21. Raymond Chen says:

    "Properties" requires stuff I haven’t covered yet.

  22. Doug Harrison says:

    This reminds me of a context menu issue. Any idea why CMINVOKECOMMANDINFO has the CMIC_MASK_ASYNCOK flag, which apparently does something similar to SEE_MASK_FLAG_DDEWAIT, but CMINVOKECOMMANDINFOEX does not define this flag? It supports all the other non-EX CMIC flags, so I suspect a documentation omission, but you never know…

    In fact, grepping the headers reveals:


    #define SEE_MASK_ASYNCOK 0x00100000

    #define SEE_MASK_FLAG_DDEWAIT 0x00000100

    So it’s not exactly the same flag. I can’t find any documentation on SEE_MASK_ASYNCOK, but the CMIC_MASK_ASYNCOK is described very similarly to SEE_MASK_FLAG_DDEWAIT. Is there a significant difference?

  23. Raymond Chen says:

    Those docs are confused. I’ll make a note to see what I can do to clean them up, but I’m backlogged until May.

  24. Tom:

    To get "properties" out of ShellExecuteEx, you have to use the SEE_MASK_INVOKEIDLIST flag and keep your program alive "as long as necessary"; I’ll point you to a more detailed discussion of this below.

    There is a free program "runmenu" that can execute the "properties" command and most every other context menu command programmatically, either by verb or by menu item text, including weird things like items on the "Send To" menu:

    This page also discusses some of the difficulties peculiar to handling "properties" and actually many other context menu commands from a console or other short-lived program. This discussion begins under the heading, "Running a Command: The /verb option", and the issues discussed there also affect ShellExecuteEx(SEE_MASK_INVOKEIDLIST).

    Runmenu provides a number of ways to work around the caller lifetime problem, and it does a number of other things ShellExecuteEx cannot do, such as running verbless commands using the menu item text, including items on submenus. Runmenu’s /list option also makes it possible to discover verbs, which can come in handy for those times when you want to give ShellExecuteEx a try.

  25. Martin Kochanski says:

    Do you have a reference to the KB article on ShellExecuteEx that refers to the fact that you need to use an STA? I’ve looked at the main doc page on ShellExecuteEx and I can’t see a reference there.

    Also – is it *the* STA I need to use (the first one, that does OLE), or will *an* STA (possibly created on the fly) be enough?

  26. Quick note: Maybe it should ‘return 1;’ if argc is not equal to 3.

  27. Raymond Chen says:

    This was not a complete solution, just a technology demo.

Comments are closed.