Date: | November 26, 2004 / year-entry #404 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20041126-00/?p=37193 |
Comments: | 29 |
Summary: | Here's a tiny little program: #include |
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]; ShellExecuteEx(&sei); } 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 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...
|
Comments (29)
Comments are closed. |
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!
Sam
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.
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?
Speaking of which, you know there is a DDE bug introduced in MFC7 (VC2003), apparently a side effect when buffer overflow checking was added.
Raymond:
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. =))
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. :)
Ben,
As an alternative exercise, you might consider upgrading to XP :-)
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://" to a command line that could be executed directly by Windows.
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).
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: http://blogs.msdn.com/larryosterman/archive/2004/10/20/245172.aspx
Everything under HKCR is internal and doesn’t necessarily work the way that you think it should.
Ben – you want to do things the bloody hard way.
Add Open With to all files
REGEDIT4
[HKEY_CLASSES_ROOT*shellopenascommand]
@="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.
REGEDIT4
[HKEY_CLASSES_ROOTUnknownshellopenascommand]
@="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.
Noting of course that this entry point is undocumented and therefore can change at any time.
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.
That’s only while the Raymond Chen Camp hold sway at Microsoft, as soon as the MSDN Camp take over, watch out baby! :)
FYI, you haven’t initialized nShow. As a result, "shex open notepad.exe" opens Notepad in a hidden window.
Rats.
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)
Rats – Peanuts qoutation. Means ‘Darn’
Rats as in "oops". I fixed the article.
Does anyone know why
shex.exe properties C:WINNT
and
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.
Very good idea with a very simple implementation.
Please, release it with the PowerToys. :-)
"Properties" requires stuff I haven’t covered yet.
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 CMIC_MASK_ASYNCOK SEE_MASK_ASYNCOK
#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?
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.
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:
http://www.eluent.com/freeware.htm
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.
just google it
http://support.microsoft.com/default.aspx?scid=287087
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?
Quick note: Maybe it should ‘return 1;’ if argc is not equal to 3.
This was not a complete solution, just a technology demo.