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

使用 ProcessStartInfo 和 Process 类进行外部工具插件开发

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.80/5 (2投票s)

2007 年 12 月 7 日

CPOL

5分钟阅读

viewsIcon

17654

介绍如何使用 System.Diagnostics 中的 ProcessStartInfo 和 Process 类制作外部工具插件

下载源代码

下载 PragmaSQL 外部工具插件源代码

引言

在本文中,我们将展示如何利用 System.Diagnostics 命名空间中的 ProcessStartInfo 和 Process 类为 PragmaSQL 编辑器开发一个非常常见的实用工具。

PragmaSQL T-SQL 编辑器具有非常广泛的插件支持。本文介绍的外部工具插件有两个目标:

    1- 作为 PragmaSQL 插件开发的示例。
    2- 提供所有开发 IDE 和编辑器中包含的非常常见的功能。

IC#Code 插件架构概述

PragmaSQL 使用 IC#Code 的插件架构。该架构提供了
出色的功能,使我们能够轻松地为应用程序开发插件,从而
使我们的应用程序具有可扩展性。IC#Code 插件架构提供了

    1- 通过 .addin 文件进行简单而整洁的 XML 插件定义
    2- 加载插件程序集
    3- 通过宿主应用程序的菜单和工具栏公开插件功能
    4- 以及许多实用服务,如 MenuService 和 MessageService

有关架构的详细信息,请参阅 SODA - SharpDevelop Open Development Architecture by Mike Krueger

PragmaSQL 服务概述

PragmaSQL 通过核心库向插件开发者公开了许多内置的宿主功能。
公开的功能包括

    1- 宿主选项
    2- 编辑器服务:访问 T-SQL 脚本编辑器和文本编辑器
    3- 对象浏览器服务:访问数据库对象浏览器
    4- 项目浏览器服务
    5- 共享脚本和代码片段
    6- 内部网络浏览器
    7- 文本差异服务
    8- 代码完成列表
    9- 应用程序消息服务

所有这些服务的入口点是 HostServicesSingleton,顾名思义,这个类是一个单例。

PragmaSQL.ExternalTools 中的 HostServicesSingleton 用法示例

下面提供的示例代码展示了

1) 如何使用应用程序消息服务将消息打印到宿主应用程序消息窗口。
2) 使用 HostServices EvalMacro 函数评估宏并准备工具参数

private void process_Exited(object sender, EventArgs e)
{
    Process p = sender as Process;
    if (p == null)
        return;

    long handle = p.Handle.ToInt64();
    if (!_runningToolDefs.ContainsKey(handle))
        return;

    try
    {
        ExternalToolDef def = _runningToolDefs[handle];
        
        //Here we clear application messages window



        if (chkClearOutput.Checked)
            HostServicesSingleton.HostServices.MsgService.ClearMessages();

        bool shallShow = false;

        while (p.StandardOutput.Peek() > -1)
        {
            string info = p.StandardOutput.ReadLine();
            if (!String.IsNullOrEmpty(info))
            {
                // Print info message to Applicatin Messages Window



                HostServicesSingleton.HostServices.MsgService.InfoMsg(info, "Tool : " + def.Title, String.Empty, String.Empty);
                shallShow = true;
            }
        }

        while (p.StandardError.Peek() > -1)
        {
            string error = p.StandardError.ReadLine();
            if (!String.IsNullOrEmpty(error))
            {
                // Print error message to Applicatin Messages Window



                HostServicesSingleton.HostServices.MsgService.ErrorMsg(error, "Tool : " + def.Title, String.Empty, String.Empty);
                shallShow = true;
            }
        }

        if (shallShow == true)
            HostServicesSingleton.HostServices.MsgService.ShowMessages();
    }
    finally
    {
        _runningToolDefs.Remove(handle);
    }
}




private void RenderExternalToolDef(ExternalToolDef exDef)
{
    tbCmd.Text = String.Empty;
    tbArgs.Text = String.Empty;
    tbWorkingDir.Text = String.Empty;
    
    if (exDef == null)
        return;
        
    tbCmd.Text = exDef.Command;
    // Here we prepare arguments by evaluating macros



    tbArgs.Text = HostServicesSingleton.HostServices.EvalMacro(exDef.Args);
    tbWorkingDir.Text = exDef.WorkingDir; 
}

        

注意:为了开发 PragmaSQL 插件,您需要获取 PragmaSQL.Core.dll。所有宿主功能
和许多实用程序类都托管在此程序集中。从此处下载

PragmaSQL.ExternalTools

插件定义

<AddIn name        = "External Tools AddIn for PragmaSQL"
       author      = "Ali Özgür"
       description = "Enables you to define external tools for PragmaSQL">
    <Manifest>
        <Identity name = "PragmaSQL.ExternalTools"/>
    </Manifest>

    <Runtime>
        <Import assembly="PragmaSQL.ExternalTools.dll"/>
    </Runtime>

    <Path name = "/Workspace/ToolsMenu">
        <MenuItem id = "ExtTools.Configure"
                         label = "External Tools..."
                         class ="PragmaSQL.ExternalTools.ConfigureTools"/>
        <MenuItem id = "ExtTools.Run"
                         label = "Run External Tool"
                         shortcut     = "Control|Shift|E"
                         class ="PragmaSQL.ExternalTools.RunExternalTool"/>
    </Path>
</AddIn>

在上面的插件定义文件中,我们提供了插件的描述以及插件如何集成到 PragmaSQL。
此插件定义中最重要的部分是 Path 标签。我们提供了预定义的宿主路径以及我们希望为插件创建的 MenuItems
我们希望为我们的插件创建的菜单项。

另一个非常重要的标签是 Class。IC#Code 插件架构使用命令模式。
我们通过此标签使用 Command 类定义与指定菜单/工具栏项目关联的命令。
在上面的示例中,对于 External Tools... 菜单项,您可以看到我们希望调用 PragmaSQL.ExternalTools.ConfigureTools 命令。
ConfigureTools 命令类继承自 AbstractMenuCommand 并具有 Run() 方法。

注意:预定义的宿主路径可以在 PragmaSQL 安装附带的 Base.addin 文件中找到。

ExternalTools 插件特定类

  • ExternalToolDef:可序列化类,用于保存单个外部工具的配置数据
  • ExternalToolsCfg:静态类,用于将序列化的工具配置数据从文件加载并保存到 IList<ExternalToolDef>
    静态实例。工具配置项可通过 Current 公共静态属性访问。
  • ConfigureTools:继承自 AbstractMenuCommand 的命令类。此命令用于显示工具配置表单
  • RunExternalTool:继承自 AbstractMenuCommand 的命令类。此命令用于显示运行工具表单。
  • ConfigForm:工具配置表单。表单通过 ConfigureExternalTools 公共静态方法打开。
    此方法返回以下 DialogResult 枚举值之一

      OK:用户按下 OK 按钮并应用了工具配置的更改

      Ignore:用户按下 OK 按钮且工具配置没有更改

      Cancel:用户按下 Cancel 按钮。

  • RunToolForm:列出外部工具定义并用于运行选定工具的表单。

    使用 Process 和 ProcessStartInfo 运行工具

    .NET Framework 在 System.Diagnostics 命名空间下提供了 ProcessStartInfoProcess 类,可用于从我们的代码中运行外部进程。
    ExternalTools 插件从 RunToolForm 中使用这些类。

    RunTool 函数如下所示

    private void RunTool()
    {
        if (CurrentDef == null)
            return;
    
        ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
        tbArgs.Text = HostServicesSingleton.HostServices.EvalMacro(CurrentDef.Args);
        psi.FileName = CurrentDef.Command;
        psi.Arguments = tbArgs.Text;
        psi.WorkingDirectory = CurrentDef.WorkingDir;
        psi.RedirectStandardOutput = CurrentDef.UseOuput;
        psi.RedirectStandardError = CurrentDef.UseOuput;
        psi.CreateNoWindow = CurrentDef.UseOuput;
        psi.UseShellExecute = !CurrentDef.UseOuput;
        
        Process p = new Process();
        p.EnableRaisingEvents = CurrentDef.UseOuput;
        if (CurrentDef.UseOuput)
            p.Exited += new EventHandler(p_Exited);
        
        p.StartInfo = psi;
        p.Start();
    
        if (CurrentDef.UseOuput)
            _runningToolDefs.Add(p.Handle.ToInt64(), CurrentDef);
    }
    

    如您所见,启动进程没有什么特别之处。我们只需使用 ProcessStartInfo 类定义进程的文件名、参数和工作目录,

    然后使用此 ProcessStartInfo 创建一个 Process 实例。
    此实现中值得注意的几点是

    1) 标准输出和错误重定向。

    您可以将进程的标准输出/错误重定向到任何您喜欢的位置,前提是将

    • ProcessStartInfo 实例的 RedirectStandardOutputRedirectStandardError 属性设置为 true
    • ProcessStartInfo 实例的 UseShellExecute 属性设置为 false
    • 外部工具将输出和错误打印到标准输出。例如,您无法重定向 Windows 应用程序的输出,
      但您可以重定向控制台应用程序的输出。

    2) 同步与异步输出读取

    Process 类提供重定向输出/错误的同步与异步读取。然而在实现中,
    选择其中一种方法会产生很大的不同。在我们的实现中,看起来我们选择了同步
    输出读取。但这并非如此。再次查看 RunTool 方法。我们通过第 17 行的 p.Exited += new EventHandler(p_Exited); 附加到
    Process 实例的 Exited 事件。在考虑同步读取后,
    p_Exited 方法抛出了跨线程调用异常。此异常表明了两个问题:

    • 1- p_Exited 方法是从不同的线程调用的,因此表明操作是异步的
    • 2- PragmaSQL 消息服务不支持跨线程调用。

    我只需通过添加一些 Invoke() 相关代码来修复 PragmaSQL 消息服务以支持跨线程调用,就是这样。

    结论

    外部工具支持是 PragmaSQL 的一项通用要求。在本文中,我们介绍了 IC#Code 插件支持、PragmaSQL 如何利用
    IC#Code 插件架构、PragmaSQL 公开了哪些服务来提供可插拔/可扩展的应用程序以及一些关于
    PragmaSQL 插件开发的初步见解,并附有源代码示例。

  • © . All rights reserved.