我们有时需要写一些小工具,或者是需要写一些简短的测试程序,这时编写命令行程序会比较方便。但是命令行程序用起来不太方便,比如——
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),也是简单的复制、改名就行了,如图——
以后编写命令行程序时,只需把这个程序放过去就行了,能极大的方便测试和使用。
(完)