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

Rhinoceros RhinoCommon 的托管可扩展性框架 (MEF) 插件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2016年4月8日

CPOL

4分钟阅读

viewsIcon

13891

downloadIcon

369

通过 MEF 加载 Rhinoceros 插件。

Visual Studio 2010 项目

主要目标

  • 避免每次重新编译插件时都重启 Rhino
  • 加快代码速度并尝试开发流程
  • 展示一个简单的 Mef 插件技术

引言

我刚开始学习使用 C# 的 RhinoCommon。所以,我开始编写我的第一个 插件,以此页面为参考:Rhinoceros for Windows 的第一个插件

在那之后,我开始编写我自己的插件,涉及更复杂的几何工作。然后我遇到了一个问题,每次重新编译并尝试运行时,Rhinoceros 都必须启动,并且插件必须重新加载。

由于我的代码不是很严谨,这个过程涉及大量的试错尝试,浪费了我很多宝贵的时间和不多的耐心。

为了加快开发过程,我有了下一个想法

创建一个 RhinoCommon 插件,它将加载一个 Managed Extensibility Framework (MEF) 插件。

所以你必须编写

  • 一个 Rhinoceros 插件
  • 一个 MEF 插件,它将完成我们的绘图工作
  • 一个合同接口 DLL,两者都引用它,它充当 Rhinocommon 插件和我们的 MEF 插件之间的管道

Visual Studio 项目

必须编写三个项目

项目名称 引用
RhinoPluginLoader RhinoCommon.dll, RhinoPluginContract.dll, System.ComponentModel.Composition;
RhinoPluginMakeLine RhinoCommon.dll, RhinoPluginContract.dll, System.ComponentModel.Composition;
RhinoPluginContract RhinoCommon.dll

RhinoCommon.dll 必须引用 Copy local = false。RhinoPluginContract.dll 必须引用 Copy local = true。

RhinoPluginLoader 项目

加载插件命令

在继续之前,请确保您理解 Rhinoceros for Windows 的第一个插件指南。现在开始我们的第一个项目,在 Visual Studio 中,创建一个新项目并选择已安装的模板
Rhinoceros->RhinoCommon Plug-In,(如果您没有模板,请从以下位置安装:RhinoCommon 模板 for v5)。

设置以下名称

项目名称:RhinoPluginLoader

插件类名称:PluginLoader
命令类名称:LoadPlugin (此命令名称在 Rhinoceros 中不存在)

向导完成后,我们将得到以下文件

文件 定义 派生自
PluginLoader.cs Rhinoceros 插件定义 派生自 Rhino.PlugIns.PlugIn
LoadPlugin.cs Rhinoceros 命令定义 派生自 Rhino.Commands.Command

代码

现在编辑 LoadPlugin.cs 文件

首先,创建一个 MEF 端口,用于连接管道接口

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

public class LoadPlugin : Command
    {
        [Import(typeof(IPlugin))]
        public IPlugin Plugin;
		
	.......

然后是加载插件到 Catalog 中的代码
(将生成的 RunCommand 模板代码替换为以下内容)。

  protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            // Prompt the user to enter a Plugin name
            Rhino.Input.Custom.GetString gs = new Rhino.Input.Custom.GetString();
            gs.SetCommandPrompt("File name of plugin to run:");
            gs.AcceptNothing(true);
            gs.Get();
            if (gs.CommandResult() != Rhino.Commands.Result.Success)
                return gs.CommandResult();

            // Was a plugin named entered?
            string plugin_name = gs.StringResult().Trim();
            if (string.IsNullOrEmpty(plugin_name))
            {
                Rhino.RhinoApp.WriteLine("Plugin name cannot be blank.");
                return Rhino.Commands.Result.Cancel;
                //Uncomment to load a default dll name 
                // plugin_name = @"D:\the full path\PluginMakeLine.dll";
            }
            // Check if file exist?
            if (!System.IO.File.Exists(plugin_name)) 
            {
                Rhino.RhinoApp.WriteLine("File not exist !!!");
                return Rhino.Commands.Result.Cancel;
            } 

            //--------------------------------------
            // MEF - Managed Extensibility Framework
            //--------------------------------------
            var catalog = new AggregateCatalog
            (new AssemblyCatalog(Assembly.Load(System.IO.File.ReadAllBytes(plugin_name))));
            // To avoid dll locked file->System.IO.File.ReadAllBytes(plugin_name)
			
            // Create a composition container                       
            CompositionContainer  container = new CompositionContainer(catalog);
           
            try
            {
                container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                RhinoApp.WriteLine(compositionException.ToString());
                return Rhino.Commands.Result.Cancel;
            }

            // Execute the Run method of Plugin interface
            Result result = Plugin.Run(doc, mode);            
           
            catalog.Dispose();
            container.Dispose();
            return result;            
        }

设置如上表所示的引用,并构建解决方案 [F6]。

安装 Rhino 插件

构建解决方案后,在 Rhinoceros 中安装插件,执行:Tools->Options->Rhino Options,[Install...], 并选择 RhinoPluginLoader.rhp 文件。或者最简单的方法:将 RhinoPluginLoader.rhp 从文件资源管理器拖放到 Rhino 中。

运行 Rhino 插件

将来,从 Rhino 命令中输入:LoadPlugin [ENTER]
要运行的插件文件名:c:\OurMefDrawingPlugin.dll [ENTER]

先不要尝试,因为我们需要再添加两个项目!

我们的 MEF 绘图插件

这是我们执行所有绘图工作的地方。创建一个新的项目,选择 Class Library 模板,将项目名称设置为:RhinoPluginMakeLine

在这种情况下,为了演示目的,将只使用模板中的代码。但在我的情况下,我有一个代码可以参数化地生成无人机的机身,而无需在 Rhino 绘图区域进行任何点击。

在这里,重要的是要突出 MEF 导出子句,以表明该类将使用合同接口类型 IPlugin 导出到组合模型。

namespace RhinoPluginMakeLine
{
    [Export(typeof(IPlugin))]

    public class CodeEjecutor : IPlugin
    {
        public Result Run(RhinoDoc doc, RunMode mode)
        {
            // TODO: start here modifying the behaviour of your command.
            // ---          
            Point3d pt0;
            using (GetPoint getPointAction = new GetPoint())
            {
                getPointAction.SetCommandPrompt
                ("Please select the start point for a line: 1");
                if (getPointAction.Get() != GetResult.Point)
                {
                    RhinoApp.WriteLine("No start point was selected.");
                    return getPointAction.CommandResult();
                }
                pt0 = getPointAction.Point();
            }

            Point3d pt1;
            using (GetPoint getPointAction = new GetPoint())
            {
                getPointAction.SetCommandPrompt
                ("Please select the end point");
                getPointAction.SetBasePoint(pt0, true);
                getPointAction.DynamicDraw +=
                  (sender, e) => e.Display.DrawLine
                  (pt0, e.CurrentPoint, System.Drawing.Color.DarkRed);
                if (getPointAction.Get() != GetResult.Point)
                {
                    RhinoApp.WriteLine("No end point was selected.");
                    return getPointAction.CommandResult();
                }
                pt1 = getPointAction.Point();
            }
            pt0 = null;
            doc.Objects.AddLine(pt0, pt1);
            doc.Views.Redraw();                       

            return Result.Success;
        }
    }
}

MEF 中一个奇怪的地方是,我们的类名无关紧要,但它必须派生自 IPlugin。我们从不直接使用 CodeEjecutor 类,只通过接口使用。

合同接口

为了连接这两个项目,我们需要一个管道,在 MEF 的术语中称为 Contract。导入和导出的部分需要通过此合同进行通信。合同可以是一个接口或一个预定义的数据类型,如 string

创建一个新项目,选择 Class Library 模板,将项目名称设置为:RhinoPluginContract

using Rhino; //remember add a reference to: rhinocommon.dll with "Copy Local" = False
using Rhino.Commands;

namespace RhinoPluginContractInterface
{
    public interface IPlugin
    {
        Result Run(Rhino.RhinoDoc doc, Rhino.Commands.RunMode mode);
    }

}

设置如上表所示的引用,并构建解决方案 [F6]。

测试所有

现在我们有了这三个项目,是时候运行所有项目了

安装 Rhp 文件后,从 Rhino 命令中输入:LoadPlugin [ENTER]
要运行的插件文件名:c:\ all the path\RhinoPluginMakeLine.dll [ENTER]

提示:在处理复杂插件时,为了避免输入 DLL 名称,请取消注释 RunCommand 行。

     //Uncomment to load a default dll name //
     plugin_name = @"D:\the full path\RhinoPluginMakeLine.dll";

关注点

由于 Rhinoceros 始终处于打开状态,编译和尝试非常快,缩短为

  1. [F6] 在 Visual Studio 中构建解决方案。
  2. 点击 Rhino。
  3. [ENTER] 最后一个命令将被重新加载。
  4. 看看会发生什么……,修改代码……,[F6] 然后重新开始。

如果您丢失了正常的调试方式,那么您有两种选择

Debug->Attach to a running process (在 VS Express 版本中不可用)。
或者您必须向 Rhino 控制台打印大量的调试消息
示例

RhinoApp.WriteLine("Is valid:{0}", myCurve.IsValid);

我希望您觉得它有用,对我来说它非常有帮助。

© . All rights reserved.