65.9K
CodeProject 正在变化。 阅读更多。
Home

WinDos 工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (20投票s)

2006年2月20日

9分钟阅读

viewsIcon

50823

downloadIcon

1583

一个使用属性编辑器执行任何 DOS 命令的工具

WinDos using the prompt WinDos running a command

引言

这是一个用于构建和执行 DOS 命令的工具。该工具的理念是将任何 DOS 命令构建为 Windows 对话框,并为特定的命令参数提供编辑器/对话框。例如,如果一个 DOS 命令接受一个目录参数,您可以使用一个目录对话框。

这样做的好处是,不必麻烦地创建和保存大量难以维护的 BAT 文件,就能让复杂的 DOS 命令变得简单。还会显示所选命令或参数的帮助信息,最后,命令被组织成类别。命令列表可以保存为 XML 文件或从 XML 文件加载,以便拥有不同的集合并与他人共享常用命令。

用法

使用命令

当您启动该工具时,会显示一个默认的(示例)命令列表以及命令行(DOS)。可以在配置文件中指定一个启动命令列表文件。

WinDos default view

您可以从列表中选择一个命令并展开它以显示和编辑其参数。请注意,不同的参数类型有不同的编辑器。

Run button 单击“运行”按钮在 DOS 中运行该命令。在状态栏中,会显示将要执行的命令的预览。
Zoom button

“缩放”按钮可用于放大某个命令,或者,如果您已经放大,则返回到列表。

Edit button “编辑”按钮可用于开始编辑当前选定的项。请参阅下一节。
FileOpen button “打开文件”按钮用于打开一个包含命令的 XML 文件。
FileSave button “保存文件”按钮用于将命令列表保存到 XML 文件。
FileSave button “描述”按钮显示或隐藏所选项目的描述。

标题和标题栏显示当前命令的名称。

编辑命令

要开始编辑命令,请单击“编辑”按钮。会出现第二个属性网格,显示当前命令的属性。下一个屏幕截图说明了这一点(请注意,屏幕截图已放大到命令)。

WinDos viewing details of a property

编辑模式下会出现两个额外的按钮。

Add button 单击“添加”按钮以添加新命令(在查看命令列表时)或参数(在放大视图中)。
Delete button 单击“删除”按钮以删除选定的项。

“常规”属性对每个属性都相同。它们主要与显示属性有关,而且有望一目了然。最重要的属性是“类型”,它决定了属性的行为。这包括默认值和属性的编辑器。展开 Type 属性会显示特定于所选类型的可编辑属性。

在示例图片中,Dir 命令的 Directory 属性的类型为 DirectoryPropertyType,这使其可以通过目录对话框进行选择。Format 属性定义了值如何转换为命令值(在这种情况下,值被加引号)。

属性类型

属性类型是此工具的重要组成部分。它们决定了用户可以输入的数据类型。目前,此工具包含以下属性类型:

简单类型
布尔值 用户可以选择 true/false。布尔值会被映射到命令字符串。
字符串 用户可以输入文本。文本可以为命令字符串进行格式化。
对话框类型
Directory(目录) 用户可以选择一个目录。目录路径可以为命令字符串进行格式化。
文件 用户可以选择一个文件。文件名可以为命令字符串进行格式化。
列表类型
列表 用户可以从列表中选择一个项目。每个项目都有一个显示值和一个命令值。
复选列表 用户可以从列表中选择多个项目。每个项目都有一个显示值和一个命令值。有一个格式字符串和一个分隔符字符串。
复合类型
命令 一个命令包含其他属性。有一个格式字符串和一个分隔符字符串。

我希望能够扩大和优化这个列表。最后但同样重要的是,可以复制和粘贴命令。在 Commands 属性网格上单击鼠标右键选择 CopyPaste

  • Copy 会复制选定的属性或命令。
  • Paste 会将复制的属性或命令粘贴到当前的 CommandList

我希望这能解释基本用法。请随意尝试,感受一下所有东西是如何工作的。请注意,该工具本身并不十分有用,但拥有一个可以经常使用的命令集是很有用的。创建命令需要花费一些时间;好处是您可以一遍又一遍地使用它。我提供了一个默认的集合供您尝试。

Using the Code

我在代码设计方面的一些目标是拥有一个灵活的模型,并限制添加新属性类型所需的代码量。事实上,这个项目确实不需要多少代码。这是因为 .NET 中已经内置了该功能。首先,是一个小图。该图显示了基本设置,包括两种属性类型:命令和目录参数。

WinDos global class model

CommandList

所有命令和参数的容器是 CommandList。它实现了 ICustomTypeDescriptor,以便能够运行时定义我们自己的属性。它通过包装提供的 CommandProperty 实例和一个 CommandPropertyDescriptor 来实现这一点。请注意,在工具中,主 PropertyGrid 显示这些运行时属性。详细信息网格显示选定的 CommandProperty 本身,即作为一个类的标准属性。CommandList 包含命令属性,并存在于不同的函数中:

  • 它充当全局命令列表(包含命令)。在最简单的设置中,这是唯一的 CommandList
  • 它充当命令(包含参数)。
  • 它充当复杂的命令参数。

通过嵌套 CommandList,一个项可以是一个完整的批处理。

CommandProperty

命令的任何部分(无论是命令还是参数)都封装在 CommandProperty 中。这是一个密封类,包含通用属性,如 NameCategory

最重要的是,它有一个 Type 属性,它定义了实际的行为。该属性必须是 IPropertyType 的子类(更具体地说,是 BasePropertyType,因为我无法 XML 序列化接口类型)。Type 属性使用一个类型转换器(PropertyTypeConverter)来显示所有已知类型,因此用户可以从下拉列表中选择一个。一旦做出选择,属性值就会实例化为一个选定类型的新实例。

IPropertyType

IPropertyType 的后代是“发生动作的地方”。此类必须提供以下功能:

  • Default 值(这也将决定值的类型)。
  • 用于 DOS 命令行的值(FormatValue),基于用户输入的值。

另外,您可以添加:

  • 一个值编辑器(EditorType),用于对话框或控件。
  • 一个值类型转换器(ConverterType),用于列表或类型转换。

这听起来可能很复杂,但实际上比看起来要简单。为了证明我的观点,让我们看一下 DirectoryPropertyType

public class DirectoryPropertyType: BasePropertyType {
    string format = "\"{0}\"";

    [Description("Format string for the directory command.")]
    public string Format {
        get {  return format; }
        set { format = value; }
    }
    public override object Default {
        get {
            return string.Empty;
        }
    }
    public override Type EditorType {
        get {
            return typeof(DirectoryEditor);
        }
    }
    public override string FormatValue(object value) {
        return value == null ? string.Empty : string.Format(Format, value);
    }
}

此类有一个 Format 属性,它在其 FormatValue 方法中使用该值来返回要在 DOS 命令中使用的值。此类覆盖了 EditorType 以提供用于选择目录的编辑器。编辑器看起来像这样:

public class DirectoryEditor: UITypeEditor {
    public override object EditValue(ITypeDescriptorContext context,
        IServiceProvider provider, object value) {
        FolderBrowserDialog dialog = new FolderBrowserDialog();
        if (value != null && value.ToString().Length > 0) {
            string path = Path.GetFullPath(value.ToString());
            if (!Directory.Exists(path))
                throw new InvalidOperationException();
            dialog.SelectedPath = path;
        }
        if (dialog.ShowDialog() == DialogResult.OK)
            value = dialog.SelectedPath;
        return value;
    }
    public override UITypeEditorEditStyle 
           GetEditStyle(ITypeDescriptorContext context) {
        return UITypeEditorEditStyle.Modal;
    }
}

这两个类是能够在任何 DOS 命令中进行目录选择所需的所有内容。

以及其余的...

还有许多其他类和功能值得提及。我已尝试在所有类和成员中添加注释,因此您可能会发现将源代码通过 NDoc 运行很有用(浏览起来比源代码更容易)。

关注点

XML 文件是 XML 序列化的 CommandList。读取旧版本应用程序的 XML 文件可能会在某些类发生更改时导致问题(这是一个普遍问题)。解决方案是提供一个 XSL 样式表来转换为新版本。属性窗体和命令提示符是独立的进程,并像这样运行。如果它们作为一个整体运行(例如,当一个被激活时,两个都出现在前景)会更好。我对此没有任何(优雅的)解决方案。一些设计选择:

  • 那些在其他类之外没有用途的类已被制作为私有的内部类。这适用于大多数 ConverterTypeEditorType 类。其中一些内部类与其父类有类型依赖关系。
  • 类遵循松耦合规则。这意味着它们具有简单的接口。这也意味着您最终会得到更多的类。
  • 设计遵循基本的设计模式原则。

我本来希望使用标准的 verb 机制来显示操作(添加命令,添加参数)。不幸的是,PropertyGrid 在运行时不显示 verbs。

演变和鸣谢

我经常使用 CodeProject,您的文章曾多次帮助我,包括构建这个工具。所以,谢谢 CodeProject,谢谢互联网 ;-)

最初,我的命令提示符是一个与隐藏命令提示符通信的文本框,类似于 S. Senthil Kumar 的 ShellControl 的设置。他有一个出色的文本框控件,用于捕获输入/输出。我对此进行了一些重写,以使进程保持运行,从而使命令上下文保持活动(例如当前目录),并使用线程进行异步处理。但后来我遇到了各种各样的问题,试图精确地模拟命令提示符的行为(例如,看似微不足道的任务,如处理 CTRL-C 等特殊键)。

对我来说,首要任务是拥有精确的功能,而这比我预期的要困难得多。最终,我厌倦了,决定最佳方向是使用现有的命令提示符。这使我的问题简化为向(单独的)命令提示符发送命令。当前的解决方案可能不会赢得任何美誉奖,但至少它只有几行代码(因此易于维护)。

对于属性编辑器逻辑,我参考了 Tony Allowatt 的优秀文章 Bending the .NET PropertyGrid to Your Will。同样,这并非我想要的全部,但它帮助我理解了一些关键点。最后但同样重要的是,我要感谢我的同事兼朋友 Koen Muilwijk。他帮助完成了这个小练习的许多部分,因为他是一个挑剔的人。;-)

已知bug

在某些机器上,启动时会生成一个异常,其中访问了进程的 MainWindowHandle。消息是“Couldn't get process information from remote machine”,这是由 System.Diagnostics.PerformanceMonitor.GetData(String item) 行引起的。Process 使用 PerformanceCounter 来确定许多进程属性。异常发生是因为帐户没有访问此计数器的适当权限。我还没有解决这个问题的方法,只能尝试“升级”您的帐户。如果发生这种情况,应用程序将退出。

历史

这是第一个版本,所以我将称之为 1.0 beta。该工具的历史长度很可能取决于我收到的反馈。我很感激任何建设性的评论或代码改进。我个人的“愿望清单”包括:

  • 命令管理
  • 与命令提示符更好地集成
  • 更多/改进的 PropertyTypes
  • 用于验证和替换的正则表达式

再次,欢迎评论。感谢您的阅读和/或尝试。

  • 2006 年 2 月 20 日 -- 发布原始版本
  • 2007 年 10 月 26 日 -- 更新了演示和源代码下载
© . All rights reserved.