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





5.00/5 (2投票s)
通过 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 始终处于打开状态,编译和尝试非常快,缩短为
- [F6] 在 Visual Studio 中构建解决方案。
- 点击 Rhino。
- [ENTER] 最后一个命令将被重新加载。
- 看看会发生什么……,修改代码……,[F6] 然后重新开始。
如果您丢失了正常的调试方式,那么您有两种选择
Debug->Attach to a running process (在 VS Express 版本中不可用)。
或者您必须向 Rhino 控制台打印大量的调试消息
示例
RhinoApp.WriteLine("Is valid:{0}", myCurve.IsValid);
我希望您觉得它有用,对我来说它非常有帮助。