zyl910

优化技巧、硬件体系、图像处理、图形学、游戏编程、国际化与文本信息处理。

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

  我们有时需要写一些小工具,或者是需要写一些简短的测试程序,这时编写命令行程序会比较方便。但是命令行程序用起来不太方便,比如——
1.查看信息困难。有时候我们需要观察命令行程序的输出结果,但是在默认情况下,命令行程序执行完毕后会自动关闭窗口,来不及观察信息。这时可以采取“在程序中等待按键”、“手工打开命令提示符输入命令”、“在批处理中等待按键”、“批处理重定向”等方法,但是都比较麻烦。
2.命令参数困难。某些命令行程序需要参数,这时只有手工打开命令提示符输入命令。有时要键入文件的全限定名,那就更花功夫了。

  怎么解决以上难题呢?
  首先想到的是把那些小程序改写为图形界面程序,或者为命令行程序分别开发图形界面。但这两个方案的开发成本都太高了,这仅仅只是简短测试程序,很不值得。
  所以,我编写了一个通用的图形界面,专门解决这些命令行程序的使用问题。


一、设计

1.1 如何关联

  首先,怎么将图形界面与原来的命令行程序关联起来呢?
  传统的做法是使用配置文件,在配置文件中指定原来的命令行程序的文件名和路径等信息。但这样做配置起来比较麻烦,因为现在有大量的命令行程序,逐个逐个的修改配置文件就太麻烦了。
  于是我决定不使用配置文件,设计两种关联方式——根据自身文件名、根据命令行参数。

1.1.1 根据自身文件名(“*_ui.exe”)

  用法:假设某命令行程序的文件名为“filesize.exe”,那么将“cmdarg_ui.exe”复制到该目录下并改名为“filesize_ui.exe”,然后就可以利用“filesize_ui.exe”来操作“filesize.exe”了。
  优点:配置十分方便,复制、改名就行了,不需要切换窗口。而且可以为每一个命令行程序分别配置图形界面,方便以后随时双击打开。
  缺点:对于每一个命令行程序都需要配置一次。


1.1.2 根据命令行参数(鼠标拖曳)

  在很多时候只需要看一次就行了。为了看一次而建立“*_ui.exe”,随后又删掉“*_ui.exe”,那就有些啰嗦了。
  于是设计了另一种关联方式——根据命令行参数。

  用法:A)打开命令提示符,输入“cmdarg_ui.exe <空格> <命令行程序>”启动cmdarg_ui。
     B)在资源管理器中选择命令行程序,然后按住鼠标左键拖曳,将其放置在“cmdarg_ui.exe”的文件图标上,于是资源管理器会启动“cmdarg_ui.exe”,并将刚才拖曳的命令行程序作为参数。
  优点:操作十分简单,只需鼠标拖曳一下。
  缺点:每次启动时都需要拖曳。如果需要频繁使用时,建议还是使用上面的办法(复制、改名“*_ui.exe”)。


1.1.3 特例:“*_wui.exe”会显示命令行窗口,适用于等待按键的命令行程序

  在使用图形界面时,我们一般不希望再弹出命令行窗口。
  但是,某些命令行程序需要等待按键。而现在没有命令行窗口,会导致程序一直等待,不会结束。
  最简单的解决办法是让显示命令行窗口,然后按键使程序继续运行,直至结束。
  这时可以改名为“*_wui.exe”,会自动切换到“显示命令行窗口”模式。


1.2 命令行参数

  对于小工具和测试程序来说,很多是没有参数的,或者是参数格式很简单。例如只有一个输入参数,用于传递文件名。
  于是我设计了两种参数模式——
1.文件。使用文件对话框选择文件,确定后自动运行。
2.自定义。在文本框中手动填好参数,然后点击“运行”。


1.3 其他特性

  为了方便使用,还设计了这些功能——
接收文件:当文件拖曳到窗口上时,将该文件作为参数来调用命令行程序。因为在很多时候,文件拖曳比打开对话框用起来方便一些。
实时优先级:调用命令行程序时,自动将它的进程优先级设为实时。因为某些测试程序需要实时优先级,如果每次在任务管理器中配置的话就太麻烦了。


二、cmdarg_ui的代码

  编程语言为C#。开发工具是VS2005。

  界面如下——

  代码如下——

using System;
//using System.Collections.Generic;
//using System.ComponentModel;
//using System.Data;
using System.Drawing;
//using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;

namespace cmdarg_ui
{
    public partial class FrmCmdarg_ui : Form
    {
        /// <summary>
        /// 命令行程序的文件名
        /// </summary>
        private string _FileExe;

        public FrmCmdarg_ui()
        {
            InitializeComponent();
        }

        private void FrmCmdarg_ui_Load(object sender, EventArgs e)
        {
            _FileExe = string.Empty;

            // 计算命令行程序的文件名——根据自身文件名
            string sFile = Application.ExecutablePath;    // 全限定文件名
            string sFileBase = Path.GetFileNameWithoutExtension(sFile);    // 文件基本名(无扩展名、无路径)
            this.Text = sFileBase;    // 以基本名作为窗口标题
            int n = sFileBase.LastIndexOf('_');    // 在基本名中查找下划线
            if (n > 0)
            {
                // 存在下划线,文件名有效
                // 解析后缀参数
                for (int i = n + 1; i < sFileBase.Length; ++i)
                {
                    char ch = char.ToUpper(sFileBase[i]);
                    switch (ch)
                    {
                        case 'W':
                            mnuRunNoWindow.Checked = false;
                            break;
                    }
                }
                // 计算命令行程序的文件名
                _FileExe = sFile.Substring(0, sFile.LastIndexOf(Path.DirectorySeparatorChar) + 1 + n) + ".exe";
                // 检查文件是否存在
                if (!File.Exists(_FileExe)) _FileExe = string.Empty;
            }

            // 计算命令行程序的文件名——根据命令行参数
            if (string.IsNullOrEmpty(_FileExe))
            {
                string[] lst = Environment.GetCommandLineArgs();
                if (lst.Length >= 2)
                {
                    _FileExe = lst[1];
                    // 检查文件是否存在
                    if (!File.Exists(_FileExe)) _FileExe = string.Empty;
                }
            }

            if (string.IsNullOrEmpty(_FileExe))
            {
                // 文件名无效
                btnRun.Enabled = false;
                txtOut.Text = "Invalid file path!";
            }
            ttpMain.SetToolTip(btnRun, _FileExe);
            mnuRunName.ToolTipText = _FileExe;
        }

        private void FrmCmdarg_ui_FormClosed(object sender, FormClosedEventArgs e)
        {
            //
        }

        private void btnRun_Click(object sender, EventArgs e)
        {
            string sAll;

            if (string.IsNullOrEmpty(_FileExe)) return;    // 文件无效

            // 参数
            string sArg = txtFile.Text;
            if (optCmd.Checked) sArg = txtCmd.Text;

            // 执行
            Process p = new Process();
            p.StartInfo.FileName = _FileExe;
            p.StartInfo.Arguments = sArg;
            p.StartInfo.UseShellExecute = false;
            //p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.RedirectStandardOutput = true;
            //p.StartInfo.RedirectStandardError = true;
            p.StartInfo.CreateNoWindow = mnuRunNoWindow.Checked;
            Cursor = Cursors.WaitCursor;
            try
            {
                p.Start();
                if (mnuRunRealTime.Checked)
                {
                    p.PriorityClass = ProcessPriorityClass.RealTime;
                }
                try
                {
                    sAll = p.StandardOutput.ReadToEnd();
                }
                finally
                {
                    p.Close();
                }
            }
            catch (Exception ex)
            {
                sAll = ex.ToString();
            }
            finally
            {
                Cursor = Cursors.Default;
            }

            // 显示
            txtOut.Text = sAll;
        }

        private void btnRunEx_Click(object sender, EventArgs e)
        {
            mnuRun.Show(btnRunEx, new Point(0, btnRunEx.Height));
        }

        /// <summary>
        /// 更新文件
        /// </summary>
        /// <param name="sFile"></param>
        private void updateFile(string sFile)
        {
            Debug.WriteLine(sFile);
            if (string.IsNullOrEmpty(sFile)) return;

            // 加上双引号,改善对长文件名的支持性
            if ('\"' != sFile[0])
            {
                sFile = "\"" + sFile + "\"";
            }

            // 更新
            txtFile.Text = sFile;
            txtFile.SelectionStart = txtFile.Text.Length;    // 优先显示最后面的,便于识别文件名
            if (mnuRunAutoFile.Checked)
            {
                btnRun_Click(btnRun, null);
            }
        }

        private void btnFile_Click(object sender, EventArgs e)
        {
            if (dlgOpen.ShowDialog(this) == DialogResult.OK)
            {
                updateFile(dlgOpen.FileName);
            }
        }

        private void FrmCmdarg_ui_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Link;
        }

        private void FrmCmdarg_ui_DragDrop(object sender, DragEventArgs e)
        {
            string[] lst = (string[])e.Data.GetData(DataFormats.FileDrop);
            if (lst.Length > 0)
            {
                optFile.Checked = true;
                updateFile(lst[0]);
            }
        }

        private void mnuRunName_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(_FileExe);
        }
    }
}


  其实原理很简单,利用Process类运行程序,并配置StartInfo.RedirectStandardOutput进行重定向,然后使用StandardOutput.ReadToEnd()获得所有输出信息。


三、测试程序的代码(filesize)

  为了检测参数传递与信息显示,编写了一个小测试程序,功能是显示文件的大小。

  编程语言为C语言。开发工具是VC6。

  代码如下——

#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>

int _tmain(int argc, _TCHAR* argv[])
{
    long fileSize = 0;
    FILE* pFile;

    // 检查参数个数
    if (argc<2)
    {
        // 没有给出文件参数。提示语法
        _tprintf(_T("filesize [filename]\n"));
        return 1;
    }

    // 显示文件名
    _tprintf(_T("file:\t%s\n"), argv[1]);

    // 获取文件大小
    pFile = _tfopen(argv[1], _T("rb"));
    if ( pFile == NULL )
    {
        _tprintf(_T("[error] File open failed!\n"));
    }
    else
    { 
        fseek(pFile, 0L, SEEK_END );    // 将指针定位到文件末尾
        fileSize = ftell( pFile );    // 返回当前位置
        fclose(pFile);
        _tprintf(_T("size:\t%d\n"), fileSize);
    }
    
    return 0;
}

 


四、测试

4.1.1 测试filesize

  filesize具有一个文件名输入参数。所以我们主要测试文件参数。

  测试A——
将“filesize.exe”放在“test”文件夹。
复制“cmdarg_ui.exe”至“test”文件夹,并改名为“filesize_ui.exe”。
双击“filesize_ui.exe”启动图形界面。
“文件”单选框模式是已勾选的,这时点击右侧的“...”按钮弹出文件对话框,选择一个文件。便可发现成功的调用“filesize.exe”,并正确的返回了文件大小信息。
点击“自定义”单选框,然后在右侧文本框中输入参数,再点击“运行”。便可发现下面的文本框更新了,如果手工输入的参数不正确的话,“filesize.exe”会报告“[error] File open failed!”。
在资源管理器中选择一个文件,将其拖曳到本程序。可发现“文件”单选框自动被勾选了,成功的将该文件作为参数调用了“filesize.exe”。

  测试B——
在资源管理器中选择“filesize.exe”,然后按住鼠标左键拖曳,将其放置在“cmdarg_ui.exe”的文件图标上。于是“cmdarg_ui.exe”会启动,并关联“filesize.exe”。
“文件”单选框模式是已勾选的,这时点击右侧的“...”按钮弹出文件对话框,选择一个文件。便可发现成功的调用“filesize.exe”,并正确的返回了文件大小信息。
其他测试同上。


4.1.2 测试noifVC6s.exe

  noifVC6s.exe是我以前写的一个测试程序(http://www.cnblogs.com/zyl910/archive/2012/04/12/noifopex8.html),没有参数,它需要等待按键。

  测试C——
将“noifVC6s.exe”放在“test”文件夹。
复制“cmdarg_ui.exe”至“test”文件夹,并改名为“noifVC6s_wui.exe”。(注意这里是_wui)
双击“noifVC6s_wui.exe”启动图形界面。
界面启动后,点击“运行”按钮右侧的“V”按钮,可以观察到“无窗口”菜单项没有勾选。此时不需操作,点击空白地方关闭菜单。
因为“noifVC6s.exe”没有命令行参数,可以直接点“运行”按钮。弹出“noifVC6s_wui.exe”的命令行窗口,这时按两下空格键时期继续运行,运行结束可发现正确的返回了输出信息。

  测试D——
在资源管理器中选择“noifVC6s.exe”,然后按住鼠标左键拖曳,将其放置在“cmdarg_ui.exe”的文件图标上。于是“cmdarg_ui.exe”会启动,并关联“noifVC6s.exe”。
界面启动后,点击“运行”按钮右侧的“V”按钮,可以观察到“无窗口”菜单项已经勾选。这时应该点击“无窗口”菜单项,使其取消勾选。
因为“noifVC6s.exe”没有命令行参数,可以直接点“运行”按钮。弹出“noifVC6s_wui.exe”的命令行窗口,这时按两下空格键时期继续运行,运行结束可发现正确的返回了输出信息。

 

五、小结

  本程序能方便命令行程序的使用,而且配置使用非常简单。

  例如将它关联到“checksimd64_2010.exe”(http://www.cnblogs.com/zyl910/archive/2012/05/25/checksimd64.html),也是简单的复制、改名就行了,如图——

  以后编写命令行程序时,只需把这个程序放过去就行了,能极大的方便测试和使用。

(完)


源码下载——
https://files.cnblogs.com/zyl910/cmdarg_ui.rar

posted on 2012-06-19 17:29  zyl910  阅读(3034)  评论(0编辑  收藏  举报