COM add-ins是我对这种开发方式的称呼,Esri的官方文档里称其为“Extending ArcObject”或者“Classic COM extensibility”,Esri所称的addins是指esriAddin加载项。esriAddin的安装包是扩展名为“.esriAddin”的压缩文件,而本文所讲述的Com add-ins的安装文件只是一个dll,可以选择是否生成.tlb(Type Library)文件以通过在ArcGis的桌面程序中加载.tlb文件实现插件的加载,不过这种加载方式有时会存在问题,具体的会在后续的“安装与卸载”博文中作出说明。
COM add-ins是一种不同于esriAddin的开发方式,该种开发方式较后者对com组件的操作更直接,而且更全面。参考官方链接。
add-ins应该称为“加载项”才对,plug in翻译成“插件”,混起来都叫“插件”。
本例说明
开发目的:在ArcMap添加一个工具条ToolBar,上添加一个按钮Command,点击按钮,弹窗 say hello。
ArcGis版本:10.1
VisualStudio版本:2010
开发环境(IDE)的搭建
开发使用的Visual Studio版本最好与安装的ArcGis版本一致,避免不必要的麻烦(VS可以多版本并存的),本实例开发ArcMap 10.1的工具条插件,适配VS2010版本。先安装VS2010,再安装与ArcGis同版本的ArcObjects SDK for .NET,然后就可以开始在VS开发插件了。
Let's code
一、创建一个解决方案
1、名为ArcGis Classic COM extensibility demo
2、模板选择ArcGis——Extending ArcObject——Class Library(ArcMap)
3、可以选择添加引用,也可以直接“Finish”,在创建解决方案之后添加。
4、创建“解决方案”后
在项目名称上右键——属性——生成,勾选“为COM互操作注册”,这样就可以在生成的时候自动注册Com组件并且生成.tlb文件用以加载插件到ArcGis。
在属性——调试——启动外部程序,添加ArcGis的路径。
一般在利用模板创建解决方案的时候以上已经自动设置。
5、默认项目里会生成一个class1.cs,可以直接删掉。后面用到的时候再添加“类库”。
二、添加一个Toolbar
1、在项目名上右键——添加——新建项,如下图,选择模板Base Toolbar,向导程序类型当然是ArcMap。
2、看一下模板里都自动添加了啥。
①这个类继承了BaseToolbar,以实现其方法、属性;
②自动生成了类的特性,GUID以标识该类,ProgId也用于标识该类,不过前者具有唯一性,COM组件注册时将GUID写入注册表。
[Guid("deb23831-5b2e-453c-b900-f12462b000fb")] [ClassInterface(ClassInterfaceType.None)] [ProgId("ArcGis_Classic_COM_extensibility_demo.ArcGISToolbar1")]
③从模板创建toolbar类时还创建了一个构造函数,重写了两个方法(属性)。
在构造函数中,
使用基类提供的AddItem方法将Command、Menu Command等添加到工具条(Toolbar),该方法提供了6个重载,可以根据需要选用,一般使用AddItem("ProgId")即可;
使用基类提供的BeginGroup方法创建一个分隔器,其在工具条上表现为分隔开不同类别功能(自己定义)的竖线。
在重写的两个方法中,
Caption返回string类型的标题,用以显示Toolbar的标题,这里我们命为"My First Toolbar",Name则是程序内部的标识。
public ArcGISToolbar1() { //构造函数,添加Command、Menu Command等至此,加载到工具条(Toolbar) //AddItem("esriArcMapUI.ZoomInTool"); //BeginGroup(); //Separator //AddItem("{FBF8C3FB-0480-11D2-8D21-080009EE4E51}", 1); //AddItem(new Guid("FBF8C3FB-0480-11D2-8D21-080009EE4E51"), 2); } public override string Caption { get { // bar caption return "My First Toolbar"; } } public override string Name { get { // bar ID return "ArcGISToolbar1"; } }
④最后看一下COM组件注册函数,没兴趣可以跳过。
ArcGISCategoryRegistration与ArcGISCategoryUnregistration两个方法在COM组件注册或者反注册时分别会被RegisterFunction、UnregisterFunction两个方法调用。
#region COM Registration Function(s)
[ComRegisterFunction()]
[ComVisible(false)]
static void RegisterFunction(Type registerType)
{
ArcGISCategoryRegistration(registerType);
}
[ComUnregisterFunction()]
[ComVisible(false)]
static void UnregisterFunction(Type registerType)
{
ArcGISCategoryUnregistration(registerType);
}
#region ArcGIS Component Category Registrar generated code
private static void ArcGISCategoryRegistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID);
MxCommandBars.Register(regKey);
}
private static void ArcGISCategoryUnregistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID);
MxCommandBars.Unregister(regKey);
}
#endregion
#endregion
RegisterFunction、UnregisterFunction两个方法是COM注册与反注册方法,执行COM组件注册时调用。
ArcGISCategoryRegistration与ArcGISCategoryUnregistration两个方法分别调用MxCommandBars类提供的Register与Unregister方法实现注册与反注册。
而MxCommandBars等的组件注册的类由ESRI.ArcGIS.ADF.CATIDs这个命名空间提供,该命名空间在ESRI.ArcGIS.ADF.dll中,可以在C:Program Files (x86)ArcGISDeveloperKit10.1DotNetESRI.ArcGIS.ADF.dll找到该dll,具体路径可能因ArcObjects SDK for .NET安装位置与系统位数而异。
自10.0开始ESri改变了以往的把COM组件类别部分注册信息写进注册表的注册方法,采用了自家的ESRIRegAsm.exe对dll进行注册,将部分信息写入注册表,搭配一个xml文档保存注册信息。
对注册表写入的内容与位置可以通过插件注册前后的注册表快照分析得出。
XML配置文档的被包裹在一个.ecfg格式的文档中,而这个.ecfg文档实质是一个“匿名的”压缩文件,嗯,又是“压缩文件”,看来Esri的大神喜欢把东西塞进压缩文件以创造新格式……
.ecfg文档在~:Program FilesCommon FilesArcGISDesktop10.0ConfigurationCATID路径下
using System;
namespace ESRI.ArcGIS.ADF.CATIDs
{
/// <summary>Registers or unregisters a class to the MxCommandBars component category.</summary>
public class MxCommandBars : CatReg
{
/// <summary>Registers a class to the MxCommandBars component category.</summary>
/// <param name="CLSID">The CLSID of the class to be registered.</param>
public static void Register(string CLSID)
{
CatReg.Reg(CLSID, "{B56A7C4A-83D4-11D2-A2E9-080009B6F22B}");
}
/// <summary>Unregisters a class from the MxCommandBars component category.</summary>
/// <param name="CLSID">The CLSID of the class to be unregistered.</param>
public static void Unregister(string CLSID)
{
CatReg.Unreg(CLSID, "{B56A7C4A-83D4-11D2-A2E9-080009B6F22B}");
}
}
}
三、添加一个Command
同样是添加“新建项”,这次模板选BaseCommand,向导程序类型ArcMap。
1、看一按下构造函数
上边是一堆按钮的属性设置代码,一般地可以只设置m_caption与m_toolTip,后者提供鼠标悬浮于按钮之上显示的提示文字。
try……catch……语句块里是对按钮图标的定义,如果注释掉,按钮显示m_caption的内容。
可以替换自动生成的图片为自己需要的图片,注意格式;另外,俺提供一种更为简单的方法,在资源文件中添加图标,直接使用下面method2的方式使用即可。
public Command1() { base.m_category = ""; //localizable text base.m_caption = "SayHello"; //localizable text base.m_message = ""; //localizable text base.m_toolTip = "show a messagebox"; //localizable text base.m_name = ""; //unique id, non-localizable (e.g. "MyCategory_ArcMapCommand") try { //method1: string bitmapResourceName = GetType().Name + ".bmp"; base.m_bitmap = new Bitmap(GetType(), bitmapResourceName); //method2:把图标塞进资源文件,然后直接使用 // base.m_bitmap = Properties.Resources.cmd_ExportExcel; } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap"); } }
2、创建一个WinFrom
为了贴近实际编程需求,这里创造难度,在点击按钮时弹出WinFrom,WinFrom上加一个button按钮,点击在ArcMap的Statusbar上留一行文字。
引入两个命名空间
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Framework
在窗体初始化时传入hookHeper,在点击button时获取当前宿主application,在Statusbar上写字
using System; using System.Windows.Forms; using ESRI.ArcGIS.Controls; using ESRI.ArcGIS.Framework; namespace ArcGis_Classic_COM_extensibility_demo { public partial class Form1 : Form { IHookHelper m_hookHelper; IApplication m_application; public Form1(IHookHelper hookHeper) { InitializeComponent(); m_hookHelper = new HookHelperClass(); m_hookHelper = hookHeper; } private void button1_Click(object sender, EventArgs e) { m_application= m_hookHelper.Hook as IApplication; m_application.StatusBar.set_Message(0,"I am good,I am great,I am wonderful."); } } }
3、回到Command1
重写OnCreate方法,在command按钮初始化的时候实例化IHookHelper类型。
重写OnClick方法,在点击的时候实例化Form1,并弹窗。
IHookHelper m_hookHelper; public override void OnCreate(object hook) { if (hook == null) return; m_hookHelper = new HookHelperClass(); m_hookHelper.Hook = hook; } public override void OnClick() { //Form实例化时传入IHookHelper 参数 Form1 f = new Form1(m_hookHelper); f.Show(); }
④ 去ToolBar1添加按钮ProgID
AddItem("ArcGis_Classic_COM_extensibility_demo.Command1");
四、生成一下
在ArcMap就可以看到效果了。
插件咋加载到ArcMap的,还有什么其他方法加载,怎么卸载?戳 ArcGis Classic COM Add-Ins插件dll的安装与卸载