ESRI's ArcGIS Desktop product is comprised of a number of applications. These include ArcCatalog, ArcMap, ArcScene and ArcGlobe. All of which can be heavily customized by developers using any COM or .NET language.
For developers using a COM compatible language like C++ or VB6 deployment is fairly easy. Deployment would consist of running regsvr32.exe against compiled DLLs on target machines.
To register a COM DLL:
regsvr32.exe /s .\MyCommand.dll
To unregister a COM DLL:
regsvr32.exe /u /s .\MyCommand.dll
For more information on Packing and deployment ArcGIS Desktop components please visit EDN.
But now most of us have embraced C# (or VB.NET) and consider VB6 a distant memory. The hardships endured using VB6 are long forgotten. However, whilst .NET development is a joy to use it can be a nightmare to deployment. In this article I will describe the dangers of creating an install (msi) using installer classes and document a robust alternative.
The EDN article How to create a custom install program has step by step instructions on how to create a Setup Project for .NET assemblies that contain custom components. This method involves the addition of an installer class in the .NET assembly to correctly register the .NET assembly for COM interop. The installer class is called by the Windows Installer application (at install time) on target computers. Problems arise if:
- ArcGIS Desktop is not installed,
- A different version of ArcGIS Desktop is install,
- The ESRI .NET Support component is not installed.
If the Windows Installer application fails to install you may get an error message like:
"Unable to get installer types in the C:\program files\application_name\app.dll assembly. --> One or more of the types in the assembly unable to load."
This problem can be easily resolved by the user on the target computer by install one or more of the missing components listed above.
However, once installed correctly a user may experience problems uninstalling the custom component if one or more of the dependencies is missing. This may arise if the user has uninstalled ArcGIS Desktop or upgraded (or downgraded) to another version. The only workaround is for the user to restore the same environment as during install time.
Below are XXX easy steps to create a Setup Project for a .NET assembly that is independent of ArcGIS Desktop environment, that is, easy on and easy off.
Step 1: Create a New Project
Start Microsoft Visual Studio 2005 and select File > New > Project...
In the New Project dialog, select Class Library (ArcMap) and enter the name of the project (and assembly). In the example, the name of the project is DeveloperSample. Click OK.
Next, the ArcGIS Project Wizard will appear. You can use this dialog to select ESRI references that may be used in your project. Click Finished to accept the default settings.
The template document automatically created a class called Class1. Select the class in the Solution Explorer and delete it (right click and select Delete).
Step 2: Add an ArcMap Command
Select the newly created project in the Solution Explorer and from the Project dropdown menu select Add New Item....
Select the Base Command item in the ArcGIS category. Enter the name of the new item and click Add. In this example, the name of the new command class is SampleCommand.cs.
Next, the ArcGIS New Item Wizard Option will appear prompting for the type of command that you would like to create. Select Desktop ArcMap Command and then click OK.
Step 3: Edit the Command Class
Your solution should now include a new class, command bitmap and new references to the ESRI assemblies.
Double click on the command class, SampleCommand.cs, to open it in the Visual Studio code editor. Your class should look very similar to the code displayed below. I have made a few minor edits for clarify and edited values of the protected properties like base.m_category. Also, in the overloaded method OnClick I have added a "Hello World" message box. You can do the same in your code, but don't forget to add a reference to the "System.Windows.Forms" assembly in your project.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.ArcMapUI;
namespace DeveloperSample {
[Guid("5C0CC07D-A04F-4FE3-BF8E-16645DCBDC1D")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("DeveloperSample.SampleCommand")]
public sealed class SampleCommand : BaseCommand {
[ComRegisterFunction()]
[ComVisible(false)]
private static void RegisterFunction(Type registerType) {
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
MxCommands.Register(regKey);
}
[ComUnregisterFunction()]
[ComVisible(false)]
private static void UnregisterFunction(Type registerType) {
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
MxCommands.Unregister(regKey);
}
private IApplication m_application = null;
public SampleCommand() {
base.m_category = "DeveloperSample";
base.m_caption = "Sample";
base.m_message = "Sample";
base.m_toolTip = "Sample";
base.m_name = "DeveloperSample_SampleCommand";
try {
string bitmapResourceName = GetType().Name + ".bmp";
base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
}
catch (Exception ex) {
System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
}
}
public override void OnCreate(object hook) {
if (hook == null) {
return;
}
this.m_application = hook as IApplication;
base.m_enabled = (hook is IMxApplication);
}
public override void OnClick() {
MessageBox.Show("Hello World");
}
}
}
Step 4: Test the Command in ArcMap
Start ArcMap from the Windows Start menu (Start > All Programs > ArcGIS > ArcMap). Display the Customize dialog by clicking Tools > Customize...
When the Customize dialog appear click the Commands tab and click on the category name (base.m_category) used by you command. When clicked, all commands that use that category will be listed in the box on the right hand side. Click on the command that you created above, the name of the command comes from the base.m_caption value.
Drag the command from the Customize dialog to the any existing toolbar. Click close on the Customize dialog.
Click the command to display the "Hello World" message box.
Step 5: Export Interop Registry Information from the Assembly.
The ArcGIS Desktop product was first release in 2000. The desktop applications are written in C++ and reference a large collection of ESRI developed COM components called ArcObjects.
Because ArcMap only recognizes COM components we need to make our .NET assembly look like COM DLL with COM classes. This is easier than it sounds!
Start Windows Explorer and navigate to your project folder. In my case the project folder is C:\TEST\DeveloperSample. Inside this folder you should see your Visual Studio Project (.csprj) and Solution (.sln).
From the Window Explorer main menu select File > New > Text Document. This will create a new empty text document in your project folder. Right click on the text file and select Rename. Type "MAKE.BAT" and then press ENTER. You may be prompted to confirm the renaming, if so click Yes.
Open the BAT file using a text editor like Microsoft Notepad and enter the following text as a single line.
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe C:\TEST\DeveloperSample\bin\Debug\DeveloperSample.dll /codebase /regfile:DeveloperSample.reg
Where:
- C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe
is the full path name to the REGASM ultility in the .NET Framework
- C:\TEST\DeveloperSample\bin\Debug\DeveloperSample.dll
is the full path name to your .NET assembly
- DeveloperSample.reg
is the name of the output registry file that will be created by REGASM
Save the changes to the MAKE.BAT in the text editor. Close the text editor.
Return to the Windows Explorer, double click on MAKE.BAT.
Running the BAT file will launch a DOS command window as shown below. Briefly you will see messages from the REGASM command displayed. When the REGASM command is complete the DOS command window will disappear.
In the project folder you will see a new file called DeveloperSample.reg.
Step 6: Edit the Interop Registry Information
Open the registry file DeveloperSample.reg in a text editor like Microsoft Notepad. You will see hard-coded references to the location of the .NET assembly. We need to change this pathname so that it reflects the pathname of the installed assembly on target computers.
Search and replace the text in the text editor so that the codebase entry reads:
"CodeBase"="[TARGETDIR]DeveloperSample.dll"
The [TARGETDIR] flag will be substituted by the Windows Installer application at install time for the correct pathname.
The registry information created by REGASM exposes the .NET class to COM. That is, to an application like ArcMap, your command looks, tastes and feels like a COM class.
Before we are done we need to add one more piece of information. When ArcMap starts up it scans through the registry looking for COM classes that are commands. The registry contains literally thousands of COM classes but only a new few hundred are ArcMap commands. To identify our class SampleCommand as an ArcMap command we need to add a category component to the registry.
The category component for an ArcMap command is:
{B56A7C42-83D4-11D2-A2E9-080009B6F22B}
At the end of the registry file, DeveloperSample.reg, add the following line:
[HKEY_CLASSES_ROOT\CLSID\\Implemented Categories\{B56A7C42-83D4-11D2-A2E9-080009B6F22B}]

(Microsoft Notepad with Word Wrap enabled)
Save changes in the text editor and then close the application.
In future, you may develop other types of ArcGIS application customizations. Here are a few commonly used category component guids:
[ComVisible(false)]
public sealed class EsriRegistry {
// General
public const string COMPONENT_LAYER_PROPERTY_PAGE = "{1476C782-6F57-11D2-A2C6-080009B6F22B}";
public const string COMPONENT_LAYER_FACTORY = "{34C20001-4D3C-11D0-92D8-00805F7C28B0}";
public const string COMPONENT_RENDERER_PROPERTY_PAGE = "{962BD9A9-1EC8-11D3-9F4D-00C04F6BC709}";
// ArcMap
public const string COMPONENT_MX_COMMAND = "{B56A7C42-83D4-11D2-A2E9-080009B6F22B}";
public const string COMPONENT_MX_TOOLBAR = "{B56A7C4A-83D4-11D2-A2E9-080009B6F22B}";
public const string COMPONENT_MX_DOCKABLEWINDOW = &quo