VS调用CMD(或外部程序)传递参数

来源:互联网 发布:直接对二维数组排序 编辑:程序博客网 时间:2024/05/21 00:01

      项目中有一部分核心代码是用C++控制台来写的。需要在.net中去调用。本以为是很简单的事情,但是中间却有一个非技术性但是却又没有头绪的问题困扰了我一天,现在终于云开雾散,特在此记述纪念这一天的时间。

      调用C++使用的是.net中的Process类。代码如下
                if (dosCommand != null && dosCommand != " ")
                {
                    Process process = new Process();
                    ProcessStartInfo startInfo = new ProcessStartInfo();
                    startInfo.FileName = "cmd.exe";
                    startInfo.Arguments = "/c " + dosCommand;//设定参数,其中的“/”表示执行完命令后马上退出
                    startInfo.UseShellExecute = false;
                    startInfo.RedirectStandardInput = false;
                    startInfo.RedirectStandardOutput = true;
                    startInfo.CreateNoWindow = true;
                    process.StartInfo = startInfo;
                    try
                    {
                        if (process.Start())
                        {
                            if (outtime == 0)
                            {
                                process.WaitForExit();
                            }
                            else
                            {
                                process.WaitForExit(outtime);
                            }
                            output = process.StandardOutput.ReadToEnd();
                            return process.ExitCode;
                        }
                        else
                        {
                            throw new ApplicationException("未能创建进程。");
                        }
                    }
                    finally
                    {
                        if (process != null)
                            process.Close();
                    }

                }
                else
                    throw new ApplicationException("命令行不能为空!");

     这段代码的功能是调用cmd.exe控制台,然后传递参数,参数中包含c++控制台程序的路径,要传递给该控制台程序的参数。我的代码中其中有一个传递给cmd的参数为E:/Task/WJ/SafekeyManager/bin/Debug/Manager/caller.exe GetKeyInfo E:/Task/WJ/SafekeyManager/bin/Debug/keyinfo.info

参数的意思是调用caller.exe程序,向caller.exe程序传递两个参数。测试通过没有任何问题

      提交给测试人员,悲剧发生了,立刻出现问题,老是返回错误。追踪问题发现根本就没调用caller.exe程序。    原来他的测试路径是在C:/Program files下,该目录下有空格,导致cmd识别错误。

      ok!立刻修改,在该参数的执行程序路径上加双引号,参数改变为"E:/Task/WJ/考试系统/Exam/SafekeyManager/SafekeyManager/bin/Debug/Manager/caller.exe" GetKeyInfo E:/Task/WJ/SafekeyManager/SafekeyManager/bin/Debug/keyinfo.info测试通过. 想起来在第二个路径参数中也需要加上同样的双引号,因为路径也是在C:/Program Files下,也有空格。将参数改变为"E:/Task/WJ/SafekeyManager/bin/Debug/Manager/caller.exe" GetKeyInfo "E:/Task/WJ/SafekeyManager/bin/Debug/keyinfo.info"在每个参数中都加上双引号。再测试,问题又回来了,调用不起来caller.exe程序。

     现在明确问题,1.如果传递给cmd.exe的参数都不加参数,而且各参数中没有空格都是可以成功的。2.如果传递的执行文件路径中有空格,那么只需在此路径上加双引号即可。3.如果除了可执行文件的参数外,其它两个参数中有空格,那么在其它两个参数中加双引号也会导致程序调用失败。

     遇到这种问题,应该想到不会是程序的问题,而是调用cmd命令的参数传递的问题。后来在cmd的help文件中找到了解决该问题的答案。解决办法如下

     首先运行cmd.exe程序。然后运行cmd help。其中会有如下这一段话:

     注意,如果字符串加有引号,可以接受用命令分隔符 "&&"分隔多个命令。另外,由于兼容性
原因,/X 与 /E:ON 相同,/Y 与 /E:OFF 相同,且 /R 与/C 相同。任何其他开关都将被忽略。

如果指定了 /C 或 /K,则会将该开关之后的命令行的剩余部分作为一个命令行处理.

     其中,会使用下列逻辑处理引号(")字符:

     1.  如果符合下列所有条件,则会保留命令行上的引号字符:

        - 不带 /S 开关
        - 正好两个引号字符
        - 在两个引号字符之间无任何特殊字符,
          特殊字符指下列字符: &<>()@^|
        - 在两个引号字符之间至少有
          一个空格字符
        - 在两个引号字符之间的字符串是某个
          可执行文件的名称。

    2.  否则,老办法是看第一个字符是否是引号字符,如果是,则去掉首字符并删除命令行上最后一个引号,保留最后一个引号之后的所有文本。

    可以看到cmd对传递的参数中的引号的处理方式。如果我们将我工程中传递给cmd的三个参数都加引号的话,我们来看他是否符合第一条规则,首先不带/s开关,但是第二条,我工程中传递参数方式肯定不符合。那cmd就会按照第二种方式来处理我传递参数中的字符串,也就是会去掉我参数中的第一个和最后一个字符串。那么我的参数传递进去就会是个错误的参数。

    知道问题的原因,那就容易解决了,我的解决办法是在我的参数开头和结尾各加一个引号将我的三个参数都包围起来,那么cmd在接收到这个字符串后就会去掉我加的两个冗余引号,那么我传递进去的参数就是正确的。

    上面的说明也能解释为什么我在三个参数中的第一个参数中加了双引号,参数传递给cmd命令能正确执行,因为它符合第一条所有条件,所以我的引号会保留,而它本身就是我需要的参数传递方式。

 

    结尾:其实这个问题还有一个最简单的解决方案:那就是将代码中的传递给cmd命令的三个参数中的第一个参数拿出来(我们叫他caller.exe).然后将剩余两个参数传递进去而无需关心双引号问题

                   startInfo.FileName = "cmd.exe";
                   startInfo.Arguments = "/c " + dosCommand;//设定参数,其中的“/”表示执行完命令后马上退出

这两行修改为:

                  startInfo.FileName = "caller.exe";
                  startInfo.Arguments = dosCommandNew;//设定参数,该参数为去掉caller.exe的新参数.

      这样调用参数是直接传递给caller.exe程序的而无需传递给cmd。

     

原创粉丝点击