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)\ArcGIS\DeveloperKit10.1\DotNet\ESRI.ArcGIS.ADF.dll找到該dll,具體路徑可能因ArcObjects SDK for .NET安裝位置與系統位數而異。
反編譯一下dll,跟蹤一下就可以看看注冊的時候發生啥了?
看看注冊表你會驚奇地發現插件注冊時並沒有按照ESRI.ArcGIS.ADF.CATIDs.CatReg的方法干活兒。自10.0開始ESri改變了以往的把COM組件類別部分注冊信息寫進注冊表的注冊方法,采用了自家的ESRIRegAsm.exe對dll進行注冊,將部分信息寫入注冊表,搭配一個xml文檔保存注冊信息。
對注冊表寫入的內容與位置可以通過插件注冊前后的注冊表快照分析得出。
XML配置文檔的被包裹在一個.ecfg格式的文檔中,而這個.ecfg文檔實質是一個“匿名的”壓縮文件,嗯,又是“壓縮文件”,看來Esri的大神喜歡把東西塞進壓縮文件以創造新格式……
.ecfg文檔在~:\Program Files\Common Files\ArcGIS\Desktop10.0\Configuration\CATID路徑下
至於ESRIRegAsm.exe對dll進行注冊時干了什么樣神奇的操作,不得而知,俺沒有找到相關支持材料。
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的安裝與卸載