1
votes

I'm writing a toolbox program in C# and on of its functions is to emulate the Windows Start menu "Run" dialog, but also to allow the user to elevate the privileges if needed.

For this, I have a simple WinForm with a textBox (where the user types), and a checkBox (if checked, add the "runas" verb). And of course 3 buttons : OK, Cancel and Browse (which opens a file dialog). So my code looks like this :

var psi = new ProcessStartInfo(textBox1.Text, "");
psi.CreateNoWindow = true;
psi.UseShellExecute = true;
if (checkBox1.Checked == true)
{
    psi.Verb = "runas";
}
try
{
    Process.Start(psi);
}
catch (System.ComponentModel.Win32Exception ex)
{
    // do something
    MessageBox.Show("Error : " + ex.Message);
}

If users types "notepad" or "notepad.exe" or "c:\whatever\path\notepad", it works. Problems start to occur when arguments are passed : "notepad test.txt" won't work.

My first thought was to split the textBox1.Text when a space is encountered, then use the first part for the ProcessStartInfo.Filename, and the second part for the Arguments. "notepad test.txt" is ok then. But if the users uses the file dialog to select a file where the path and/or filename contains a space (or types it), of course the string will be splitted and everything will go wrong.

Unfortunately, ParseStartInfo needs both the filename and arguments (can be an empty string), but the filename cannot contain the arguments... Using quotes (for the whole textBox1.Text as filename) doesn't work either.

So, does anyone has a solution to :

  1. Either split correctly textBox1.Text to valid filename + arguments, just as the Windows Start - Run dialog does it

OR

  1. Maybe use Shell32.FileRun() but in this case, how to ask for elevation (UAC prompt) at will?

Edit : added the MessageBox.Show to show error messages

3
How about using 2 textboxes?Sinatr
Sure it will help, but my goal is really to work exactly as the Windows Start / Run dialog works (users are used to it)...Dédé Lateur
if you let the user browse you can be quite sure that there will be no arguments?=! am I right? You grab the file name directly from the dialog afterwards ?=! So in this case no splitting is necessary, only when the textbox is used. Or do you write the dialog filename into the textbox?Mong Zhu
@Mong Zhu user still can edit the textBox1.Text after browsing and add arguments, so I won't rely on this...Dédé Lateur

3 Answers

1
votes

While I usually dislike when a longer code is dumped on SO, maybe this time it is still somewhat helpful for you.

This is my own function I'm using since years to split:

/// <summary>
/// Splits the file name if it contains an executable AND an argument.
/// </summary>
public static void CheckSplitFileName( ref string fileName, ref string arguments )
{
    if ( !string.IsNullOrEmpty( fileName ) )
    {
        if ( fileName.IndexOf( @"http://" ) == 0 ||
            fileName.IndexOf( @"https://" ) == 0 ||
            fileName.IndexOf( @"ftp://" ) == 0 ||
            fileName.IndexOf( @"file://" ) == 0 )
        {
            // URLs not supported, skip.
            return;
        }
        if ( File.Exists( fileName ) )
        {
            // Already a full path, do nothing.
            return;
        }
        else if ( Directory.Exists( fileName ) )
        {
            // Already a full path, do nothing.
            return;
        }
        else
        {
            // Remember.
            string originalFileName = fileName;

            if ( !string.IsNullOrEmpty( fileName ) )
            {
                fileName = fileName.Trim();
            }

            if ( !string.IsNullOrEmpty( arguments ) )
            {
                arguments = arguments.Trim();
            }

            // --

            if ( string.IsNullOrEmpty( arguments ) &&
                !string.IsNullOrEmpty( fileName ) && fileName.Length > 2 )
            {
                if ( fileName.StartsWith( @"""" ) )
                {
                    int pos = fileName.IndexOf( @"""", 1 );

                    if ( pos > 0 && fileName.Length > pos + 1 )
                    {
                        arguments = fileName.Substring( pos + 1 ).Trim();
                        fileName = fileName.Substring( 0, pos + 1 ).Trim();
                    }
                }
                else
                {
                    int pos = fileName.IndexOf( @" " );
                    if ( pos > 0 && fileName.Length > pos + 1 )
                    {
                        arguments = fileName.Substring( pos + 1 ).Trim();
                        fileName = fileName.Substring( 0, pos + 1 ).Trim();
                    }
                }
            }

            // --
            // Possibly revert back.

            if ( !string.IsNullOrEmpty( fileName ) )
            {
                string s = fileName.Trim( '"' );
                if ( !File.Exists( s ) && !Directory.Exists( s ) )
                {
                    fileName = originalFileName;
                }
            }
        }
    }
}

I'm using it in the following way:

var fileName = textBox1.Text.Trim();
var arguments = string.Empty;
CheckSplitFileName( ref fileName, ref arguments );

Then, pass it to the ProcessStartupInfo class:

var info = new ProcessStartInfo();
info.FileName = fileName;
info.Arguments = arguments;
0
votes

I would try to split the TextBox string like that:

  1. string[] elements1 = textBox1.Text.Split(new string[] {"\\"}, StringSplitOptions.RemoveEmptyEntries); and you will get the path elements
  2. string[] elements2 = elements1[elements1.length-1].Split(new string[] {" "}, StringSplitOptions.RemoveEmptyEntries); so elements2[0] contains the application name(ex. notepad) and the other elements the filename
  3. string filename = String.Concat(elements2[1], " ", elements2[2], " ", ...)
-1
votes

You can still use the split method and than use the "first string" (the first element of the array returned from split method) as the command, and then re-concatenate the other strings to compose the name of the file.