Jump to content
  • Advertisement
Sign in to follow this  
doctorsixstring

[.net] ShellExecuteEx API call in C#

This topic is 3858 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have successfully made a call to the Win32 API ShellExecuteEx function from my C# app (a game resource manager). I did this so that I can open resources from my app via a "Open With..." dialog (after extraction of the file to the TEMP folder). This also allows users to specify what application they want to use to open a file with an unknown extension (you know, the "Windows cannot open this file:" dialog). When a resouce file is opened, the resource editor creates a thread that monitors the file for changes. When changes are detected, the app asks the user if they want to update the archive with the changes (like WinZip). My next problem is getting a handle to the application process generated after the "Windows cannot open this file:" dialog. I want to be able to grab the process handle of Photoshop, Notepad, or whatever, so I know when the application closes and I can close the thread that monitors the file. The API documentation says to set the SHELLEXECUTEINFO structure's 'mask' property to SEE_MASK_NOCLOSEPROCESS. As you can see in the code below, I am doing this, but the hProcess property always returns a 0. I also overrode the form's WndProc() method so I can see if any related messages are received from the API (none are). A possible problem could be that ShellExecuteEx() returns immediately, and does not wait for the application-selection dialogs to be completed by the user. Obviously, it wouldn't know about any process if the property is getting set before the user picks an application. So, how can I get ShellExecuteEx() to return the process of the executed application? Also, if anyone knows of a better/easier way to accomplish all this (especially w/o calling the API directly), feel free to let me know! Here is the code I used to call ShellExecute:
class FileForm
{
	/* tons of other stuff */

	[DllImport("Shell32.dll")]
	public static extern int ShellExecuteEx(SHELLEXECUTEINFO lpExecInfo);

	protected override void WndProc(ref Message m)
	{
		base.WndProc(ref m);
	}

	private void OpenFile(string filepath)
	{
		if(File.Exists(filepath))
		{
			try
			{
				System.Diagnostics.Process.Start(filepath);
			}
			catch(System.ComponentModel.Win32Exception ex)
			{
				if( ex.NativeErrorCode == 1155 )	// 1155 = ERROR_NO_ASSOCIATION
				{
					const int SEE_MASK_NOCLOSEPROCESS = 64,		// 0x00000040
						SW_SHOWNORMAL = 1;

					SHELLEXECUTEINFO ei = new SHELLEXECUTEINFO();
					ei.cbSize = 60;			// sizeof(SHELLEXECUTEINFO);
					ei.fMask = SEE_MASK_NOCLOSEPROCESS;
					ei.lpVerb = "openas";
					ei.lpFile = filepath;
					ei.nShow = SW_SHOWNORMAL;			// 1 = SW_SHOWNORMAL

					int result = ShellExecuteEx(ei);
					if(result == 1)
						success = true;
				}
			}
		}
		else
			MessageBox.Show("File \"" + filepath + "\" not found!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
	}
}

[StructLayout(LayoutKind.Sequential)]
public class SHELLEXECUTEINFO 
{
	public int cbSize = 60;
	public int fMask = 0;
	public int hwnd = 0;
	public string lpVerb = null;
	public string lpFile = null;
	public string lpParameters = null;
	public string lpDirectory = null;
	public int nShow = 0;
	public int hInstApp = 0;
	public int lpIDList = 0;
	public string lpClass = null;
	public int hkeyClass = 0;
	public int dwHotKey = 0;
	public int hIcon = 0;
	public int hProcess = 0;
}


Share this post


Link to post
Share on other sites
Advertisement
Why don't you use System.Diagnostics.Process.Start ?
This will return a Process object.

Cheers

Share this post


Link to post
Share on other sites
As far as I can tell, System.Diagnostics.Process.Start() won't display the "Open With..." dialog, it merely throws a System.ComponentModel.Win32Exception. I was originally hoping that the "OpenWith" dialog would be one of the common windows dialogs, like the Open, Save, Font, Color and Print dialogs.

Share this post


Link to post
Share on other sites
Well can you not just make your own Open With... Dialog or use an open file dialog window to prompt the user to open a file?

Share this post


Link to post
Share on other sites
I've thought about implementing my own dialog, but that would be extra work that I'd rather not do. I won't have a choice if I can't figure this out, though :( Also, just as an FYI, the dialog I want is not used to select a file, but is used to select an application to open a previously-selected file. For example, if the ABC file extension has no assigned application in Windows, you would see this dialog when you double-click an ABC file. You would then use the Open With... dialog to specify what application (like Notepad) that you want to use to open the file with.

-Mike

Share this post


Link to post
Share on other sites
Any ideas? I can't believe that this is not possible with the main .NET library, let alone calls to the Win32 API.

Any [additional] thoughts would be apprecited.

Share this post


Link to post
Share on other sites
I found some information about using the "openas" verb when starting the process, but it hasn't worked in my tests. You could always add the needed information to the registry if opening the file fails...

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You can get the "Open with..." dialog to appear using the Process managed class.

Process proc = new Process();
ProcessStartInfo info = new ProcessStartInfo("someFile.hello");
info.UseShellExecute = true; // here's the trick

proc.StartInfo = info;

Share this post


Link to post
Share on other sites
That doesn't seem to work. I assumed that I need to Start() the process after initializing it using the AP's code. In addition, I tried using the "openas" verb. In any case, a Win32Exception is thrown on the call to Start():

"An unhandled exception of type 'System.ComponentModel.Win32Exception' occurred in system.dll

Additional information: No application is associated with the specified file for this operation"

Here is the exact code I used:

Process proc = new Process();
ProcessStartInfo psi = new ProcessStartInfo(filepath);
psi.UseShellExecute = true;
psi.Verb = "openas";
proc.StartInfo = psi;
proc.Start();

Share this post


Link to post
Share on other sites
The approach I have been playing with recently has been to call a function in a seperate, unmanaged C++ DLL. I know this is overkill, but it is the only method that seems to work. Here is the code for the function in my ShellExecuter.DLL:


#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#include <stdio.h>

extern "C" int __declspec(dllexport) OpenFileWith(LPCTSTR filepath, HWND parent, HANDLE* process)
{
SHELLEXECUTEINFO sei;
ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));
sei.cbSize = sizeof(SHELLEXECUTEINFO);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = "openas";
sei.lpFile = filepath;
sei.nShow = SW_SHOWNORMAL;
sei.hwnd = parent;

int result = ShellExecuteEx(&sei);

*process = sei.hProcess;

return result;
}





Here is the .NET code that uses it:

// declare the DLLImport function
[DllImport("ShellExecuter.dll")]
public static extern int OpenFileWith(string filepath, IntPtr parent, ref IntPtr process);

// calling the function
IntPtr process = IntPtr.Zero;
int result = OpenFileWith(filepath, Handle, ref process);





This runs great - it opens the "Open With" dialog and returns the process handle to my app. My only problem now is that I'm not sure what to do with the process handle. Can I simply cast it to a System.Diagnostics.Process object?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!