【问题标题】:Split filename and arguments from user string从用户字符串中拆分文件名和参数
【发布时间】:2018-01-09 10:19:00
【问题描述】:

我正在用 C# 编写一个工具箱程序,它的一个功能是模拟 Windows 开始菜单的“运行”对话框,而且还允许用户在需要时提升权限。

为此,我有一个简单的 WinForm,其中包含一个文本框(用户在其中键入)和一个复选框(如果选中,则添加“runas”动词)。当然还有 3 个按钮:确定、取消和浏览(打开文件对话框)。所以我的代码看起来像这样:

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);
}

如果用户键入“notepad”或“notepad.exe”或“c:\whatever\path\notepad”,它就可以工作。传递参数时开始出现问题:“notepad test.txt”不起作用。

我的第一个想法是在遇到空格时拆分 textBox1.Text,然后将第一部分用于 ProcessStartInfo.Filename,将第二部分用于 Arguments。 “notepad test.txt”就可以了。但是如果用户使用文件对话框来选择路径和/或文件名包含空格(或键入它)的文件,那么字符串当然会被拆分,一切都会出错。

不幸的是,ParseStartInfo 需要文件名和参数(可以是空字符串),但文件名不能包含参数......使用引号(整个 textBox1.Text 作为文件名)也不起作用。

那么,有没有人有解决方案:

  1. 将 textBox1.Text 正确拆分为有效的文件名 + 参数, 就像 Windows 的“开始 - 运行”对话框一样

  1. 也许使用 Shell32.FileRun() 但在这种情况下,如何请求 随意提升(UAC 提示)?

编辑:添加 MessageBox.Show 以显示错误消息

【问题讨论】:

  • 使用2个文本框怎么样?
  • 当然会有所帮助,但我的目标是完全按照 Windows 开始/运行对话框的工作方式工作(用户已经习惯了)...
  • 如果你让用户浏览你可以很确定不会有参数?=!我对吗?之后直接从对话框中获取文件名 ?=!所以在这种情况下,只有在使用文本框时才需要拆分。还是将对话框文件名写入文本框?
  • @Mong Zhu 用户浏览后仍然可以编辑 textBox1.Text 并添加参数,所以我不会依赖这个...

标签: c# winforms uac processstartinfo


【解决方案1】:

虽然我通常不喜欢将较长的代码转储到 SO 上,但也许这一次它对您仍然有些帮助。

这是我多年来一直在使用的自己的函数:

/// <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;
                }
            }
        }
    }
}

我的使用方式如下:

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

然后,将其传递给ProcessStartupInfo 类:

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

【讨论】:

  • 我认为我们真的很近了,不幸的是,它在“notepad test.txt”上失败,并出现 File not found 错误(而“运行”对话框打开记事本,询问是否要使用 test.txt创建)。但它适用于“记事本”或“C:\A long\Path with\Spaces\my file.xlsx”...
  • 好的,刚刚在“可能恢复”之后注释掉了函数中的代码块(嵌套的 ifs),它看起来像我预期的那样工作! URL,带间距的路径/文件名,带或不带 args 的记事本,一切似乎都有效!非常感谢!
  • 感谢您选择我的答案!
  • 感谢 Uwe 的回答! :)
【解决方案2】:

我会尝试像这样拆分 TextBox 字符串:

  1. string[] elements1 = textBox1.Text.Split(new string[] {"\\"}, StringSplitOptions.RemoveEmptyEntries); 你会得到路径元素
  2. string[] elements2 = elements1[elements1.length-1].Split(new string[] {" "}, StringSplitOptions.RemoveEmptyEntries); 所以elements2[0] 包含应用程序名称(例如记事本)和文件名的其他元素
  3. string filename = String.Concat(elements2[1], " ", elements2[2], " ", ...)

【讨论】:

    【解决方案3】:

    您仍然可以使用 split 方法,然后使用“第一个字符串”(从 split 方法返回的数组的第一个元素)作为命令,然后重新连接其他字符串以组成文件名。

    【讨论】:

    • 除非命令类似于“C:\Program Files\whatever.exe”,否则它将不起作用,因为第一个字符串将是“C:\Program”(第二个是“Files\不管什么.exe”)... 例如,它适用于“notepad test.txt”,但不适用于包含一个或多个空格的任何其他内容(也可以考虑“c:\users\me\My Documents\latest figure.xlsx” : 两个空格)
    • 正确,我的错
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-16
    • 2020-12-01
    相关资源
    最近更新 更多