2

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

6
  • How about using 2 textboxes? Commented Jan 9, 2018 at 10:23
  • 1
    Sure it will help, but my goal is really to work exactly as the Windows Start / Run dialog works (users are used to it)... Commented Jan 9, 2018 at 10:25
  • 1
    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? Commented Jan 9, 2018 at 10:27
  • 1
    stackoverflow.com/q/298830/1271037 stackoverflow.com/a/17052018/1271037 Commented Jan 9, 2018 at 10:36
  • @Mong Zhu user still can edit the textBox1.Text after browsing and add arguments, so I won't rely on this... Commented Jan 9, 2018 at 10:54

3 Answers 3

2

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;
Sign up to request clarification or add additional context in comments.

4 Comments

I think we're really near, unfortunately, it fails on "notepad test.txt" with a File not found error (whereas the Run dialog opens notepad, which asks if test.txt is to be created). But it works on "notepad" or "C:\A long\Path with\Spaces\my file.xlsx"...
Ok, just commented out the code block (the nested ifs) in the function after "Possibly revert back" and it looks like it works as I expected ! URLs, spaced path/file names, notepad with or without args, everything seems to work ! Many thanks !
Thanks for choosing my answer!
Thanks Uwe for answering ! :)
0

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], " ", ...)

Comments

-1

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.

2 Comments

Except it won't work if the command is something like "C:\Program Files\whatever.exe" as the first string will be "C:\Program" (and the second "Files\whatever.exe")... It works for "notepad test.txt", for instance, but not for anything else containing one or more space (also consider "c:\users\me\My Documents\latest figures.xlsx" : two spaces)
Correct, my fault

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.